bcm2835_sdhci.c revision 243688
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 243688 2012-11-30 02:32:37Z 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/fdt/fdt_common.h>
60#include <dev/ofw/ofw_bus.h>
61#include <dev/ofw/ofw_bus_subr.h>
62
63#include <dev/mmc/bridge.h>
64#include <dev/mmc/mmcreg.h>
65#include <dev/mmc/mmcbrvar.h>
66
67#include <dev/sdhci/sdhci.h>
68#include "sdhci_if.h"
69
70#define	BCM2835_DEFAULT_SDHCI_FREQ	50
71
72#define	DEBUG
73
74#ifdef DEBUG
75#define dprintf(fmt, args...) do { printf("%s(): ", __func__);   \
76    printf(fmt,##args); } while (0)
77#else
78#define dprintf(fmt, args...)
79#endif
80
81struct bcm_sdhci_dmamap_arg {
82	bus_addr_t		sc_dma_busaddr;
83};
84
85struct bcm_sdhci_softc {
86	device_t		sc_dev;
87	struct mtx		sc_mtx;
88	struct resource *	sc_mem_res;
89	struct resource *	sc_irq_res;
90	bus_space_tag_t		sc_bst;
91	bus_space_handle_t	sc_bsh;
92	void *			sc_intrhand;
93	struct mmc_request *	sc_req;
94	struct mmc_data *	sc_data;
95	uint32_t		sc_flags;
96#define	LPC_SD_FLAGS_IGNORECRC		(1 << 0)
97	int			sc_xfer_direction;
98#define	DIRECTION_READ		0
99#define	DIRECTION_WRITE		1
100	int			sc_xfer_done;
101	int			sc_bus_busy;
102	struct sdhci_slot	sc_slot;
103};
104
105#define	SD_MAX_BLOCKSIZE	1024
106/* XXX */
107
108static int bcm_sdhci_probe(device_t);
109static int bcm_sdhci_attach(device_t);
110static int bcm_sdhci_detach(device_t);
111static void bcm_sdhci_intr(void *);
112
113static int bcm_sdhci_get_ro(device_t, device_t);
114
115#define	bcm_sdhci_lock(_sc)						\
116    mtx_lock(&_sc->sc_mtx);
117#define	bcm_sdhci_unlock(_sc)						\
118    mtx_unlock(&_sc->sc_mtx);
119
120static int
121bcm_sdhci_probe(device_t dev)
122{
123	if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-sdhci"))
124		return (ENXIO);
125
126	device_set_desc(dev, "Broadcom 2708 SDHCI controller");
127	return (BUS_PROBE_DEFAULT);
128}
129
130static int
131bcm_sdhci_attach(device_t dev)
132{
133	struct bcm_sdhci_softc *sc = device_get_softc(dev);
134	int rid, err;
135	phandle_t node;
136	pcell_t cell;
137	int default_freq;
138
139	sc->sc_dev = dev;
140	sc->sc_req = NULL;
141
142	default_freq = BCM2835_DEFAULT_SDHCI_FREQ;
143	node = ofw_bus_get_node(sc->sc_dev);
144	if ((OF_getprop(node, "clock-frequency", &cell, sizeof(cell))) > 0)
145		default_freq = (int)fdt32_to_cpu(cell)/1000000;
146
147	dprintf("SDHCI frequency: %dMHz\n", default_freq);
148
149	mtx_init(&sc->sc_mtx, "bcm sdhci", "sdhci", MTX_DEF);
150
151	rid = 0;
152	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
153	    RF_ACTIVE);
154	if (!sc->sc_mem_res) {
155		device_printf(dev, "cannot allocate memory window\n");
156		err = ENXIO;
157		goto fail;
158	}
159
160	sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
161	sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
162
163	rid = 0;
164	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
165	    RF_ACTIVE);
166	if (!sc->sc_irq_res) {
167		device_printf(dev, "cannot allocate interrupt\n");
168		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
169		err = ENXIO;
170		goto fail;
171	}
172
173	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
174	    NULL, bcm_sdhci_intr, sc, &sc->sc_intrhand))
175	{
176		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
177		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
178		device_printf(dev, "cannot setup interrupt handler\n");
179		err = ENXIO;
180		goto fail;
181	}
182
183	sc->sc_slot.caps = SDHCI_CAN_VDD_330 | SDHCI_CAN_VDD_180 | SDHCI_CAN_DO_HISPD;
184	sc->sc_slot.caps |= (default_freq << SDHCI_CLOCK_BASE_SHIFT);
185	sc->sc_slot.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK
186		| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
187		| SDHCI_QUIRK_MISSING_CAPS;
188
189	sdhci_init_slot(dev, &sc->sc_slot, 0);
190
191	bus_generic_probe(dev);
192	bus_generic_attach(dev);
193
194	sdhci_start_slot(&sc->sc_slot);
195
196	return (0);
197
198fail:
199	if (sc->sc_intrhand)
200		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
201	if (sc->sc_irq_res)
202		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
203	if (sc->sc_mem_res)
204		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
205
206	return (err);
207}
208
209static int
210bcm_sdhci_detach(device_t dev)
211{
212
213	return (EBUSY);
214}
215
216static void
217bcm_sdhci_intr(void *arg)
218{
219	struct bcm_sdhci_softc *sc = arg;
220
221	sdhci_generic_intr(&sc->sc_slot);
222}
223
224static int
225bcm_sdhci_get_ro(device_t bus, device_t child)
226{
227
228	return (0);
229}
230
231static inline uint32_t
232RD4(struct bcm_sdhci_softc *sc, bus_size_t off)
233{
234	uint32_t val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off);
235	return val;
236}
237
238static inline void
239WR4(struct bcm_sdhci_softc *sc, bus_size_t off, uint32_t val)
240{
241	bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);
242
243	if ((off != SDHCI_BUFFER && off != SDHCI_INT_STATUS && off != SDHCI_CLOCK_CONTROL))
244	{
245		int timeout = 100000;
246		while (val != bus_space_read_4(sc->sc_bst, sc->sc_bsh, off)
247		    && --timeout > 0)
248			continue;
249
250		if (timeout <= 0)
251			printf("sdhci_brcm: writing 0x%X to reg 0x%X "
252				"always gives 0x%X\n",
253				val, (uint32_t)off,
254				bus_space_read_4(sc->sc_bst, sc->sc_bsh, off));
255	}
256}
257
258static uint8_t
259bcm_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
260{
261	struct bcm_sdhci_softc *sc = device_get_softc(dev);
262	uint32_t val = RD4(sc, off & ~3);
263
264	return ((val >> (off & 3)*8) & 0xff);
265}
266
267static uint16_t
268bcm_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
269{
270	struct bcm_sdhci_softc *sc = device_get_softc(dev);
271	uint32_t val = RD4(sc, off & ~3);
272
273	return ((val >> (off & 3)*8) & 0xffff);
274}
275
276static uint32_t
277bcm_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
278{
279	struct bcm_sdhci_softc *sc = device_get_softc(dev);
280
281	return RD4(sc, off);
282}
283
284static void
285bcm_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
286    uint32_t *data, bus_size_t count)
287{
288	struct bcm_sdhci_softc *sc = device_get_softc(dev);
289
290	bus_space_read_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count);
291}
292
293static void
294bcm_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val)
295{
296	struct bcm_sdhci_softc *sc = device_get_softc(dev);
297	uint32_t val32 = RD4(sc, off & ~3);
298	val32 &= ~(0xff << (off & 3)*8);
299	val32 |= (val << (off & 3)*8);
300	WR4(sc, off & ~3, val32);
301}
302
303static void
304bcm_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val)
305{
306	struct bcm_sdhci_softc *sc = device_get_softc(dev);
307	static uint32_t cmd_and_trandfer_mode;
308	uint32_t val32;
309	if (off == SDHCI_COMMAND_FLAGS)
310		val32 = cmd_and_trandfer_mode;
311	else
312		val32 = RD4(sc, off & ~3);
313	val32 &= ~(0xffff << (off & 3)*8);
314	val32 |= (val << (off & 3)*8);
315	if (off == SDHCI_TRANSFER_MODE)
316		cmd_and_trandfer_mode = val32;
317	else
318		WR4(sc, off & ~3, val32);
319}
320
321static void
322bcm_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val)
323{
324	struct bcm_sdhci_softc *sc = device_get_softc(dev);
325	WR4(sc, off, val);
326}
327
328static void
329bcm_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
330    uint32_t *data, bus_size_t count)
331{
332	struct bcm_sdhci_softc *sc = device_get_softc(dev);
333
334	bus_space_write_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count);
335}
336
337static device_method_t bcm_sdhci_methods[] = {
338	/* Device interface */
339	DEVMETHOD(device_probe,		bcm_sdhci_probe),
340	DEVMETHOD(device_attach,	bcm_sdhci_attach),
341	DEVMETHOD(device_detach,	bcm_sdhci_detach),
342
343	/* Bus interface */
344	DEVMETHOD(bus_read_ivar,	sdhci_generic_read_ivar),
345	DEVMETHOD(bus_write_ivar,	sdhci_generic_write_ivar),
346	DEVMETHOD(bus_print_child,	bus_generic_print_child),
347
348	/* MMC bridge interface */
349	DEVMETHOD(mmcbr_update_ios,	sdhci_generic_update_ios),
350	DEVMETHOD(mmcbr_request,	sdhci_generic_request),
351	DEVMETHOD(mmcbr_get_ro,		bcm_sdhci_get_ro),
352	DEVMETHOD(mmcbr_acquire_host,	sdhci_generic_acquire_host),
353	DEVMETHOD(mmcbr_release_host,	sdhci_generic_release_host),
354
355	/* SDHCI registers accessors */
356	DEVMETHOD(sdhci_read_1,		bcm_sdhci_read_1),
357	DEVMETHOD(sdhci_read_2,		bcm_sdhci_read_2),
358	DEVMETHOD(sdhci_read_4,		bcm_sdhci_read_4),
359	DEVMETHOD(sdhci_read_multi_4,	bcm_sdhci_read_multi_4),
360	DEVMETHOD(sdhci_write_1,	bcm_sdhci_write_1),
361	DEVMETHOD(sdhci_write_2,	bcm_sdhci_write_2),
362	DEVMETHOD(sdhci_write_4,	bcm_sdhci_write_4),
363	DEVMETHOD(sdhci_write_multi_4,	bcm_sdhci_write_multi_4),
364
365	{ 0, 0 }
366};
367
368static devclass_t bcm_sdhci_devclass;
369
370static driver_t bcm_sdhci_driver = {
371	"sdhci_bcm",
372	bcm_sdhci_methods,
373	sizeof(struct bcm_sdhci_softc),
374};
375
376DRIVER_MODULE(sdhci_bcm, simplebus, bcm_sdhci_driver, bcm_sdhci_devclass, 0, 0);
377MODULE_DEPEND(sdhci_bcm, sdhci, 1, 1, 1);
378