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

Reply via email to