1 /*- 2 * Copyright (c) 2006 M. Warner Losh. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #include <sys/cdefs.h> 26 __FBSDID("$FreeBSD$"); 27 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/bio.h> 31 #include <sys/bus.h> 32 #include <sys/conf.h> 33 #include <sys/kernel.h> 34 #include <sys/kthread.h> 35 #include <sys/lock.h> 36 #include <sys/mbuf.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/spibus/spi.h> 43 #include "spibus_if.h" 44 45 struct at45d_softc 46 { 47 struct intr_config_hook config_intrhook; 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 AT45D_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 56 #define AT45D_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 57 #define AT45D_LOCK_INIT(_sc) \ 58 mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 59 "at45d", MTX_DEF) 60 #define AT45D_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 61 #define AT45D_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 62 #define AT45D_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 63 64 static void at45d_delayed_attach(void *xsc); 65 66 /* disk routines */ 67 static int at45d_open(struct disk *dp); 68 static int at45d_close(struct disk *dp); 69 static void at45d_strategy(struct bio *bp); 70 static void at45d_task(void *arg); 71 72 #define CONTINUOUS_ARRAY_READ 0xE8 73 #define CONTINUOUS_ARRAY_READ_HF 0x0B 74 #define CONTINUOUS_ARRAY_READ_LF 0x03 75 #define STATUS_REGISTER_READ 0xD7 76 #define PROGRAM_THROUGH_BUFFER 0x82 77 #define MANUFACTURER_ID 0x9F 78 79 static uint8_t 80 at45d_get_status(device_t dev) 81 { 82 uint8_t txBuf[8], rxBuf[8]; 83 struct spi_command cmd; 84 int err; 85 86 memset(&cmd, 0, sizeof(cmd)); 87 memset(txBuf, 0, sizeof(txBuf)); 88 memset(rxBuf, 0, sizeof(rxBuf)); 89 90 txBuf[0] = STATUS_REGISTER_READ; 91 cmd.tx_cmd = txBuf; 92 cmd.rx_cmd = rxBuf; 93 cmd.rx_cmd_sz = 2; 94 cmd.tx_cmd_sz = 2; 95 err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); 96 return (rxBuf[1]); 97 } 98 99 static void 100 at45d_wait_for_device_ready(device_t dev) 101 { 102 while (!(at45d_get_status(dev) & 0x80)) 103 continue; 104 } 105 106 static int 107 at45d_get_mfg_info(device_t dev, uint8_t *resp) 108 { 109 uint8_t txBuf[8], rxBuf[8]; 110 struct spi_command cmd; 111 int err; 112 113 memset(&cmd, 0, sizeof(cmd)); 114 memset(txBuf, 0, sizeof(txBuf)); 115 memset(rxBuf, 0, sizeof(rxBuf)); 116 117 txBuf[0] = MANUFACTURER_ID; 118 cmd.tx_cmd = &txBuf; 119 cmd.rx_cmd = &rxBuf; 120 cmd.tx_cmd_sz = 5; 121 cmd.rx_cmd_sz = 5; 122 err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); 123 if (err) 124 return (err); 125 memcpy(resp, rxBuf + 1, 4); 126 // XXX We really should 'decode' the reply into some kind of 127 // XXX structure. To be generic (and not just for atmel parts) 128 // XXX we'd have to loop until we got a full reply. 129 return (0); 130 } 131 132 static int 133 at45d_probe(device_t dev) 134 { 135 device_set_desc(dev, "AT45 Flash Family"); 136 return (0); 137 } 138 139 static int 140 at45d_attach(device_t dev) 141 { 142 struct at45d_softc *sc; 143 144 sc = device_get_softc(dev); 145 sc->dev = dev; 146 AT45D_LOCK_INIT(sc); 147 148 /* We'll see what kind of flash we have later... */ 149 sc->config_intrhook.ich_func = at45d_delayed_attach; 150 sc->config_intrhook.ich_arg = sc; 151 if (config_intrhook_establish(&sc->config_intrhook) != 0) 152 device_printf(dev, "config_intrhook_establish failed\n"); 153 return (0); 154 } 155 156 static int 157 at45d_detach(device_t dev) 158 { 159 return EIO; 160 } 161 162 static void 163 at45d_delayed_attach(void *xsc) 164 { 165 struct at45d_softc *sc = xsc; 166 uint8_t buf[4]; 167 168 at45d_get_mfg_info(sc->dev, buf); 169 at45d_wait_for_device_ready(sc->dev); 170 171 sc->disk = disk_alloc(); 172 sc->disk->d_open = at45d_open; 173 sc->disk->d_close = at45d_close; 174 sc->disk->d_strategy = at45d_strategy; 175 sc->disk->d_name = "flash/spi"; 176 sc->disk->d_drv1 = sc; 177 sc->disk->d_maxsize = DFLTPHYS; 178 sc->disk->d_sectorsize = 1056; /* XXX */ 179 sc->disk->d_mediasize = 8192 * 1056; /* XXX */ 180 sc->disk->d_unit = device_get_unit(sc->dev); 181 disk_create(sc->disk, DISK_VERSION); 182 bioq_init(&sc->bio_queue); 183 kproc_create(&at45d_task, sc, &sc->p, 0, 0, "task: at45d flash"); 184 185 config_intrhook_disestablish(&sc->config_intrhook); 186 } 187 188 static int 189 at45d_open(struct disk *dp) 190 { 191 return 0; 192 } 193 194 static int 195 at45d_close(struct disk *dp) 196 { 197 return 0; 198 } 199 200 static void 201 at45d_strategy(struct bio *bp) 202 { 203 struct at45d_softc *sc; 204 205 sc = (struct at45d_softc *)bp->bio_disk->d_drv1; 206 AT45D_LOCK(sc); 207 bioq_disksort(&sc->bio_queue, bp); 208 wakeup(sc); 209 AT45D_UNLOCK(sc); 210 } 211 212 static void 213 at45d_task(void *arg) 214 { 215 struct at45d_softc *sc = (struct at45d_softc*)arg; 216 struct bio *bp; 217 uint8_t txBuf[8], rxBuf[8]; 218 struct spi_command cmd; 219 int sz; 220 daddr_t block, end; 221 device_t dev, pdev; 222 int err; 223 224 for (;;) { 225 dev = sc->dev; 226 pdev = device_get_parent(dev); 227 AT45D_LOCK(sc); 228 do { 229 bp = bioq_first(&sc->bio_queue); 230 if (bp == NULL) 231 msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0); 232 } while (bp == NULL); 233 bioq_remove(&sc->bio_queue, bp); 234 AT45D_UNLOCK(sc); 235 sz = sc->disk->d_sectorsize; 236 end = bp->bio_pblkno + (bp->bio_bcount / sz); 237 for (block = bp->bio_pblkno; block < end; block++) { 238 char *vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz; 239 if (bp->bio_cmd == BIO_READ) { 240 txBuf[0] = CONTINUOUS_ARRAY_READ_HF; 241 cmd.tx_cmd_sz = 5; 242 cmd.rx_cmd_sz = 5; 243 } else { 244 txBuf[0] = PROGRAM_THROUGH_BUFFER; 245 cmd.tx_cmd_sz = 4; 246 cmd.rx_cmd_sz = 4; 247 } 248 // XXX only works on certain devices... Fixme 249 txBuf[1] = ((block >> 5) & 0xFF); 250 txBuf[2] = ((block << 3) & 0xF8); 251 txBuf[3] = 0; 252 txBuf[4] = 0; 253 cmd.tx_cmd = txBuf; 254 cmd.rx_cmd = rxBuf; 255 cmd.tx_data = vaddr; 256 cmd.tx_data_sz = sz; 257 cmd.rx_data = vaddr; 258 cmd.rx_data_sz = sz; 259 err = SPIBUS_TRANSFER(pdev, dev, &cmd); 260 // XXX err check? 261 } 262 biodone(bp); 263 } 264 } 265 266 static devclass_t at45d_devclass; 267 268 static device_method_t at45d_methods[] = { 269 /* Device interface */ 270 DEVMETHOD(device_probe, at45d_probe), 271 DEVMETHOD(device_attach, at45d_attach), 272 DEVMETHOD(device_detach, at45d_detach), 273 274 { 0, 0 } 275 }; 276 277 static driver_t at45d_driver = { 278 "at45d", 279 at45d_methods, 280 sizeof(struct at45d_softc), 281 }; 282 283 DRIVER_MODULE(at45d, spibus, at45d_driver, at45d_devclass, 0, 0); 284