1 /*- 2 * Copyright (c) 2006 M. Warner Losh 3 * Copyright (c) 2011-2012 Ian Lepore 4 * Copyright (c) 2012 Marius Strobl <marius@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/bio.h> 34 #include <sys/bus.h> 35 #include <sys/conf.h> 36 #include <sys/kernel.h> 37 #include <sys/kthread.h> 38 #include <sys/lock.h> 39 #include <sys/mbuf.h> 40 #include <sys/malloc.h> 41 #include <sys/module.h> 42 #include <sys/mutex.h> 43 #include <geom/geom_disk.h> 44 45 #include <dev/spibus/spi.h> 46 #include "spibus_if.h" 47 48 struct at45d_flash_ident 49 { 50 const char *name; 51 uint32_t jedec; 52 uint16_t pagecount; 53 uint16_t pageoffset; 54 uint16_t pagesize; 55 uint16_t pagesize2n; 56 }; 57 58 struct at45d_softc 59 { 60 struct bio_queue_head bio_queue; 61 struct mtx sc_mtx; 62 struct disk *disk; 63 struct proc *p; 64 struct intr_config_hook config_intrhook; 65 device_t dev; 66 uint16_t pagecount; 67 uint16_t pageoffset; 68 uint16_t pagesize; 69 }; 70 71 #define AT45D_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 72 #define AT45D_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 73 #define AT45D_LOCK_INIT(_sc) \ 74 mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 75 "at45d", MTX_DEF) 76 #define AT45D_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 77 #define AT45D_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 78 #define AT45D_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 79 80 /* bus entry points */ 81 static device_attach_t at45d_attach; 82 static device_detach_t at45d_detach; 83 static device_probe_t at45d_probe; 84 85 /* disk routines */ 86 static int at45d_close(struct disk *dp); 87 static int at45d_open(struct disk *dp); 88 static void at45d_strategy(struct bio *bp); 89 static void at45d_task(void *arg); 90 91 /* helper routines */ 92 static void at45d_delayed_attach(void *xsc); 93 static int at45d_get_mfg_info(device_t dev, uint8_t *resp); 94 static int at45d_get_status(device_t dev, uint8_t *status); 95 static int at45d_wait_ready(device_t dev, uint8_t *status); 96 97 #define BUFFER_TRANSFER 0x53 98 #define BUFFER_COMPARE 0x60 99 #define PROGRAM_THROUGH_BUFFER 0x82 100 #define MANUFACTURER_ID 0x9f 101 #define STATUS_REGISTER_READ 0xd7 102 #define CONTINUOUS_ARRAY_READ 0xe8 103 104 /* 105 * A sectorsize2n != 0 is used to indicate that a device optionally supports 106 * 2^N byte pages. If support for the latter is enabled, the sector offset 107 * has to be reduced by one. 108 */ 109 static const struct at45d_flash_ident at45d_flash_devices[] = { 110 { "AT45DB011B", 0x1f2200, 512, 9, 264, 256 }, 111 { "AT45DB021B", 0x1f2300, 1024, 9, 264, 256 }, 112 { "AT45DB041x", 0x1f2400, 2028, 9, 264, 256 }, 113 { "AT45DB081B", 0x1f2500, 4096, 9, 264, 256 }, 114 { "AT45DB161x", 0x1f2600, 4096, 10, 528, 512 }, 115 { "AT45DB321x", 0x1f2700, 8192, 10, 528, 0 }, 116 { "AT45DB321x", 0x1f2701, 8192, 10, 528, 512 }, 117 { "AT45DB642x", 0x1f2800, 8192, 11, 1056, 1024 } 118 }; 119 120 static int 121 at45d_get_status(device_t dev, uint8_t *status) 122 { 123 uint8_t rxBuf[8], txBuf[8]; 124 struct spi_command cmd; 125 int err; 126 127 memset(&cmd, 0, sizeof(cmd)); 128 memset(txBuf, 0, sizeof(txBuf)); 129 memset(rxBuf, 0, sizeof(rxBuf)); 130 131 txBuf[0] = STATUS_REGISTER_READ; 132 cmd.tx_cmd = txBuf; 133 cmd.rx_cmd = rxBuf; 134 cmd.rx_cmd_sz = cmd.tx_cmd_sz = 2; 135 err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); 136 *status = rxBuf[1]; 137 return (err); 138 } 139 140 static int 141 at45d_get_mfg_info(device_t dev, uint8_t *resp) 142 { 143 uint8_t rxBuf[8], txBuf[8]; 144 struct spi_command cmd; 145 int err; 146 147 memset(&cmd, 0, sizeof(cmd)); 148 memset(txBuf, 0, sizeof(txBuf)); 149 memset(rxBuf, 0, sizeof(rxBuf)); 150 151 txBuf[0] = MANUFACTURER_ID; 152 cmd.tx_cmd = &txBuf; 153 cmd.rx_cmd = &rxBuf; 154 cmd.tx_cmd_sz = cmd.rx_cmd_sz = 5; 155 err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); 156 if (err) 157 return (err); 158 memcpy(resp, rxBuf + 1, 4); 159 return (0); 160 } 161 162 static int 163 at45d_wait_ready(device_t dev, uint8_t *status) 164 { 165 struct timeval now, tout; 166 int err; 167 168 getmicrouptime(&tout); 169 tout.tv_sec += 3; 170 do { 171 getmicrouptime(&now); 172 if (now.tv_sec > tout.tv_sec) 173 err = ETIMEDOUT; 174 else 175 err = at45d_get_status(dev, status); 176 } while (err == 0 && (*status & 0x80) == 0); 177 return (err); 178 } 179 180 static int 181 at45d_probe(device_t dev) 182 { 183 184 device_set_desc(dev, "AT45D Flash Family"); 185 return (0); 186 } 187 188 static int 189 at45d_attach(device_t dev) 190 { 191 struct at45d_softc *sc; 192 193 sc = device_get_softc(dev); 194 sc->dev = dev; 195 AT45D_LOCK_INIT(sc); 196 197 /* We'll see what kind of flash we have later... */ 198 sc->config_intrhook.ich_func = at45d_delayed_attach; 199 sc->config_intrhook.ich_arg = sc; 200 if (config_intrhook_establish(&sc->config_intrhook) != 0) 201 device_printf(dev, "config_intrhook_establish failed\n"); 202 return (0); 203 } 204 205 static int 206 at45d_detach(device_t dev) 207 { 208 209 return (EBUSY) /* XXX */; 210 } 211 212 static void 213 at45d_delayed_attach(void *xsc) 214 { 215 struct at45d_softc *sc; 216 const struct at45d_flash_ident *ident; 217 u_int i; 218 uint32_t jedec; 219 uint16_t pagesize; 220 uint8_t buf[4], status; 221 222 sc = xsc; 223 ident = NULL; 224 jedec = 0; 225 226 if (at45d_wait_ready(sc->dev, &status) != 0) 227 device_printf(sc->dev, "Error waiting for device-ready.\n"); 228 else if (at45d_get_mfg_info(sc->dev, buf) != 0) 229 device_printf(sc->dev, "Failed to get ID.\n"); 230 else { 231 jedec = buf[0] << 16 | buf[1] << 8 | buf[2]; 232 for (i = 0; i < nitems(at45d_flash_devices); i++) { 233 if (at45d_flash_devices[i].jedec == jedec) { 234 ident = &at45d_flash_devices[i]; 235 break; 236 } 237 } 238 } 239 if (ident == NULL) 240 device_printf(sc->dev, "JEDEC 0x%x not in list.\n", jedec); 241 else { 242 sc->pagecount = ident->pagecount; 243 sc->pageoffset = ident->pageoffset; 244 if (ident->pagesize2n != 0 && (status & 0x01) != 0) { 245 sc->pageoffset -= 1; 246 pagesize = ident->pagesize2n; 247 } else 248 pagesize = ident->pagesize; 249 sc->pagesize = pagesize; 250 251 sc->disk = disk_alloc(); 252 sc->disk->d_open = at45d_open; 253 sc->disk->d_close = at45d_close; 254 sc->disk->d_strategy = at45d_strategy; 255 sc->disk->d_name = "flash/spi"; 256 sc->disk->d_drv1 = sc; 257 sc->disk->d_maxsize = DFLTPHYS; 258 sc->disk->d_sectorsize = pagesize; 259 sc->disk->d_mediasize = pagesize * ident->pagecount; 260 sc->disk->d_unit = device_get_unit(sc->dev); 261 disk_create(sc->disk, DISK_VERSION); 262 bioq_init(&sc->bio_queue); 263 kproc_create(&at45d_task, sc, &sc->p, 0, 0, 264 "task: at45d flash"); 265 device_printf(sc->dev, "%s, %d bytes per page, %d pages\n", 266 ident->name, pagesize, ident->pagecount); 267 } 268 269 config_intrhook_disestablish(&sc->config_intrhook); 270 } 271 272 static int 273 at45d_open(struct disk *dp) 274 { 275 276 return (0); 277 } 278 279 static int 280 at45d_close(struct disk *dp) 281 { 282 283 return (0); 284 } 285 286 static void 287 at45d_strategy(struct bio *bp) 288 { 289 struct at45d_softc *sc; 290 291 sc = (struct at45d_softc *)bp->bio_disk->d_drv1; 292 AT45D_LOCK(sc); 293 bioq_disksort(&sc->bio_queue, bp); 294 wakeup(sc); 295 AT45D_UNLOCK(sc); 296 } 297 298 static void 299 at45d_task(void *arg) 300 { 301 uint8_t rxBuf[8], txBuf[8]; 302 struct at45d_softc *sc; 303 struct bio *bp; 304 struct spi_command cmd; 305 device_t dev, pdev; 306 caddr_t buf; 307 u_long len, resid; 308 u_int addr, berr, err, offset, page; 309 uint8_t status; 310 311 sc = (struct at45d_softc*)arg; 312 dev = sc->dev; 313 pdev = device_get_parent(dev); 314 memset(&cmd, 0, sizeof(cmd)); 315 memset(txBuf, 0, sizeof(txBuf)); 316 memset(rxBuf, 0, sizeof(rxBuf)); 317 cmd.tx_cmd = txBuf; 318 cmd.rx_cmd = rxBuf; 319 320 for (;;) { 321 AT45D_LOCK(sc); 322 do { 323 bp = bioq_takefirst(&sc->bio_queue); 324 if (bp == NULL) 325 msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0); 326 } while (bp == NULL); 327 AT45D_UNLOCK(sc); 328 329 berr = 0; 330 buf = bp->bio_data; 331 len = resid = bp->bio_bcount; 332 page = bp->bio_offset / sc->pagesize; 333 offset = bp->bio_offset % sc->pagesize; 334 335 switch (bp->bio_cmd) { 336 case BIO_READ: 337 txBuf[0] = CONTINUOUS_ARRAY_READ; 338 cmd.tx_cmd_sz = cmd.rx_cmd_sz = 8; 339 cmd.tx_data = cmd.rx_data = buf; 340 break; 341 case BIO_WRITE: 342 cmd.tx_cmd_sz = cmd.rx_cmd_sz = 4; 343 cmd.tx_data = cmd.rx_data = buf; 344 if (resid + offset > sc->pagesize) 345 len = sc->pagesize - offset; 346 break; 347 default: 348 berr = EINVAL; 349 goto out; 350 } 351 352 /* 353 * NB: for BIO_READ, this loop is only traversed once. 354 */ 355 while (resid > 0) { 356 if (page > sc->pagecount) { 357 berr = EINVAL; 358 goto out; 359 } 360 addr = page << sc->pageoffset; 361 if (bp->bio_cmd == BIO_WRITE) { 362 if (len != sc->pagesize) { 363 txBuf[0] = BUFFER_TRANSFER; 364 txBuf[1] = ((addr >> 16) & 0xff); 365 txBuf[2] = ((addr >> 8) & 0xff); 366 txBuf[3] = 0; 367 cmd.tx_data_sz = cmd.rx_data_sz = 0; 368 err = SPIBUS_TRANSFER(pdev, dev, 369 &cmd); 370 if (err == 0) 371 err = at45d_wait_ready(dev, 372 &status); 373 if (err != 0) { 374 berr = EIO; 375 goto out; 376 } 377 } 378 txBuf[0] = PROGRAM_THROUGH_BUFFER; 379 } 380 381 addr += offset; 382 txBuf[1] = ((addr >> 16) & 0xff); 383 txBuf[2] = ((addr >> 8) & 0xff); 384 txBuf[3] = (addr & 0xff); 385 cmd.tx_data_sz = cmd.rx_data_sz = len; 386 err = SPIBUS_TRANSFER(pdev, dev, &cmd); 387 if (err == 0 && bp->bio_cmd != BIO_READ) 388 err = at45d_wait_ready(dev, &status); 389 if (err != 0) { 390 berr = EIO; 391 goto out; 392 } 393 if (bp->bio_cmd == BIO_WRITE) { 394 addr = page << sc->pageoffset; 395 txBuf[0] = BUFFER_COMPARE; 396 txBuf[1] = ((addr >> 16) & 0xff); 397 txBuf[2] = ((addr >> 8) & 0xff); 398 txBuf[3] = 0; 399 cmd.tx_data_sz = cmd.rx_data_sz = 0; 400 err = SPIBUS_TRANSFER(pdev, dev, &cmd); 401 if (err == 0) 402 err = at45d_wait_ready(dev, &status); 403 if (err != 0 || (status & 0x40) != 0) { 404 device_printf(dev, "comparing page " 405 "%d failed (status=0x%x)\n", addr, 406 status); 407 berr = EIO; 408 goto out; 409 } 410 } 411 page++; 412 buf += len; 413 offset = 0; 414 resid -= len; 415 if (resid > sc->pagesize) 416 len = sc->pagesize; 417 else 418 len = resid; 419 cmd.tx_data = cmd.rx_data = buf; 420 } 421 out: 422 if (berr != 0) { 423 bp->bio_flags |= BIO_ERROR; 424 bp->bio_error = berr; 425 } 426 bp->bio_resid = resid; 427 biodone(bp); 428 } 429 } 430 431 static devclass_t at45d_devclass; 432 433 static device_method_t at45d_methods[] = { 434 /* Device interface */ 435 DEVMETHOD(device_probe, at45d_probe), 436 DEVMETHOD(device_attach, at45d_attach), 437 DEVMETHOD(device_detach, at45d_detach), 438 439 DEVMETHOD_END 440 }; 441 442 static driver_t at45d_driver = { 443 "at45d", 444 at45d_methods, 445 sizeof(struct at45d_softc), 446 }; 447 448 DRIVER_MODULE(at45d, spibus, at45d_driver, at45d_devclass, NULL, NULL); 449