1 /*- 2 * Copyright (c) 2006 Bernd Walter. All rights reserved. 3 * Copyright (c) 2006 M. Warner Losh. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/bio.h> 32 #include <sys/bus.h> 33 #include <sys/conf.h> 34 #include <sys/kernel.h> 35 #include <sys/kthread.h> 36 #include <sys/lock.h> 37 #include <sys/malloc.h> 38 #include <sys/module.h> 39 #include <sys/mutex.h> 40 #include <geom/geom_disk.h> 41 42 #include <dev/mmc/mmcvar.h> 43 #include <dev/mmc/mmcreg.h> 44 45 #include "mmcbus_if.h" 46 47 struct mmcsd_softc { 48 device_t dev; 49 struct mtx sc_mtx; 50 struct disk *disk; 51 struct proc *p; 52 struct bio_queue_head bio_queue; 53 int running; 54 }; 55 56 #define MULTI_BLOCK_READ_BROKEN 57 58 /* bus entry points */ 59 static int mmcsd_probe(device_t dev); 60 static int mmcsd_attach(device_t dev); 61 static int mmcsd_detach(device_t dev); 62 63 /* disk routines */ 64 static int mmcsd_open(struct disk *dp); 65 static int mmcsd_close(struct disk *dp); 66 static void mmcsd_strategy(struct bio *bp); 67 static void mmcsd_task(void *arg); 68 69 #define MMCSD_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 70 #define MMCSD_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 71 #define MMCSD_LOCK_INIT(_sc) \ 72 mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 73 "mmcsd", MTX_DEF) 74 #define MMCSD_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 75 #define MMCSD_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 76 #define MMCSD_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 77 78 static int 79 mmcsd_probe(device_t dev) 80 { 81 82 device_set_desc(dev, "mmc or sd flash card"); 83 return (0); 84 } 85 86 static int 87 mmcsd_attach(device_t dev) 88 { 89 struct mmcsd_softc *sc; 90 91 sc = device_get_softc(dev); 92 sc->dev = dev; 93 MMCSD_LOCK_INIT(sc); 94 95 sc->disk = disk_alloc(); 96 sc->disk->d_open = mmcsd_open; 97 sc->disk->d_close = mmcsd_close; 98 sc->disk->d_strategy = mmcsd_strategy; 99 // sc->disk->d_dump = mmcsd_dump; Need polling mmc layer 100 sc->disk->d_name = "mmcsd"; 101 sc->disk->d_drv1 = sc; 102 sc->disk->d_maxsize = DFLTPHYS; 103 sc->disk->d_sectorsize = mmc_get_sector_size(dev); 104 sc->disk->d_mediasize = mmc_get_media_size(dev); 105 sc->disk->d_unit = device_get_unit(dev); 106 disk_create(sc->disk, DISK_VERSION); 107 bioq_init(&sc->bio_queue); 108 109 sc->running = 1; 110 kthread_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card"); 111 112 return (0); 113 } 114 115 static int 116 mmcsd_detach(device_t dev) 117 { 118 struct mmcsd_softc *sc = device_get_softc(dev); 119 120 /* kill thread */ 121 MMCSD_LOCK(sc); 122 sc->running = 0; 123 wakeup(sc); 124 MMCSD_UNLOCK(sc); 125 126 /* wait for thread to finish. XXX probably want timeout. -sorbo */ 127 MMCSD_LOCK(sc); 128 while (sc->running != -1) 129 msleep(sc, &sc->sc_mtx, PRIBIO, "detach", 0); 130 MMCSD_UNLOCK(sc); 131 132 /* kill disk */ 133 disk_destroy(sc->disk); 134 /* XXX destroy anything in queue */ 135 136 MMCSD_LOCK_DESTROY(sc); 137 138 return 0; 139 } 140 141 static int 142 mmcsd_open(struct disk *dp) 143 { 144 return 0; 145 } 146 147 static int 148 mmcsd_close(struct disk *dp) 149 { 150 return 0; 151 } 152 153 static void 154 mmcsd_strategy(struct bio *bp) 155 { 156 struct mmcsd_softc *sc; 157 158 sc = (struct mmcsd_softc *)bp->bio_disk->d_drv1; 159 MMCSD_LOCK(sc); 160 bioq_disksort(&sc->bio_queue, bp); 161 wakeup(sc); 162 MMCSD_UNLOCK(sc); 163 } 164 165 static void 166 mmcsd_task(void *arg) 167 { 168 struct mmcsd_softc *sc = (struct mmcsd_softc*)arg; 169 struct bio *bp; 170 int sz; 171 daddr_t block, end; 172 struct mmc_command cmd; 173 struct mmc_command stop; 174 struct mmc_request req; 175 struct mmc_data data; 176 device_t dev; 177 178 dev = sc->dev; 179 while (sc->running) { 180 MMCSD_LOCK(sc); 181 do { 182 bp = bioq_first(&sc->bio_queue); 183 if (bp == NULL) 184 msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0); 185 } while (bp == NULL && sc->running); 186 if (bp) 187 bioq_remove(&sc->bio_queue, bp); 188 MMCSD_UNLOCK(sc); 189 if (!sc->running) 190 break; 191 MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev); 192 // printf("mmc_task: request %p for block %lld\n", bp, bp->bio_pblkno); 193 sz = sc->disk->d_sectorsize; 194 end = bp->bio_pblkno + (bp->bio_bcount / sz); 195 // XXX should use read/write_mulit 196 for (block = bp->bio_pblkno; block < end;) { 197 char *vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz; 198 memset(&req, 0, sizeof(req)); 199 memset(&cmd, 0, sizeof(cmd)); 200 memset(&stop, 0, sizeof(stop)); 201 req.cmd = &cmd; 202 cmd.data = &data; 203 if (bp->bio_cmd == BIO_READ) { 204 #ifdef MULTI_BLOCK_READ 205 if (end - block > 1) 206 cmd.opcode = MMC_READ_MULTIPLE_BLOCK; 207 else 208 cmd.opcode = MMC_READ_SINGLE_BLOCK; 209 #else 210 cmd.opcode = MMC_READ_SINGLE_BLOCK; 211 #endif 212 } else 213 cmd.opcode = MMC_WRITE_BLOCK; 214 cmd.arg = block << 9; 215 cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; 216 // data.timeout_ns = ; 217 // data.timeout_clks = ; 218 data.data = vaddr; 219 data.mrq = &req; 220 if (bp->bio_cmd == BIO_READ) { 221 data.flags = MMC_DATA_READ; 222 #ifdef MULTI_BLOCK_READ 223 data.len = bp->bio_bcount; 224 if (end - block > 1) { 225 req.stop = &stop; 226 data.flags |= MMC_DATA_MULTI; 227 } 228 printf("Len %d %lld-%lld flags %#x sz %d\n", 229 data.len, block, end, data.flags, sz); 230 block = end; 231 #else 232 data.len = sz; 233 block++; 234 #endif 235 } else { 236 data.flags = MMC_DATA_WRITE; 237 data.len = sz; 238 block++; 239 } 240 stop.opcode = MMC_STOP_TRANSMISSION; 241 stop.arg = 0; 242 stop.flags = MMC_RSP_R1B | MMC_CMD_AC; 243 MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, 244 &req); 245 // XXX error handling 246 //XXX while (!(mmc_send_status(dev) & R1_READY_FOR_DATA)) 247 // continue; 248 // XXX decode mmc status 249 } 250 MMCBUS_RELEASE_BUS(device_get_parent(dev), dev); 251 biodone(bp); 252 } 253 254 /* tell parent we're done */ 255 MMCSD_LOCK(sc); 256 sc->running = -1; 257 wakeup(sc); 258 MMCSD_UNLOCK(sc); 259 260 kthread_exit(0); 261 } 262 263 static device_method_t mmcsd_methods[] = { 264 DEVMETHOD(device_probe, mmcsd_probe), 265 DEVMETHOD(device_attach, mmcsd_attach), 266 DEVMETHOD(device_detach, mmcsd_detach), 267 {0, 0}, 268 }; 269 270 static driver_t mmcsd_driver = { 271 "mmcsd", 272 mmcsd_methods, 273 sizeof(struct mmcsd_softc), 274 }; 275 static devclass_t mmcsd_devclass; 276 277 278 DRIVER_MODULE(mmcsd, mmc, mmcsd_driver, mmcsd_devclass, 0, 0); 279