lpc_mmc.c revision 260326
1239278Sgonzo/*-
2239278Sgonzo * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org>
3239278Sgonzo * All rights reserved.
4239278Sgonzo *
5239278Sgonzo * Redistribution and use in source and binary forms, with or without
6239278Sgonzo * modification, are permitted provided that the following conditions
7239278Sgonzo * are met:
8239278Sgonzo * 1. Redistributions of source code must retain the above copyright
9239278Sgonzo *    notice, this list of conditions and the following disclaimer.
10239278Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
11239278Sgonzo *    notice, this list of conditions and the following disclaimer in the
12239278Sgonzo *    documentation and/or other materials provided with the distribution.
13239278Sgonzo *
14239278Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15239278Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16239278Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17239278Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18239278Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19239278Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20239278Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21239278Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22239278Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23239278Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24239278Sgonzo * SUCH DAMAGE.
25239278Sgonzo *
26239278Sgonzo */
27239278Sgonzo#include <sys/cdefs.h>
28239278Sgonzo__FBSDID("$FreeBSD: head/sys/arm/lpc/lpc_mmc.c 260326 2014-01-05 18:40:06Z ian $");
29239278Sgonzo
30239278Sgonzo#include <sys/param.h>
31239278Sgonzo#include <sys/systm.h>
32239278Sgonzo#include <sys/bio.h>
33239278Sgonzo#include <sys/bus.h>
34239278Sgonzo#include <sys/conf.h>
35239278Sgonzo#include <sys/endian.h>
36239278Sgonzo#include <sys/kernel.h>
37239278Sgonzo#include <sys/kthread.h>
38239278Sgonzo#include <sys/lock.h>
39239278Sgonzo#include <sys/malloc.h>
40239278Sgonzo#include <sys/module.h>
41239278Sgonzo#include <sys/mutex.h>
42239278Sgonzo#include <sys/queue.h>
43239278Sgonzo#include <sys/resource.h>
44239278Sgonzo#include <sys/rman.h>
45239278Sgonzo#include <sys/time.h>
46239278Sgonzo#include <sys/timetc.h>
47239278Sgonzo#include <sys/watchdog.h>
48239278Sgonzo
49239278Sgonzo#include <sys/kdb.h>
50239278Sgonzo
51239278Sgonzo#include <machine/bus.h>
52239278Sgonzo#include <machine/cpu.h>
53239278Sgonzo#include <machine/cpufunc.h>
54239278Sgonzo#include <machine/resource.h>
55239278Sgonzo#include <machine/intr.h>
56239278Sgonzo
57239278Sgonzo#include <dev/ofw/ofw_bus.h>
58239278Sgonzo#include <dev/ofw/ofw_bus_subr.h>
59239278Sgonzo
60239278Sgonzo#include <dev/mmc/bridge.h>
61239278Sgonzo#include <dev/mmc/mmcreg.h>
62239278Sgonzo#include <dev/mmc/mmcbrvar.h>
63239278Sgonzo
64239278Sgonzo#include <arm/lpc/lpcreg.h>
65239278Sgonzo#include <arm/lpc/lpcvar.h>
66239278Sgonzo
67239278Sgonzo#ifdef DEBUG
68239278Sgonzo#define debugf(fmt, args...) do { printf("%s(): ", __func__);   \
69239278Sgonzo    printf(fmt,##args); } while (0)
70239278Sgonzo#else
71239278Sgonzo#define debugf(fmt, args...)
72239278Sgonzo#endif
73239278Sgonzo
74239278Sgonzostruct lpc_mmc_dmamap_arg {
75239278Sgonzo	bus_addr_t		lm_dma_busaddr;
76239278Sgonzo};
77239278Sgonzo
78239278Sgonzostruct lpc_mmc_softc {
79239278Sgonzo	device_t		lm_dev;
80239278Sgonzo	struct mtx		lm_mtx;
81239278Sgonzo	struct resource *	lm_mem_res;
82239278Sgonzo	struct resource *	lm_irq_res;
83239278Sgonzo	bus_space_tag_t		lm_bst;
84239278Sgonzo	bus_space_handle_t	lm_bsh;
85239278Sgonzo	void *			lm_intrhand;
86239278Sgonzo	struct mmc_host		lm_host;
87239278Sgonzo	struct mmc_request *	lm_req;
88239278Sgonzo	struct mmc_data *	lm_data;
89239278Sgonzo	uint32_t		lm_flags;
90239278Sgonzo#define	LPC_SD_FLAGS_IGNORECRC		(1 << 0)
91239278Sgonzo	int			lm_xfer_direction;
92239278Sgonzo#define	DIRECTION_READ		0
93239278Sgonzo#define	DIRECTION_WRITE		1
94239278Sgonzo	int			lm_xfer_done;
95239278Sgonzo	int			lm_bus_busy;
96239278Sgonzo	bus_dma_tag_t		lm_dma_tag;
97239278Sgonzo	bus_dmamap_t		lm_dma_map;
98239278Sgonzo	bus_addr_t		lm_buffer_phys;
99239278Sgonzo	void *			lm_buffer;
100239278Sgonzo};
101239278Sgonzo
102239278Sgonzo#define	LPC_SD_MAX_BLOCKSIZE	1024
103239278Sgonzo/* XXX */
104239278Sgonzo#define	LPC_MMC_DMACH_READ	1
105239278Sgonzo#define	LPC_MMC_DMACH_WRITE	0
106239278Sgonzo
107239278Sgonzo
108239278Sgonzostatic int lpc_mmc_probe(device_t);
109239278Sgonzostatic int lpc_mmc_attach(device_t);
110239278Sgonzostatic int lpc_mmc_detach(device_t);
111239278Sgonzostatic void lpc_mmc_intr(void *);
112239278Sgonzo
113239278Sgonzostatic void lpc_mmc_cmd(struct lpc_mmc_softc *, struct mmc_command *);
114239278Sgonzostatic void lpc_mmc_setup_xfer(struct lpc_mmc_softc *, struct mmc_data *);
115239278Sgonzo
116239278Sgonzostatic int lpc_mmc_update_ios(device_t, device_t);
117239278Sgonzostatic int lpc_mmc_request(device_t, device_t, struct mmc_request *);
118239278Sgonzostatic int lpc_mmc_get_ro(device_t, device_t);
119239278Sgonzostatic int lpc_mmc_acquire_host(device_t, device_t);
120239278Sgonzostatic int lpc_mmc_release_host(device_t, device_t);
121239278Sgonzo
122239278Sgonzostatic void lpc_mmc_dma_rxfinish(void *);
123239278Sgonzostatic void lpc_mmc_dma_rxerror(void *);
124239278Sgonzostatic void lpc_mmc_dma_txfinish(void *);
125239278Sgonzostatic void lpc_mmc_dma_txerror(void *);
126239278Sgonzo
127239278Sgonzostatic void lpc_mmc_dmamap_cb(void *, bus_dma_segment_t *, int, int);
128239278Sgonzo
129239278Sgonzo#define	lpc_mmc_lock(_sc)						\
130239278Sgonzo    mtx_lock(&_sc->lm_mtx);
131239278Sgonzo#define	lpc_mmc_unlock(_sc)						\
132239278Sgonzo    mtx_unlock(&_sc->lm_mtx);
133239278Sgonzo#define	lpc_mmc_read_4(_sc, _reg)					\
134239278Sgonzo    bus_space_read_4(_sc->lm_bst, _sc->lm_bsh, _reg)
135239278Sgonzo#define	lpc_mmc_write_4(_sc, _reg, _value)				\
136239278Sgonzo    bus_space_write_4(_sc->lm_bst, _sc->lm_bsh, _reg, _value)
137239278Sgonzo
138239278Sgonzostatic struct lpc_dmac_channel_config lpc_mmc_dma_rxconf = {
139239278Sgonzo	.ldc_fcntl = LPC_DMAC_FLOW_D_P2M,
140239278Sgonzo	.ldc_src_periph = LPC_DMAC_SD_ID,
141239278Sgonzo	.ldc_src_width = LPC_DMAC_CH_CONTROL_WIDTH_4,
142239278Sgonzo	.ldc_src_incr = 0,
143239278Sgonzo	.ldc_src_burst = LPC_DMAC_CH_CONTROL_BURST_8,
144239278Sgonzo	.ldc_dst_periph = LPC_DMAC_SD_ID,
145239278Sgonzo	.ldc_dst_width = LPC_DMAC_CH_CONTROL_WIDTH_4,
146239278Sgonzo	.ldc_dst_incr = 1,
147239278Sgonzo	.ldc_dst_burst = LPC_DMAC_CH_CONTROL_BURST_8,
148239278Sgonzo	.ldc_success_handler = lpc_mmc_dma_rxfinish,
149239278Sgonzo	.ldc_error_handler = lpc_mmc_dma_rxerror,
150239278Sgonzo};
151239278Sgonzo
152239278Sgonzostatic struct lpc_dmac_channel_config lpc_mmc_dma_txconf = {
153239278Sgonzo	.ldc_fcntl = LPC_DMAC_FLOW_P_M2P,
154239278Sgonzo	.ldc_src_periph = LPC_DMAC_SD_ID,
155239278Sgonzo	.ldc_src_width = LPC_DMAC_CH_CONTROL_WIDTH_4,
156239278Sgonzo	.ldc_src_incr = 1,
157239278Sgonzo	.ldc_src_burst = LPC_DMAC_CH_CONTROL_BURST_8,
158239278Sgonzo	.ldc_dst_periph = LPC_DMAC_SD_ID,
159239278Sgonzo	.ldc_dst_width = LPC_DMAC_CH_CONTROL_WIDTH_4,
160239278Sgonzo	.ldc_dst_incr = 0,
161239278Sgonzo	.ldc_dst_burst = LPC_DMAC_CH_CONTROL_BURST_8,
162239278Sgonzo	.ldc_success_handler = lpc_mmc_dma_txfinish,
163239278Sgonzo	.ldc_error_handler = lpc_mmc_dma_txerror,
164239278Sgonzo};
165239278Sgonzo
166239278Sgonzostatic int
167239278Sgonzolpc_mmc_probe(device_t dev)
168239278Sgonzo{
169239278Sgonzo	if (!ofw_bus_is_compatible(dev, "lpc,mmc"))
170239278Sgonzo		return (ENXIO);
171239278Sgonzo
172239278Sgonzo	device_set_desc(dev, "LPC32x0 MMC/SD controller");
173239278Sgonzo	return (BUS_PROBE_DEFAULT);
174239278Sgonzo}
175239278Sgonzo
176239278Sgonzostatic int
177239278Sgonzolpc_mmc_attach(device_t dev)
178239278Sgonzo{
179239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(dev);
180239278Sgonzo	struct lpc_mmc_dmamap_arg ctx;
181239278Sgonzo	device_t child;
182239278Sgonzo	int rid, err;
183239278Sgonzo
184239278Sgonzo	sc->lm_dev = dev;
185239278Sgonzo	sc->lm_req = NULL;
186239278Sgonzo
187239278Sgonzo	mtx_init(&sc->lm_mtx, "lpcmmc", "mmc", MTX_DEF);
188239278Sgonzo
189239278Sgonzo	rid = 0;
190239278Sgonzo	sc->lm_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
191239278Sgonzo	    RF_ACTIVE);
192239278Sgonzo	if (!sc->lm_mem_res) {
193239278Sgonzo		device_printf(dev, "cannot allocate memory window\n");
194239278Sgonzo		return (ENXIO);
195239278Sgonzo	}
196239278Sgonzo
197239278Sgonzo	sc->lm_bst = rman_get_bustag(sc->lm_mem_res);
198239278Sgonzo	sc->lm_bsh = rman_get_bushandle(sc->lm_mem_res);
199239278Sgonzo
200239278Sgonzo	debugf("virtual register space: 0x%08lx\n", sc->lm_bsh);
201239278Sgonzo
202239278Sgonzo	rid = 0;
203239278Sgonzo	sc->lm_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
204239278Sgonzo	    RF_ACTIVE);
205239278Sgonzo	if (!sc->lm_irq_res) {
206239278Sgonzo		device_printf(dev, "cannot allocate interrupt\n");
207239278Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res);
208239278Sgonzo		return (ENXIO);
209239278Sgonzo	}
210239278Sgonzo
211239278Sgonzo	if (bus_setup_intr(dev, sc->lm_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
212239278Sgonzo	    NULL, lpc_mmc_intr, sc, &sc->lm_intrhand))
213239278Sgonzo	{
214239278Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res);
215239278Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res);
216239278Sgonzo		device_printf(dev, "cannot setup interrupt handler\n");
217239278Sgonzo		return (ENXIO);
218239278Sgonzo	}
219239278Sgonzo
220239278Sgonzo	sc->lm_host.f_min = 312500;
221239278Sgonzo	sc->lm_host.f_max = 2500000;
222239278Sgonzo	sc->lm_host.host_ocr = MMC_OCR_300_310 | MMC_OCR_310_320 |
223239278Sgonzo	    MMC_OCR_320_330 | MMC_OCR_330_340;
224239278Sgonzo#if 0
225239278Sgonzo	sc->lm_host.caps = MMC_CAP_4_BIT_DATA;
226239278Sgonzo#endif
227239278Sgonzo
228239278Sgonzo	lpc_pwr_write(dev, LPC_CLKPWR_MS_CTRL,
229239278Sgonzo	    LPC_CLKPWR_MS_CTRL_CLOCK_EN | LPC_CLKPWR_MS_CTRL_SD_CLOCK | 1);
230239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_POWER, LPC_SD_POWER_CTRL_ON);
231239278Sgonzo
232239278Sgonzo	device_set_ivars(dev, &sc->lm_host);
233239278Sgonzo
234239278Sgonzo	child = device_add_child(dev, "mmc", -1);
235239278Sgonzo	if (!child) {
236239278Sgonzo		device_printf(dev, "attaching MMC bus failed!\n");
237239278Sgonzo		bus_teardown_intr(dev, sc->lm_irq_res, sc->lm_intrhand);
238239278Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res);
239239278Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res);
240239278Sgonzo		return (ENXIO);
241239278Sgonzo	}
242239278Sgonzo
243239278Sgonzo	/* Alloc DMA memory */
244239278Sgonzo	err = bus_dma_tag_create(
245239278Sgonzo	    bus_get_dma_tag(sc->lm_dev),
246239278Sgonzo	    4, 0,			/* alignment, boundary */
247239278Sgonzo	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
248239278Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
249239278Sgonzo	    NULL, NULL,			/* filter, filterarg */
250239278Sgonzo	    LPC_SD_MAX_BLOCKSIZE, 1,	/* maxsize, nsegments */
251239278Sgonzo	    LPC_SD_MAX_BLOCKSIZE, 0,	/* maxsegsize, flags */
252239278Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
253239278Sgonzo	    &sc->lm_dma_tag);
254239278Sgonzo
255239278Sgonzo	err = bus_dmamem_alloc(sc->lm_dma_tag, (void **)&sc->lm_buffer,
256239278Sgonzo	    0, &sc->lm_dma_map);
257239278Sgonzo	if (err) {
258239278Sgonzo		device_printf(dev, "cannot allocate framebuffer\n");
259239278Sgonzo		goto fail;
260239278Sgonzo	}
261239278Sgonzo
262239278Sgonzo	err = bus_dmamap_load(sc->lm_dma_tag, sc->lm_dma_map, sc->lm_buffer,
263239278Sgonzo	    LPC_SD_MAX_BLOCKSIZE, lpc_mmc_dmamap_cb, &ctx, BUS_DMA_NOWAIT);
264239278Sgonzo	if (err) {
265239278Sgonzo		device_printf(dev, "cannot load DMA map\n");
266239278Sgonzo		goto fail;
267239278Sgonzo	}
268239278Sgonzo
269239278Sgonzo	sc->lm_buffer_phys = ctx.lm_dma_busaddr;
270239278Sgonzo
271239278Sgonzo	lpc_mmc_dma_rxconf.ldc_handler_arg = (void *)sc;
272239278Sgonzo	err = lpc_dmac_config_channel(dev, LPC_MMC_DMACH_READ, &lpc_mmc_dma_rxconf);
273239278Sgonzo	if (err) {
274239278Sgonzo		device_printf(dev, "cannot allocate RX DMA channel\n");
275239278Sgonzo		goto fail;
276239278Sgonzo	}
277239278Sgonzo
278239278Sgonzo
279239278Sgonzo	lpc_mmc_dma_txconf.ldc_handler_arg = (void *)sc;
280239278Sgonzo	err = lpc_dmac_config_channel(dev, LPC_MMC_DMACH_WRITE, &lpc_mmc_dma_txconf);
281239278Sgonzo	if (err) {
282239278Sgonzo		device_printf(dev, "cannot allocate TX DMA channel\n");
283239278Sgonzo		goto fail;
284239278Sgonzo	}
285239278Sgonzo
286239278Sgonzo	bus_generic_probe(dev);
287239278Sgonzo	bus_generic_attach(dev);
288239278Sgonzo
289239278Sgonzo	return (0);
290239278Sgonzo
291239278Sgonzofail:
292239278Sgonzo	if (sc->lm_intrhand)
293239278Sgonzo		bus_teardown_intr(dev, sc->lm_irq_res, sc->lm_intrhand);
294239278Sgonzo	if (sc->lm_irq_res)
295239278Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res);
296239278Sgonzo	if (sc->lm_mem_res)
297239278Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res);
298239278Sgonzo	return (err);
299239278Sgonzo}
300239278Sgonzo
301239278Sgonzostatic int
302239278Sgonzolpc_mmc_detach(device_t dev)
303239278Sgonzo{
304239278Sgonzo	return (EBUSY);
305239278Sgonzo}
306239278Sgonzo
307239278Sgonzostatic void
308239278Sgonzolpc_mmc_intr(void *arg)
309239278Sgonzo{
310239278Sgonzo	struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg;
311239278Sgonzo	struct mmc_command *cmd;
312239278Sgonzo	uint32_t status;
313239278Sgonzo
314239278Sgonzo	status = lpc_mmc_read_4(sc, LPC_SD_STATUS);
315239278Sgonzo
316239278Sgonzo	debugf("interrupt: 0x%08x\n", status);
317239278Sgonzo
318239278Sgonzo	if (status & LPC_SD_STATUS_CMDCRCFAIL) {
319239278Sgonzo		cmd = sc->lm_req->cmd;
320239278Sgonzo		cmd->error = sc->lm_flags & LPC_SD_FLAGS_IGNORECRC
321239278Sgonzo		    ? MMC_ERR_NONE : MMC_ERR_BADCRC;
322239278Sgonzo		cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0);
323239278Sgonzo		sc->lm_req->done(sc->lm_req);
324239278Sgonzo		sc->lm_req = NULL;
325239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDCRCFAIL);
326239278Sgonzo	}
327239278Sgonzo
328239278Sgonzo	if (status & LPC_SD_STATUS_CMDACTIVE)
329239278Sgonzo	{
330239278Sgonzo		debugf("command active\n");
331239278Sgonzo		cmd = sc->lm_req->cmd;
332239278Sgonzo		cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0);
333239278Sgonzo		sc->lm_req->done(sc->lm_req);
334239278Sgonzo		sc->lm_req = NULL;
335239278Sgonzo	}
336239278Sgonzo
337239278Sgonzo	if (status & LPC_SD_STATUS_DATATIMEOUT) {
338239278Sgonzo		device_printf(sc->lm_dev, "data timeout\n");
339239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATATIMEOUT);
340239278Sgonzo	}
341239278Sgonzo
342239278Sgonzo	if (status & LPC_SD_STATUS_TXUNDERRUN) {
343239278Sgonzo		device_printf(sc->lm_dev, "TX underrun\n");
344239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_TXUNDERRUN);
345239278Sgonzo	}
346239278Sgonzo
347239278Sgonzo	if (status & LPC_SD_STATUS_CMDRESPEND) {
348239278Sgonzo		debugf("command response\n");
349239278Sgonzo		cmd = sc->lm_req->cmd;
350239278Sgonzo
351239278Sgonzo		if (cmd->flags & MMC_RSP_136) {
352239278Sgonzo			cmd->resp[3] = lpc_mmc_read_4(sc, LPC_SD_RESP3);
353239278Sgonzo			cmd->resp[2] = lpc_mmc_read_4(sc, LPC_SD_RESP2);
354239278Sgonzo			cmd->resp[1] = lpc_mmc_read_4(sc, LPC_SD_RESP1);
355239278Sgonzo		}
356239278Sgonzo
357239278Sgonzo		cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0);
358239278Sgonzo		cmd->error = MMC_ERR_NONE;
359239278Sgonzo
360239278Sgonzo		if (cmd->data && (cmd->data->flags & MMC_DATA_WRITE))
361239278Sgonzo			lpc_mmc_setup_xfer(sc, sc->lm_req->cmd->data);
362239278Sgonzo
363239278Sgonzo		if (!cmd->data) {
364239278Sgonzo			sc->lm_req->done(sc->lm_req);
365239278Sgonzo			sc->lm_req = NULL;
366239278Sgonzo		}
367239278Sgonzo
368239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDRESPEND);
369239278Sgonzo	}
370239278Sgonzo
371239278Sgonzo	if (status & LPC_SD_STATUS_CMDSENT) {
372239278Sgonzo		debugf("command sent\n");
373239278Sgonzo		cmd = sc->lm_req->cmd;
374239278Sgonzo		cmd->error = MMC_ERR_NONE;
375239278Sgonzo		sc->lm_req->done(sc->lm_req);
376239278Sgonzo		sc->lm_req = NULL;
377239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDSENT);
378239278Sgonzo	}
379239278Sgonzo
380239278Sgonzo	if (status & LPC_SD_STATUS_DATAEND) {
381239278Sgonzo		if (sc->lm_xfer_direction == DIRECTION_READ)
382239278Sgonzo			lpc_dmac_start_burst(sc->lm_dev, LPC_DMAC_SD_ID);
383239278Sgonzo
384239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATAEND);
385239278Sgonzo	}
386239278Sgonzo
387239278Sgonzo	if (status & LPC_SD_STATUS_CMDTIMEOUT) {
388239278Sgonzo		device_printf(sc->lm_dev, "command response timeout\n");
389239278Sgonzo		cmd = sc->lm_req->cmd;
390239278Sgonzo		cmd->error = MMC_ERR_TIMEOUT;
391239278Sgonzo		sc->lm_req->done(sc->lm_req);
392239278Sgonzo		sc->lm_req = NULL;
393239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDTIMEOUT);
394239278Sgonzo		return;
395239278Sgonzo	}
396239278Sgonzo
397239278Sgonzo	if (status & LPC_SD_STATUS_STARTBITERR) {
398239278Sgonzo		device_printf(sc->lm_dev, "start bit error\n");
399239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_STARTBITERR);
400239278Sgonzo	}
401239278Sgonzo
402239278Sgonzo	if (status & LPC_SD_STATUS_DATACRCFAIL) {
403239278Sgonzo		device_printf(sc->lm_dev, "data CRC error\n");
404239278Sgonzo		debugf("data buffer: %p\n", sc->lm_buffer);
405239278Sgonzo		cmd = sc->lm_req->cmd;
406239278Sgonzo		cmd->error = MMC_ERR_BADCRC;
407239278Sgonzo		sc->lm_req->done(sc->lm_req);
408239278Sgonzo		sc->lm_req = NULL;
409239278Sgonzo
410239278Sgonzo		if (sc->lm_xfer_direction == DIRECTION_READ)
411239278Sgonzo			lpc_dmac_start_burst(sc->lm_dev, LPC_DMAC_SD_ID);
412239278Sgonzo
413239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATACRCFAIL);
414239278Sgonzo	}
415239278Sgonzo
416239278Sgonzo	if (status & LPC_SD_STATUS_DATABLOCKEND) {
417239278Sgonzo		debugf("data block end\n");
418239278Sgonzo		if (sc->lm_xfer_direction == DIRECTION_READ)
419239278Sgonzo			memcpy(sc->lm_data->data, sc->lm_buffer, sc->lm_data->len);
420239278Sgonzo
421239278Sgonzo		if (sc->lm_xfer_direction == DIRECTION_WRITE) {
422239278Sgonzo			lpc_dmac_disable_channel(sc->lm_dev, LPC_MMC_DMACH_WRITE);
423239278Sgonzo			lpc_mmc_write_4(sc, LPC_SD_DATACTRL, 0);
424239278Sgonzo		}
425239278Sgonzo
426239278Sgonzo		sc->lm_req->done(sc->lm_req);
427239278Sgonzo		sc->lm_req = NULL;
428239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATABLOCKEND);
429239278Sgonzo	}
430239278Sgonzo
431239278Sgonzo	debugf("done\n");
432239278Sgonzo}
433239278Sgonzo
434239278Sgonzostatic int
435239278Sgonzolpc_mmc_request(device_t bus, device_t child, struct mmc_request *req)
436239278Sgonzo{
437239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
438239278Sgonzo
439239278Sgonzo	debugf("request: %p\n", req);
440239278Sgonzo
441239278Sgonzo	lpc_mmc_lock(sc);
442239278Sgonzo	if (sc->lm_req)
443239278Sgonzo		return (EBUSY);
444239278Sgonzo
445239278Sgonzo	sc->lm_req = req;
446239278Sgonzo
447239278Sgonzo	if (req->cmd->data && req->cmd->data->flags & MMC_DATA_WRITE) {
448239278Sgonzo		memcpy(sc->lm_buffer, req->cmd->data->data, req->cmd->data->len);
449239278Sgonzo		lpc_mmc_cmd(sc, req->cmd);
450239278Sgonzo		lpc_mmc_unlock(sc);
451239278Sgonzo		return (0);
452239278Sgonzo	}
453239278Sgonzo
454239278Sgonzo	if (req->cmd->data)
455239278Sgonzo		lpc_mmc_setup_xfer(sc, req->cmd->data);
456239278Sgonzo
457239278Sgonzo	lpc_mmc_cmd(sc, req->cmd);
458239278Sgonzo	lpc_mmc_unlock(sc);
459239278Sgonzo
460239278Sgonzo	return (0);
461239278Sgonzo}
462239278Sgonzo
463239278Sgonzostatic void
464239278Sgonzolpc_mmc_cmd(struct lpc_mmc_softc *sc, struct mmc_command *cmd)
465239278Sgonzo{
466239278Sgonzo	uint32_t cmdreg = 0;
467239278Sgonzo
468239278Sgonzo	debugf("cmd: %d arg: 0x%08x\n", cmd->opcode, cmd->arg);
469239278Sgonzo
470239278Sgonzo	if (lpc_mmc_read_4(sc, LPC_SD_COMMAND) & LPC_SD_COMMAND_ENABLE) {
471239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_COMMAND, 0);
472239278Sgonzo		DELAY(1000);
473239278Sgonzo	}
474239278Sgonzo
475239278Sgonzo	sc->lm_flags &= ~LPC_SD_FLAGS_IGNORECRC;
476239278Sgonzo
477239278Sgonzo	if (cmd->flags & MMC_RSP_PRESENT)
478239278Sgonzo		cmdreg |= LPC_SD_COMMAND_RESPONSE;
479239278Sgonzo
480239278Sgonzo	if (MMC_RSP(cmd->flags) == MMC_RSP_R2)
481239278Sgonzo		cmdreg |= LPC_SD_COMMAND_LONGRSP;
482239278Sgonzo
483239278Sgonzo	if (MMC_RSP(cmd->flags) == MMC_RSP_R3)
484239278Sgonzo		sc->lm_flags |= LPC_SD_FLAGS_IGNORECRC;
485239278Sgonzo
486239278Sgonzo	cmdreg |= LPC_SD_COMMAND_ENABLE;
487239278Sgonzo	cmdreg |= (cmd->opcode & LPC_SD_COMMAND_CMDINDEXMASK);
488239278Sgonzo
489239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_MASK0, 0xffffffff);
490239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_MASK1, 0xffffffff);
491239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_ARGUMENT, cmd->arg);
492239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_COMMAND, cmdreg);
493239278Sgonzo}
494239278Sgonzo
495239278Sgonzostatic void
496239278Sgonzolpc_mmc_setup_xfer(struct lpc_mmc_softc *sc, struct mmc_data *data)
497239278Sgonzo{
498239278Sgonzo	uint32_t datactrl = 0;
499239278Sgonzo	int data_words = data->len / 4;
500239278Sgonzo
501239278Sgonzo	sc->lm_data = data;
502239278Sgonzo	sc->lm_xfer_done = 0;
503239278Sgonzo
504239278Sgonzo	debugf("data: %p, len: %d, %s\n", data,
505239278Sgonzo	    data->len, (data->flags & MMC_DATA_READ) ? "read" : "write");
506239278Sgonzo
507239278Sgonzo	if (data->flags & MMC_DATA_READ) {
508239278Sgonzo		sc->lm_xfer_direction = DIRECTION_READ;
509239278Sgonzo		lpc_dmac_setup_transfer(sc->lm_dev, LPC_MMC_DMACH_READ,
510260326Sian		    LPC_SD_PHYS_BASE + LPC_SD_FIFO, sc->lm_buffer_phys,
511239278Sgonzo		    data_words, 0);
512239278Sgonzo	}
513239278Sgonzo
514239278Sgonzo	if (data->flags & MMC_DATA_WRITE) {
515239278Sgonzo		sc->lm_xfer_direction = DIRECTION_WRITE;
516239278Sgonzo		lpc_dmac_setup_transfer(sc->lm_dev, LPC_MMC_DMACH_WRITE,
517260326Sian		    sc->lm_buffer_phys, LPC_SD_PHYS_BASE + LPC_SD_FIFO,
518239278Sgonzo		    data_words, 0);
519239278Sgonzo	}
520239278Sgonzo
521239278Sgonzo	datactrl |= (sc->lm_xfer_direction
522239278Sgonzo	    ? LPC_SD_DATACTRL_WRITE
523239278Sgonzo	    : LPC_SD_DATACTRL_READ);
524239278Sgonzo
525239278Sgonzo	datactrl |= LPC_SD_DATACTRL_DMAENABLE | LPC_SD_DATACTRL_ENABLE;
526239278Sgonzo	datactrl |= (ffs(data->len) - 1) << 4;
527239278Sgonzo
528239278Sgonzo	debugf("datactrl: 0x%08x\n", datactrl);
529239278Sgonzo
530239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_DATATIMER, 0xFFFF0000);
531239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_DATALENGTH, data->len);
532239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_DATACTRL, datactrl);
533239278Sgonzo}
534239278Sgonzo
535239278Sgonzostatic int
536239278Sgonzolpc_mmc_read_ivar(device_t bus, device_t child, int which,
537239278Sgonzo    uintptr_t *result)
538239278Sgonzo{
539239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
540239278Sgonzo
541239278Sgonzo	switch (which) {
542239278Sgonzo	default:
543239278Sgonzo		return (EINVAL);
544239278Sgonzo	case MMCBR_IVAR_BUS_MODE:
545239278Sgonzo		*(int *)result = sc->lm_host.ios.bus_mode;
546239278Sgonzo		break;
547239278Sgonzo	case MMCBR_IVAR_BUS_WIDTH:
548239278Sgonzo		*(int *)result = sc->lm_host.ios.bus_width;
549239278Sgonzo		break;
550239278Sgonzo	case MMCBR_IVAR_CHIP_SELECT:
551239278Sgonzo		*(int *)result = sc->lm_host.ios.chip_select;
552239278Sgonzo		break;
553239278Sgonzo	case MMCBR_IVAR_CLOCK:
554239278Sgonzo		*(int *)result = sc->lm_host.ios.clock;
555239278Sgonzo		break;
556239278Sgonzo	case MMCBR_IVAR_F_MIN:
557239278Sgonzo		*(int *)result = sc->lm_host.f_min;
558239278Sgonzo		break;
559239278Sgonzo	case MMCBR_IVAR_F_MAX:
560239278Sgonzo		*(int *)result = sc->lm_host.f_max;
561239278Sgonzo		break;
562239278Sgonzo	case MMCBR_IVAR_HOST_OCR:
563239278Sgonzo		*(int *)result = sc->lm_host.host_ocr;
564239278Sgonzo		break;
565239278Sgonzo	case MMCBR_IVAR_MODE:
566239278Sgonzo		*(int *)result = sc->lm_host.mode;
567239278Sgonzo		break;
568239278Sgonzo	case MMCBR_IVAR_OCR:
569239278Sgonzo		*(int *)result = sc->lm_host.ocr;
570239278Sgonzo		break;
571239278Sgonzo	case MMCBR_IVAR_POWER_MODE:
572239278Sgonzo		*(int *)result = sc->lm_host.ios.power_mode;
573239278Sgonzo		break;
574239278Sgonzo	case MMCBR_IVAR_VDD:
575239278Sgonzo		*(int *)result = sc->lm_host.ios.vdd;
576239278Sgonzo		break;
577239278Sgonzo	case MMCBR_IVAR_CAPS:
578239278Sgonzo		*(int *)result = sc->lm_host.caps;
579239278Sgonzo		break;
580239278Sgonzo	case MMCBR_IVAR_MAX_DATA:
581239278Sgonzo		*(int *)result = 1;
582239278Sgonzo		break;
583239278Sgonzo	}
584239278Sgonzo
585239278Sgonzo	return (0);
586239278Sgonzo}
587239278Sgonzo
588239278Sgonzostatic int
589239278Sgonzolpc_mmc_write_ivar(device_t bus, device_t child, int which,
590239278Sgonzo    uintptr_t value)
591239278Sgonzo{
592239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
593239278Sgonzo
594239278Sgonzo	switch (which) {
595239278Sgonzo	default:
596239278Sgonzo		return (EINVAL);
597239278Sgonzo	case MMCBR_IVAR_BUS_MODE:
598239278Sgonzo		sc->lm_host.ios.bus_mode = value;
599239278Sgonzo		break;
600239278Sgonzo	case MMCBR_IVAR_BUS_WIDTH:
601239278Sgonzo		sc->lm_host.ios.bus_width = value;
602239278Sgonzo		break;
603239278Sgonzo	case MMCBR_IVAR_CHIP_SELECT:
604239278Sgonzo		sc->lm_host.ios.chip_select = value;
605239278Sgonzo		break;
606239278Sgonzo	case MMCBR_IVAR_CLOCK:
607239278Sgonzo		sc->lm_host.ios.clock = value;
608239278Sgonzo		break;
609239278Sgonzo	case MMCBR_IVAR_MODE:
610239278Sgonzo		sc->lm_host.mode = value;
611239278Sgonzo		break;
612239278Sgonzo	case MMCBR_IVAR_OCR:
613239278Sgonzo		sc->lm_host.ocr = value;
614239278Sgonzo		break;
615239278Sgonzo	case MMCBR_IVAR_POWER_MODE:
616239278Sgonzo		sc->lm_host.ios.power_mode = value;
617239278Sgonzo		break;
618239278Sgonzo	case MMCBR_IVAR_VDD:
619239278Sgonzo		sc->lm_host.ios.vdd = value;
620239278Sgonzo		break;
621239278Sgonzo	/* These are read-only */
622239278Sgonzo	case MMCBR_IVAR_CAPS:
623239278Sgonzo	case MMCBR_IVAR_HOST_OCR:
624239278Sgonzo	case MMCBR_IVAR_F_MIN:
625239278Sgonzo	case MMCBR_IVAR_F_MAX:
626239278Sgonzo	case MMCBR_IVAR_MAX_DATA:
627239278Sgonzo		return (EINVAL);
628239278Sgonzo	}
629239278Sgonzo	return (0);
630239278Sgonzo}
631239278Sgonzo
632239278Sgonzostatic int
633239278Sgonzolpc_mmc_update_ios(device_t bus, device_t child)
634239278Sgonzo{
635239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
636239278Sgonzo	struct mmc_ios *ios = &sc->lm_host.ios;
637239278Sgonzo	uint32_t clkdiv = 0, pwr = 0;
638239278Sgonzo
639239278Sgonzo	if (ios->bus_width == bus_width_4)
640239278Sgonzo		clkdiv |= LPC_SD_CLOCK_WIDEBUS;
641239278Sgonzo
642239278Sgonzo	/* Calculate clock divider */
643239278Sgonzo	clkdiv = (LPC_SD_CLK / (2 * ios->clock)) - 1;
644239278Sgonzo
645239278Sgonzo	/* Clock rate should not exceed rate requested in ios */
646239278Sgonzo	if ((LPC_SD_CLK / (2 * (clkdiv + 1))) > ios->clock)
647239278Sgonzo		clkdiv++;
648239278Sgonzo
649239278Sgonzo	debugf("clock: %dHz, clkdiv: %d\n", ios->clock, clkdiv);
650239278Sgonzo
651239278Sgonzo	if (ios->bus_width == bus_width_4) {
652239278Sgonzo		debugf("using wide bus mode\n");
653239278Sgonzo		clkdiv |= LPC_SD_CLOCK_WIDEBUS;
654239278Sgonzo	}
655239278Sgonzo
656239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_CLOCK, clkdiv | LPC_SD_CLOCK_ENABLE);
657239278Sgonzo
658239278Sgonzo	switch (ios->power_mode) {
659239278Sgonzo	case power_off:
660239278Sgonzo		pwr |= LPC_SD_POWER_CTRL_OFF;
661239278Sgonzo		break;
662239278Sgonzo	case power_up:
663239278Sgonzo		pwr |= LPC_SD_POWER_CTRL_UP;
664239278Sgonzo		break;
665239278Sgonzo	case power_on:
666239278Sgonzo		pwr |= LPC_SD_POWER_CTRL_ON;
667239278Sgonzo		break;
668239278Sgonzo	}
669239278Sgonzo
670239278Sgonzo	if (ios->bus_mode == opendrain)
671239278Sgonzo		pwr |= LPC_SD_POWER_OPENDRAIN;
672239278Sgonzo
673239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_POWER, pwr);
674239278Sgonzo
675239278Sgonzo	return (0);
676239278Sgonzo}
677239278Sgonzo
678239278Sgonzostatic int
679239278Sgonzolpc_mmc_get_ro(device_t bus, device_t child)
680239278Sgonzo{
681239278Sgonzo
682239278Sgonzo	return (0);
683239278Sgonzo}
684239278Sgonzo
685239278Sgonzostatic int
686239278Sgonzolpc_mmc_acquire_host(device_t bus, device_t child)
687239278Sgonzo{
688239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
689239278Sgonzo	int error = 0;
690239278Sgonzo
691239278Sgonzo	lpc_mmc_lock(sc);
692239278Sgonzo	while (sc->lm_bus_busy)
693239278Sgonzo		error = mtx_sleep(sc, &sc->lm_mtx, PZERO, "mmcah", 0);
694239278Sgonzo
695239278Sgonzo	sc->lm_bus_busy++;
696239278Sgonzo	lpc_mmc_unlock(sc);
697239278Sgonzo	return (error);
698239278Sgonzo}
699239278Sgonzo
700239278Sgonzostatic int
701239278Sgonzolpc_mmc_release_host(device_t bus, device_t child)
702239278Sgonzo{
703239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
704239278Sgonzo
705239278Sgonzo	lpc_mmc_lock(sc);
706239278Sgonzo	sc->lm_bus_busy--;
707239278Sgonzo	wakeup(sc);
708239278Sgonzo	lpc_mmc_unlock(sc);
709239278Sgonzo	return (0);
710239278Sgonzo}
711239278Sgonzo
712239278Sgonzostatic void lpc_mmc_dma_rxfinish(void *arg)
713239278Sgonzo{
714239278Sgonzo}
715239278Sgonzo
716239278Sgonzostatic void lpc_mmc_dma_rxerror(void *arg)
717239278Sgonzo{
718239278Sgonzo	struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg;
719239278Sgonzo	device_printf(sc->lm_dev, "DMA RX error\n");
720239278Sgonzo}
721239278Sgonzo
722239278Sgonzostatic void lpc_mmc_dma_txfinish(void *arg)
723239278Sgonzo{
724239278Sgonzo}
725239278Sgonzo
726239278Sgonzostatic void lpc_mmc_dma_txerror(void *arg)
727239278Sgonzo{
728239278Sgonzo	struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg;
729239278Sgonzo	device_printf(sc->lm_dev, "DMA TX error\n");
730239278Sgonzo}
731239278Sgonzo
732239278Sgonzostatic void
733239278Sgonzolpc_mmc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
734239278Sgonzo{
735239278Sgonzo	struct lpc_mmc_dmamap_arg *ctx;
736239278Sgonzo
737239278Sgonzo	if (err)
738239278Sgonzo		return;
739239278Sgonzo
740239278Sgonzo	ctx = (struct lpc_mmc_dmamap_arg *)arg;
741239278Sgonzo	ctx->lm_dma_busaddr = segs[0].ds_addr;
742239278Sgonzo}
743239278Sgonzo
744239278Sgonzostatic device_method_t lpc_mmc_methods[] = {
745239278Sgonzo	/* Device interface */
746239278Sgonzo	DEVMETHOD(device_probe,		lpc_mmc_probe),
747239278Sgonzo	DEVMETHOD(device_attach,	lpc_mmc_attach),
748239278Sgonzo	DEVMETHOD(device_detach,	lpc_mmc_detach),
749239278Sgonzo
750239278Sgonzo	/* Bus interface */
751239278Sgonzo	DEVMETHOD(bus_read_ivar,	lpc_mmc_read_ivar),
752239278Sgonzo	DEVMETHOD(bus_write_ivar,	lpc_mmc_write_ivar),
753239278Sgonzo	DEVMETHOD(bus_print_child,	bus_generic_print_child),
754239278Sgonzo
755239278Sgonzo	/* MMC bridge interface */
756239278Sgonzo	DEVMETHOD(mmcbr_update_ios,	lpc_mmc_update_ios),
757239278Sgonzo	DEVMETHOD(mmcbr_request,	lpc_mmc_request),
758239278Sgonzo	DEVMETHOD(mmcbr_get_ro,		lpc_mmc_get_ro),
759239278Sgonzo	DEVMETHOD(mmcbr_acquire_host,	lpc_mmc_acquire_host),
760239278Sgonzo	DEVMETHOD(mmcbr_release_host,	lpc_mmc_release_host),
761239278Sgonzo
762239278Sgonzo	{ 0, 0 }
763239278Sgonzo};
764239278Sgonzo
765239278Sgonzostatic devclass_t lpc_mmc_devclass;
766239278Sgonzo
767239278Sgonzostatic driver_t lpc_mmc_driver = {
768239278Sgonzo	"lpcmmc",
769239278Sgonzo	lpc_mmc_methods,
770239278Sgonzo	sizeof(struct lpc_mmc_softc),
771239278Sgonzo};
772239278Sgonzo
773239278SgonzoDRIVER_MODULE(lpcmmc, simplebus, lpc_mmc_driver, lpc_mmc_devclass, 0, 0);
774