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 }; 54 55 #define MULTI_BLOCK_READ_BROKEN 56 57 /* bus entry points */ 58 static int mmcsd_probe(device_t dev); 59 static int mmcsd_attach(device_t dev); 60 static int mmcsd_detach(device_t dev); 61 62 /* disk routines */ 63 static int mmcsd_open(struct disk *dp); 64 static int mmcsd_close(struct disk *dp); 65 static void mmcsd_strategy(struct bio *bp); 66 static void mmcsd_task(void *arg); 67 68 #define MMCSD_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 69 #define MMCSD_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 70 #define MMCSD_LOCK_INIT(_sc) \ 71 mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 72 "mmcsd", MTX_DEF) 73 #define MMCSD_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 74 #define MMCSD_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 75 #define MMCSD_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 76 77 static int 78 mmcsd_probe(device_t dev) 79 { 80 81 device_set_desc(dev, "mmc or sd flash card"); 82 return (0); 83 } 84 85 static int 86 mmcsd_attach(device_t dev) 87 { 88 struct mmcsd_softc *sc; 89 90 sc = device_get_softc(dev); 91 sc->dev = dev; 92 MMCSD_LOCK_INIT(sc); 93 94 sc->disk = disk_alloc(); 95 sc->disk->d_open = mmcsd_open; 96 sc->disk->d_close = mmcsd_close; 97 sc->disk->d_strategy = mmcsd_strategy; 98 // sc->disk->d_dump = mmcsd_dump; Need polling mmc layer 99 sc->disk->d_name = "mmcsd"; 100 sc->disk->d_drv1 = sc; 101 sc->disk->d_maxsize = DFLTPHYS; 102 sc->disk->d_sectorsize = mmc_get_sector_size(dev); 103 sc->disk->d_mediasize = mmc_get_media_size(dev); 104 sc->disk->d_unit = device_get_unit(dev); 105 disk_create(sc->disk, DISK_VERSION); 106 bioq_init(&sc->bio_queue); 107 kthread_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card"); 108 109 return (0); 110 } 111 112 static int 113 mmcsd_detach(device_t dev) 114 { 115 return (EBUSY); /* XXX */ 116 } 117 118 static int 119 mmcsd_open(struct disk *dp) 120 { 121 return 0; 122 } 123 124 static int 125 mmcsd_close(struct disk *dp) 126 { 127 return 0; 128 } 129 130 static void 131 mmcsd_strategy(struct bio *bp) 132 { 133 struct mmcsd_softc *sc; 134 135 sc = (struct mmcsd_softc *)bp->bio_disk->d_drv1; 136 MMCSD_LOCK(sc); 137 bioq_disksort(&sc->bio_queue, bp); 138 wakeup(sc); 139 MMCSD_UNLOCK(sc); 140 } 141 142 static void 143 mmcsd_task(void *arg) 144 { 145 struct mmcsd_softc *sc = (struct mmcsd_softc*)arg; 146 struct bio *bp; 147 int sz; 148 daddr_t block, end; 149 struct mmc_command cmd; 150 struct mmc_command stop; 151 struct mmc_request req; 152 struct mmc_data data; 153 device_t dev; 154 155 dev = sc->dev; 156 for (;;) { 157 MMCSD_LOCK(sc); 158 do { 159 bp = bioq_first(&sc->bio_queue); 160 if (bp == NULL) 161 msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0); 162 } while (bp == NULL); 163 bioq_remove(&sc->bio_queue, bp); 164 MMCSD_UNLOCK(sc); 165 MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev); 166 // printf("mmc_task: request %p for block %lld\n", bp, bp->bio_pblkno); 167 sz = sc->disk->d_sectorsize; 168 end = bp->bio_pblkno + (bp->bio_bcount / sz); 169 // XXX should use read/write_mulit 170 for (block = bp->bio_pblkno; block < end;) { 171 char *vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz; 172 memset(&req, 0, sizeof(req)); 173 memset(&cmd, 0, sizeof(cmd)); 174 memset(&stop, 0, sizeof(stop)); 175 req.cmd = &cmd; 176 cmd.data = &data; 177 if (bp->bio_cmd == BIO_READ) { 178 #ifdef MULTI_BLOCK_READ 179 if (end - block > 1) 180 cmd.opcode = MMC_READ_MULTIPLE_BLOCK; 181 else 182 cmd.opcode = MMC_READ_SINGLE_BLOCK; 183 #else 184 cmd.opcode = MMC_READ_SINGLE_BLOCK; 185 #endif 186 } else 187 cmd.opcode = MMC_WRITE_BLOCK; 188 cmd.arg = block << 9; 189 cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; 190 // data.timeout_ns = ; 191 // data.timeout_clks = ; 192 data.data = vaddr; 193 data.mrq = &req; 194 if (bp->bio_cmd == BIO_READ) { 195 data.flags = MMC_DATA_READ; 196 #ifdef MULTI_BLOCK_READ 197 data.len = bp->bio_bcount; 198 if (end - block > 1) { 199 req.stop = &stop; 200 data.flags |= MMC_DATA_MULTI; 201 } 202 printf("Len %d %lld-%lld flags %#x sz %d\n", 203 data.len, block, end, data.flags, sz); 204 block = end; 205 #else 206 data.len = sz; 207 block++; 208 #endif 209 } else { 210 data.flags = MMC_DATA_WRITE; 211 data.len = sz; 212 block++; 213 } 214 stop.opcode = MMC_STOP_TRANSMISSION; 215 stop.arg = 0; 216 stop.flags = MMC_RSP_R1B | MMC_CMD_AC; 217 MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, 218 &req); 219 // XXX error handling 220 //XXX while (!(mmc_send_status(dev) & R1_READY_FOR_DATA)) 221 // continue; 222 // XXX decode mmc status 223 } 224 MMCBUS_RELEASE_BUS(device_get_parent(dev), dev); 225 biodone(bp); 226 } 227 } 228 229 static device_method_t mmcsd_methods[] = { 230 DEVMETHOD(device_probe, mmcsd_probe), 231 DEVMETHOD(device_attach, mmcsd_attach), 232 DEVMETHOD(device_detach, mmcsd_detach), 233 {0, 0}, 234 }; 235 236 static driver_t mmcsd_driver = { 237 "mmcsd", 238 mmcsd_methods, 239 sizeof(struct mmcsd_softc), 240 }; 241 static devclass_t mmcsd_devclass; 242 243 244 DRIVER_MODULE(mmcsd, mmc, mmcsd_driver, mmcsd_devclass, 0, 0); 245