mmcsd.c revision 169567
1163516Simp/*-
2163516Simp * Copyright (c) 2006 Bernd Walter.  All rights reserved.
3163516Simp * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
4163516Simp *
5163516Simp * Redistribution and use in source and binary forms, with or without
6163516Simp * modification, are permitted provided that the following conditions
7163516Simp * are met:
8163516Simp * 1. Redistributions of source code must retain the above copyright
9163516Simp *    notice, this list of conditions and the following disclaimer.
10163516Simp * 2. Redistributions in binary form must reproduce the above copyright
11163516Simp *    notice, this list of conditions and the following disclaimer in the
12163516Simp *    documentation and/or other materials provided with the distribution.
13163516Simp *
14163516Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15163516Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16163516Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17163516Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18163516Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19163516Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20163516Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21163516Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22163516Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23163516Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24163516Simp */
25163516Simp
26163516Simp#include <sys/cdefs.h>
27163516Simp__FBSDID("$FreeBSD: head/sys/dev/mmc/mmcsd.c 169567 2007-05-15 05:49:14Z imp $");
28163516Simp
29163516Simp#include <sys/param.h>
30163516Simp#include <sys/systm.h>
31163516Simp#include <sys/bio.h>
32163516Simp#include <sys/bus.h>
33163516Simp#include <sys/conf.h>
34163516Simp#include <sys/kernel.h>
35163516Simp#include <sys/kthread.h>
36163516Simp#include <sys/lock.h>
37163516Simp#include <sys/malloc.h>
38163516Simp#include <sys/module.h>
39163516Simp#include <sys/mutex.h>
40163516Simp#include <geom/geom_disk.h>
41163516Simp
42163516Simp#include <dev/mmc/mmcvar.h>
43163516Simp#include <dev/mmc/mmcreg.h>
44163516Simp
45163516Simp#include "mmcbus_if.h"
46163516Simp
47163516Simpstruct mmcsd_softc {
48163516Simp	device_t dev;
49163516Simp	struct mtx sc_mtx;
50163516Simp	struct disk *disk;
51163516Simp	struct proc *p;
52163516Simp	struct bio_queue_head bio_queue;
53169567Simp	int running;
54163516Simp};
55163516Simp
56163516Simp#define	MULTI_BLOCK_READ_BROKEN
57163516Simp
58163516Simp/* bus entry points */
59163516Simpstatic int mmcsd_probe(device_t dev);
60163516Simpstatic int mmcsd_attach(device_t dev);
61163516Simpstatic int mmcsd_detach(device_t dev);
62163516Simp
63163516Simp/* disk routines */
64163516Simpstatic int mmcsd_open(struct disk *dp);
65163516Simpstatic int mmcsd_close(struct disk *dp);
66163516Simpstatic void mmcsd_strategy(struct bio *bp);
67163516Simpstatic void mmcsd_task(void *arg);
68163516Simp
69163516Simp#define MMCSD_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
70163516Simp#define	MMCSD_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
71163516Simp#define MMCSD_LOCK_INIT(_sc) \
72163516Simp	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
73163516Simp	    "mmcsd", MTX_DEF)
74163516Simp#define MMCSD_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
75163516Simp#define MMCSD_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED);
76163516Simp#define MMCSD_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
77163516Simp
78163516Simpstatic int
79163516Simpmmcsd_probe(device_t dev)
80163516Simp{
81163516Simp
82163516Simp	device_set_desc(dev, "mmc or sd flash card");
83163516Simp	return (0);
84163516Simp}
85163516Simp
86163516Simpstatic int
87163516Simpmmcsd_attach(device_t dev)
88163516Simp{
89163516Simp	struct mmcsd_softc *sc;
90163516Simp
91163516Simp	sc = device_get_softc(dev);
92163516Simp	sc->dev = dev;
93163516Simp	MMCSD_LOCK_INIT(sc);
94163516Simp
95163516Simp	sc->disk = disk_alloc();
96163516Simp	sc->disk->d_open = mmcsd_open;
97163516Simp	sc->disk->d_close = mmcsd_close;
98163516Simp	sc->disk->d_strategy = mmcsd_strategy;
99163516Simp	// sc->disk->d_dump = mmcsd_dump;	Need polling mmc layer
100163516Simp	sc->disk->d_name = "mmcsd";
101163516Simp	sc->disk->d_drv1 = sc;
102163516Simp	sc->disk->d_maxsize = DFLTPHYS;
103163516Simp	sc->disk->d_sectorsize = mmc_get_sector_size(dev);
104163516Simp	sc->disk->d_mediasize = mmc_get_media_size(dev);
105163516Simp	sc->disk->d_unit = device_get_unit(dev);
106163516Simp	disk_create(sc->disk, DISK_VERSION);
107163516Simp	bioq_init(&sc->bio_queue);
108169567Simp
109169567Simp	sc->running = 1;
110163516Simp	kthread_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card");
111163516Simp
112163516Simp	return (0);
113163516Simp}
114163516Simp
115163516Simpstatic int
116163516Simpmmcsd_detach(device_t dev)
117163516Simp{
118169567Simp	struct mmcsd_softc *sc = device_get_softc(dev);
119169567Simp
120169567Simp	/* kill thread */
121169567Simp	MMCSD_LOCK(sc);
122169567Simp	sc->running = 0;
123169567Simp	wakeup(sc);
124169567Simp	MMCSD_UNLOCK(sc);
125169567Simp
126169567Simp	/* wait for thread to finish.  XXX probably want timeout.  -sorbo */
127169567Simp	MMCSD_LOCK(sc);
128169567Simp	while (sc->running != -1)
129169567Simp		msleep(sc, &sc->sc_mtx, PRIBIO, "detach", 0);
130169567Simp	MMCSD_UNLOCK(sc);
131169567Simp
132169567Simp	/* kill disk */
133169567Simp	disk_destroy(sc->disk);
134169567Simp	/* XXX destroy anything in queue */
135169567Simp
136169567Simp	MMCSD_LOCK_DESTROY(sc);
137169567Simp
138169567Simp	return 0;
139163516Simp}
140163516Simp
141163516Simpstatic int
142163516Simpmmcsd_open(struct disk *dp)
143163516Simp{
144163516Simp	return 0;
145163516Simp}
146163516Simp
147163516Simpstatic int
148163516Simpmmcsd_close(struct disk *dp)
149163516Simp{
150163516Simp	return 0;
151163516Simp}
152163516Simp
153163516Simpstatic void
154163516Simpmmcsd_strategy(struct bio *bp)
155163516Simp{
156163516Simp	struct mmcsd_softc *sc;
157163516Simp
158163516Simp	sc = (struct mmcsd_softc *)bp->bio_disk->d_drv1;
159163516Simp	MMCSD_LOCK(sc);
160163516Simp	bioq_disksort(&sc->bio_queue, bp);
161163516Simp	wakeup(sc);
162163516Simp	MMCSD_UNLOCK(sc);
163163516Simp}
164163516Simp
165163516Simpstatic void
166163516Simpmmcsd_task(void *arg)
167163516Simp{
168163516Simp	struct mmcsd_softc *sc = (struct mmcsd_softc*)arg;
169163516Simp	struct bio *bp;
170163516Simp	int sz;
171163516Simp	daddr_t block, end;
172163516Simp	struct mmc_command cmd;
173163516Simp	struct mmc_command stop;
174163516Simp	struct mmc_request req;
175163516Simp	struct mmc_data data;
176163516Simp	device_t dev;
177163516Simp
178163516Simp	dev = sc->dev;
179169567Simp	while (sc->running) {
180163516Simp		MMCSD_LOCK(sc);
181163516Simp		do {
182163516Simp			bp = bioq_first(&sc->bio_queue);
183163516Simp			if (bp == NULL)
184163516Simp				msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0);
185169567Simp		} while (bp == NULL && sc->running);
186169567Simp		if (bp)
187169567Simp			bioq_remove(&sc->bio_queue, bp);
188163516Simp		MMCSD_UNLOCK(sc);
189169567Simp		if (!sc->running)
190169567Simp			break;
191163516Simp		MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
192163516Simp//		printf("mmc_task: request %p for block %lld\n", bp, bp->bio_pblkno);
193163516Simp		sz = sc->disk->d_sectorsize;
194163516Simp		end = bp->bio_pblkno + (bp->bio_bcount / sz);
195163516Simp		// XXX should use read/write_mulit
196163516Simp		for (block = bp->bio_pblkno; block < end;) {
197163516Simp			char *vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz;
198163516Simp			memset(&req, 0, sizeof(req));
199163516Simp			memset(&cmd, 0, sizeof(cmd));
200163516Simp			memset(&stop, 0, sizeof(stop));
201163516Simp			req.cmd = &cmd;
202163516Simp			cmd.data = &data;
203163516Simp			if (bp->bio_cmd == BIO_READ) {
204163516Simp#ifdef MULTI_BLOCK_READ
205163516Simp				if (end - block > 1)
206163516Simp					cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
207163516Simp				else
208163516Simp					cmd.opcode = MMC_READ_SINGLE_BLOCK;
209163516Simp#else
210163516Simp				cmd.opcode = MMC_READ_SINGLE_BLOCK;
211163516Simp#endif
212163516Simp			} else
213163516Simp				cmd.opcode = MMC_WRITE_BLOCK;
214163516Simp			cmd.arg = block << 9;
215163516Simp			cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
216163516Simp			// data.timeout_ns = ;
217163516Simp			// data.timeout_clks = ;
218163516Simp			data.data = vaddr;
219163516Simp			data.mrq = &req;
220163516Simp			if (bp->bio_cmd == BIO_READ) {
221163516Simp				data.flags = MMC_DATA_READ;
222163516Simp#ifdef MULTI_BLOCK_READ
223163516Simp				data.len = bp->bio_bcount;
224163516Simp				if (end - block > 1) {
225163516Simp					req.stop = &stop;
226163516Simp					data.flags |= MMC_DATA_MULTI;
227163516Simp				}
228163516Simp				printf("Len %d  %lld-%lld flags %#x sz %d\n",
229163516Simp				    data.len, block, end, data.flags, sz);
230163516Simp				block = end;
231163516Simp#else
232163516Simp				data.len = sz;
233163516Simp				block++;
234163516Simp#endif
235163516Simp			} else {
236163516Simp				data.flags = MMC_DATA_WRITE;
237163516Simp				data.len = sz;
238163516Simp				block++;
239163516Simp			}
240163516Simp			stop.opcode = MMC_STOP_TRANSMISSION;
241163516Simp			stop.arg = 0;
242163516Simp			stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
243163516Simp			MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
244163516Simp			    &req);
245163516Simp			// XXX error handling
246163516Simp//XXX			while (!(mmc_send_status(dev) & R1_READY_FOR_DATA))
247163516Simp//				continue;
248163516Simp			// XXX decode mmc status
249163516Simp		}
250163516Simp		MMCBUS_RELEASE_BUS(device_get_parent(dev), dev);
251163516Simp		biodone(bp);
252163516Simp	}
253169567Simp
254169567Simp	/* tell parent we're done */
255169567Simp	MMCSD_LOCK(sc);
256169567Simp	sc->running = -1;
257169567Simp	wakeup(sc);
258169567Simp	MMCSD_UNLOCK(sc);
259169567Simp
260169567Simp	kthread_exit(0);
261163516Simp}
262163516Simp
263163516Simpstatic device_method_t mmcsd_methods[] = {
264163516Simp	DEVMETHOD(device_probe, mmcsd_probe),
265163516Simp	DEVMETHOD(device_attach, mmcsd_attach),
266163516Simp	DEVMETHOD(device_detach, mmcsd_detach),
267163516Simp	{0, 0},
268163516Simp};
269163516Simp
270163516Simpstatic driver_t mmcsd_driver = {
271163516Simp	"mmcsd",
272163516Simp	mmcsd_methods,
273163516Simp	sizeof(struct mmcsd_softc),
274163516Simp};
275163516Simpstatic devclass_t mmcsd_devclass;
276163516Simp
277163516Simp
278163516SimpDRIVER_MODULE(mmcsd, mmc, mmcsd_driver, mmcsd_devclass, 0, 0);
279