On Sun, Dec 31, 2017 at 01:12:42PM +0100, Mark Kettenis wrote: > > Date: Sun, 31 Dec 2017 13:46:44 +0200 > > From: Artturi Alm <artturi....@gmail.com> > > > > On Sat, Dec 30, 2017 at 08:39:42PM +0200, Artturi Alm wrote: > > > Hi, > > > > > > porting sdhc driver(bcm2835_emmc.c) from NetBSD does lack something > > > like this, among possibly few other (small?) changes, but consider this > > > as a step towards. > > > > > > Comments? to keep changes minimal/safe/easy to review like below, or to > > > backport in bigger chunks? > > > > > > -Artturi > > > > > > > Hi, > > > > while working out the second step i came to a conclusion, > > that the diff should work in theory like below, > > and be easier to review w/o the use of SHF_USE_DMA flag. > > > > any comments? > > Is the BCM2835 hardware really a standard SDHC controller? If there > are too many quirks, I tend to prefer a seperate driver instead of a > bit of SoC-specific glue for shdc(4). > > Otherwise this mostly makes sense to me as infrastructure to use an > external DMA controller. I don't think we should at it unless there > is an actual user though. > > Cheers, > > Mark >
not counting non-ADMA2 DMA support, to me it seems to be. + ofc. the usual access width restrictions, which the diff below does aim to 'fix', but no register-offset quirks needed or anything ugly like for freescale e/usdhc. bcm2835_dmac.c was rather mechanical to port, so the user for ext dma does exist already, just unpolished/tested (and lacking dev/ofw/ofw_dmac.h, for standard bindings:). spent some extra effort to keep this a +++ diff :) -Artturi sys/dev/sdmmc/sdhc.c | 256 ++++++++++++++++++++++++++++++++++++++++ sys/dev/sdmmc/sdhcvar.h | 1 + 2 files changed, 257 insertions(+) diff --git a/sys/dev/sdmmc/sdhc.c b/sys/dev/sdmmc/sdhc.c index c242a600d4a..b8365581bef 100644 --- a/sys/dev/sdmmc/sdhc.c +++ b/sys/dev/sdmmc/sdhc.c @@ -61,6 +61,33 @@ struct sdhc_host { /* flag values */ #define SHF_USE_DMA 0x0001 +#ifdef SDHC_VARIABLE_BSACW +static inline uint8_t hread1(struct sdhc_host *, bus_size_t); +static inline uint16_t hread2(struct sdhc_host *, bus_size_t); +static inline void hwrite1(struct sdhc_host *, bus_size_t, uint8_t); +static inline void hwrite2(struct sdhc_host *, bus_size_t, uint16_t); + +#define HREAD1(hp, reg) hread1(hp, reg) +#define HREAD2(hp, reg) hread2(hp, reg) +#define HREAD4(hp, reg) \ + (bus_space_read_4((hp)->iot, (hp)->ioh, (reg))) +#define HWRITE1(hp, reg, val) hwrite1(hp, reg, val) +#define HWRITE2(hp, reg, val) hwrite2(hp, reg, val) +#define HWRITE4(hp, reg, val) \ + bus_space_write_4((hp)->iot, (hp)->ioh, (reg), (val)) +#define HCLR1(hp, reg, b) \ + do if (b) HWRITE1((hp), (reg), HREAD1((hp), (reg)) & ~(b)); while (0) +#define HCLR2(hp, reg, b) \ + do if (b) HWRITE2((hp), (reg), HREAD2((hp), (reg)) & ~(b)); while (0) +#define HCLR4(hp, reg, b) \ + do if (b) HWRITE4((hp), (reg), HREAD4((hp), (reg)) & ~(b)); while (0) +#define HSET1(hp, reg, b) \ + do if (b) HWRITE1((hp), (reg), HREAD1((hp), (reg)) | (b)); while (0) +#define HSET2(hp, reg, b) \ + do if (b) HWRITE2((hp), (reg), HREAD2((hp), (reg)) | (b)); while (0) +#define HSET4(hp, reg, b) \ + do if (b) HWRITE4((hp), (reg), HREAD4((hp), (reg)) | (b)); while (0) +#else #define HREAD1(hp, reg) \ (bus_space_read_1((hp)->iot, (hp)->ioh, (reg))) #define HREAD2(hp, reg) \ @@ -77,10 +104,15 @@ struct sdhc_host { HWRITE1((hp), (reg), HREAD1((hp), (reg)) & ~(bits)) #define HCLR2(hp, reg, bits) \ HWRITE2((hp), (reg), HREAD2((hp), (reg)) & ~(bits)) +#define HCLR4(hp, reg, bits) \ + HWRITE4((hp), (reg), HREAD4((hp), (reg)) & ~(bits)) #define HSET1(hp, reg, bits) \ HWRITE1((hp), (reg), HREAD1((hp), (reg)) | (bits)) #define HSET2(hp, reg, bits) \ HWRITE2((hp), (reg), HREAD2((hp), (reg)) | (bits)) +#define HSET4(hp, reg, bits) \ + HWRITE4((hp), (reg), HREAD4((hp), (reg)) | (bits)) +#endif /* !SDHC_VARIABLE_BSACW */ int sdhc_host_reset(sdmmc_chipset_handle_t); u_int32_t sdhc_host_ocr(sdmmc_chipset_handle_t); @@ -350,6 +382,19 @@ sdhc_activate(struct device *self, int act) /* Save the host controller state. */ for (n = 0; n < sc->sc_nhosts; n++) { hp = sc->sc_host[n]; +#ifdef SDHC_VARIABLE_BSACW + if (ISSET(sc->sc_flags, SDHC_F_32BIT_ACCESS)) { + for (i = 0; i < sizeof hp->regs; i += 4) { + uint32_t v = HREAD4(hp, i); + hp->regs[i + 0] = (v >> 0); + hp->regs[i + 1] = (v >> 8); + if (i + 3 < sizeof hp->regs) { + hp->regs[i + 2] = (v >> 16); + hp->regs[i + 3] = (v >> 24); + } + } + } else +#endif for (i = 0; i < sizeof hp->regs; i++) hp->regs[i] = HREAD1(hp, i); } @@ -359,6 +404,23 @@ sdhc_activate(struct device *self, int act) for (n = 0; n < sc->sc_nhosts; n++) { hp = sc->sc_host[n]; (void)sdhc_host_reset(hp); +#ifdef SDHC_VARIABLE_BSACW + if (ISSET(sc->sc_flags, SDHC_F_32BIT_ACCESS)) { + for (i = 0; i < sizeof hp->regs; i += 4) { + if (i + 3 < sizeof hp->regs) { + HWRITE4(hp, i, + (hp->regs[i + 0] << 0) + | (hp->regs[i + 1] << 8) + | (hp->regs[i + 2] << 16) + | (hp->regs[i + 3] << 24)); + } else { + HWRITE4(hp, i, + (hp->regs[i + 0] << 0) + | (hp->regs[i + 1] << 8)); + } + } + } else +#endif for (i = 0; i < sizeof hp->regs; i++) HWRITE1(hp, i, hp->regs[i]); } @@ -427,6 +489,17 @@ sdhc_host_reset(sdmmc_chipset_handle_t sch) SDHC_DMA_INTERRUPT | SDHC_BLOCK_GAP_EVENT | SDHC_TRANSFER_COMPLETE | SDHC_COMMAND_COMPLETE; +#ifdef SDHC_VARIABLE_BSACW + if (ISSET(hp->sc->sc_flags, SDHC_F_32BIT_ACCESS)) { + imask |= SDHC_EINTR_STATUS_MASK << 16; + HWRITE4(hp, SDHC_NINTR_STATUS_EN, imask); + imask ^=(SDHC_EINTR_STATUS_MASK ^ SDHC_EINTR_SIGNAL_MASK) <<16; + imask ^= SDHC_BUFFER_READ_READY ^ SDHC_BUFFER_WRITE_READY; + HWRITE4(hp, SDHC_NINTR_SIGNAL_EN, imask); + splx(s); + return 0; + } +#endif HWRITE2(hp, SDHC_NINTR_STATUS_EN, imask); HWRITE2(hp, SDHC_EINTR_STATUS_EN, SDHC_EINTR_STATUS_MASK); HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, imask); @@ -482,6 +555,9 @@ sdhc_bus_power(sdmmc_chipset_handle_t sch, u_int32_t ocr) * Disable bus power before voltage change. */ if (!(hp->sc->sc_flags & SDHC_F_NOPWR0)) +#ifdef SDHC_VARIABLE_BSACW + if (!ISSET(hp->sc->sc_flags, SDHC_F_32BIT_ACCESS)) +#endif HWRITE1(hp, SDHC_POWER_CTL, 0); /* If power is disabled, reset the host and return now. */ @@ -511,6 +587,17 @@ sdhc_bus_power(sdmmc_chipset_handle_t sch, u_int32_t ocr) * Enable bus power. Wait at least 1 ms (or 74 clocks) plus * voltage ramp until power rises. */ +#ifdef SDHC_VARIABLE_BSACW + /* XXX this is default on netbsd. only tegra_sdhc does the else */ + if (ISSET(hp->sc->sc_flags, SDHC_F_PWRMULTISTEP)) { + HCLR1(hp, SDHC_POWER_CTL, SDHC_BUS_POWER | + (SDHC_VOLTAGE_MASK << SDHC_VOLTAGE_SHIFT)); + sdmmc_delay(1); + HWRITE1(hp, SDHC_POWER_CTL, (vdd << SDHC_VOLTAGE_SHIFT)); + sdmmc_delay(1); + HSET1(hp, SDHC_POWER_CTL, SDHC_BUS_POWER); + } else +#endif HWRITE1(hp, SDHC_POWER_CTL, (vdd << SDHC_VOLTAGE_SHIFT) | SDHC_BUS_POWER); sdmmc_delay(10000); @@ -766,6 +853,15 @@ sdhc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) * driver (without padding). */ if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { +#ifdef SDHC_VARIABLE_BSACW + if (ISSET(hp->sc->sc_flags, SDHC_F_32BIT_ACCESS) && + ISSET(cmd->c_flags, SCF_RSP_136)) { + cmd->c_resp[0] = HREAD4(hp, SDHC_RESPONSE); + cmd->c_resp[1] = HREAD4(hp, SDHC_RESPONSE + 4); + cmd->c_resp[2] = HREAD4(hp, SDHC_RESPONSE + 8); + cmd->c_resp[3] = HREAD4(hp, SDHC_RESPONSE + 12); + } else +#endif if (ISSET(cmd->c_flags, SCF_RSP_136)) { u_char *p = (u_char *)cmd->c_resp; int i; @@ -917,6 +1013,15 @@ sdhc_start_command(struct sdhc_host *hp, struct sdmmc_command *cmd) * Start a CPU data transfer. Writing to the high order byte * of the SDHC_COMMAND register triggers the SD command. (1.5) */ +#ifdef SDHC_VARIABLE_BSACW + if (ISSET(hp->sc->sc_flags, SDHC_F_32BIT_ACCESS)) { + HWRITE4(hp, SDHC_BLOCK_SIZE, blksize | (blkcount << 16)); + HWRITE4(hp, SDHC_ARGUMENT, cmd->c_arg); + HWRITE4(hp, SDHC_TRANSFER_MODE, mode | (command << 16)); + splx(s); + return 0; + } +#endif HWRITE2(hp, SDHC_TRANSFER_MODE, mode); HWRITE2(hp, SDHC_BLOCK_SIZE, blksize); HWRITE2(hp, SDHC_BLOCK_COUNT, blkcount); @@ -1009,6 +1114,45 @@ done: void sdhc_read_data(struct sdhc_host *hp, u_char *datap, int datalen) { +#ifdef SDHC_VARIABLE_BSACW + if (ISSET(hp->sc->sc_flags, SDHC_F_32BIT_ACCESS)) { + if (((uintptr_t)datap & 3) == 0) { + while (datalen > 3) { + *(uint32_t *)datap = le32toh(HREAD4(hp, SDHC_DATA)); + datap += 4; + datalen -= 4; + } + if (datalen > 1) { + *(uint16_t *)datap = le16toh(HREAD2(hp, SDHC_DATA)); + datap += 2; + datalen -= 2; + } + if (datalen > 0) { + *datap = HREAD1(hp, SDHC_DATA); + datap += 1; + datalen -= 1; + } + } else if (((uintptr_t)datap & 1) == 0) { + while (datalen > 1) { + *(uint16_t *)datap = le16toh(HREAD2(hp, SDHC_DATA)); + datap += 2; + datalen -= 2; + } + if (datalen > 0) { + *datap = HREAD1(hp, SDHC_DATA); + datap += 1; + datalen -= 1; + } + } else { + while (datalen > 0) { + *datap = HREAD1(hp, SDHC_DATA); + datap += 1; + datalen -= 1; + } + } + return; + } +#endif while (datalen > 3) { *(u_int32_t *)datap = HREAD4(hp, SDHC_DATA); datap += 4; @@ -1026,6 +1170,44 @@ sdhc_read_data(struct sdhc_host *hp, u_char *datap, int datalen) void sdhc_write_data(struct sdhc_host *hp, u_char *datap, int datalen) { +#ifdef SDHC_VARIABLE_BSACW + if (ISSET(hp->sc->sc_flags, SDHC_F_32BIT_ACCESS)) { + if (((uintptr_t)data & 3) == 0) { + while (datalen > 3) { + HWRITE4(hp, SDHC_DATA, htole32(*(uint32_t *)data)); + data += 4; + datalen -= 4; + } + if (datalen > 1) { + HWRITE2(hp, SDHC_DATA, htole16(*(uint16_t *)data)); + data += 2; + datalen -= 2; + } + if (datalen > 0) { + HWRITE1(hp, SDHC_DATA, *data); + data += 1; + datalen -= 1; + } + } else if (((uintptr_t)data & 1) == 0) { + while (datalen > 1) { + HWRITE2(hp, SDHC_DATA, htole16(*(uint16_t *)data)); + data += 2; + datalen -= 2; + } + if (datalen > 0) { + HWRITE1(hp, SDHC_DATA, *data); + data += 1; + datalen -= 1; + } + } else { + while (datalen > 0) { + HWRITE1(hp, SDHC_DATA, *data); + data += 1; + datalen -= 1; + } + } + } +#endif while (datalen > 3) { DPRINTF(3,("%08x\n", *(u_int32_t *)datap)); HWRITE4(hp, SDHC_DATA, *((u_int32_t *)datap)); @@ -1121,11 +1303,30 @@ sdhc_intr(void *arg) continue; /* Find out which interrupts are pending. */ +#ifdef SDHC_VARIABLE_BSACW + uint32_t xstatus; + if (ISSET(sc->sc_flags, SDHC_F_32BIT_ACCESS)) { + /* Find out which interrupts are pending. */ + xstatus = HREAD4(hp, SDHC_NINTR_STATUS); + u_int16_t error = xstatus >> 16; + + if (xstatus >> 16) + xstatus |= SDHC_ERROR_INTERRUPT; + /* Acknowledge the interrupts we are about to handle. */ + if (ISSET(xstatus, SDHC_NINTR_STATUS_MASK)) + HWRITE4(hp, SDHC_NINTR_STATUS, xstatus); + status = xstatus; + + } else +#endif status = HREAD2(hp, SDHC_NINTR_STATUS); if (!ISSET(status, SDHC_NINTR_STATUS_MASK)) continue; /* no interrupt for us */ /* Acknowledge the interrupts we are about to handle. */ +#ifdef SDHC_VARIABLE_BSACW /* already ack'ed above */ + if (!ISSET(sc->sc_flags, SDHC_F_32BIT_ACCESS)) +#endif HWRITE2(hp, SDHC_NINTR_STATUS, status); DPRINTF(2,("%s: interrupt status=%b\n", DEVNAME(hp->sc), status, SDHC_NINTR_STATUS_BITS)); @@ -1140,7 +1341,15 @@ sdhc_intr(void *arg) u_int16_t error; /* Acknowledge error interrupts. */ +#ifdef SDHC_VARIABLE_BSACW + if (ISSET(sc->sc_flags, SDHC_F_32BIT_ACCESS)) + error = xstatus >> 16; + else +#endif error = HREAD2(hp, SDHC_EINTR_STATUS); +#ifdef SDHC_VARIABLE_BSACW /* already ack'ed above */ + if (!ISSET(sc->sc_flags, SDHC_F_32BIT_ACCESS)) +#endif HWRITE2(hp, SDHC_EINTR_STATUS, error); DPRINTF(2,("%s: error interrupt, status=%b\n", DEVNAME(hp->sc), error, SDHC_EINTR_STATUS_BITS)); @@ -1191,6 +1400,53 @@ sdhc_needs_discover(struct sdhc_softc *sc) sdmmc_needs_discover(sc->sc_host[host]->sdmmc); } +#ifdef SDHC_VARIABLE_BSACW +/* inlines used for variable bus_space "access width"-support */ +uint8_t +hread1(struct sdhc_host *hp, bus_size_t reg) +{ + if (!ISSET(hp->sc->sc_flags, SDHC_F_32BIT_ACCESS)) + return bus_space_read_1(hp->iot, hp->ioh, reg); + return bus_space_read_4(hp->iot, hp->ioh, reg & -4) >> (8 * (reg & 3)); +} + +uint16_t +hread2(struct sdhc_host *hp, bus_size_t reg) +{ + if (!ISSET(hp->sc->sc_flags, SDHC_F_32BIT_ACCESS)) + return bus_space_read_2(hp->iot, hp->ioh, reg); + return bus_space_read_4(hp->iot, hp->ioh, reg & -4) >> (8 * (reg & 2)); +} + +void +hwrite1(struct sdhc_host *hp, bus_size_t o, uint8_t val) +{ + if (!ISSET(hp->sc->sc_flags, SDHC_F_32BIT_ACCESS)) { + bus_space_write_1(hp->iot, hp->ioh, o, val); + } else { + const size_t shift = 8 * (o & 3); + o &= -4; + uint32_t tmp = bus_space_read_4(hp->iot, hp->ioh, o); + tmp = (val << shift) | (tmp & ~(0xff << shift)); + bus_space_write_4(hp->iot, hp->ioh, o, tmp); + } +} + +void +hwrite2(struct sdhc_host *hp, bus_size_t o, uint16_t val) +{ + if (!ISSET(hp->sc->sc_flags, SDHC_F_32BIT_ACCESS)) { + bus_space_write_2(hp->iot, hp->ioh, o, val); + } else { + const size_t shift = 8 * (o & 2); + o &= -4; + uint32_t tmp = bus_space_read_4(hp->iot, hp->ioh, o); + tmp = (val << shift) | (tmp & ~(0xffff << shift)); + bus_space_write_4(hp->iot, hp->ioh, o, tmp); + } +} +#endif + #ifdef SDHC_DEBUG void sdhc_dump_regs(struct sdhc_host *hp) diff --git a/sys/dev/sdmmc/sdhcvar.h b/sys/dev/sdmmc/sdhcvar.h index 9d4d759a2bc..00227c835ae 100644 --- a/sys/dev/sdmmc/sdhcvar.h +++ b/sys/dev/sdmmc/sdhcvar.h @@ -47,5 +47,6 @@ void sdhc_needs_discover(struct sdhc_softc *); /* flag values */ #define SDHC_F_NOPWR0 (1 << 0) #define SDHC_F_NODDR50 (1 << 1) +#define SDHC_F_32BIT_ACCESS (1 << 2) #endif