Fri Sep 12 20:18:42 2014 UTC ()
Add and enable DMA support for SD cards on Raspberry Pi/BCM2835.


(jakllsch)
diff -r1.10 -r1.11 src/sys/arch/arm/broadcom/bcm2835_emmc.c

cvs diff -r1.10 -r1.11 src/sys/arch/arm/broadcom/bcm2835_emmc.c (expand / switch to unified diff)

--- src/sys/arch/arm/broadcom/bcm2835_emmc.c 2014/09/10 13:45:52 1.10
+++ src/sys/arch/arm/broadcom/bcm2835_emmc.c 2014/09/12 20:18:42 1.11
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: bcm2835_emmc.c,v 1.10 2014/09/10 13:45:52 jakllsch Exp $ */ 1/* $NetBSD: bcm2835_emmc.c,v 1.11 2014/09/12 20:18:42 jakllsch Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 2012 The NetBSD Foundation, Inc. 4 * Copyright (c) 2012 The NetBSD Foundation, Inc.
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to The NetBSD Foundation 7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Nick Hudson 8 * by Nick Hudson
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
@@ -20,123 +20,286 @@ @@ -20,123 +20,286 @@
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE. 29 * POSSIBILITY OF SUCH DAMAGE.
30 */ 30 */
31 31
32#include <sys/cdefs.h> 32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: bcm2835_emmc.c,v 1.10 2014/09/10 13:45:52 jakllsch Exp $"); 33__KERNEL_RCSID(0, "$NetBSD: bcm2835_emmc.c,v 1.11 2014/09/12 20:18:42 jakllsch Exp $");
34 34
35#include <sys/param.h> 35#include <sys/param.h>
36#include <sys/systm.h> 36#include <sys/systm.h>
37#include <sys/device.h> 37#include <sys/device.h>
38#include <sys/bus.h> 38#include <sys/bus.h>
 39#include <sys/condvar.h>
 40#include <sys/mutex.h>
39 41
40#include <arm/broadcom/bcm2835reg.h> 42#include <arm/broadcom/bcm2835reg.h>
41#include <arm/broadcom/bcm_amba.h> 43#include <arm/broadcom/bcm_amba.h>
 44#include <arm/broadcom/bcm2835_dmac.h>
42 45
43#include <dev/sdmmc/sdhcreg.h> 46#include <dev/sdmmc/sdhcreg.h>
44#include <dev/sdmmc/sdhcvar.h> 47#include <dev/sdmmc/sdhcvar.h>
45#include <dev/sdmmc/sdmmcvar.h> 48#include <dev/sdmmc/sdmmcvar.h>
46 49
 50enum bcmemmc_dma_state {
 51 EMMC_DMA_STATE_IDLE = 27,
 52 EMMC_DMA_STATE_BUSY = 42,
 53};
 54
47struct bcmemmc_softc { 55struct bcmemmc_softc {
48 struct sdhc_softc sc; 56 struct sdhc_softc sc;
49 57
50 bus_space_tag_t sc_iot; 58 bus_space_tag_t sc_iot;
51 bus_space_handle_t sc_ioh; 59 bus_space_handle_t sc_ioh;
 60 bus_size_t sc_ios;
52 struct sdhc_host *sc_hosts[1]; 61 struct sdhc_host *sc_hosts[1];
53 void *sc_ih; 62 void *sc_ih;
 63
 64 kmutex_t sc_lock;
 65 kcondvar_t sc_cv;
 66
 67 enum bcmemmc_dma_state sc_state;
 68
 69 struct bcm_dmac_channel *sc_dmac;
 70
 71 bus_dmamap_t sc_dmamap;
 72 bus_dma_segment_t sc_segs[1]; /* XXX assumes enough descriptors fit in one page */
 73 struct bcm_dmac_conblk *sc_cblk;
 74
 75 uint32_t sc_physaddr;
54}; 76};
55 77
56static int bcmemmc_match(device_t, struct cfdata *, void *); 78static int bcmemmc_match(device_t, struct cfdata *, void *);
57static void bcmemmc_attach(device_t, device_t, void *); 79static void bcmemmc_attach(device_t, device_t, void *);
 80static void bcmemmc_attach_i(device_t);
 81static int bcmemmc_xfer_data_dma(struct sdhc_host *, struct sdmmc_command *);
 82static void bcmemmc_dma_done(void *);
58 83
59CFATTACH_DECL_NEW(bcmemmc, sizeof(struct bcmemmc_softc), 84CFATTACH_DECL_NEW(bcmemmc, sizeof(struct bcmemmc_softc),
60 bcmemmc_match, bcmemmc_attach, NULL, NULL); 85 bcmemmc_match, bcmemmc_attach, NULL, NULL);
61 86
62/* ARGSUSED */ 87/* ARGSUSED */
63static int 88static int
64bcmemmc_match(device_t parent, struct cfdata *match, void *aux) 89bcmemmc_match(device_t parent, struct cfdata *match, void *aux)
65{ 90{
66 struct amba_attach_args *aaa = aux; 91 struct amba_attach_args *aaa = aux;
67 92
68 if (strcmp(aaa->aaa_name, "emmc") != 0) 93 if (strcmp(aaa->aaa_name, "emmc") != 0)
69 return 0; 94 return 0;
70 95
71 return 1; 96 return 1;
72} 97}
73 98
74/* ARGSUSED */ 99/* ARGSUSED */
75static void 100static void
76bcmemmc_attach(device_t parent, device_t self, void *aux) 101bcmemmc_attach(device_t parent, device_t self, void *aux)
77{ 102{
78 struct bcmemmc_softc *sc = device_private(self); 103 struct bcmemmc_softc *sc = device_private(self);
79 prop_dictionary_t dict = device_properties(self); 104 prop_dictionary_t dict = device_properties(self);
80 struct amba_attach_args *aaa = aux; 105 struct amba_attach_args *aaa = aux;
81 prop_number_t frequency; 106 prop_number_t frequency;
82 int error; 107 int error;
 108 int rseg;
83 109
84 sc->sc.sc_dev = self; 110 sc->sc.sc_dev = self;
85 sc->sc.sc_dmat = aaa->aaa_dmat; 111 sc->sc.sc_dmat = aaa->aaa_dmat;
86 sc->sc.sc_flags = 0; 112 sc->sc.sc_flags = 0;
87 sc->sc.sc_flags |= SDHC_FLAG_32BIT_ACCESS; 113 sc->sc.sc_flags |= SDHC_FLAG_32BIT_ACCESS;
88 sc->sc.sc_flags |= SDHC_FLAG_HOSTCAPS; 114 sc->sc.sc_flags |= SDHC_FLAG_HOSTCAPS;
89 sc->sc.sc_flags |= SDHC_FLAG_NO_HS_BIT; 115 sc->sc.sc_flags |= SDHC_FLAG_NO_HS_BIT;
90 sc->sc.sc_caps = SDHC_VOLTAGE_SUPP_3_3V | SDHC_HIGH_SPEED_SUPP | 116 sc->sc.sc_caps = SDHC_VOLTAGE_SUPP_3_3V | SDHC_HIGH_SPEED_SUPP |
91 SDHC_MAX_BLK_LEN_1024; 117 SDHC_MAX_BLK_LEN_1024;
92#if notyet 
93 sc->sc.sc_flags |= SDHC_FLAG_USE_DMA; 118 sc->sc.sc_flags |= SDHC_FLAG_USE_DMA;
 119 sc->sc.sc_flags |= SDHC_FLAG_EXTERNAL_DMA;
94 sc->sc.sc_caps |= SDHC_DMA_SUPPORT; 120 sc->sc.sc_caps |= SDHC_DMA_SUPPORT;
95#endif 121
96 sc->sc.sc_host = sc->sc_hosts; 122 sc->sc.sc_host = sc->sc_hosts;
97 sc->sc.sc_clkbase = 50000; /* Default to 50MHz */ 123 sc->sc.sc_clkbase = 50000; /* Default to 50MHz */
98 sc->sc_iot = aaa->aaa_iot; 124 sc->sc_iot = aaa->aaa_iot;
 125 sc->sc.sc_vendor_transfer_data_dma = bcmemmc_xfer_data_dma;
99 126
100 /* Fetch the EMMC clock frequency from property if set. */ 127 /* Fetch the EMMC clock frequency from property if set. */
101 frequency = prop_dictionary_get(dict, "frequency"); 128 frequency = prop_dictionary_get(dict, "frequency");
102 if (frequency != NULL) { 129 if (frequency != NULL) {
103 sc->sc.sc_clkbase = prop_number_integer_value(frequency) / 1000; 130 sc->sc.sc_clkbase = prop_number_integer_value(frequency) / 1000;
104 } 131 }
105 132
106 error = bus_space_map(sc->sc_iot, aaa->aaa_addr, aaa->aaa_size, 0, 133 error = bus_space_map(sc->sc_iot, aaa->aaa_addr, aaa->aaa_size, 0,
107 &sc->sc_ioh); 134 &sc->sc_ioh);
108 if (error) { 135 if (error) {
109 aprint_error_dev(self, 136 aprint_error_dev(self,
110 "can't map registers for %s: %d\n", aaa->aaa_name, error); 137 "can't map registers for %s: %d\n", aaa->aaa_name, error);
111 return; 138 return;
112 } 139 }
 140 sc->sc_ios = aaa->aaa_size;
 141 sc->sc_physaddr = aaa->aaa_addr;
113 142
114 aprint_naive(": SDHC controller\n"); 143 aprint_naive(": SDHC controller\n");
115 aprint_normal(": SDHC controller\n"); 144 aprint_normal(": SDHC controller\n");
116 145
117 sc->sc_ih = bcm2835_intr_establish(aaa->aaa_intr, IPL_SDMMC, sdhc_intr, 146 sc->sc_ih = bcm2835_intr_establish(aaa->aaa_intr, IPL_SDMMC, sdhc_intr,
118 &sc->sc); 147 &sc->sc);
119 148
120 if (sc->sc_ih == NULL) { 149 if (sc->sc_ih == NULL) {
121 aprint_error_dev(self, "failed to establish interrupt %d\n", 150 aprint_error_dev(self, "failed to establish interrupt %d\n",
122 aaa->aaa_intr); 151 aaa->aaa_intr);
123 goto fail; 152 goto fail;
124 } 153 }
125 aprint_normal_dev(self, "interrupting on intr %d\n", aaa->aaa_intr); 154 aprint_normal_dev(self, "interrupting on intr %d\n", aaa->aaa_intr);
126 155
127 error = sdhc_host_found(&sc->sc, sc->sc_iot, sc->sc_ioh, 156 sc->sc_dmac = bcm_dmac_alloc(BCM_DMAC_TYPE_NORMAL, IPL_SDMMC,
128 aaa->aaa_size); 157 bcmemmc_dma_done, sc);
 158 if (sc->sc_dmac == NULL)
 159 goto fail;
 160
 161 sc->sc_state = EMMC_DMA_STATE_IDLE;
 162 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SDMMC);
 163 cv_init(&sc->sc_cv, "bcmemmcdma");
 164
 165 error = bus_dmamem_alloc(sc->sc.sc_dmat, PAGE_SIZE, PAGE_SIZE,
 166 PAGE_SIZE, sc->sc_segs, 1, &rseg, BUS_DMA_WAITOK);
 167 if (error) {
 168 aprint_error_dev(self, "dmamem_alloc failed (%d)\n", error);
 169 goto fail;
 170 }
 171
 172 error = bus_dmamem_map(sc->sc.sc_dmat, sc->sc_segs, rseg, PAGE_SIZE,
 173 (void **)&sc->sc_cblk, BUS_DMA_WAITOK);
 174 if (error) {
 175 aprint_error_dev(self, "dmamem_map failed (%d)\n", error);
 176 goto fail;
 177 }
 178 KASSERT(sc->sc_cblk != NULL);
 179
 180 memset(sc->sc_cblk, 0, PAGE_SIZE);
 181
 182 error = bus_dmamap_create(sc->sc.sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, 0,
 183 BUS_DMA_WAITOK, &sc->sc_dmamap);
 184 if (error) {
 185 aprint_error_dev(self, "dmamap_create failed (%d)\n", error);
 186 goto fail;
 187 }
 188
 189 error = bus_dmamap_load(sc->sc.sc_dmat, sc->sc_dmamap, sc->sc_cblk,
 190 PAGE_SIZE, NULL, BUS_DMA_WAITOK|BUS_DMA_WRITE);
 191 if (error) {
 192 aprint_error_dev(self, "dmamap_load failed (%d)\n", error);
 193 goto fail;
 194 }
 195
 196 config_interrupts(self, bcmemmc_attach_i);
 197 return;
 198
 199fail:
 200 /* XXX add bus_dma failure cleanup */
 201 if (sc->sc_ih) {
 202 intr_disestablish(sc->sc_ih);
 203 sc->sc_ih = NULL;
 204 }
 205 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
 206}
 207
 208static void
 209bcmemmc_attach_i(device_t self)
 210{
 211 struct bcmemmc_softc * const sc = device_private(self);
 212 int error;
 213
 214 error = sdhc_host_found(&sc->sc, sc->sc_iot, sc->sc_ioh, sc->sc_ios);
129 if (error != 0) { 215 if (error != 0) {
130 aprint_error_dev(self, "couldn't initialize host, error=%d\n", 216 aprint_error_dev(self, "couldn't initialize host, error=%d\n",
131 error); 217 error);
132 goto fail; 218 goto fail;
133 } 219 }
134 return; 220 return;
135 221
136fail: 222fail:
 223 /* XXX add bus_dma failure cleanup */
137 if (sc->sc_ih) { 224 if (sc->sc_ih) {
138 intr_disestablish(sc->sc_ih); 225 intr_disestablish(sc->sc_ih);
139 sc->sc_ih = NULL; 226 sc->sc_ih = NULL;
140 } 227 }
141 bus_space_unmap(sc->sc_iot, sc->sc_ioh, aaa->aaa_size); 228 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
 229}
 230
 231static int
 232bcmemmc_xfer_data_dma(struct sdhc_host *hp, struct sdmmc_command *cmd)
 233{
 234 struct bcmemmc_softc * const sc = *(void **)hp; /* XXX XXX XXX */
 235 size_t seg;
 236
 237 for (seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) {
 238 sc->sc_cblk[seg].cb_ti =
 239 __SHIFTIN(11, DMAC_TI_PERMAP); /* e.MMC */
 240 if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
 241 sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_INC;
 242 sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_WIDTH;
 243 sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_DREQ;
 244 sc->sc_cblk[seg].cb_source_ad =
 245 BCM2835_PERIPHERALS_TO_BUS(sc->sc_physaddr +
 246 SDHC_DATA);
 247 sc->sc_cblk[seg].cb_dest_ad =
 248 cmd->c_dmamap->dm_segs[seg].ds_addr;
 249 } else {
 250 sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_INC;
 251 sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_WIDTH;
 252 sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_DREQ;
 253 sc->sc_cblk[seg].cb_source_ad =
 254 cmd->c_dmamap->dm_segs[seg].ds_addr;
 255 sc->sc_cblk[seg].cb_dest_ad =
 256 BCM2835_PERIPHERALS_TO_BUS(sc->sc_physaddr +
 257 SDHC_DATA);
 258 }
 259 sc->sc_cblk[seg].cb_txfr_len =
 260 cmd->c_dmamap->dm_segs[seg].ds_len;
 261 sc->sc_cblk[seg].cb_stride = 0;
 262 if (seg == cmd->c_dmamap->dm_nsegs - 1) {
 263 sc->sc_cblk[seg].cb_ti |= DMAC_TI_WAIT_RESP;
 264 sc->sc_cblk[seg].cb_ti |= DMAC_TI_INTEN;
 265 sc->sc_cblk[seg].cb_nextconbk = 0;
 266 } else {
 267 sc->sc_cblk[seg].cb_nextconbk =
 268 sc->sc_dmamap->dm_segs[0].ds_addr +
 269 sizeof(struct bcm_dmac_conblk) * (seg+1);
 270 }
 271 sc->sc_cblk[seg].cb_padding[0] = 0;
 272 sc->sc_cblk[seg].cb_padding[1] = 0;
 273 }
 274
 275 bus_dmamap_sync(sc->sc.sc_dmat, sc->sc_dmamap, 0,
 276 sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
 277
 278 mutex_enter(&sc->sc_lock);
 279 KASSERT(sc->sc_state == EMMC_DMA_STATE_IDLE);
 280 sc->sc_state = EMMC_DMA_STATE_BUSY;
 281 bcm_dmac_set_conblk_addr(sc->sc_dmac,
 282 sc->sc_dmamap->dm_segs[0].ds_addr);
 283 bcm_dmac_transfer(sc->sc_dmac);
 284 while (sc->sc_state == EMMC_DMA_STATE_BUSY)
 285 cv_wait(&sc->sc_cv, &sc->sc_lock);
 286 mutex_exit(&sc->sc_lock);
 287
 288 bus_dmamap_sync(sc->sc.sc_dmat, sc->sc_dmamap, 0,
 289 sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE);
 290
 291 return 0;
 292}
 293
 294static void
 295bcmemmc_dma_done(void *arg)
 296{
 297 struct bcmemmc_softc * const sc = arg;
 298
 299 mutex_enter(&sc->sc_lock);
 300 KASSERT(sc->sc_state == EMMC_DMA_STATE_BUSY);
 301 sc->sc_state = EMMC_DMA_STATE_IDLE;
 302 cv_broadcast(&sc->sc_cv);
 303 mutex_exit(&sc->sc_lock);
 304
142} 305}