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$");
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{
169266152Sian
170266152Sian	if (!ofw_bus_status_okay(dev))
171266152Sian		return (ENXIO);
172266152Sian
173239278Sgonzo	if (!ofw_bus_is_compatible(dev, "lpc,mmc"))
174239278Sgonzo		return (ENXIO);
175239278Sgonzo
176239278Sgonzo	device_set_desc(dev, "LPC32x0 MMC/SD controller");
177239278Sgonzo	return (BUS_PROBE_DEFAULT);
178239278Sgonzo}
179239278Sgonzo
180239278Sgonzostatic int
181239278Sgonzolpc_mmc_attach(device_t dev)
182239278Sgonzo{
183239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(dev);
184239278Sgonzo	struct lpc_mmc_dmamap_arg ctx;
185239278Sgonzo	device_t child;
186239278Sgonzo	int rid, err;
187239278Sgonzo
188239278Sgonzo	sc->lm_dev = dev;
189239278Sgonzo	sc->lm_req = NULL;
190239278Sgonzo
191239278Sgonzo	mtx_init(&sc->lm_mtx, "lpcmmc", "mmc", MTX_DEF);
192239278Sgonzo
193239278Sgonzo	rid = 0;
194239278Sgonzo	sc->lm_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
195239278Sgonzo	    RF_ACTIVE);
196239278Sgonzo	if (!sc->lm_mem_res) {
197239278Sgonzo		device_printf(dev, "cannot allocate memory window\n");
198239278Sgonzo		return (ENXIO);
199239278Sgonzo	}
200239278Sgonzo
201239278Sgonzo	sc->lm_bst = rman_get_bustag(sc->lm_mem_res);
202239278Sgonzo	sc->lm_bsh = rman_get_bushandle(sc->lm_mem_res);
203239278Sgonzo
204239278Sgonzo	debugf("virtual register space: 0x%08lx\n", sc->lm_bsh);
205239278Sgonzo
206239278Sgonzo	rid = 0;
207239278Sgonzo	sc->lm_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
208239278Sgonzo	    RF_ACTIVE);
209239278Sgonzo	if (!sc->lm_irq_res) {
210239278Sgonzo		device_printf(dev, "cannot allocate interrupt\n");
211239278Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res);
212239278Sgonzo		return (ENXIO);
213239278Sgonzo	}
214239278Sgonzo
215239278Sgonzo	if (bus_setup_intr(dev, sc->lm_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
216239278Sgonzo	    NULL, lpc_mmc_intr, sc, &sc->lm_intrhand))
217239278Sgonzo	{
218239278Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res);
219239278Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res);
220239278Sgonzo		device_printf(dev, "cannot setup interrupt handler\n");
221239278Sgonzo		return (ENXIO);
222239278Sgonzo	}
223239278Sgonzo
224239278Sgonzo	sc->lm_host.f_min = 312500;
225239278Sgonzo	sc->lm_host.f_max = 2500000;
226239278Sgonzo	sc->lm_host.host_ocr = MMC_OCR_300_310 | MMC_OCR_310_320 |
227239278Sgonzo	    MMC_OCR_320_330 | MMC_OCR_330_340;
228239278Sgonzo#if 0
229239278Sgonzo	sc->lm_host.caps = MMC_CAP_4_BIT_DATA;
230239278Sgonzo#endif
231239278Sgonzo
232239278Sgonzo	lpc_pwr_write(dev, LPC_CLKPWR_MS_CTRL,
233239278Sgonzo	    LPC_CLKPWR_MS_CTRL_CLOCK_EN | LPC_CLKPWR_MS_CTRL_SD_CLOCK | 1);
234239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_POWER, LPC_SD_POWER_CTRL_ON);
235239278Sgonzo
236239278Sgonzo	device_set_ivars(dev, &sc->lm_host);
237239278Sgonzo
238239278Sgonzo	child = device_add_child(dev, "mmc", -1);
239239278Sgonzo	if (!child) {
240239278Sgonzo		device_printf(dev, "attaching MMC bus failed!\n");
241239278Sgonzo		bus_teardown_intr(dev, sc->lm_irq_res, sc->lm_intrhand);
242239278Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res);
243239278Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res);
244239278Sgonzo		return (ENXIO);
245239278Sgonzo	}
246239278Sgonzo
247239278Sgonzo	/* Alloc DMA memory */
248239278Sgonzo	err = bus_dma_tag_create(
249239278Sgonzo	    bus_get_dma_tag(sc->lm_dev),
250239278Sgonzo	    4, 0,			/* alignment, boundary */
251239278Sgonzo	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
252239278Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
253239278Sgonzo	    NULL, NULL,			/* filter, filterarg */
254239278Sgonzo	    LPC_SD_MAX_BLOCKSIZE, 1,	/* maxsize, nsegments */
255239278Sgonzo	    LPC_SD_MAX_BLOCKSIZE, 0,	/* maxsegsize, flags */
256239278Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
257239278Sgonzo	    &sc->lm_dma_tag);
258239278Sgonzo
259239278Sgonzo	err = bus_dmamem_alloc(sc->lm_dma_tag, (void **)&sc->lm_buffer,
260239278Sgonzo	    0, &sc->lm_dma_map);
261239278Sgonzo	if (err) {
262239278Sgonzo		device_printf(dev, "cannot allocate framebuffer\n");
263239278Sgonzo		goto fail;
264239278Sgonzo	}
265239278Sgonzo
266239278Sgonzo	err = bus_dmamap_load(sc->lm_dma_tag, sc->lm_dma_map, sc->lm_buffer,
267239278Sgonzo	    LPC_SD_MAX_BLOCKSIZE, lpc_mmc_dmamap_cb, &ctx, BUS_DMA_NOWAIT);
268239278Sgonzo	if (err) {
269239278Sgonzo		device_printf(dev, "cannot load DMA map\n");
270239278Sgonzo		goto fail;
271239278Sgonzo	}
272239278Sgonzo
273239278Sgonzo	sc->lm_buffer_phys = ctx.lm_dma_busaddr;
274239278Sgonzo
275239278Sgonzo	lpc_mmc_dma_rxconf.ldc_handler_arg = (void *)sc;
276239278Sgonzo	err = lpc_dmac_config_channel(dev, LPC_MMC_DMACH_READ, &lpc_mmc_dma_rxconf);
277239278Sgonzo	if (err) {
278239278Sgonzo		device_printf(dev, "cannot allocate RX DMA channel\n");
279239278Sgonzo		goto fail;
280239278Sgonzo	}
281239278Sgonzo
282239278Sgonzo
283239278Sgonzo	lpc_mmc_dma_txconf.ldc_handler_arg = (void *)sc;
284239278Sgonzo	err = lpc_dmac_config_channel(dev, LPC_MMC_DMACH_WRITE, &lpc_mmc_dma_txconf);
285239278Sgonzo	if (err) {
286239278Sgonzo		device_printf(dev, "cannot allocate TX DMA channel\n");
287239278Sgonzo		goto fail;
288239278Sgonzo	}
289239278Sgonzo
290239278Sgonzo	bus_generic_probe(dev);
291239278Sgonzo	bus_generic_attach(dev);
292239278Sgonzo
293239278Sgonzo	return (0);
294239278Sgonzo
295239278Sgonzofail:
296239278Sgonzo	if (sc->lm_intrhand)
297239278Sgonzo		bus_teardown_intr(dev, sc->lm_irq_res, sc->lm_intrhand);
298239278Sgonzo	if (sc->lm_irq_res)
299239278Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lm_irq_res);
300239278Sgonzo	if (sc->lm_mem_res)
301239278Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lm_mem_res);
302239278Sgonzo	return (err);
303239278Sgonzo}
304239278Sgonzo
305239278Sgonzostatic int
306239278Sgonzolpc_mmc_detach(device_t dev)
307239278Sgonzo{
308239278Sgonzo	return (EBUSY);
309239278Sgonzo}
310239278Sgonzo
311239278Sgonzostatic void
312239278Sgonzolpc_mmc_intr(void *arg)
313239278Sgonzo{
314239278Sgonzo	struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg;
315239278Sgonzo	struct mmc_command *cmd;
316239278Sgonzo	uint32_t status;
317239278Sgonzo
318239278Sgonzo	status = lpc_mmc_read_4(sc, LPC_SD_STATUS);
319239278Sgonzo
320239278Sgonzo	debugf("interrupt: 0x%08x\n", status);
321239278Sgonzo
322239278Sgonzo	if (status & LPC_SD_STATUS_CMDCRCFAIL) {
323239278Sgonzo		cmd = sc->lm_req->cmd;
324239278Sgonzo		cmd->error = sc->lm_flags & LPC_SD_FLAGS_IGNORECRC
325239278Sgonzo		    ? MMC_ERR_NONE : MMC_ERR_BADCRC;
326239278Sgonzo		cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0);
327239278Sgonzo		sc->lm_req->done(sc->lm_req);
328239278Sgonzo		sc->lm_req = NULL;
329239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDCRCFAIL);
330239278Sgonzo	}
331239278Sgonzo
332239278Sgonzo	if (status & LPC_SD_STATUS_CMDACTIVE)
333239278Sgonzo	{
334239278Sgonzo		debugf("command active\n");
335239278Sgonzo		cmd = sc->lm_req->cmd;
336239278Sgonzo		cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0);
337239278Sgonzo		sc->lm_req->done(sc->lm_req);
338239278Sgonzo		sc->lm_req = NULL;
339239278Sgonzo	}
340239278Sgonzo
341239278Sgonzo	if (status & LPC_SD_STATUS_DATATIMEOUT) {
342239278Sgonzo		device_printf(sc->lm_dev, "data timeout\n");
343239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATATIMEOUT);
344239278Sgonzo	}
345239278Sgonzo
346239278Sgonzo	if (status & LPC_SD_STATUS_TXUNDERRUN) {
347239278Sgonzo		device_printf(sc->lm_dev, "TX underrun\n");
348239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_TXUNDERRUN);
349239278Sgonzo	}
350239278Sgonzo
351239278Sgonzo	if (status & LPC_SD_STATUS_CMDRESPEND) {
352239278Sgonzo		debugf("command response\n");
353239278Sgonzo		cmd = sc->lm_req->cmd;
354239278Sgonzo
355239278Sgonzo		if (cmd->flags & MMC_RSP_136) {
356239278Sgonzo			cmd->resp[3] = lpc_mmc_read_4(sc, LPC_SD_RESP3);
357239278Sgonzo			cmd->resp[2] = lpc_mmc_read_4(sc, LPC_SD_RESP2);
358239278Sgonzo			cmd->resp[1] = lpc_mmc_read_4(sc, LPC_SD_RESP1);
359239278Sgonzo		}
360239278Sgonzo
361239278Sgonzo		cmd->resp[0] = lpc_mmc_read_4(sc, LPC_SD_RESP0);
362239278Sgonzo		cmd->error = MMC_ERR_NONE;
363239278Sgonzo
364239278Sgonzo		if (cmd->data && (cmd->data->flags & MMC_DATA_WRITE))
365239278Sgonzo			lpc_mmc_setup_xfer(sc, sc->lm_req->cmd->data);
366239278Sgonzo
367239278Sgonzo		if (!cmd->data) {
368239278Sgonzo			sc->lm_req->done(sc->lm_req);
369239278Sgonzo			sc->lm_req = NULL;
370239278Sgonzo		}
371239278Sgonzo
372239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDRESPEND);
373239278Sgonzo	}
374239278Sgonzo
375239278Sgonzo	if (status & LPC_SD_STATUS_CMDSENT) {
376239278Sgonzo		debugf("command sent\n");
377239278Sgonzo		cmd = sc->lm_req->cmd;
378239278Sgonzo		cmd->error = MMC_ERR_NONE;
379239278Sgonzo		sc->lm_req->done(sc->lm_req);
380239278Sgonzo		sc->lm_req = NULL;
381239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDSENT);
382239278Sgonzo	}
383239278Sgonzo
384239278Sgonzo	if (status & LPC_SD_STATUS_DATAEND) {
385239278Sgonzo		if (sc->lm_xfer_direction == DIRECTION_READ)
386239278Sgonzo			lpc_dmac_start_burst(sc->lm_dev, LPC_DMAC_SD_ID);
387239278Sgonzo
388239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATAEND);
389239278Sgonzo	}
390239278Sgonzo
391239278Sgonzo	if (status & LPC_SD_STATUS_CMDTIMEOUT) {
392239278Sgonzo		device_printf(sc->lm_dev, "command response timeout\n");
393239278Sgonzo		cmd = sc->lm_req->cmd;
394239278Sgonzo		cmd->error = MMC_ERR_TIMEOUT;
395239278Sgonzo		sc->lm_req->done(sc->lm_req);
396239278Sgonzo		sc->lm_req = NULL;
397239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_CMDTIMEOUT);
398239278Sgonzo		return;
399239278Sgonzo	}
400239278Sgonzo
401239278Sgonzo	if (status & LPC_SD_STATUS_STARTBITERR) {
402239278Sgonzo		device_printf(sc->lm_dev, "start bit error\n");
403239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_STARTBITERR);
404239278Sgonzo	}
405239278Sgonzo
406239278Sgonzo	if (status & LPC_SD_STATUS_DATACRCFAIL) {
407239278Sgonzo		device_printf(sc->lm_dev, "data CRC error\n");
408239278Sgonzo		debugf("data buffer: %p\n", sc->lm_buffer);
409239278Sgonzo		cmd = sc->lm_req->cmd;
410239278Sgonzo		cmd->error = MMC_ERR_BADCRC;
411239278Sgonzo		sc->lm_req->done(sc->lm_req);
412239278Sgonzo		sc->lm_req = NULL;
413239278Sgonzo
414239278Sgonzo		if (sc->lm_xfer_direction == DIRECTION_READ)
415239278Sgonzo			lpc_dmac_start_burst(sc->lm_dev, LPC_DMAC_SD_ID);
416239278Sgonzo
417239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATACRCFAIL);
418239278Sgonzo	}
419239278Sgonzo
420239278Sgonzo	if (status & LPC_SD_STATUS_DATABLOCKEND) {
421239278Sgonzo		debugf("data block end\n");
422239278Sgonzo		if (sc->lm_xfer_direction == DIRECTION_READ)
423239278Sgonzo			memcpy(sc->lm_data->data, sc->lm_buffer, sc->lm_data->len);
424239278Sgonzo
425239278Sgonzo		if (sc->lm_xfer_direction == DIRECTION_WRITE) {
426239278Sgonzo			lpc_dmac_disable_channel(sc->lm_dev, LPC_MMC_DMACH_WRITE);
427239278Sgonzo			lpc_mmc_write_4(sc, LPC_SD_DATACTRL, 0);
428239278Sgonzo		}
429239278Sgonzo
430239278Sgonzo		sc->lm_req->done(sc->lm_req);
431239278Sgonzo		sc->lm_req = NULL;
432239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_CLEAR, LPC_SD_STATUS_DATABLOCKEND);
433239278Sgonzo	}
434239278Sgonzo
435239278Sgonzo	debugf("done\n");
436239278Sgonzo}
437239278Sgonzo
438239278Sgonzostatic int
439239278Sgonzolpc_mmc_request(device_t bus, device_t child, struct mmc_request *req)
440239278Sgonzo{
441239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
442239278Sgonzo
443239278Sgonzo	debugf("request: %p\n", req);
444239278Sgonzo
445239278Sgonzo	lpc_mmc_lock(sc);
446239278Sgonzo	if (sc->lm_req)
447239278Sgonzo		return (EBUSY);
448239278Sgonzo
449239278Sgonzo	sc->lm_req = req;
450239278Sgonzo
451239278Sgonzo	if (req->cmd->data && req->cmd->data->flags & MMC_DATA_WRITE) {
452239278Sgonzo		memcpy(sc->lm_buffer, req->cmd->data->data, req->cmd->data->len);
453239278Sgonzo		lpc_mmc_cmd(sc, req->cmd);
454239278Sgonzo		lpc_mmc_unlock(sc);
455239278Sgonzo		return (0);
456239278Sgonzo	}
457239278Sgonzo
458239278Sgonzo	if (req->cmd->data)
459239278Sgonzo		lpc_mmc_setup_xfer(sc, req->cmd->data);
460239278Sgonzo
461239278Sgonzo	lpc_mmc_cmd(sc, req->cmd);
462239278Sgonzo	lpc_mmc_unlock(sc);
463239278Sgonzo
464239278Sgonzo	return (0);
465239278Sgonzo}
466239278Sgonzo
467239278Sgonzostatic void
468239278Sgonzolpc_mmc_cmd(struct lpc_mmc_softc *sc, struct mmc_command *cmd)
469239278Sgonzo{
470239278Sgonzo	uint32_t cmdreg = 0;
471239278Sgonzo
472239278Sgonzo	debugf("cmd: %d arg: 0x%08x\n", cmd->opcode, cmd->arg);
473239278Sgonzo
474239278Sgonzo	if (lpc_mmc_read_4(sc, LPC_SD_COMMAND) & LPC_SD_COMMAND_ENABLE) {
475239278Sgonzo		lpc_mmc_write_4(sc, LPC_SD_COMMAND, 0);
476239278Sgonzo		DELAY(1000);
477239278Sgonzo	}
478239278Sgonzo
479239278Sgonzo	sc->lm_flags &= ~LPC_SD_FLAGS_IGNORECRC;
480239278Sgonzo
481239278Sgonzo	if (cmd->flags & MMC_RSP_PRESENT)
482239278Sgonzo		cmdreg |= LPC_SD_COMMAND_RESPONSE;
483239278Sgonzo
484239278Sgonzo	if (MMC_RSP(cmd->flags) == MMC_RSP_R2)
485239278Sgonzo		cmdreg |= LPC_SD_COMMAND_LONGRSP;
486239278Sgonzo
487239278Sgonzo	if (MMC_RSP(cmd->flags) == MMC_RSP_R3)
488239278Sgonzo		sc->lm_flags |= LPC_SD_FLAGS_IGNORECRC;
489239278Sgonzo
490239278Sgonzo	cmdreg |= LPC_SD_COMMAND_ENABLE;
491239278Sgonzo	cmdreg |= (cmd->opcode & LPC_SD_COMMAND_CMDINDEXMASK);
492239278Sgonzo
493239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_MASK0, 0xffffffff);
494239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_MASK1, 0xffffffff);
495239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_ARGUMENT, cmd->arg);
496239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_COMMAND, cmdreg);
497239278Sgonzo}
498239278Sgonzo
499239278Sgonzostatic void
500239278Sgonzolpc_mmc_setup_xfer(struct lpc_mmc_softc *sc, struct mmc_data *data)
501239278Sgonzo{
502239278Sgonzo	uint32_t datactrl = 0;
503239278Sgonzo	int data_words = data->len / 4;
504239278Sgonzo
505239278Sgonzo	sc->lm_data = data;
506239278Sgonzo	sc->lm_xfer_done = 0;
507239278Sgonzo
508239278Sgonzo	debugf("data: %p, len: %d, %s\n", data,
509239278Sgonzo	    data->len, (data->flags & MMC_DATA_READ) ? "read" : "write");
510239278Sgonzo
511239278Sgonzo	if (data->flags & MMC_DATA_READ) {
512239278Sgonzo		sc->lm_xfer_direction = DIRECTION_READ;
513239278Sgonzo		lpc_dmac_setup_transfer(sc->lm_dev, LPC_MMC_DMACH_READ,
514266084Sian		    LPC_SD_PHYS_BASE + LPC_SD_FIFO, sc->lm_buffer_phys,
515239278Sgonzo		    data_words, 0);
516239278Sgonzo	}
517239278Sgonzo
518239278Sgonzo	if (data->flags & MMC_DATA_WRITE) {
519239278Sgonzo		sc->lm_xfer_direction = DIRECTION_WRITE;
520239278Sgonzo		lpc_dmac_setup_transfer(sc->lm_dev, LPC_MMC_DMACH_WRITE,
521266084Sian		    sc->lm_buffer_phys, LPC_SD_PHYS_BASE + LPC_SD_FIFO,
522239278Sgonzo		    data_words, 0);
523239278Sgonzo	}
524239278Sgonzo
525239278Sgonzo	datactrl |= (sc->lm_xfer_direction
526239278Sgonzo	    ? LPC_SD_DATACTRL_WRITE
527239278Sgonzo	    : LPC_SD_DATACTRL_READ);
528239278Sgonzo
529239278Sgonzo	datactrl |= LPC_SD_DATACTRL_DMAENABLE | LPC_SD_DATACTRL_ENABLE;
530239278Sgonzo	datactrl |= (ffs(data->len) - 1) << 4;
531239278Sgonzo
532239278Sgonzo	debugf("datactrl: 0x%08x\n", datactrl);
533239278Sgonzo
534239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_DATATIMER, 0xFFFF0000);
535239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_DATALENGTH, data->len);
536239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_DATACTRL, datactrl);
537239278Sgonzo}
538239278Sgonzo
539239278Sgonzostatic int
540239278Sgonzolpc_mmc_read_ivar(device_t bus, device_t child, int which,
541239278Sgonzo    uintptr_t *result)
542239278Sgonzo{
543239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
544239278Sgonzo
545239278Sgonzo	switch (which) {
546239278Sgonzo	default:
547239278Sgonzo		return (EINVAL);
548239278Sgonzo	case MMCBR_IVAR_BUS_MODE:
549239278Sgonzo		*(int *)result = sc->lm_host.ios.bus_mode;
550239278Sgonzo		break;
551239278Sgonzo	case MMCBR_IVAR_BUS_WIDTH:
552239278Sgonzo		*(int *)result = sc->lm_host.ios.bus_width;
553239278Sgonzo		break;
554239278Sgonzo	case MMCBR_IVAR_CHIP_SELECT:
555239278Sgonzo		*(int *)result = sc->lm_host.ios.chip_select;
556239278Sgonzo		break;
557239278Sgonzo	case MMCBR_IVAR_CLOCK:
558239278Sgonzo		*(int *)result = sc->lm_host.ios.clock;
559239278Sgonzo		break;
560239278Sgonzo	case MMCBR_IVAR_F_MIN:
561239278Sgonzo		*(int *)result = sc->lm_host.f_min;
562239278Sgonzo		break;
563239278Sgonzo	case MMCBR_IVAR_F_MAX:
564239278Sgonzo		*(int *)result = sc->lm_host.f_max;
565239278Sgonzo		break;
566239278Sgonzo	case MMCBR_IVAR_HOST_OCR:
567239278Sgonzo		*(int *)result = sc->lm_host.host_ocr;
568239278Sgonzo		break;
569239278Sgonzo	case MMCBR_IVAR_MODE:
570239278Sgonzo		*(int *)result = sc->lm_host.mode;
571239278Sgonzo		break;
572239278Sgonzo	case MMCBR_IVAR_OCR:
573239278Sgonzo		*(int *)result = sc->lm_host.ocr;
574239278Sgonzo		break;
575239278Sgonzo	case MMCBR_IVAR_POWER_MODE:
576239278Sgonzo		*(int *)result = sc->lm_host.ios.power_mode;
577239278Sgonzo		break;
578239278Sgonzo	case MMCBR_IVAR_VDD:
579239278Sgonzo		*(int *)result = sc->lm_host.ios.vdd;
580239278Sgonzo		break;
581239278Sgonzo	case MMCBR_IVAR_CAPS:
582239278Sgonzo		*(int *)result = sc->lm_host.caps;
583239278Sgonzo		break;
584239278Sgonzo	case MMCBR_IVAR_MAX_DATA:
585239278Sgonzo		*(int *)result = 1;
586239278Sgonzo		break;
587239278Sgonzo	}
588239278Sgonzo
589239278Sgonzo	return (0);
590239278Sgonzo}
591239278Sgonzo
592239278Sgonzostatic int
593239278Sgonzolpc_mmc_write_ivar(device_t bus, device_t child, int which,
594239278Sgonzo    uintptr_t value)
595239278Sgonzo{
596239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
597239278Sgonzo
598239278Sgonzo	switch (which) {
599239278Sgonzo	default:
600239278Sgonzo		return (EINVAL);
601239278Sgonzo	case MMCBR_IVAR_BUS_MODE:
602239278Sgonzo		sc->lm_host.ios.bus_mode = value;
603239278Sgonzo		break;
604239278Sgonzo	case MMCBR_IVAR_BUS_WIDTH:
605239278Sgonzo		sc->lm_host.ios.bus_width = value;
606239278Sgonzo		break;
607239278Sgonzo	case MMCBR_IVAR_CHIP_SELECT:
608239278Sgonzo		sc->lm_host.ios.chip_select = value;
609239278Sgonzo		break;
610239278Sgonzo	case MMCBR_IVAR_CLOCK:
611239278Sgonzo		sc->lm_host.ios.clock = value;
612239278Sgonzo		break;
613239278Sgonzo	case MMCBR_IVAR_MODE:
614239278Sgonzo		sc->lm_host.mode = value;
615239278Sgonzo		break;
616239278Sgonzo	case MMCBR_IVAR_OCR:
617239278Sgonzo		sc->lm_host.ocr = value;
618239278Sgonzo		break;
619239278Sgonzo	case MMCBR_IVAR_POWER_MODE:
620239278Sgonzo		sc->lm_host.ios.power_mode = value;
621239278Sgonzo		break;
622239278Sgonzo	case MMCBR_IVAR_VDD:
623239278Sgonzo		sc->lm_host.ios.vdd = value;
624239278Sgonzo		break;
625239278Sgonzo	/* These are read-only */
626239278Sgonzo	case MMCBR_IVAR_CAPS:
627239278Sgonzo	case MMCBR_IVAR_HOST_OCR:
628239278Sgonzo	case MMCBR_IVAR_F_MIN:
629239278Sgonzo	case MMCBR_IVAR_F_MAX:
630239278Sgonzo	case MMCBR_IVAR_MAX_DATA:
631239278Sgonzo		return (EINVAL);
632239278Sgonzo	}
633239278Sgonzo	return (0);
634239278Sgonzo}
635239278Sgonzo
636239278Sgonzostatic int
637239278Sgonzolpc_mmc_update_ios(device_t bus, device_t child)
638239278Sgonzo{
639239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
640239278Sgonzo	struct mmc_ios *ios = &sc->lm_host.ios;
641239278Sgonzo	uint32_t clkdiv = 0, pwr = 0;
642239278Sgonzo
643239278Sgonzo	if (ios->bus_width == bus_width_4)
644239278Sgonzo		clkdiv |= LPC_SD_CLOCK_WIDEBUS;
645239278Sgonzo
646239278Sgonzo	/* Calculate clock divider */
647239278Sgonzo	clkdiv = (LPC_SD_CLK / (2 * ios->clock)) - 1;
648239278Sgonzo
649239278Sgonzo	/* Clock rate should not exceed rate requested in ios */
650239278Sgonzo	if ((LPC_SD_CLK / (2 * (clkdiv + 1))) > ios->clock)
651239278Sgonzo		clkdiv++;
652239278Sgonzo
653239278Sgonzo	debugf("clock: %dHz, clkdiv: %d\n", ios->clock, clkdiv);
654239278Sgonzo
655239278Sgonzo	if (ios->bus_width == bus_width_4) {
656239278Sgonzo		debugf("using wide bus mode\n");
657239278Sgonzo		clkdiv |= LPC_SD_CLOCK_WIDEBUS;
658239278Sgonzo	}
659239278Sgonzo
660239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_CLOCK, clkdiv | LPC_SD_CLOCK_ENABLE);
661239278Sgonzo
662239278Sgonzo	switch (ios->power_mode) {
663239278Sgonzo	case power_off:
664239278Sgonzo		pwr |= LPC_SD_POWER_CTRL_OFF;
665239278Sgonzo		break;
666239278Sgonzo	case power_up:
667239278Sgonzo		pwr |= LPC_SD_POWER_CTRL_UP;
668239278Sgonzo		break;
669239278Sgonzo	case power_on:
670239278Sgonzo		pwr |= LPC_SD_POWER_CTRL_ON;
671239278Sgonzo		break;
672239278Sgonzo	}
673239278Sgonzo
674239278Sgonzo	if (ios->bus_mode == opendrain)
675239278Sgonzo		pwr |= LPC_SD_POWER_OPENDRAIN;
676239278Sgonzo
677239278Sgonzo	lpc_mmc_write_4(sc, LPC_SD_POWER, pwr);
678239278Sgonzo
679239278Sgonzo	return (0);
680239278Sgonzo}
681239278Sgonzo
682239278Sgonzostatic int
683239278Sgonzolpc_mmc_get_ro(device_t bus, device_t child)
684239278Sgonzo{
685239278Sgonzo
686239278Sgonzo	return (0);
687239278Sgonzo}
688239278Sgonzo
689239278Sgonzostatic int
690239278Sgonzolpc_mmc_acquire_host(device_t bus, device_t child)
691239278Sgonzo{
692239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
693239278Sgonzo	int error = 0;
694239278Sgonzo
695239278Sgonzo	lpc_mmc_lock(sc);
696239278Sgonzo	while (sc->lm_bus_busy)
697239278Sgonzo		error = mtx_sleep(sc, &sc->lm_mtx, PZERO, "mmcah", 0);
698239278Sgonzo
699239278Sgonzo	sc->lm_bus_busy++;
700239278Sgonzo	lpc_mmc_unlock(sc);
701239278Sgonzo	return (error);
702239278Sgonzo}
703239278Sgonzo
704239278Sgonzostatic int
705239278Sgonzolpc_mmc_release_host(device_t bus, device_t child)
706239278Sgonzo{
707239278Sgonzo	struct lpc_mmc_softc *sc = device_get_softc(bus);
708239278Sgonzo
709239278Sgonzo	lpc_mmc_lock(sc);
710239278Sgonzo	sc->lm_bus_busy--;
711239278Sgonzo	wakeup(sc);
712239278Sgonzo	lpc_mmc_unlock(sc);
713239278Sgonzo	return (0);
714239278Sgonzo}
715239278Sgonzo
716239278Sgonzostatic void lpc_mmc_dma_rxfinish(void *arg)
717239278Sgonzo{
718239278Sgonzo}
719239278Sgonzo
720239278Sgonzostatic void lpc_mmc_dma_rxerror(void *arg)
721239278Sgonzo{
722239278Sgonzo	struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg;
723239278Sgonzo	device_printf(sc->lm_dev, "DMA RX error\n");
724239278Sgonzo}
725239278Sgonzo
726239278Sgonzostatic void lpc_mmc_dma_txfinish(void *arg)
727239278Sgonzo{
728239278Sgonzo}
729239278Sgonzo
730239278Sgonzostatic void lpc_mmc_dma_txerror(void *arg)
731239278Sgonzo{
732239278Sgonzo	struct lpc_mmc_softc *sc = (struct lpc_mmc_softc *)arg;
733239278Sgonzo	device_printf(sc->lm_dev, "DMA TX error\n");
734239278Sgonzo}
735239278Sgonzo
736239278Sgonzostatic void
737239278Sgonzolpc_mmc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
738239278Sgonzo{
739239278Sgonzo	struct lpc_mmc_dmamap_arg *ctx;
740239278Sgonzo
741239278Sgonzo	if (err)
742239278Sgonzo		return;
743239278Sgonzo
744239278Sgonzo	ctx = (struct lpc_mmc_dmamap_arg *)arg;
745239278Sgonzo	ctx->lm_dma_busaddr = segs[0].ds_addr;
746239278Sgonzo}
747239278Sgonzo
748239278Sgonzostatic device_method_t lpc_mmc_methods[] = {
749239278Sgonzo	/* Device interface */
750239278Sgonzo	DEVMETHOD(device_probe,		lpc_mmc_probe),
751239278Sgonzo	DEVMETHOD(device_attach,	lpc_mmc_attach),
752239278Sgonzo	DEVMETHOD(device_detach,	lpc_mmc_detach),
753239278Sgonzo
754239278Sgonzo	/* Bus interface */
755239278Sgonzo	DEVMETHOD(bus_read_ivar,	lpc_mmc_read_ivar),
756239278Sgonzo	DEVMETHOD(bus_write_ivar,	lpc_mmc_write_ivar),
757239278Sgonzo	DEVMETHOD(bus_print_child,	bus_generic_print_child),
758239278Sgonzo
759239278Sgonzo	/* MMC bridge interface */
760239278Sgonzo	DEVMETHOD(mmcbr_update_ios,	lpc_mmc_update_ios),
761239278Sgonzo	DEVMETHOD(mmcbr_request,	lpc_mmc_request),
762239278Sgonzo	DEVMETHOD(mmcbr_get_ro,		lpc_mmc_get_ro),
763239278Sgonzo	DEVMETHOD(mmcbr_acquire_host,	lpc_mmc_acquire_host),
764239278Sgonzo	DEVMETHOD(mmcbr_release_host,	lpc_mmc_release_host),
765239278Sgonzo
766239278Sgonzo	{ 0, 0 }
767239278Sgonzo};
768239278Sgonzo
769239278Sgonzostatic devclass_t lpc_mmc_devclass;
770239278Sgonzo
771239278Sgonzostatic driver_t lpc_mmc_driver = {
772239278Sgonzo	"lpcmmc",
773239278Sgonzo	lpc_mmc_methods,
774239278Sgonzo	sizeof(struct lpc_mmc_softc),
775239278Sgonzo};
776239278Sgonzo
777239278SgonzoDRIVER_MODULE(lpcmmc, simplebus, lpc_mmc_driver, lpc_mmc_devclass, 0, 0);
778