bcm2835_sdhci.c revision 242321
1/*-
2 * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c 242321 2012-10-29 17:23:45Z gonzo $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bio.h>
33#include <sys/bus.h>
34#include <sys/conf.h>
35#include <sys/endian.h>
36#include <sys/kernel.h>
37#include <sys/kthread.h>
38#include <sys/lock.h>
39#include <sys/malloc.h>
40#include <sys/module.h>
41#include <sys/mutex.h>
42#include <sys/queue.h>
43#include <sys/resource.h>
44#include <sys/rman.h>
45#include <sys/taskqueue.h>
46#include <sys/time.h>
47#include <sys/timetc.h>
48#include <sys/watchdog.h>
49
50#include <sys/kdb.h>
51
52#include <machine/bus.h>
53#include <machine/cpu.h>
54#include <machine/cpufunc.h>
55#include <machine/resource.h>
56#include <machine/frame.h>
57#include <machine/intr.h>
58
59#include <dev/ofw/ofw_bus.h>
60#include <dev/ofw/ofw_bus_subr.h>
61
62#include <dev/mmc/bridge.h>
63#include <dev/mmc/mmcreg.h>
64#include <dev/mmc/mmcbrvar.h>
65
66#include <dev/sdhci/sdhci.h>
67#include "sdhci_if.h"
68
69#define	DEBUG
70
71#ifdef DEBUG
72#define dprintf(fmt, args...) do { printf("%s(): ", __func__);   \
73    printf(fmt,##args); } while (0)
74#else
75#define dprintf(fmt, args...)
76#endif
77
78struct bcm_sdhci_dmamap_arg {
79	bus_addr_t		sc_dma_busaddr;
80};
81
82struct bcm_sdhci_softc {
83	device_t		sc_dev;
84	struct mtx		sc_mtx;
85	struct resource *	sc_mem_res;
86	struct resource *	sc_irq_res;
87	bus_space_tag_t		sc_bst;
88	bus_space_handle_t	sc_bsh;
89	void *			sc_intrhand;
90	struct mmc_request *	sc_req;
91	struct mmc_data *	sc_data;
92	uint32_t		sc_flags;
93#define	LPC_SD_FLAGS_IGNORECRC		(1 << 0)
94	int			sc_xfer_direction;
95#define	DIRECTION_READ		0
96#define	DIRECTION_WRITE		1
97	int			sc_xfer_done;
98	int			sc_bus_busy;
99	struct sdhci_slot	sc_slot;
100};
101
102#define	SD_MAX_BLOCKSIZE	1024
103/* XXX */
104
105static int bcm_sdhci_probe(device_t);
106static int bcm_sdhci_attach(device_t);
107static int bcm_sdhci_detach(device_t);
108static void bcm_sdhci_intr(void *);
109
110static int bcm_sdhci_get_ro(device_t, device_t);
111
112#define	bcm_sdhci_lock(_sc)						\
113    mtx_lock(&_sc->sc_mtx);
114#define	bcm_sdhci_unlock(_sc)						\
115    mtx_unlock(&_sc->sc_mtx);
116
117static int
118bcm_sdhci_probe(device_t dev)
119{
120	if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-sdhci"))
121		return (ENXIO);
122
123	device_set_desc(dev, "Broadcom 2708 SDHCI controller");
124	return (BUS_PROBE_DEFAULT);
125}
126
127static int
128bcm_sdhci_attach(device_t dev)
129{
130	struct bcm_sdhci_softc *sc = device_get_softc(dev);
131	int rid, err;
132
133	sc->sc_dev = dev;
134	sc->sc_req = NULL;
135
136	mtx_init(&sc->sc_mtx, "bcm sdhci", "sdhci", MTX_DEF);
137
138	rid = 0;
139	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
140	    RF_ACTIVE);
141	if (!sc->sc_mem_res) {
142		device_printf(dev, "cannot allocate memory window\n");
143		err = ENXIO;
144		goto fail;
145	}
146
147	sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
148	sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
149
150	rid = 0;
151	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
152	    RF_ACTIVE);
153	if (!sc->sc_irq_res) {
154		device_printf(dev, "cannot allocate interrupt\n");
155		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
156		err = ENXIO;
157		goto fail;
158	}
159
160	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
161	    NULL, bcm_sdhci_intr, sc, &sc->sc_intrhand))
162	{
163		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
164		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
165		device_printf(dev, "cannot setup interrupt handler\n");
166		err = ENXIO;
167		goto fail;
168	}
169
170	sc->sc_slot.caps = SDHCI_CAN_VDD_330 | SDHCI_CAN_VDD_180 | SDHCI_CAN_DO_HISPD;
171	sc->sc_slot.caps |= (50 << SDHCI_CLOCK_BASE_SHIFT);
172	sc->sc_slot.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK
173		| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
174		| SDHCI_QUIRK_MISSING_CAPS;
175
176	sdhci_init_slot(dev, &sc->sc_slot, 0);
177
178	bus_generic_probe(dev);
179	bus_generic_attach(dev);
180
181	sdhci_start_slot(&sc->sc_slot);
182
183	return (0);
184
185fail:
186	if (sc->sc_intrhand)
187		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
188	if (sc->sc_irq_res)
189		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
190	if (sc->sc_mem_res)
191		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
192
193	return (err);
194}
195
196static int
197bcm_sdhci_detach(device_t dev)
198{
199
200	return (EBUSY);
201}
202
203static void
204bcm_sdhci_intr(void *arg)
205{
206	struct bcm_sdhci_softc *sc = arg;
207
208	sdhci_generic_intr(&sc->sc_slot);
209}
210
211static int
212bcm_sdhci_get_ro(device_t bus, device_t child)
213{
214
215	return (0);
216}
217
218static inline uint32_t
219RD4(struct bcm_sdhci_softc *sc, bus_size_t off)
220{
221	uint32_t val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off);
222	return val;
223}
224
225static inline void
226WR4(struct bcm_sdhci_softc *sc, bus_size_t off, uint32_t val)
227{
228	bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);
229
230	if ((off != SDHCI_BUFFER && off != SDHCI_INT_STATUS && off != SDHCI_CLOCK_CONTROL))
231	{
232		int timeout = 100000;
233		while (val != bus_space_read_4(sc->sc_bst, sc->sc_bsh, off)
234		    && --timeout > 0)
235			continue;
236
237		if (timeout <= 0)
238			printf("sdhci_brcm: writing 0x%X to reg 0x%X "
239				"always gives 0x%X\n",
240				val, (uint32_t)off,
241				bus_space_read_4(sc->sc_bst, sc->sc_bsh, off));
242	}
243}
244
245static uint8_t
246bcm_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
247{
248	struct bcm_sdhci_softc *sc = device_get_softc(dev);
249	uint32_t val = RD4(sc, off & ~3);
250
251	return ((val >> (off & 3)*8) & 0xff);
252}
253
254static uint16_t
255bcm_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
256{
257	struct bcm_sdhci_softc *sc = device_get_softc(dev);
258	uint32_t val = RD4(sc, off & ~3);
259
260	return ((val >> (off & 3)*8) & 0xffff);
261}
262
263static uint32_t
264bcm_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
265{
266	struct bcm_sdhci_softc *sc = device_get_softc(dev);
267
268	return RD4(sc, off);
269}
270
271static void
272bcm_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
273    uint32_t *data, bus_size_t count)
274{
275	struct bcm_sdhci_softc *sc = device_get_softc(dev);
276
277	bus_space_read_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count);
278}
279
280static void
281bcm_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val)
282{
283	struct bcm_sdhci_softc *sc = device_get_softc(dev);
284	uint32_t val32 = RD4(sc, off & ~3);
285	val32 &= ~(0xff << (off & 3)*8);
286	val32 |= (val << (off & 3)*8);
287	WR4(sc, off & ~3, val32);
288}
289
290static void
291bcm_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val)
292{
293	struct bcm_sdhci_softc *sc = device_get_softc(dev);
294	static uint32_t cmd_and_trandfer_mode;
295	uint32_t val32;
296	if (off == SDHCI_COMMAND_FLAGS)
297		val32 = cmd_and_trandfer_mode;
298	else
299		val32 = RD4(sc, off & ~3);
300	val32 &= ~(0xffff << (off & 3)*8);
301	val32 |= (val << (off & 3)*8);
302	if (off == SDHCI_TRANSFER_MODE)
303		cmd_and_trandfer_mode = val32;
304	else
305		WR4(sc, off & ~3, val32);
306}
307
308static void
309bcm_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val)
310{
311	struct bcm_sdhci_softc *sc = device_get_softc(dev);
312	WR4(sc, off, val);
313}
314
315static void
316bcm_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
317    uint32_t *data, bus_size_t count)
318{
319	struct bcm_sdhci_softc *sc = device_get_softc(dev);
320
321	bus_space_write_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count);
322}
323
324static device_method_t bcm_sdhci_methods[] = {
325	/* Device interface */
326	DEVMETHOD(device_probe,		bcm_sdhci_probe),
327	DEVMETHOD(device_attach,	bcm_sdhci_attach),
328	DEVMETHOD(device_detach,	bcm_sdhci_detach),
329
330	/* Bus interface */
331	DEVMETHOD(bus_read_ivar,	sdhci_generic_read_ivar),
332	DEVMETHOD(bus_write_ivar,	sdhci_generic_write_ivar),
333	DEVMETHOD(bus_print_child,	bus_generic_print_child),
334
335	/* MMC bridge interface */
336	DEVMETHOD(mmcbr_update_ios,	sdhci_generic_update_ios),
337	DEVMETHOD(mmcbr_request,	sdhci_generic_request),
338	DEVMETHOD(mmcbr_get_ro,		bcm_sdhci_get_ro),
339	DEVMETHOD(mmcbr_acquire_host,	sdhci_generic_acquire_host),
340	DEVMETHOD(mmcbr_release_host,	sdhci_generic_release_host),
341
342	/* SDHCI registers accessors */
343	DEVMETHOD(sdhci_read_1,		bcm_sdhci_read_1),
344	DEVMETHOD(sdhci_read_2,		bcm_sdhci_read_2),
345	DEVMETHOD(sdhci_read_4,		bcm_sdhci_read_4),
346	DEVMETHOD(sdhci_read_multi_4,	bcm_sdhci_read_multi_4),
347	DEVMETHOD(sdhci_write_1,	bcm_sdhci_write_1),
348	DEVMETHOD(sdhci_write_2,	bcm_sdhci_write_2),
349	DEVMETHOD(sdhci_write_4,	bcm_sdhci_write_4),
350	DEVMETHOD(sdhci_write_multi_4,	bcm_sdhci_write_multi_4),
351
352	{ 0, 0 }
353};
354
355static devclass_t bcm_sdhci_devclass;
356
357static driver_t bcm_sdhci_driver = {
358	"sdhci_bcm",
359	bcm_sdhci_methods,
360	sizeof(struct bcm_sdhci_softc),
361};
362
363DRIVER_MODULE(sdhci_bcm, simplebus, bcm_sdhci_driver, bcm_sdhci_devclass, 0, 0);
364MODULE_DEPEND(sdhci_bcm, sdhci, 1, 1, 1);
365