bcm2835_sdhci.c revision 270948
1242321Sgonzo/*-
2242321Sgonzo * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
3242321Sgonzo * All rights reserved.
4242321Sgonzo *
5242321Sgonzo * Redistribution and use in source and binary forms, with or without
6242321Sgonzo * modification, are permitted provided that the following conditions
7242321Sgonzo * are met:
8242321Sgonzo * 1. Redistributions of source code must retain the above copyright
9242321Sgonzo *    notice, this list of conditions and the following disclaimer.
10242321Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
11242321Sgonzo *    notice, this list of conditions and the following disclaimer in the
12242321Sgonzo *    documentation and/or other materials provided with the distribution.
13242321Sgonzo *
14242321Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15242321Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16242321Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17242321Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18242321Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19242321Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20242321Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21242321Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22242321Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23242321Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24242321Sgonzo * SUCH DAMAGE.
25242321Sgonzo *
26242321Sgonzo */
27242321Sgonzo#include <sys/cdefs.h>
28242321Sgonzo__FBSDID("$FreeBSD: head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c 270948 2014-09-01 19:20:34Z ian $");
29242321Sgonzo
30242321Sgonzo#include <sys/param.h>
31242321Sgonzo#include <sys/systm.h>
32242321Sgonzo#include <sys/bio.h>
33242321Sgonzo#include <sys/bus.h>
34242321Sgonzo#include <sys/conf.h>
35242321Sgonzo#include <sys/endian.h>
36242321Sgonzo#include <sys/kernel.h>
37242321Sgonzo#include <sys/kthread.h>
38242321Sgonzo#include <sys/lock.h>
39242321Sgonzo#include <sys/malloc.h>
40242321Sgonzo#include <sys/module.h>
41242321Sgonzo#include <sys/mutex.h>
42242321Sgonzo#include <sys/queue.h>
43242321Sgonzo#include <sys/resource.h>
44242321Sgonzo#include <sys/rman.h>
45270948Sian#include <sys/sysctl.h>
46242321Sgonzo#include <sys/taskqueue.h>
47242321Sgonzo#include <sys/time.h>
48242321Sgonzo#include <sys/timetc.h>
49242321Sgonzo#include <sys/watchdog.h>
50242321Sgonzo
51242321Sgonzo#include <sys/kdb.h>
52242321Sgonzo
53242321Sgonzo#include <machine/bus.h>
54242321Sgonzo#include <machine/cpu.h>
55242321Sgonzo#include <machine/cpufunc.h>
56242321Sgonzo#include <machine/resource.h>
57242321Sgonzo#include <machine/intr.h>
58242321Sgonzo
59243688Sgonzo#include <dev/fdt/fdt_common.h>
60242321Sgonzo#include <dev/ofw/ofw_bus.h>
61242321Sgonzo#include <dev/ofw/ofw_bus_subr.h>
62242321Sgonzo
63242321Sgonzo#include <dev/mmc/bridge.h>
64242321Sgonzo#include <dev/mmc/mmcreg.h>
65242321Sgonzo#include <dev/mmc/mmcbrvar.h>
66242321Sgonzo
67242321Sgonzo#include <dev/sdhci/sdhci.h>
68242321Sgonzo#include "sdhci_if.h"
69242321Sgonzo
70247497Sgonzo#include "bcm2835_dma.h"
71247497Sgonzo#include "bcm2835_vcbus.h"
72247497Sgonzo
73243688Sgonzo#define	BCM2835_DEFAULT_SDHCI_FREQ	50
74243688Sgonzo
75247497Sgonzo#define	BCM_SDHCI_BUFFER_SIZE		512
76247497Sgonzo
77242321Sgonzo#ifdef DEBUG
78242321Sgonzo#define dprintf(fmt, args...) do { printf("%s(): ", __func__);   \
79242321Sgonzo    printf(fmt,##args); } while (0)
80242321Sgonzo#else
81242321Sgonzo#define dprintf(fmt, args...)
82242321Sgonzo#endif
83242321Sgonzo
84247009Sgonzo/*
85247009Sgonzo * Arasan HC seems to have problem with Data CRC on lower frequencies.
86247010Sgonzo * Use this tunable to cap initialization sequence frequency at higher
87247010Sgonzo * value. Default is standard 400kHz
88247009Sgonzo */
89247009Sgonzostatic int bcm2835_sdhci_min_freq = 400000;
90246888Sgonzostatic int bcm2835_sdhci_hs = 1;
91247497Sgonzostatic int bcm2835_sdhci_pio_mode = 0;
92246888Sgonzo
93246888SgonzoTUNABLE_INT("hw.bcm2835.sdhci.min_freq", &bcm2835_sdhci_min_freq);
94246888SgonzoTUNABLE_INT("hw.bcm2835.sdhci.hs", &bcm2835_sdhci_hs);
95247497SgonzoTUNABLE_INT("hw.bcm2835.sdhci.pio_mode", &bcm2835_sdhci_pio_mode);
96246888Sgonzo
97242321Sgonzostruct bcm_sdhci_dmamap_arg {
98242321Sgonzo	bus_addr_t		sc_dma_busaddr;
99242321Sgonzo};
100242321Sgonzo
101242321Sgonzostruct bcm_sdhci_softc {
102242321Sgonzo	device_t		sc_dev;
103242321Sgonzo	struct mtx		sc_mtx;
104242321Sgonzo	struct resource *	sc_mem_res;
105242321Sgonzo	struct resource *	sc_irq_res;
106242321Sgonzo	bus_space_tag_t		sc_bst;
107242321Sgonzo	bus_space_handle_t	sc_bsh;
108242321Sgonzo	void *			sc_intrhand;
109242321Sgonzo	struct mmc_request *	sc_req;
110242321Sgonzo	struct mmc_data *	sc_data;
111242321Sgonzo	uint32_t		sc_flags;
112242321Sgonzo#define	LPC_SD_FLAGS_IGNORECRC		(1 << 0)
113242321Sgonzo	int			sc_xfer_direction;
114242321Sgonzo#define	DIRECTION_READ		0
115242321Sgonzo#define	DIRECTION_WRITE		1
116242321Sgonzo	int			sc_xfer_done;
117242321Sgonzo	int			sc_bus_busy;
118242321Sgonzo	struct sdhci_slot	sc_slot;
119247497Sgonzo	int			sc_dma_inuse;
120247497Sgonzo	int			sc_dma_ch;
121247497Sgonzo	bus_dma_tag_t		sc_dma_tag;
122247497Sgonzo	bus_dmamap_t		sc_dma_map;
123248430Sian	vm_paddr_t		sc_sdhci_buffer_phys;
124242321Sgonzo};
125242321Sgonzo
126242321Sgonzostatic int bcm_sdhci_probe(device_t);
127242321Sgonzostatic int bcm_sdhci_attach(device_t);
128242321Sgonzostatic int bcm_sdhci_detach(device_t);
129242321Sgonzostatic void bcm_sdhci_intr(void *);
130242321Sgonzo
131242321Sgonzostatic int bcm_sdhci_get_ro(device_t, device_t);
132247497Sgonzostatic void bcm_sdhci_dma_intr(int ch, void *arg);
133242321Sgonzo
134242321Sgonzo#define	bcm_sdhci_lock(_sc)						\
135242321Sgonzo    mtx_lock(&_sc->sc_mtx);
136242321Sgonzo#define	bcm_sdhci_unlock(_sc)						\
137242321Sgonzo    mtx_unlock(&_sc->sc_mtx);
138242321Sgonzo
139247497Sgonzostatic void
140247497Sgonzobcm_dmamap_cb(void *arg, bus_dma_segment_t *segs,
141247497Sgonzo	int nseg, int err)
142247497Sgonzo{
143247497Sgonzo        bus_addr_t *addr;
144247497Sgonzo
145247497Sgonzo        if (err)
146247497Sgonzo                return;
147247497Sgonzo
148247497Sgonzo        addr = (bus_addr_t*)arg;
149247497Sgonzo        *addr = segs[0].ds_addr;
150247497Sgonzo}
151247497Sgonzo
152242321Sgonzostatic int
153242321Sgonzobcm_sdhci_probe(device_t dev)
154242321Sgonzo{
155261410Sian
156261410Sian	if (!ofw_bus_status_okay(dev))
157261410Sian		return (ENXIO);
158261410Sian
159242321Sgonzo	if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-sdhci"))
160242321Sgonzo		return (ENXIO);
161242321Sgonzo
162242321Sgonzo	device_set_desc(dev, "Broadcom 2708 SDHCI controller");
163242321Sgonzo	return (BUS_PROBE_DEFAULT);
164242321Sgonzo}
165242321Sgonzo
166242321Sgonzostatic int
167242321Sgonzobcm_sdhci_attach(device_t dev)
168242321Sgonzo{
169242321Sgonzo	struct bcm_sdhci_softc *sc = device_get_softc(dev);
170242321Sgonzo	int rid, err;
171243688Sgonzo	phandle_t node;
172243688Sgonzo	pcell_t cell;
173243688Sgonzo	int default_freq;
174242321Sgonzo
175242321Sgonzo	sc->sc_dev = dev;
176242321Sgonzo	sc->sc_req = NULL;
177247497Sgonzo	err = 0;
178242321Sgonzo
179243688Sgonzo	default_freq = BCM2835_DEFAULT_SDHCI_FREQ;
180243688Sgonzo	node = ofw_bus_get_node(sc->sc_dev);
181243688Sgonzo	if ((OF_getprop(node, "clock-frequency", &cell, sizeof(cell))) > 0)
182243688Sgonzo		default_freq = (int)fdt32_to_cpu(cell)/1000000;
183243688Sgonzo
184243688Sgonzo	dprintf("SDHCI frequency: %dMHz\n", default_freq);
185243688Sgonzo
186242321Sgonzo	mtx_init(&sc->sc_mtx, "bcm sdhci", "sdhci", MTX_DEF);
187242321Sgonzo
188242321Sgonzo	rid = 0;
189242321Sgonzo	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
190242321Sgonzo	    RF_ACTIVE);
191242321Sgonzo	if (!sc->sc_mem_res) {
192242321Sgonzo		device_printf(dev, "cannot allocate memory window\n");
193242321Sgonzo		err = ENXIO;
194242321Sgonzo		goto fail;
195242321Sgonzo	}
196242321Sgonzo
197242321Sgonzo	sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
198242321Sgonzo	sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
199242321Sgonzo
200242321Sgonzo	rid = 0;
201242321Sgonzo	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
202242321Sgonzo	    RF_ACTIVE);
203242321Sgonzo	if (!sc->sc_irq_res) {
204242321Sgonzo		device_printf(dev, "cannot allocate interrupt\n");
205242321Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
206242321Sgonzo		err = ENXIO;
207242321Sgonzo		goto fail;
208242321Sgonzo	}
209242321Sgonzo
210248430Sian	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
211242321Sgonzo	    NULL, bcm_sdhci_intr, sc, &sc->sc_intrhand))
212242321Sgonzo	{
213242321Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
214242321Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
215242321Sgonzo		device_printf(dev, "cannot setup interrupt handler\n");
216242321Sgonzo		err = ENXIO;
217242321Sgonzo		goto fail;
218242321Sgonzo	}
219242321Sgonzo
220247497Sgonzo	if (!bcm2835_sdhci_pio_mode)
221247497Sgonzo		sc->sc_slot.opt = SDHCI_PLATFORM_TRANSFER;
222247497Sgonzo
223246888Sgonzo	sc->sc_slot.caps = SDHCI_CAN_VDD_330 | SDHCI_CAN_VDD_180;
224246888Sgonzo	if (bcm2835_sdhci_hs)
225246888Sgonzo		sc->sc_slot.caps |= SDHCI_CAN_DO_HISPD;
226243688Sgonzo	sc->sc_slot.caps |= (default_freq << SDHCI_CLOCK_BASE_SHIFT);
227242321Sgonzo	sc->sc_slot.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK
228242321Sgonzo		| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
229242321Sgonzo		| SDHCI_QUIRK_MISSING_CAPS;
230242321Sgonzo
231242321Sgonzo	sdhci_init_slot(dev, &sc->sc_slot, 0);
232242321Sgonzo
233247497Sgonzo	sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_FAST1);
234247497Sgonzo	if (sc->sc_dma_ch == BCM_DMA_CH_INVALID)
235247497Sgonzo		sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_FAST2);
236247497Sgonzo	if (sc->sc_dma_ch == BCM_DMA_CH_INVALID)
237247497Sgonzo		sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_ANY);
238247497Sgonzo	if (sc->sc_dma_ch == BCM_DMA_CH_INVALID)
239247497Sgonzo		goto fail;
240247497Sgonzo
241247497Sgonzo	bcm_dma_setup_intr(sc->sc_dma_ch, bcm_sdhci_dma_intr, sc);
242247497Sgonzo
243248430Sian	/* Allocate bus_dma resources. */
244247497Sgonzo	err = bus_dma_tag_create(bus_get_dma_tag(dev),
245247497Sgonzo	    1, 0, BUS_SPACE_MAXADDR_32BIT,
246247497Sgonzo	    BUS_SPACE_MAXADDR, NULL, NULL,
247247497Sgonzo	    BCM_SDHCI_BUFFER_SIZE, 1, BCM_SDHCI_BUFFER_SIZE,
248247497Sgonzo	    BUS_DMA_ALLOCNOW, NULL, NULL,
249247497Sgonzo	    &sc->sc_dma_tag);
250247497Sgonzo
251247497Sgonzo	if (err) {
252247497Sgonzo		device_printf(dev, "failed allocate DMA tag");
253247497Sgonzo		goto fail;
254247497Sgonzo	}
255247497Sgonzo
256248430Sian	err = bus_dmamap_create(sc->sc_dma_tag, 0, &sc->sc_dma_map);
257247497Sgonzo	if (err) {
258248430Sian		device_printf(dev, "bus_dmamap_create failed\n");
259247497Sgonzo		goto fail;
260247497Sgonzo	}
261247497Sgonzo
262248407Sian	sc->sc_sdhci_buffer_phys = BUS_SPACE_PHYSADDR(sc->sc_mem_res,
263248407Sian	    SDHCI_BUFFER);
264247497Sgonzo
265242321Sgonzo	bus_generic_probe(dev);
266242321Sgonzo	bus_generic_attach(dev);
267242321Sgonzo
268242321Sgonzo	sdhci_start_slot(&sc->sc_slot);
269242321Sgonzo
270242321Sgonzo	return (0);
271242321Sgonzo
272242321Sgonzofail:
273242321Sgonzo	if (sc->sc_intrhand)
274242321Sgonzo		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
275242321Sgonzo	if (sc->sc_irq_res)
276242321Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
277242321Sgonzo	if (sc->sc_mem_res)
278242321Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
279242321Sgonzo
280242321Sgonzo	return (err);
281242321Sgonzo}
282242321Sgonzo
283242321Sgonzostatic int
284242321Sgonzobcm_sdhci_detach(device_t dev)
285242321Sgonzo{
286242321Sgonzo
287242321Sgonzo	return (EBUSY);
288242321Sgonzo}
289242321Sgonzo
290242321Sgonzostatic void
291242321Sgonzobcm_sdhci_intr(void *arg)
292242321Sgonzo{
293242321Sgonzo	struct bcm_sdhci_softc *sc = arg;
294242321Sgonzo
295242321Sgonzo	sdhci_generic_intr(&sc->sc_slot);
296242321Sgonzo}
297242321Sgonzo
298242321Sgonzostatic int
299242321Sgonzobcm_sdhci_get_ro(device_t bus, device_t child)
300242321Sgonzo{
301242321Sgonzo
302242321Sgonzo	return (0);
303242321Sgonzo}
304242321Sgonzo
305242321Sgonzostatic inline uint32_t
306242321SgonzoRD4(struct bcm_sdhci_softc *sc, bus_size_t off)
307242321Sgonzo{
308242321Sgonzo	uint32_t val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off);
309242321Sgonzo	return val;
310242321Sgonzo}
311242321Sgonzo
312242321Sgonzostatic inline void
313242321SgonzoWR4(struct bcm_sdhci_softc *sc, bus_size_t off, uint32_t val)
314242321Sgonzo{
315242321Sgonzo	bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);
316242321Sgonzo
317242321Sgonzo	if ((off != SDHCI_BUFFER && off != SDHCI_INT_STATUS && off != SDHCI_CLOCK_CONTROL))
318242321Sgonzo	{
319242321Sgonzo		int timeout = 100000;
320242321Sgonzo		while (val != bus_space_read_4(sc->sc_bst, sc->sc_bsh, off)
321242321Sgonzo		    && --timeout > 0)
322242321Sgonzo			continue;
323242321Sgonzo
324242321Sgonzo		if (timeout <= 0)
325242321Sgonzo			printf("sdhci_brcm: writing 0x%X to reg 0x%X "
326242321Sgonzo				"always gives 0x%X\n",
327242321Sgonzo				val, (uint32_t)off,
328242321Sgonzo				bus_space_read_4(sc->sc_bst, sc->sc_bsh, off));
329242321Sgonzo	}
330242321Sgonzo}
331242321Sgonzo
332242321Sgonzostatic uint8_t
333242321Sgonzobcm_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
334242321Sgonzo{
335242321Sgonzo	struct bcm_sdhci_softc *sc = device_get_softc(dev);
336242321Sgonzo	uint32_t val = RD4(sc, off & ~3);
337242321Sgonzo
338242321Sgonzo	return ((val >> (off & 3)*8) & 0xff);
339242321Sgonzo}
340242321Sgonzo
341242321Sgonzostatic uint16_t
342242321Sgonzobcm_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
343242321Sgonzo{
344242321Sgonzo	struct bcm_sdhci_softc *sc = device_get_softc(dev);
345242321Sgonzo	uint32_t val = RD4(sc, off & ~3);
346242321Sgonzo
347242321Sgonzo	return ((val >> (off & 3)*8) & 0xffff);
348242321Sgonzo}
349242321Sgonzo
350242321Sgonzostatic uint32_t
351242321Sgonzobcm_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
352242321Sgonzo{
353242321Sgonzo	struct bcm_sdhci_softc *sc = device_get_softc(dev);
354242321Sgonzo
355242321Sgonzo	return RD4(sc, off);
356242321Sgonzo}
357242321Sgonzo
358242321Sgonzostatic void
359242321Sgonzobcm_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
360242321Sgonzo    uint32_t *data, bus_size_t count)
361242321Sgonzo{
362242321Sgonzo	struct bcm_sdhci_softc *sc = device_get_softc(dev);
363242321Sgonzo
364242321Sgonzo	bus_space_read_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count);
365242321Sgonzo}
366242321Sgonzo
367242321Sgonzostatic void
368242321Sgonzobcm_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val)
369242321Sgonzo{
370242321Sgonzo	struct bcm_sdhci_softc *sc = device_get_softc(dev);
371242321Sgonzo	uint32_t val32 = RD4(sc, off & ~3);
372242321Sgonzo	val32 &= ~(0xff << (off & 3)*8);
373242321Sgonzo	val32 |= (val << (off & 3)*8);
374242321Sgonzo	WR4(sc, off & ~3, val32);
375242321Sgonzo}
376242321Sgonzo
377242321Sgonzostatic void
378242321Sgonzobcm_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val)
379242321Sgonzo{
380242321Sgonzo	struct bcm_sdhci_softc *sc = device_get_softc(dev);
381242321Sgonzo	static uint32_t cmd_and_trandfer_mode;
382242321Sgonzo	uint32_t val32;
383242321Sgonzo	if (off == SDHCI_COMMAND_FLAGS)
384242321Sgonzo		val32 = cmd_and_trandfer_mode;
385242321Sgonzo	else
386242321Sgonzo		val32 = RD4(sc, off & ~3);
387242321Sgonzo	val32 &= ~(0xffff << (off & 3)*8);
388242321Sgonzo	val32 |= (val << (off & 3)*8);
389242321Sgonzo	if (off == SDHCI_TRANSFER_MODE)
390242321Sgonzo		cmd_and_trandfer_mode = val32;
391242321Sgonzo	else
392242321Sgonzo		WR4(sc, off & ~3, val32);
393242321Sgonzo}
394242321Sgonzo
395242321Sgonzostatic void
396242321Sgonzobcm_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val)
397242321Sgonzo{
398242321Sgonzo	struct bcm_sdhci_softc *sc = device_get_softc(dev);
399242321Sgonzo	WR4(sc, off, val);
400242321Sgonzo}
401242321Sgonzo
402242321Sgonzostatic void
403242321Sgonzobcm_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
404242321Sgonzo    uint32_t *data, bus_size_t count)
405242321Sgonzo{
406242321Sgonzo	struct bcm_sdhci_softc *sc = device_get_softc(dev);
407242321Sgonzo
408242321Sgonzo	bus_space_write_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count);
409242321Sgonzo}
410242321Sgonzo
411246888Sgonzostatic uint32_t
412246888Sgonzobcm_sdhci_min_freq(device_t dev, struct sdhci_slot *slot)
413246888Sgonzo{
414246888Sgonzo
415246888Sgonzo	return bcm2835_sdhci_min_freq;
416246888Sgonzo}
417246888Sgonzo
418247497Sgonzostatic void
419247497Sgonzobcm_sdhci_dma_intr(int ch, void *arg)
420247497Sgonzo{
421247497Sgonzo	struct bcm_sdhci_softc *sc = (struct bcm_sdhci_softc *)arg;
422247497Sgonzo	struct sdhci_slot *slot = &sc->sc_slot;
423247497Sgonzo	uint32_t reg, mask;
424248430Sian	bus_addr_t pmem;
425248430Sian	vm_paddr_t pdst, psrc;
426247497Sgonzo	size_t len;
427248430Sian	int left, sync_op;
428247497Sgonzo
429247497Sgonzo	mtx_lock(&slot->mtx);
430247497Sgonzo
431247497Sgonzo	len = bcm_dma_length(sc->sc_dma_ch);
432247497Sgonzo	if (slot->curcmd->data->flags & MMC_DATA_READ) {
433248430Sian		sync_op = BUS_DMASYNC_POSTREAD;
434247497Sgonzo		mask = SDHCI_INT_DATA_AVAIL;
435247497Sgonzo	} else {
436248430Sian		sync_op = BUS_DMASYNC_POSTWRITE;
437247497Sgonzo		mask = SDHCI_INT_SPACE_AVAIL;
438247497Sgonzo	}
439248430Sian	bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op);
440248430Sian	bus_dmamap_unload(sc->sc_dma_tag, sc->sc_dma_map);
441247497Sgonzo
442247497Sgonzo	slot->offset += len;
443247497Sgonzo	sc->sc_dma_inuse = 0;
444247497Sgonzo
445247497Sgonzo	left = min(BCM_SDHCI_BUFFER_SIZE,
446247497Sgonzo	    slot->curcmd->data->len - slot->offset);
447247497Sgonzo
448247497Sgonzo	/* DATA END? */
449247497Sgonzo	reg = bcm_sdhci_read_4(slot->bus, slot, SDHCI_INT_STATUS);
450247497Sgonzo
451247497Sgonzo	if (reg & SDHCI_INT_DATA_END) {
452247497Sgonzo		/* ACK for all outstanding interrupts */
453247497Sgonzo		bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS, reg);
454247497Sgonzo
455247497Sgonzo		/* enable INT */
456247497Sgonzo		slot->intmask |= SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL
457247497Sgonzo		    | SDHCI_INT_DATA_END;
458247497Sgonzo		bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE,
459247497Sgonzo		    slot->intmask);
460247497Sgonzo
461247497Sgonzo		/* finish this data */
462247497Sgonzo		sdhci_finish_data(slot);
463247497Sgonzo	}
464247497Sgonzo	else {
465247497Sgonzo		/* already available? */
466247497Sgonzo		if (reg & mask) {
467247497Sgonzo			sc->sc_dma_inuse = 1;
468247497Sgonzo
469247497Sgonzo			/* ACK for DATA_AVAIL or SPACE_AVAIL */
470247497Sgonzo			bcm_sdhci_write_4(slot->bus, slot,
471247497Sgonzo			    SDHCI_INT_STATUS, mask);
472247497Sgonzo
473247497Sgonzo			/* continue next DMA transfer */
474248430Sian			bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map,
475248430Sian			    (uint8_t *)slot->curcmd->data->data +
476248430Sian			    slot->offset, left, bcm_dmamap_cb, &pmem, 0);
477247497Sgonzo			if (slot->curcmd->data->flags & MMC_DATA_READ) {
478248430Sian				psrc = sc->sc_sdhci_buffer_phys;
479248430Sian				pdst = pmem;
480248430Sian				sync_op = BUS_DMASYNC_PREREAD;
481247497Sgonzo			} else {
482248430Sian				psrc = pmem;
483248430Sian				pdst = sc->sc_sdhci_buffer_phys;
484248430Sian				sync_op = BUS_DMASYNC_PREWRITE;
485247497Sgonzo			}
486248430Sian			bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op);
487248430Sian			if (bcm_dma_start(sc->sc_dma_ch, psrc, pdst, left)) {
488248430Sian				/* XXX stop xfer, other error recovery? */
489248430Sian				device_printf(sc->sc_dev, "failed DMA start\n");
490248430Sian			}
491247497Sgonzo		} else {
492247497Sgonzo			/* wait for next data by INT */
493247497Sgonzo
494247497Sgonzo			/* enable INT */
495247497Sgonzo			slot->intmask |= SDHCI_INT_DATA_AVAIL |
496247497Sgonzo			    SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END;
497247497Sgonzo			bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE,
498247497Sgonzo			    slot->intmask);
499247497Sgonzo		}
500247497Sgonzo	}
501247497Sgonzo
502247497Sgonzo	mtx_unlock(&slot->mtx);
503247497Sgonzo}
504247497Sgonzo
505247497Sgonzostatic void
506247497Sgonzobcm_sdhci_read_dma(struct sdhci_slot *slot)
507247497Sgonzo{
508247497Sgonzo	struct bcm_sdhci_softc *sc = device_get_softc(slot->bus);
509247497Sgonzo	size_t left;
510248430Sian	bus_addr_t paddr;
511247497Sgonzo
512247497Sgonzo	if (sc->sc_dma_inuse) {
513247497Sgonzo		device_printf(sc->sc_dev, "DMA in use\n");
514247497Sgonzo		return;
515247497Sgonzo	}
516247497Sgonzo
517247497Sgonzo	sc->sc_dma_inuse = 1;
518247497Sgonzo
519247497Sgonzo	left = min(BCM_SDHCI_BUFFER_SIZE,
520247497Sgonzo	    slot->curcmd->data->len - slot->offset);
521247497Sgonzo
522247497Sgonzo	KASSERT((left & 3) == 0,
523247497Sgonzo	    ("%s: len = %d, not word-aligned", __func__, left));
524247497Sgonzo
525247497Sgonzo	bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
526247497Sgonzo	    BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
527247497Sgonzo	bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
528247497Sgonzo	    BCM_DMA_INC_ADDR,
529247497Sgonzo	    (left & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
530247497Sgonzo
531248430Sian	bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map,
532248430Sian	    (uint8_t *)slot->curcmd->data->data + slot->offset, left,
533248430Sian	    bcm_dmamap_cb, &paddr, 0);
534248430Sian
535247497Sgonzo	bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map,
536247497Sgonzo	    BUS_DMASYNC_PREREAD);
537247497Sgonzo
538247497Sgonzo	/* DMA start */
539247497Sgonzo	if (bcm_dma_start(sc->sc_dma_ch, sc->sc_sdhci_buffer_phys,
540248430Sian	    paddr, left) != 0)
541247497Sgonzo		device_printf(sc->sc_dev, "failed DMA start\n");
542247497Sgonzo}
543247497Sgonzo
544247497Sgonzostatic void
545247497Sgonzobcm_sdhci_write_dma(struct sdhci_slot *slot)
546247497Sgonzo{
547247497Sgonzo	struct bcm_sdhci_softc *sc = device_get_softc(slot->bus);
548247497Sgonzo	size_t left;
549248430Sian	bus_addr_t paddr;
550247497Sgonzo
551247497Sgonzo	if (sc->sc_dma_inuse) {
552247497Sgonzo		device_printf(sc->sc_dev, "DMA in use\n");
553247497Sgonzo		return;
554247497Sgonzo	}
555247497Sgonzo
556247497Sgonzo	sc->sc_dma_inuse = 1;
557247497Sgonzo
558247497Sgonzo	left = min(BCM_SDHCI_BUFFER_SIZE,
559247497Sgonzo	    slot->curcmd->data->len - slot->offset);
560247497Sgonzo
561247497Sgonzo	KASSERT((left & 3) == 0,
562247497Sgonzo	    ("%s: len = %d, not word-aligned", __func__, left));
563247497Sgonzo
564248430Sian	bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map,
565248430Sian	    (uint8_t *)slot->curcmd->data->data + slot->offset, left,
566248430Sian	    bcm_dmamap_cb, &paddr, 0);
567247497Sgonzo
568247497Sgonzo	bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE,
569247497Sgonzo	    BCM_DMA_INC_ADDR,
570247497Sgonzo	    (left & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT);
571247497Sgonzo	bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC,
572247497Sgonzo	    BCM_DMA_SAME_ADDR, BCM_DMA_32BIT);
573247497Sgonzo
574247497Sgonzo	bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map,
575247497Sgonzo	    BUS_DMASYNC_PREWRITE);
576247497Sgonzo
577247497Sgonzo	/* DMA start */
578248430Sian	if (bcm_dma_start(sc->sc_dma_ch, paddr,
579247497Sgonzo	    sc->sc_sdhci_buffer_phys, left) != 0)
580247497Sgonzo		device_printf(sc->sc_dev, "failed DMA start\n");
581247497Sgonzo}
582247497Sgonzo
583247497Sgonzostatic int
584247497Sgonzobcm_sdhci_will_handle_transfer(device_t dev, struct sdhci_slot *slot)
585247497Sgonzo{
586247497Sgonzo	size_t left;
587247497Sgonzo
588248430Sian	/*
589248430Sian	 * Do not use DMA for transfers less than block size or with a length
590248430Sian	 * that is not a multiple of four.
591248430Sian	 */
592247497Sgonzo	left = min(BCM_DMA_BLOCK_SIZE,
593247497Sgonzo	    slot->curcmd->data->len - slot->offset);
594247497Sgonzo	if (left < BCM_DMA_BLOCK_SIZE)
595247497Sgonzo		return (0);
596248430Sian	if (left & 0x03)
597248430Sian		return (0);
598247497Sgonzo
599247497Sgonzo	return (1);
600247497Sgonzo}
601247497Sgonzo
602247497Sgonzostatic void
603247497Sgonzobcm_sdhci_start_transfer(device_t dev, struct sdhci_slot *slot,
604247497Sgonzo    uint32_t *intmask)
605247497Sgonzo{
606247497Sgonzo
607247497Sgonzo	/* Disable INT */
608247497Sgonzo	slot->intmask &= ~(SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END);
609247497Sgonzo	bcm_sdhci_write_4(dev, slot, SDHCI_SIGNAL_ENABLE, slot->intmask);
610247497Sgonzo
611247497Sgonzo	/* DMA transfer FIFO 1KB */
612247497Sgonzo	if (slot->curcmd->data->flags & MMC_DATA_READ)
613247497Sgonzo		bcm_sdhci_read_dma(slot);
614247497Sgonzo	else
615247497Sgonzo		bcm_sdhci_write_dma(slot);
616247497Sgonzo}
617247497Sgonzo
618247497Sgonzostatic void
619247497Sgonzobcm_sdhci_finish_transfer(device_t dev, struct sdhci_slot *slot)
620247497Sgonzo{
621247497Sgonzo
622247497Sgonzo	sdhci_finish_data(slot);
623247497Sgonzo}
624247497Sgonzo
625242321Sgonzostatic device_method_t bcm_sdhci_methods[] = {
626242321Sgonzo	/* Device interface */
627242321Sgonzo	DEVMETHOD(device_probe,		bcm_sdhci_probe),
628242321Sgonzo	DEVMETHOD(device_attach,	bcm_sdhci_attach),
629242321Sgonzo	DEVMETHOD(device_detach,	bcm_sdhci_detach),
630242321Sgonzo
631242321Sgonzo	/* Bus interface */
632242321Sgonzo	DEVMETHOD(bus_read_ivar,	sdhci_generic_read_ivar),
633242321Sgonzo	DEVMETHOD(bus_write_ivar,	sdhci_generic_write_ivar),
634242321Sgonzo	DEVMETHOD(bus_print_child,	bus_generic_print_child),
635242321Sgonzo
636242321Sgonzo	/* MMC bridge interface */
637242321Sgonzo	DEVMETHOD(mmcbr_update_ios,	sdhci_generic_update_ios),
638242321Sgonzo	DEVMETHOD(mmcbr_request,	sdhci_generic_request),
639242321Sgonzo	DEVMETHOD(mmcbr_get_ro,		bcm_sdhci_get_ro),
640242321Sgonzo	DEVMETHOD(mmcbr_acquire_host,	sdhci_generic_acquire_host),
641242321Sgonzo	DEVMETHOD(mmcbr_release_host,	sdhci_generic_release_host),
642242321Sgonzo
643247497Sgonzo	DEVMETHOD(sdhci_min_freq,	bcm_sdhci_min_freq),
644247497Sgonzo	/* Platform transfer methods */
645247497Sgonzo	DEVMETHOD(sdhci_platform_will_handle,		bcm_sdhci_will_handle_transfer),
646247497Sgonzo	DEVMETHOD(sdhci_platform_start_transfer,	bcm_sdhci_start_transfer),
647247497Sgonzo	DEVMETHOD(sdhci_platform_finish_transfer,	bcm_sdhci_finish_transfer),
648242321Sgonzo	/* SDHCI registers accessors */
649242321Sgonzo	DEVMETHOD(sdhci_read_1,		bcm_sdhci_read_1),
650242321Sgonzo	DEVMETHOD(sdhci_read_2,		bcm_sdhci_read_2),
651242321Sgonzo	DEVMETHOD(sdhci_read_4,		bcm_sdhci_read_4),
652242321Sgonzo	DEVMETHOD(sdhci_read_multi_4,	bcm_sdhci_read_multi_4),
653242321Sgonzo	DEVMETHOD(sdhci_write_1,	bcm_sdhci_write_1),
654242321Sgonzo	DEVMETHOD(sdhci_write_2,	bcm_sdhci_write_2),
655242321Sgonzo	DEVMETHOD(sdhci_write_4,	bcm_sdhci_write_4),
656242321Sgonzo	DEVMETHOD(sdhci_write_multi_4,	bcm_sdhci_write_multi_4),
657242321Sgonzo
658242321Sgonzo	{ 0, 0 }
659242321Sgonzo};
660242321Sgonzo
661242321Sgonzostatic devclass_t bcm_sdhci_devclass;
662242321Sgonzo
663242321Sgonzostatic driver_t bcm_sdhci_driver = {
664242321Sgonzo	"sdhci_bcm",
665242321Sgonzo	bcm_sdhci_methods,
666242321Sgonzo	sizeof(struct bcm_sdhci_softc),
667242321Sgonzo};
668242321Sgonzo
669242321SgonzoDRIVER_MODULE(sdhci_bcm, simplebus, bcm_sdhci_driver, bcm_sdhci_devclass, 0, 0);
670242321SgonzoMODULE_DEPEND(sdhci_bcm, sdhci, 1, 1, 1);
671