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