xref: /freebsd/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c (revision a94a63f0a6bc1ec25bf0d162e1dd9a53e020d176)
1a9387eb1SOleksandr Tymoshenko /*-
2a9387eb1SOleksandr Tymoshenko  * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
3a9387eb1SOleksandr Tymoshenko  * All rights reserved.
4a9387eb1SOleksandr Tymoshenko  *
5a9387eb1SOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
6a9387eb1SOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
7a9387eb1SOleksandr Tymoshenko  * are met:
8a9387eb1SOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
9a9387eb1SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
10a9387eb1SOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
11a9387eb1SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
12a9387eb1SOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
13a9387eb1SOleksandr Tymoshenko  *
14a9387eb1SOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15a9387eb1SOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16a9387eb1SOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17a9387eb1SOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18a9387eb1SOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19a9387eb1SOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20a9387eb1SOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21a9387eb1SOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22a9387eb1SOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23a9387eb1SOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24a9387eb1SOleksandr Tymoshenko  * SUCH DAMAGE.
25a9387eb1SOleksandr Tymoshenko  *
26a9387eb1SOleksandr Tymoshenko  */
27a9387eb1SOleksandr Tymoshenko #include <sys/cdefs.h>
28a9387eb1SOleksandr Tymoshenko __FBSDID("$FreeBSD$");
29a9387eb1SOleksandr Tymoshenko 
30a9387eb1SOleksandr Tymoshenko #include <sys/param.h>
31a9387eb1SOleksandr Tymoshenko #include <sys/systm.h>
32a9387eb1SOleksandr Tymoshenko #include <sys/bus.h>
33a9387eb1SOleksandr Tymoshenko #include <sys/kernel.h>
34a9387eb1SOleksandr Tymoshenko #include <sys/lock.h>
35a9387eb1SOleksandr Tymoshenko #include <sys/malloc.h>
36a9387eb1SOleksandr Tymoshenko #include <sys/module.h>
37a9387eb1SOleksandr Tymoshenko #include <sys/mutex.h>
38a9387eb1SOleksandr Tymoshenko #include <sys/rman.h>
398c8f31e7SIan Lepore #include <sys/sysctl.h>
40a9387eb1SOleksandr Tymoshenko #include <sys/taskqueue.h>
41a9387eb1SOleksandr Tymoshenko 
42a9387eb1SOleksandr Tymoshenko #include <machine/bus.h>
43a9387eb1SOleksandr Tymoshenko 
44a9387eb1SOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h>
45a9387eb1SOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h>
46a9387eb1SOleksandr Tymoshenko 
47a9387eb1SOleksandr Tymoshenko #include <dev/mmc/bridge.h>
48a9387eb1SOleksandr Tymoshenko #include <dev/mmc/mmcreg.h>
49a9387eb1SOleksandr Tymoshenko 
50a9387eb1SOleksandr Tymoshenko #include <dev/sdhci/sdhci.h>
51b440e965SMarius Strobl 
52b440e965SMarius Strobl #include "mmcbr_if.h"
53a9387eb1SOleksandr Tymoshenko #include "sdhci_if.h"
54a9387eb1SOleksandr Tymoshenko 
55*a94a63f0SWarner Losh #include "opt_mmccam.h"
56*a94a63f0SWarner Losh 
57adc99a8aSOleksandr Tymoshenko #include "bcm2835_dma.h"
5827eb3304SAndrew Turner #include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
59adc99a8aSOleksandr Tymoshenko #include "bcm2835_vcbus.h"
60adc99a8aSOleksandr Tymoshenko 
613b37b3c2SOleksandr Tymoshenko #define	BCM2835_DEFAULT_SDHCI_FREQ	50
623b37b3c2SOleksandr Tymoshenko 
63adc99a8aSOleksandr Tymoshenko #define	BCM_SDHCI_BUFFER_SIZE		512
64244fe94fSIan Lepore #define	NUM_DMA_SEGS			2
65adc99a8aSOleksandr Tymoshenko 
66a9387eb1SOleksandr Tymoshenko #ifdef DEBUG
67a9387eb1SOleksandr Tymoshenko #define dprintf(fmt, args...) do { printf("%s(): ", __func__);   \
68a9387eb1SOleksandr Tymoshenko     printf(fmt,##args); } while (0)
69a9387eb1SOleksandr Tymoshenko #else
70a9387eb1SOleksandr Tymoshenko #define dprintf(fmt, args...)
71a9387eb1SOleksandr Tymoshenko #endif
72a9387eb1SOleksandr Tymoshenko 
73bba987dcSIan Lepore static int bcm2835_sdhci_hs = 1;
74382ac7c8SLuiz Otavio O Souza static int bcm2835_sdhci_pio_mode = 0;
75d3d7f709SOleksandr Tymoshenko 
769d6eb8bbSOleksandr Tymoshenko static struct ofw_compat_data compat_data[] = {
779d6eb8bbSOleksandr Tymoshenko 	{"broadcom,bcm2835-sdhci",	1},
789d6eb8bbSOleksandr Tymoshenko 	{"brcm,bcm2835-mmc",		1},
799d6eb8bbSOleksandr Tymoshenko 	{NULL,				0}
809d6eb8bbSOleksandr Tymoshenko };
819d6eb8bbSOleksandr Tymoshenko 
82d3d7f709SOleksandr Tymoshenko TUNABLE_INT("hw.bcm2835.sdhci.hs", &bcm2835_sdhci_hs);
83adc99a8aSOleksandr Tymoshenko TUNABLE_INT("hw.bcm2835.sdhci.pio_mode", &bcm2835_sdhci_pio_mode);
84d3d7f709SOleksandr Tymoshenko 
85a9387eb1SOleksandr Tymoshenko struct bcm_sdhci_softc {
86a9387eb1SOleksandr Tymoshenko 	device_t		sc_dev;
87a9387eb1SOleksandr Tymoshenko 	struct resource *	sc_mem_res;
88a9387eb1SOleksandr Tymoshenko 	struct resource *	sc_irq_res;
89a9387eb1SOleksandr Tymoshenko 	bus_space_tag_t		sc_bst;
90a9387eb1SOleksandr Tymoshenko 	bus_space_handle_t	sc_bsh;
91a9387eb1SOleksandr Tymoshenko 	void *			sc_intrhand;
92a9387eb1SOleksandr Tymoshenko 	struct mmc_request *	sc_req;
93a9387eb1SOleksandr Tymoshenko 	struct sdhci_slot	sc_slot;
94adc99a8aSOleksandr Tymoshenko 	int			sc_dma_ch;
95adc99a8aSOleksandr Tymoshenko 	bus_dma_tag_t		sc_dma_tag;
96adc99a8aSOleksandr Tymoshenko 	bus_dmamap_t		sc_dma_map;
97b479b38cSIan Lepore 	vm_paddr_t		sc_sdhci_buffer_phys;
98bffed0e9SIan Lepore 	uint32_t		cmd_and_mode;
99244fe94fSIan Lepore 	bus_addr_t		dmamap_seg_addrs[NUM_DMA_SEGS];
100244fe94fSIan Lepore 	bus_size_t		dmamap_seg_sizes[NUM_DMA_SEGS];
101bf160401SIan Lepore 	int			dmamap_seg_count;
102244fe94fSIan Lepore 	int			dmamap_seg_index;
103bf160401SIan Lepore 	int			dmamap_status;
104a9387eb1SOleksandr Tymoshenko };
105a9387eb1SOleksandr Tymoshenko 
106a9387eb1SOleksandr Tymoshenko static int bcm_sdhci_probe(device_t);
107a9387eb1SOleksandr Tymoshenko static int bcm_sdhci_attach(device_t);
108a9387eb1SOleksandr Tymoshenko static int bcm_sdhci_detach(device_t);
109a9387eb1SOleksandr Tymoshenko static void bcm_sdhci_intr(void *);
110a9387eb1SOleksandr Tymoshenko 
111a9387eb1SOleksandr Tymoshenko static int bcm_sdhci_get_ro(device_t, device_t);
112adc99a8aSOleksandr Tymoshenko static void bcm_sdhci_dma_intr(int ch, void *arg);
113a9387eb1SOleksandr Tymoshenko 
114adc99a8aSOleksandr Tymoshenko static void
115bf160401SIan Lepore bcm_sdhci_dmacb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
116adc99a8aSOleksandr Tymoshenko {
117bf160401SIan Lepore 	struct bcm_sdhci_softc *sc = arg;
118bf160401SIan Lepore 	int i;
119adc99a8aSOleksandr Tymoshenko 
120bf160401SIan Lepore 	sc->dmamap_status = err;
121bf160401SIan Lepore 	sc->dmamap_seg_count = nseg;
122adc99a8aSOleksandr Tymoshenko 
123bf160401SIan Lepore 	/* Note nseg is guaranteed to be zero if err is non-zero. */
124bf160401SIan Lepore 	for (i = 0; i < nseg; i++) {
125bf160401SIan Lepore 		sc->dmamap_seg_addrs[i] = segs[i].ds_addr;
126bf160401SIan Lepore 		sc->dmamap_seg_sizes[i] = segs[i].ds_len;
127bf160401SIan Lepore 	}
128adc99a8aSOleksandr Tymoshenko }
129adc99a8aSOleksandr Tymoshenko 
130a9387eb1SOleksandr Tymoshenko static int
131a9387eb1SOleksandr Tymoshenko bcm_sdhci_probe(device_t dev)
132a9387eb1SOleksandr Tymoshenko {
133add35ed5SIan Lepore 
134add35ed5SIan Lepore 	if (!ofw_bus_status_okay(dev))
135add35ed5SIan Lepore 		return (ENXIO);
136add35ed5SIan Lepore 
1379d6eb8bbSOleksandr Tymoshenko 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
138a9387eb1SOleksandr Tymoshenko 		return (ENXIO);
139a9387eb1SOleksandr Tymoshenko 
140a9387eb1SOleksandr Tymoshenko 	device_set_desc(dev, "Broadcom 2708 SDHCI controller");
1419d6eb8bbSOleksandr Tymoshenko 
142a9387eb1SOleksandr Tymoshenko 	return (BUS_PROBE_DEFAULT);
143a9387eb1SOleksandr Tymoshenko }
144a9387eb1SOleksandr Tymoshenko 
145a9387eb1SOleksandr Tymoshenko static int
146a9387eb1SOleksandr Tymoshenko bcm_sdhci_attach(device_t dev)
147a9387eb1SOleksandr Tymoshenko {
148a9387eb1SOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = device_get_softc(dev);
149a9387eb1SOleksandr Tymoshenko 	int rid, err;
1503b37b3c2SOleksandr Tymoshenko 	phandle_t node;
1513b37b3c2SOleksandr Tymoshenko 	pcell_t cell;
15227eb3304SAndrew Turner 	u_int default_freq;
153a9387eb1SOleksandr Tymoshenko 
154a9387eb1SOleksandr Tymoshenko 	sc->sc_dev = dev;
155a9387eb1SOleksandr Tymoshenko 	sc->sc_req = NULL;
156a9387eb1SOleksandr Tymoshenko 
1578826550bSOleksandr Tymoshenko 	err = bcm2835_mbox_set_power_state(BCM2835_MBOX_POWER_ID_EMMC,
15827eb3304SAndrew Turner 	    TRUE);
15927eb3304SAndrew Turner 	if (err != 0) {
16027eb3304SAndrew Turner 		if (bootverbose)
16127eb3304SAndrew Turner 			device_printf(dev, "Unable to enable the power\n");
16227eb3304SAndrew Turner 		return (err);
16327eb3304SAndrew Turner 	}
16427eb3304SAndrew Turner 
16527eb3304SAndrew Turner 	default_freq = 0;
1668826550bSOleksandr Tymoshenko 	err = bcm2835_mbox_get_clock_rate(BCM2835_MBOX_CLOCK_ID_EMMC,
16727eb3304SAndrew Turner 	    &default_freq);
16827eb3304SAndrew Turner 	if (err == 0) {
16927eb3304SAndrew Turner 		/* Convert to MHz */
17027eb3304SAndrew Turner 		default_freq /= 1000000;
171b7fbc369SLuiz Otavio O Souza 	}
172b7fbc369SLuiz Otavio O Souza 	if (default_freq == 0) {
173b7fbc369SLuiz Otavio O Souza 		node = ofw_bus_get_node(sc->sc_dev);
174b7fbc369SLuiz Otavio O Souza 		if ((OF_getencprop(node, "clock-frequency", &cell,
175b7fbc369SLuiz Otavio O Souza 		    sizeof(cell))) > 0)
176b7fbc369SLuiz Otavio O Souza 			default_freq = cell / 1000000;
17727eb3304SAndrew Turner 	}
17827eb3304SAndrew Turner 	if (default_freq == 0)
1793b37b3c2SOleksandr Tymoshenko 		default_freq = BCM2835_DEFAULT_SDHCI_FREQ;
18027eb3304SAndrew Turner 
18127eb3304SAndrew Turner 	if (bootverbose)
18227eb3304SAndrew Turner 		device_printf(dev, "SDHCI frequency: %dMHz\n", default_freq);
1833b37b3c2SOleksandr Tymoshenko 
184a9387eb1SOleksandr Tymoshenko 	rid = 0;
185a9387eb1SOleksandr Tymoshenko 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
186a9387eb1SOleksandr Tymoshenko 	    RF_ACTIVE);
187a9387eb1SOleksandr Tymoshenko 	if (!sc->sc_mem_res) {
188a9387eb1SOleksandr Tymoshenko 		device_printf(dev, "cannot allocate memory window\n");
189a9387eb1SOleksandr Tymoshenko 		err = ENXIO;
190a9387eb1SOleksandr Tymoshenko 		goto fail;
191a9387eb1SOleksandr Tymoshenko 	}
192a9387eb1SOleksandr Tymoshenko 
193a9387eb1SOleksandr Tymoshenko 	sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
194a9387eb1SOleksandr Tymoshenko 	sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
195a9387eb1SOleksandr Tymoshenko 
196a9387eb1SOleksandr Tymoshenko 	rid = 0;
197a9387eb1SOleksandr Tymoshenko 	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
198a9387eb1SOleksandr Tymoshenko 	    RF_ACTIVE);
199a9387eb1SOleksandr Tymoshenko 	if (!sc->sc_irq_res) {
200a9387eb1SOleksandr Tymoshenko 		device_printf(dev, "cannot allocate interrupt\n");
201a9387eb1SOleksandr Tymoshenko 		err = ENXIO;
202a9387eb1SOleksandr Tymoshenko 		goto fail;
203a9387eb1SOleksandr Tymoshenko 	}
204a9387eb1SOleksandr Tymoshenko 
205b479b38cSIan Lepore 	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
20607c7a520SLuiz Otavio O Souza 	    NULL, bcm_sdhci_intr, sc, &sc->sc_intrhand)) {
207a9387eb1SOleksandr Tymoshenko 		device_printf(dev, "cannot setup interrupt handler\n");
208a9387eb1SOleksandr Tymoshenko 		err = ENXIO;
209a9387eb1SOleksandr Tymoshenko 		goto fail;
210a9387eb1SOleksandr Tymoshenko 	}
211a9387eb1SOleksandr Tymoshenko 
212adc99a8aSOleksandr Tymoshenko 	if (!bcm2835_sdhci_pio_mode)
213adc99a8aSOleksandr Tymoshenko 		sc->sc_slot.opt = SDHCI_PLATFORM_TRANSFER;
214adc99a8aSOleksandr Tymoshenko 
215d3d7f709SOleksandr Tymoshenko 	sc->sc_slot.caps = SDHCI_CAN_VDD_330 | SDHCI_CAN_VDD_180;
216d3d7f709SOleksandr Tymoshenko 	if (bcm2835_sdhci_hs)
217d3d7f709SOleksandr Tymoshenko 		sc->sc_slot.caps |= SDHCI_CAN_DO_HISPD;
2183b37b3c2SOleksandr Tymoshenko 	sc->sc_slot.caps |= (default_freq << SDHCI_CLOCK_BASE_SHIFT);
219a9387eb1SOleksandr Tymoshenko 	sc->sc_slot.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK
220a9387eb1SOleksandr Tymoshenko 		| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
221bba987dcSIan Lepore 		| SDHCI_QUIRK_DONT_SET_HISPD_BIT
222a9387eb1SOleksandr Tymoshenko 		| SDHCI_QUIRK_MISSING_CAPS;
223a9387eb1SOleksandr Tymoshenko 
224a9387eb1SOleksandr Tymoshenko 	sdhci_init_slot(dev, &sc->sc_slot, 0);
225a9387eb1SOleksandr Tymoshenko 
226adc99a8aSOleksandr Tymoshenko 	sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_ANY);
227adc99a8aSOleksandr Tymoshenko 	if (sc->sc_dma_ch == BCM_DMA_CH_INVALID)
228adc99a8aSOleksandr Tymoshenko 		goto fail;
229adc99a8aSOleksandr Tymoshenko 
230adc99a8aSOleksandr Tymoshenko 	bcm_dma_setup_intr(sc->sc_dma_ch, bcm_sdhci_dma_intr, sc);
231adc99a8aSOleksandr Tymoshenko 
232b479b38cSIan Lepore 	/* Allocate bus_dma resources. */
233adc99a8aSOleksandr Tymoshenko 	err = bus_dma_tag_create(bus_get_dma_tag(dev),
234adc99a8aSOleksandr Tymoshenko 	    1, 0, BUS_SPACE_MAXADDR_32BIT,
235adc99a8aSOleksandr Tymoshenko 	    BUS_SPACE_MAXADDR, NULL, NULL,
236244fe94fSIan Lepore 	    BCM_SDHCI_BUFFER_SIZE, NUM_DMA_SEGS, BCM_SDHCI_BUFFER_SIZE,
237adc99a8aSOleksandr Tymoshenko 	    BUS_DMA_ALLOCNOW, NULL, NULL,
238adc99a8aSOleksandr Tymoshenko 	    &sc->sc_dma_tag);
239adc99a8aSOleksandr Tymoshenko 
240adc99a8aSOleksandr Tymoshenko 	if (err) {
241adc99a8aSOleksandr Tymoshenko 		device_printf(dev, "failed allocate DMA tag");
242adc99a8aSOleksandr Tymoshenko 		goto fail;
243adc99a8aSOleksandr Tymoshenko 	}
244adc99a8aSOleksandr Tymoshenko 
245b479b38cSIan Lepore 	err = bus_dmamap_create(sc->sc_dma_tag, 0, &sc->sc_dma_map);
246adc99a8aSOleksandr Tymoshenko 	if (err) {
247b479b38cSIan Lepore 		device_printf(dev, "bus_dmamap_create failed\n");
248adc99a8aSOleksandr Tymoshenko 		goto fail;
249adc99a8aSOleksandr Tymoshenko 	}
250adc99a8aSOleksandr Tymoshenko 
2518ff1636cSOleksandr Tymoshenko 	/* FIXME: Fix along with other BUS_SPACE_PHYSADDR instances */
2528ff1636cSOleksandr Tymoshenko 	sc->sc_sdhci_buffer_phys = rman_get_start(sc->sc_mem_res) +
2538ff1636cSOleksandr Tymoshenko 	    SDHCI_BUFFER;
254adc99a8aSOleksandr Tymoshenko 
255a9387eb1SOleksandr Tymoshenko 	bus_generic_probe(dev);
256a9387eb1SOleksandr Tymoshenko 	bus_generic_attach(dev);
257a9387eb1SOleksandr Tymoshenko 
258*a94a63f0SWarner Losh #ifdef MMCCAM
259*a94a63f0SWarner Losh 	sdhci_cam_start_slot(&sc->sc_slot);
260*a94a63f0SWarner Losh #else
261a9387eb1SOleksandr Tymoshenko 	sdhci_start_slot(&sc->sc_slot);
262*a94a63f0SWarner Losh #endif
263a9387eb1SOleksandr Tymoshenko 
264a9387eb1SOleksandr Tymoshenko 	return (0);
265a9387eb1SOleksandr Tymoshenko 
266a9387eb1SOleksandr Tymoshenko fail:
267a9387eb1SOleksandr Tymoshenko 	if (sc->sc_intrhand)
268a9387eb1SOleksandr Tymoshenko 		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
269a9387eb1SOleksandr Tymoshenko 	if (sc->sc_irq_res)
270a9387eb1SOleksandr Tymoshenko 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
271a9387eb1SOleksandr Tymoshenko 	if (sc->sc_mem_res)
272a9387eb1SOleksandr Tymoshenko 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
273a9387eb1SOleksandr Tymoshenko 
274a9387eb1SOleksandr Tymoshenko 	return (err);
275a9387eb1SOleksandr Tymoshenko }
276a9387eb1SOleksandr Tymoshenko 
277a9387eb1SOleksandr Tymoshenko static int
278a9387eb1SOleksandr Tymoshenko bcm_sdhci_detach(device_t dev)
279a9387eb1SOleksandr Tymoshenko {
280a9387eb1SOleksandr Tymoshenko 
281a9387eb1SOleksandr Tymoshenko 	return (EBUSY);
282a9387eb1SOleksandr Tymoshenko }
283a9387eb1SOleksandr Tymoshenko 
284a9387eb1SOleksandr Tymoshenko static void
285a9387eb1SOleksandr Tymoshenko bcm_sdhci_intr(void *arg)
286a9387eb1SOleksandr Tymoshenko {
287a9387eb1SOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = arg;
288a9387eb1SOleksandr Tymoshenko 
289a9387eb1SOleksandr Tymoshenko 	sdhci_generic_intr(&sc->sc_slot);
290a9387eb1SOleksandr Tymoshenko }
291a9387eb1SOleksandr Tymoshenko 
292a9387eb1SOleksandr Tymoshenko static int
293a9387eb1SOleksandr Tymoshenko bcm_sdhci_get_ro(device_t bus, device_t child)
294a9387eb1SOleksandr Tymoshenko {
295a9387eb1SOleksandr Tymoshenko 
296a9387eb1SOleksandr Tymoshenko 	return (0);
297a9387eb1SOleksandr Tymoshenko }
298a9387eb1SOleksandr Tymoshenko 
299a9387eb1SOleksandr Tymoshenko static inline uint32_t
300a9387eb1SOleksandr Tymoshenko RD4(struct bcm_sdhci_softc *sc, bus_size_t off)
301a9387eb1SOleksandr Tymoshenko {
302a9387eb1SOleksandr Tymoshenko 	uint32_t val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off);
303a9387eb1SOleksandr Tymoshenko 	return val;
304a9387eb1SOleksandr Tymoshenko }
305a9387eb1SOleksandr Tymoshenko 
306a9387eb1SOleksandr Tymoshenko static inline void
307a9387eb1SOleksandr Tymoshenko WR4(struct bcm_sdhci_softc *sc, bus_size_t off, uint32_t val)
308a9387eb1SOleksandr Tymoshenko {
3097c26b0a7SLuiz Otavio O Souza 
310a9387eb1SOleksandr Tymoshenko 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);
3117c26b0a7SLuiz Otavio O Souza 	/*
3127c26b0a7SLuiz Otavio O Souza 	 * The Arasan HC has a bug where it may lose the content of
3137c26b0a7SLuiz Otavio O Souza 	 * consecutive writes to registers that are within two SD-card
3147c26b0a7SLuiz Otavio O Souza 	 * clock cycles of each other (a clock domain crossing problem).
3157c26b0a7SLuiz Otavio O Souza 	 */
3167c26b0a7SLuiz Otavio O Souza 	if (sc->sc_slot.clock > 0)
3177c26b0a7SLuiz Otavio O Souza 		DELAY(((2 * 1000000) / sc->sc_slot.clock) + 1);
318a9387eb1SOleksandr Tymoshenko }
319a9387eb1SOleksandr Tymoshenko 
320a9387eb1SOleksandr Tymoshenko static uint8_t
321a9387eb1SOleksandr Tymoshenko bcm_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
322a9387eb1SOleksandr Tymoshenko {
323a9387eb1SOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = device_get_softc(dev);
324a9387eb1SOleksandr Tymoshenko 	uint32_t val = RD4(sc, off & ~3);
325a9387eb1SOleksandr Tymoshenko 
326a9387eb1SOleksandr Tymoshenko 	return ((val >> (off & 3)*8) & 0xff);
327a9387eb1SOleksandr Tymoshenko }
328a9387eb1SOleksandr Tymoshenko 
329a9387eb1SOleksandr Tymoshenko static uint16_t
330a9387eb1SOleksandr Tymoshenko bcm_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
331a9387eb1SOleksandr Tymoshenko {
332a9387eb1SOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = device_get_softc(dev);
333a9387eb1SOleksandr Tymoshenko 	uint32_t val = RD4(sc, off & ~3);
334a9387eb1SOleksandr Tymoshenko 
335bffed0e9SIan Lepore 	/*
336bffed0e9SIan Lepore 	 * Standard 32-bit handling of command and transfer mode.
337bffed0e9SIan Lepore 	 */
338bffed0e9SIan Lepore 	if (off == SDHCI_TRANSFER_MODE) {
339bffed0e9SIan Lepore 		return (sc->cmd_and_mode >> 16);
340bffed0e9SIan Lepore 	} else if (off == SDHCI_COMMAND_FLAGS) {
341bffed0e9SIan Lepore 		return (sc->cmd_and_mode & 0x0000ffff);
342bffed0e9SIan Lepore 	}
343a9387eb1SOleksandr Tymoshenko 	return ((val >> (off & 3)*8) & 0xffff);
344a9387eb1SOleksandr Tymoshenko }
345a9387eb1SOleksandr Tymoshenko 
346a9387eb1SOleksandr Tymoshenko static uint32_t
347a9387eb1SOleksandr Tymoshenko bcm_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
348a9387eb1SOleksandr Tymoshenko {
349a9387eb1SOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = device_get_softc(dev);
350a9387eb1SOleksandr Tymoshenko 
351a9387eb1SOleksandr Tymoshenko 	return RD4(sc, off);
352a9387eb1SOleksandr Tymoshenko }
353a9387eb1SOleksandr Tymoshenko 
354a9387eb1SOleksandr Tymoshenko static void
355a9387eb1SOleksandr Tymoshenko bcm_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
356a9387eb1SOleksandr Tymoshenko     uint32_t *data, bus_size_t count)
357a9387eb1SOleksandr Tymoshenko {
358a9387eb1SOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = device_get_softc(dev);
359a9387eb1SOleksandr Tymoshenko 
360a9387eb1SOleksandr Tymoshenko 	bus_space_read_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count);
361a9387eb1SOleksandr Tymoshenko }
362a9387eb1SOleksandr Tymoshenko 
363a9387eb1SOleksandr Tymoshenko static void
364a9387eb1SOleksandr Tymoshenko bcm_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val)
365a9387eb1SOleksandr Tymoshenko {
366a9387eb1SOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = device_get_softc(dev);
367a9387eb1SOleksandr Tymoshenko 	uint32_t val32 = RD4(sc, off & ~3);
368a9387eb1SOleksandr Tymoshenko 	val32 &= ~(0xff << (off & 3)*8);
369a9387eb1SOleksandr Tymoshenko 	val32 |= (val << (off & 3)*8);
370a9387eb1SOleksandr Tymoshenko 	WR4(sc, off & ~3, val32);
371a9387eb1SOleksandr Tymoshenko }
372a9387eb1SOleksandr Tymoshenko 
373a9387eb1SOleksandr Tymoshenko static void
374a9387eb1SOleksandr Tymoshenko bcm_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val)
375a9387eb1SOleksandr Tymoshenko {
376a9387eb1SOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = device_get_softc(dev);
377a9387eb1SOleksandr Tymoshenko 	uint32_t val32;
378a9387eb1SOleksandr Tymoshenko 	if (off == SDHCI_COMMAND_FLAGS)
379bffed0e9SIan Lepore 		val32 = sc->cmd_and_mode;
380a9387eb1SOleksandr Tymoshenko 	else
381a9387eb1SOleksandr Tymoshenko 		val32 = RD4(sc, off & ~3);
382a9387eb1SOleksandr Tymoshenko 	val32 &= ~(0xffff << (off & 3)*8);
383a9387eb1SOleksandr Tymoshenko 	val32 |= (val << (off & 3)*8);
384a9387eb1SOleksandr Tymoshenko 	if (off == SDHCI_TRANSFER_MODE)
385bffed0e9SIan Lepore 		sc->cmd_and_mode = val32;
38686ee58d9SIan Lepore 	else {
387a9387eb1SOleksandr Tymoshenko 		WR4(sc, off & ~3, val32);
38886ee58d9SIan Lepore 		if (off == SDHCI_COMMAND_FLAGS)
38986ee58d9SIan Lepore 			sc->cmd_and_mode = val32;
39086ee58d9SIan Lepore 	}
391a9387eb1SOleksandr Tymoshenko }
392a9387eb1SOleksandr Tymoshenko 
393a9387eb1SOleksandr Tymoshenko static void
394a9387eb1SOleksandr Tymoshenko bcm_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val)
395a9387eb1SOleksandr Tymoshenko {
396a9387eb1SOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = device_get_softc(dev);
397a9387eb1SOleksandr Tymoshenko 	WR4(sc, off, val);
398a9387eb1SOleksandr Tymoshenko }
399a9387eb1SOleksandr Tymoshenko 
400a9387eb1SOleksandr Tymoshenko static void
401a9387eb1SOleksandr Tymoshenko bcm_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
402a9387eb1SOleksandr Tymoshenko     uint32_t *data, bus_size_t count)
403a9387eb1SOleksandr Tymoshenko {
404a9387eb1SOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = device_get_softc(dev);
405a9387eb1SOleksandr Tymoshenko 
406a9387eb1SOleksandr Tymoshenko 	bus_space_write_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count);
407a9387eb1SOleksandr Tymoshenko }
408a9387eb1SOleksandr Tymoshenko 
409adc99a8aSOleksandr Tymoshenko static void
410244fe94fSIan Lepore bcm_sdhci_start_dma_seg(struct bcm_sdhci_softc *sc)
411244fe94fSIan Lepore {
412244fe94fSIan Lepore 	struct sdhci_slot *slot;
413244fe94fSIan Lepore 	vm_paddr_t pdst, psrc;
414244fe94fSIan Lepore 	int err, idx, len, sync_op;
415244fe94fSIan Lepore 
416244fe94fSIan Lepore 	slot = &sc->sc_slot;
417244fe94fSIan Lepore 	idx = sc->dmamap_seg_index++;
418244fe94fSIan Lepore 	len = sc->dmamap_seg_sizes[idx];
419244fe94fSIan Lepore 	slot->offset += len;
420244fe94fSIan Lepore 
421244fe94fSIan Lepore 	if (slot->curcmd->data->flags & MMC_DATA_READ) {
422244fe94fSIan Lepore 		bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
423244fe94fSIan Lepore 		    BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
424244fe94fSIan Lepore 		bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
425244fe94fSIan Lepore 		    BCM_DMA_INC_ADDR,
426244fe94fSIan Lepore 		    (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
427244fe94fSIan Lepore 		psrc = sc->sc_sdhci_buffer_phys;
428244fe94fSIan Lepore 		pdst = sc->dmamap_seg_addrs[idx];
429244fe94fSIan Lepore 		sync_op = BUS_DMASYNC_PREREAD;
430244fe94fSIan Lepore 	} else {
431244fe94fSIan Lepore 		bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
432244fe94fSIan Lepore 		    BCM_DMA_INC_ADDR,
433244fe94fSIan Lepore 		    (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
434244fe94fSIan Lepore 		bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
435244fe94fSIan Lepore 		    BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
436244fe94fSIan Lepore 		psrc = sc->dmamap_seg_addrs[idx];
437244fe94fSIan Lepore 		pdst = sc->sc_sdhci_buffer_phys;
438244fe94fSIan Lepore 		sync_op = BUS_DMASYNC_PREWRITE;
439244fe94fSIan Lepore 	}
440244fe94fSIan Lepore 
441244fe94fSIan Lepore 	/*
442244fe94fSIan Lepore 	 * When starting a new DMA operation do the busdma sync operation, and
443244fe94fSIan Lepore 	 * disable SDCHI data interrrupts because we'll be driven by DMA
444244fe94fSIan Lepore 	 * interrupts (or SDHCI error interrupts) until the IO is done.
445244fe94fSIan Lepore 	 */
446244fe94fSIan Lepore 	if (idx == 0) {
447244fe94fSIan Lepore 		bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op);
448244fe94fSIan Lepore 		slot->intmask &= ~(SDHCI_INT_DATA_AVAIL |
449244fe94fSIan Lepore 		    SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END);
450244fe94fSIan Lepore 		bcm_sdhci_write_4(sc->sc_dev, &sc->sc_slot, SDHCI_SIGNAL_ENABLE,
451244fe94fSIan Lepore 		    slot->intmask);
452244fe94fSIan Lepore 	}
453244fe94fSIan Lepore 
454244fe94fSIan Lepore 	/*
455244fe94fSIan Lepore 	 * Start the DMA transfer.  Only programming errors (like failing to
456244fe94fSIan Lepore 	 * allocate a channel) cause a non-zero return from bcm_dma_start().
457244fe94fSIan Lepore 	 */
458244fe94fSIan Lepore 	err = bcm_dma_start(sc->sc_dma_ch, psrc, pdst, len);
459244fe94fSIan Lepore 	KASSERT((err == 0), ("bcm2835_sdhci: failed DMA start"));
460244fe94fSIan Lepore }
461244fe94fSIan Lepore 
462244fe94fSIan Lepore static void
463adc99a8aSOleksandr Tymoshenko bcm_sdhci_dma_intr(int ch, void *arg)
464adc99a8aSOleksandr Tymoshenko {
465adc99a8aSOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = (struct bcm_sdhci_softc *)arg;
466adc99a8aSOleksandr Tymoshenko 	struct sdhci_slot *slot = &sc->sc_slot;
467adc99a8aSOleksandr Tymoshenko 	uint32_t reg, mask;
468b479b38cSIan Lepore 	int left, sync_op;
469adc99a8aSOleksandr Tymoshenko 
470adc99a8aSOleksandr Tymoshenko 	mtx_lock(&slot->mtx);
471adc99a8aSOleksandr Tymoshenko 
472244fe94fSIan Lepore 	/*
473244fe94fSIan Lepore 	 * If there are more segments for the current dma, start the next one.
474244fe94fSIan Lepore 	 * Otherwise unload the dma map and decide what to do next based on the
475244fe94fSIan Lepore 	 * status of the sdhci controller and whether there's more data left.
476244fe94fSIan Lepore 	 */
477244fe94fSIan Lepore 	if (sc->dmamap_seg_index < sc->dmamap_seg_count) {
478244fe94fSIan Lepore 		bcm_sdhci_start_dma_seg(sc);
479244fe94fSIan Lepore 		mtx_unlock(&slot->mtx);
480244fe94fSIan Lepore 		return;
481244fe94fSIan Lepore 	}
482244fe94fSIan Lepore 
483adc99a8aSOleksandr Tymoshenko 	if (slot->curcmd->data->flags & MMC_DATA_READ) {
484b479b38cSIan Lepore 		sync_op = BUS_DMASYNC_POSTREAD;
485adc99a8aSOleksandr Tymoshenko 		mask = SDHCI_INT_DATA_AVAIL;
486adc99a8aSOleksandr Tymoshenko 	} else {
487b479b38cSIan Lepore 		sync_op = BUS_DMASYNC_POSTWRITE;
488adc99a8aSOleksandr Tymoshenko 		mask = SDHCI_INT_SPACE_AVAIL;
489adc99a8aSOleksandr Tymoshenko 	}
490b479b38cSIan Lepore 	bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op);
491b479b38cSIan Lepore 	bus_dmamap_unload(sc->sc_dma_tag, sc->sc_dma_map);
492adc99a8aSOleksandr Tymoshenko 
493244fe94fSIan Lepore 	sc->dmamap_seg_count = 0;
494244fe94fSIan Lepore 	sc->dmamap_seg_index = 0;
495adc99a8aSOleksandr Tymoshenko 
496adc99a8aSOleksandr Tymoshenko 	left = min(BCM_SDHCI_BUFFER_SIZE,
497adc99a8aSOleksandr Tymoshenko 	    slot->curcmd->data->len - slot->offset);
498adc99a8aSOleksandr Tymoshenko 
499adc99a8aSOleksandr Tymoshenko 	/* DATA END? */
500adc99a8aSOleksandr Tymoshenko 	reg = bcm_sdhci_read_4(slot->bus, slot, SDHCI_INT_STATUS);
501adc99a8aSOleksandr Tymoshenko 
502adc99a8aSOleksandr Tymoshenko 	if (reg & SDHCI_INT_DATA_END) {
503adc99a8aSOleksandr Tymoshenko 		/* ACK for all outstanding interrupts */
504adc99a8aSOleksandr Tymoshenko 		bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS, reg);
505adc99a8aSOleksandr Tymoshenko 
506adc99a8aSOleksandr Tymoshenko 		/* enable INT */
507adc99a8aSOleksandr Tymoshenko 		slot->intmask |= SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL
508adc99a8aSOleksandr Tymoshenko 		    | SDHCI_INT_DATA_END;
509adc99a8aSOleksandr Tymoshenko 		bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE,
510adc99a8aSOleksandr Tymoshenko 		    slot->intmask);
511adc99a8aSOleksandr Tymoshenko 
512adc99a8aSOleksandr Tymoshenko 		/* finish this data */
513adc99a8aSOleksandr Tymoshenko 		sdhci_finish_data(slot);
514adc99a8aSOleksandr Tymoshenko 	}
515adc99a8aSOleksandr Tymoshenko 	else {
516adc99a8aSOleksandr Tymoshenko 		/* already available? */
517adc99a8aSOleksandr Tymoshenko 		if (reg & mask) {
518adc99a8aSOleksandr Tymoshenko 
519adc99a8aSOleksandr Tymoshenko 			/* ACK for DATA_AVAIL or SPACE_AVAIL */
520adc99a8aSOleksandr Tymoshenko 			bcm_sdhci_write_4(slot->bus, slot,
521adc99a8aSOleksandr Tymoshenko 			    SDHCI_INT_STATUS, mask);
522adc99a8aSOleksandr Tymoshenko 
523adc99a8aSOleksandr Tymoshenko 			/* continue next DMA transfer */
524bf160401SIan Lepore 			if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map,
525b479b38cSIan Lepore 			    (uint8_t *)slot->curcmd->data->data +
526bf160401SIan Lepore 			    slot->offset, left, bcm_sdhci_dmacb, sc,
527bf160401SIan Lepore 			    BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) {
528bf160401SIan Lepore 				slot->curcmd->error = MMC_ERR_NO_MEMORY;
529bf160401SIan Lepore 				sdhci_finish_data(slot);
530bf160401SIan Lepore 			} else {
531244fe94fSIan Lepore 				bcm_sdhci_start_dma_seg(sc);
532adc99a8aSOleksandr Tymoshenko 			}
533adc99a8aSOleksandr Tymoshenko 		} else {
534adc99a8aSOleksandr Tymoshenko 			/* wait for next data by INT */
535adc99a8aSOleksandr Tymoshenko 
536adc99a8aSOleksandr Tymoshenko 			/* enable INT */
537adc99a8aSOleksandr Tymoshenko 			slot->intmask |= SDHCI_INT_DATA_AVAIL |
538adc99a8aSOleksandr Tymoshenko 			    SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END;
539adc99a8aSOleksandr Tymoshenko 			bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE,
540adc99a8aSOleksandr Tymoshenko 			    slot->intmask);
541adc99a8aSOleksandr Tymoshenko 		}
542adc99a8aSOleksandr Tymoshenko 	}
543adc99a8aSOleksandr Tymoshenko 
544adc99a8aSOleksandr Tymoshenko 	mtx_unlock(&slot->mtx);
545adc99a8aSOleksandr Tymoshenko }
546adc99a8aSOleksandr Tymoshenko 
547adc99a8aSOleksandr Tymoshenko static void
548bf160401SIan Lepore bcm_sdhci_read_dma(device_t dev, struct sdhci_slot *slot)
549adc99a8aSOleksandr Tymoshenko {
550adc99a8aSOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = device_get_softc(slot->bus);
551adc99a8aSOleksandr Tymoshenko 	size_t left;
552adc99a8aSOleksandr Tymoshenko 
553244fe94fSIan Lepore 	if (sc->dmamap_seg_count != 0) {
554adc99a8aSOleksandr Tymoshenko 		device_printf(sc->sc_dev, "DMA in use\n");
555adc99a8aSOleksandr Tymoshenko 		return;
556adc99a8aSOleksandr Tymoshenko 	}
557adc99a8aSOleksandr Tymoshenko 
558adc99a8aSOleksandr Tymoshenko 	left = min(BCM_SDHCI_BUFFER_SIZE,
559adc99a8aSOleksandr Tymoshenko 	    slot->curcmd->data->len - slot->offset);
560adc99a8aSOleksandr Tymoshenko 
561adc99a8aSOleksandr Tymoshenko 	KASSERT((left & 3) == 0,
5628ff1636cSOleksandr Tymoshenko 	    ("%s: len = %zu, not word-aligned", __func__, left));
563adc99a8aSOleksandr Tymoshenko 
564bf160401SIan Lepore 	if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map,
565bf160401SIan Lepore 	    (uint8_t *)slot->curcmd->data->data + slot->offset, left,
566bf160401SIan Lepore 	    bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 ||
567bf160401SIan Lepore 	    sc->dmamap_status != 0) {
568bf160401SIan Lepore 		slot->curcmd->error = MMC_ERR_NO_MEMORY;
569bf160401SIan Lepore 		return;
570bf160401SIan Lepore 	}
571bf160401SIan Lepore 
572adc99a8aSOleksandr Tymoshenko 	/* DMA start */
573244fe94fSIan Lepore 	bcm_sdhci_start_dma_seg(sc);
574adc99a8aSOleksandr Tymoshenko }
575adc99a8aSOleksandr Tymoshenko 
576adc99a8aSOleksandr Tymoshenko static void
577bf160401SIan Lepore bcm_sdhci_write_dma(device_t dev, struct sdhci_slot *slot)
578adc99a8aSOleksandr Tymoshenko {
579adc99a8aSOleksandr Tymoshenko 	struct bcm_sdhci_softc *sc = device_get_softc(slot->bus);
580adc99a8aSOleksandr Tymoshenko 	size_t left;
581adc99a8aSOleksandr Tymoshenko 
582244fe94fSIan Lepore 	if (sc->dmamap_seg_count != 0) {
583adc99a8aSOleksandr Tymoshenko 		device_printf(sc->sc_dev, "DMA in use\n");
584adc99a8aSOleksandr Tymoshenko 		return;
585adc99a8aSOleksandr Tymoshenko 	}
586adc99a8aSOleksandr Tymoshenko 
587adc99a8aSOleksandr Tymoshenko 	left = min(BCM_SDHCI_BUFFER_SIZE,
588adc99a8aSOleksandr Tymoshenko 	    slot->curcmd->data->len - slot->offset);
589adc99a8aSOleksandr Tymoshenko 
590adc99a8aSOleksandr Tymoshenko 	KASSERT((left & 3) == 0,
5918ff1636cSOleksandr Tymoshenko 	    ("%s: len = %zu, not word-aligned", __func__, left));
592adc99a8aSOleksandr Tymoshenko 
593bf160401SIan Lepore 	if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map,
594b479b38cSIan Lepore 	    (uint8_t *)slot->curcmd->data->data + slot->offset, left,
595bf160401SIan Lepore 	    bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 ||
596bf160401SIan Lepore 	    sc->dmamap_status != 0) {
597bf160401SIan Lepore 		slot->curcmd->error = MMC_ERR_NO_MEMORY;
598bf160401SIan Lepore 		return;
599bf160401SIan Lepore 	}
600adc99a8aSOleksandr Tymoshenko 
601adc99a8aSOleksandr Tymoshenko 	/* DMA start */
602244fe94fSIan Lepore 	bcm_sdhci_start_dma_seg(sc);
603adc99a8aSOleksandr Tymoshenko }
604adc99a8aSOleksandr Tymoshenko 
605adc99a8aSOleksandr Tymoshenko static int
606adc99a8aSOleksandr Tymoshenko bcm_sdhci_will_handle_transfer(device_t dev, struct sdhci_slot *slot)
607adc99a8aSOleksandr Tymoshenko {
608adc99a8aSOleksandr Tymoshenko 	size_t left;
609adc99a8aSOleksandr Tymoshenko 
610b479b38cSIan Lepore 	/*
611b479b38cSIan Lepore 	 * Do not use DMA for transfers less than block size or with a length
612b479b38cSIan Lepore 	 * that is not a multiple of four.
613b479b38cSIan Lepore 	 */
614adc99a8aSOleksandr Tymoshenko 	left = min(BCM_DMA_BLOCK_SIZE,
615adc99a8aSOleksandr Tymoshenko 	    slot->curcmd->data->len - slot->offset);
616adc99a8aSOleksandr Tymoshenko 	if (left < BCM_DMA_BLOCK_SIZE)
617adc99a8aSOleksandr Tymoshenko 		return (0);
618b479b38cSIan Lepore 	if (left & 0x03)
619b479b38cSIan Lepore 		return (0);
620adc99a8aSOleksandr Tymoshenko 
621adc99a8aSOleksandr Tymoshenko 	return (1);
622adc99a8aSOleksandr Tymoshenko }
623adc99a8aSOleksandr Tymoshenko 
624adc99a8aSOleksandr Tymoshenko static void
625adc99a8aSOleksandr Tymoshenko bcm_sdhci_start_transfer(device_t dev, struct sdhci_slot *slot,
626adc99a8aSOleksandr Tymoshenko     uint32_t *intmask)
627adc99a8aSOleksandr Tymoshenko {
628adc99a8aSOleksandr Tymoshenko 
629adc99a8aSOleksandr Tymoshenko 	/* DMA transfer FIFO 1KB */
630adc99a8aSOleksandr Tymoshenko 	if (slot->curcmd->data->flags & MMC_DATA_READ)
631bf160401SIan Lepore 		bcm_sdhci_read_dma(dev, slot);
632adc99a8aSOleksandr Tymoshenko 	else
633bf160401SIan Lepore 		bcm_sdhci_write_dma(dev, slot);
634adc99a8aSOleksandr Tymoshenko }
635adc99a8aSOleksandr Tymoshenko 
636adc99a8aSOleksandr Tymoshenko static void
637adc99a8aSOleksandr Tymoshenko bcm_sdhci_finish_transfer(device_t dev, struct sdhci_slot *slot)
638adc99a8aSOleksandr Tymoshenko {
639adc99a8aSOleksandr Tymoshenko 
640adc99a8aSOleksandr Tymoshenko 	sdhci_finish_data(slot);
641adc99a8aSOleksandr Tymoshenko }
642adc99a8aSOleksandr Tymoshenko 
643a9387eb1SOleksandr Tymoshenko static device_method_t bcm_sdhci_methods[] = {
644a9387eb1SOleksandr Tymoshenko 	/* Device interface */
645a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(device_probe,		bcm_sdhci_probe),
646a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(device_attach,	bcm_sdhci_attach),
647a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(device_detach,	bcm_sdhci_detach),
648a9387eb1SOleksandr Tymoshenko 
649a9387eb1SOleksandr Tymoshenko 	/* Bus interface */
650a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(bus_read_ivar,	sdhci_generic_read_ivar),
651a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(bus_write_ivar,	sdhci_generic_write_ivar),
652a9387eb1SOleksandr Tymoshenko 
653a9387eb1SOleksandr Tymoshenko 	/* MMC bridge interface */
654a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(mmcbr_update_ios,	sdhci_generic_update_ios),
655a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(mmcbr_request,	sdhci_generic_request),
656a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(mmcbr_get_ro,		bcm_sdhci_get_ro),
657a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(mmcbr_acquire_host,	sdhci_generic_acquire_host),
658a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(mmcbr_release_host,	sdhci_generic_release_host),
659a9387eb1SOleksandr Tymoshenko 
660adc99a8aSOleksandr Tymoshenko 	/* Platform transfer methods */
661adc99a8aSOleksandr Tymoshenko 	DEVMETHOD(sdhci_platform_will_handle,		bcm_sdhci_will_handle_transfer),
662adc99a8aSOleksandr Tymoshenko 	DEVMETHOD(sdhci_platform_start_transfer,	bcm_sdhci_start_transfer),
663adc99a8aSOleksandr Tymoshenko 	DEVMETHOD(sdhci_platform_finish_transfer,	bcm_sdhci_finish_transfer),
664adc99a8aSOleksandr Tymoshenko 	/* SDHCI registers accessors */
665a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(sdhci_read_1,		bcm_sdhci_read_1),
666a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(sdhci_read_2,		bcm_sdhci_read_2),
667a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(sdhci_read_4,		bcm_sdhci_read_4),
668a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(sdhci_read_multi_4,	bcm_sdhci_read_multi_4),
669a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(sdhci_write_1,	bcm_sdhci_write_1),
670a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(sdhci_write_2,	bcm_sdhci_write_2),
671a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(sdhci_write_4,	bcm_sdhci_write_4),
672a9387eb1SOleksandr Tymoshenko 	DEVMETHOD(sdhci_write_multi_4,	bcm_sdhci_write_multi_4),
673a9387eb1SOleksandr Tymoshenko 
674b440e965SMarius Strobl 	DEVMETHOD_END
675a9387eb1SOleksandr Tymoshenko };
676a9387eb1SOleksandr Tymoshenko 
677a9387eb1SOleksandr Tymoshenko static devclass_t bcm_sdhci_devclass;
678a9387eb1SOleksandr Tymoshenko 
679a9387eb1SOleksandr Tymoshenko static driver_t bcm_sdhci_driver = {
680a9387eb1SOleksandr Tymoshenko 	"sdhci_bcm",
681a9387eb1SOleksandr Tymoshenko 	bcm_sdhci_methods,
682a9387eb1SOleksandr Tymoshenko 	sizeof(struct bcm_sdhci_softc),
683a9387eb1SOleksandr Tymoshenko };
684a9387eb1SOleksandr Tymoshenko 
685b440e965SMarius Strobl DRIVER_MODULE(sdhci_bcm, simplebus, bcm_sdhci_driver, bcm_sdhci_devclass,
686b440e965SMarius Strobl     NULL, NULL);
687a9387eb1SOleksandr Tymoshenko MODULE_DEPEND(sdhci_bcm, sdhci, 1, 1, 1);
68855dae242SMarius Strobl MMC_DECLARE_BRIDGE(sdhci_bcm);
689