1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2011-2012 Ian Lepore All rights reserved. 5 * Copyright (c) 2012 Marius Strobl <marius@FreeBSD.org> All rights reserved. 6 * Copyright (c) 2006 M. Warner Losh <imp@FreeBSD.org> 7 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bio.h> 35 #include <sys/bus.h> 36 #include <sys/conf.h> 37 #include <sys/endian.h> 38 #include <sys/kernel.h> 39 #include <sys/kthread.h> 40 #include <sys/lock.h> 41 #include <sys/mbuf.h> 42 #include <sys/malloc.h> 43 #include <sys/module.h> 44 #include <sys/mutex.h> 45 #include <geom/geom_disk.h> 46 47 #include <dev/spibus/spi.h> 48 #include "spibus_if.h" 49 50 #include "opt_platform.h" 51 52 #ifdef FDT 53 #include <dev/fdt/fdt_common.h> 54 #include <dev/ofw/ofw_bus_subr.h> 55 #include <dev/ofw/openfirm.h> 56 57 static struct ofw_compat_data compat_data[] = { 58 { "atmel,at45", 1 }, 59 { "atmel,dataflash", 1 }, 60 { NULL, 0 }, 61 }; 62 #endif 63 64 /* This is the information returned by the MANUFACTURER_ID command. */ 65 struct at45d_mfg_info { 66 uint32_t jedec_id; /* Mfg ID, DevId1, DevId2, ExtLen */ 67 uint16_t ext_id; /* ExtId1, ExtId2 */ 68 }; 69 70 /* 71 * This is an entry in our table of metadata describing the chips. We match on 72 * both jedec id and extended id info returned by the MANUFACTURER_ID command. 73 */ 74 struct at45d_flash_ident 75 { 76 const char *name; 77 uint32_t jedec; 78 uint16_t extid; 79 uint16_t extmask; 80 uint16_t pagecount; 81 uint16_t pageoffset; 82 uint16_t pagesize; 83 uint16_t pagesize2n; 84 }; 85 86 struct at45d_softc 87 { 88 struct bio_queue_head bio_queue; 89 struct mtx sc_mtx; 90 struct disk *disk; 91 struct proc *p; 92 device_t dev; 93 u_int taskstate; 94 uint16_t pagecount; 95 uint16_t pageoffset; 96 uint16_t pagesize; 97 void *dummybuf; 98 }; 99 100 #define TSTATE_STOPPED 0 101 #define TSTATE_STOPPING 1 102 #define TSTATE_RUNNING 2 103 104 #define AT45D_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 105 #define AT45D_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 106 #define AT45D_LOCK_INIT(_sc) \ 107 mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 108 "at45d", MTX_DEF) 109 #define AT45D_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 110 #define AT45D_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 111 #define AT45D_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 112 113 /* bus entry points */ 114 static device_attach_t at45d_attach; 115 static device_detach_t at45d_detach; 116 static device_probe_t at45d_probe; 117 118 /* disk routines */ 119 static int at45d_close(struct disk *dp); 120 static int at45d_open(struct disk *dp); 121 static int at45d_getattr(struct bio *bp); 122 static void at45d_strategy(struct bio *bp); 123 static void at45d_task(void *arg); 124 125 /* helper routines */ 126 static void at45d_delayed_attach(void *xsc); 127 static int at45d_get_mfg_info(device_t dev, struct at45d_mfg_info *resp); 128 static int at45d_get_status(device_t dev, uint8_t *status); 129 static int at45d_wait_ready(device_t dev, uint8_t *status); 130 131 #define PAGE_TO_BUFFER_TRANSFER 0x53 132 #define PAGE_TO_BUFFER_COMPARE 0x60 133 #define PROGRAM_THROUGH_BUFFER 0x82 134 #define MANUFACTURER_ID 0x9f 135 #define STATUS_REGISTER_READ 0xd7 136 #define CONTINUOUS_ARRAY_READ 0xe8 137 138 #define STATUS_READY (1u << 7) 139 #define STATUS_CMPFAIL (1u << 6) 140 #define STATUS_PAGE2N (1u << 0) 141 142 /* 143 * Metadata for supported chips. 144 * 145 * The jedec id in this table includes the extended id length byte. A match is 146 * based on both jedec id and extended id matching. The chip's extended id (not 147 * present in most chips) is ANDed with ExtMask and the result is compared to 148 * ExtId. If a chip only returns 1 ext id byte it will be in the upper 8 bits 149 * of ExtId in this table. 150 * 151 * A sectorsize2n != 0 is used to indicate that a device optionally supports 152 * 2^N byte pages. If support for the latter is enabled, the sector offset 153 * has to be reduced by one. 154 */ 155 static const struct at45d_flash_ident at45d_flash_devices[] = { 156 /* Part Name Jedec ID ExtId ExtMask PgCnt Offs PgSz PgSz2n */ 157 { "AT45DB011B", 0x1f220000, 0x0000, 0x0000, 512, 9, 264, 256 }, 158 { "AT45DB021B", 0x1f230000, 0x0000, 0x0000, 1024, 9, 264, 256 }, 159 { "AT45DB041x", 0x1f240000, 0x0000, 0x0000, 2028, 9, 264, 256 }, 160 { "AT45DB081B", 0x1f250000, 0x0000, 0x0000, 4096, 9, 264, 256 }, 161 { "AT45DB161x", 0x1f260000, 0x0000, 0x0000, 4096, 10, 528, 512 }, 162 { "AT45DB321x", 0x1f270000, 0x0000, 0x0000, 8192, 10, 528, 0 }, 163 { "AT45DB321x", 0x1f270100, 0x0000, 0x0000, 8192, 10, 528, 512 }, 164 { "AT45DB641E", 0x1f280001, 0x0000, 0xff00, 32768, 9, 264, 256 }, 165 { "AT45DB642x", 0x1f280000, 0x0000, 0x0000, 8192, 11, 1056, 1024 }, 166 }; 167 168 static int 169 at45d_get_status(device_t dev, uint8_t *status) 170 { 171 uint8_t rxBuf[8], txBuf[8]; 172 struct spi_command cmd; 173 int err; 174 175 memset(&cmd, 0, sizeof(cmd)); 176 memset(txBuf, 0, sizeof(txBuf)); 177 memset(rxBuf, 0, sizeof(rxBuf)); 178 179 txBuf[0] = STATUS_REGISTER_READ; 180 cmd.tx_cmd = txBuf; 181 cmd.rx_cmd = rxBuf; 182 cmd.rx_cmd_sz = cmd.tx_cmd_sz = 2; 183 err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); 184 *status = rxBuf[1]; 185 return (err); 186 } 187 188 static int 189 at45d_get_mfg_info(device_t dev, struct at45d_mfg_info *resp) 190 { 191 uint8_t rxBuf[8], txBuf[8]; 192 struct spi_command cmd; 193 int err; 194 195 memset(&cmd, 0, sizeof(cmd)); 196 memset(txBuf, 0, sizeof(txBuf)); 197 memset(rxBuf, 0, sizeof(rxBuf)); 198 199 txBuf[0] = MANUFACTURER_ID; 200 cmd.tx_cmd = &txBuf; 201 cmd.rx_cmd = &rxBuf; 202 cmd.tx_cmd_sz = cmd.rx_cmd_sz = 7; 203 err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); 204 if (err) 205 return (err); 206 207 resp->jedec_id = be32dec(rxBuf + 1); 208 resp->ext_id = be16dec(rxBuf + 5); 209 210 return (0); 211 } 212 213 static int 214 at45d_wait_ready(device_t dev, uint8_t *status) 215 { 216 struct timeval now, tout; 217 int err; 218 219 getmicrouptime(&tout); 220 tout.tv_sec += 3; 221 do { 222 getmicrouptime(&now); 223 if (now.tv_sec > tout.tv_sec) 224 err = ETIMEDOUT; 225 else 226 err = at45d_get_status(dev, status); 227 } while (err == 0 && !(*status & STATUS_READY)); 228 return (err); 229 } 230 231 static int 232 at45d_probe(device_t dev) 233 { 234 int rv; 235 236 #ifdef FDT 237 if (!ofw_bus_status_okay(dev)) 238 return (ENXIO); 239 240 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 241 return (ENXIO); 242 243 rv = BUS_PROBE_DEFAULT; 244 #else 245 rv = BUS_PROBE_NOWILDCARD; 246 #endif 247 248 device_set_desc(dev, "AT45D Flash Family"); 249 return (rv); 250 } 251 252 static int 253 at45d_attach(device_t dev) 254 { 255 struct at45d_softc *sc; 256 257 sc = device_get_softc(dev); 258 sc->dev = dev; 259 AT45D_LOCK_INIT(sc); 260 261 config_intrhook_oneshot(at45d_delayed_attach, sc); 262 return (0); 263 } 264 265 static int 266 at45d_detach(device_t dev) 267 { 268 struct at45d_softc *sc; 269 int err; 270 271 sc = device_get_softc(dev); 272 err = 0; 273 274 AT45D_LOCK(sc); 275 if (sc->taskstate == TSTATE_RUNNING) { 276 sc->taskstate = TSTATE_STOPPING; 277 wakeup(sc); 278 while (err == 0 && sc->taskstate != TSTATE_STOPPED) { 279 err = msleep(sc, &sc->sc_mtx, 0, "at45dt", hz * 3); 280 if (err != 0) { 281 sc->taskstate = TSTATE_RUNNING; 282 device_printf(sc->dev, 283 "Failed to stop queue task\n"); 284 } 285 } 286 } 287 AT45D_UNLOCK(sc); 288 289 if (err == 0 && sc->taskstate == TSTATE_STOPPED) { 290 if (sc->disk) { 291 disk_destroy(sc->disk); 292 bioq_flush(&sc->bio_queue, NULL, ENXIO); 293 free(sc->dummybuf, M_DEVBUF); 294 } 295 AT45D_LOCK_DESTROY(sc); 296 } 297 return (err); 298 } 299 300 static void 301 at45d_delayed_attach(void *xsc) 302 { 303 struct at45d_softc *sc; 304 struct at45d_mfg_info mfginfo; 305 const struct at45d_flash_ident *ident; 306 u_int i; 307 int sectorsize; 308 uint32_t jedec; 309 uint16_t pagesize; 310 uint8_t status; 311 312 sc = xsc; 313 ident = NULL; 314 jedec = 0; 315 316 if (at45d_wait_ready(sc->dev, &status) != 0) { 317 device_printf(sc->dev, "Error waiting for device-ready.\n"); 318 return; 319 } 320 if (at45d_get_mfg_info(sc->dev, &mfginfo) != 0) { 321 device_printf(sc->dev, "Failed to get ID.\n"); 322 return; 323 } 324 for (i = 0; i < nitems(at45d_flash_devices); i++) { 325 ident = &at45d_flash_devices[i]; 326 if (mfginfo.jedec_id == ident->jedec && 327 (mfginfo.ext_id & ident->extmask) == ident->extid) { 328 break; 329 } 330 } 331 if (i == nitems(at45d_flash_devices)) { 332 device_printf(sc->dev, "JEDEC 0x%x not in list.\n", jedec); 333 return; 334 } 335 336 sc->pagecount = ident->pagecount; 337 sc->pageoffset = ident->pageoffset; 338 if (ident->pagesize2n != 0 && (status & STATUS_PAGE2N)) { 339 sc->pageoffset -= 1; 340 pagesize = ident->pagesize2n; 341 } else 342 pagesize = ident->pagesize; 343 sc->pagesize = pagesize; 344 345 /* 346 * By default we set up a disk with a sector size that matches the 347 * device page size. If there is a device hint or fdt property 348 * requesting a different size, use that, as long as it is a multiple of 349 * the device page size). 350 */ 351 sectorsize = pagesize; 352 #ifdef FDT 353 { 354 pcell_t size; 355 if (OF_getencprop(ofw_bus_get_node(sc->dev), 356 "freebsd,sectorsize", &size, sizeof(size)) > 0) 357 sectorsize = size; 358 } 359 #endif 360 resource_int_value(device_get_name(sc->dev), device_get_unit(sc->dev), 361 "sectorsize", §orsize); 362 363 if ((sectorsize % pagesize) != 0) { 364 device_printf(sc->dev, "Invalid sectorsize %d, " 365 "must be a multiple of %d\n", sectorsize, pagesize); 366 return; 367 } 368 369 sc->dummybuf = malloc(pagesize, M_DEVBUF, M_WAITOK | M_ZERO); 370 371 sc->disk = disk_alloc(); 372 sc->disk->d_open = at45d_open; 373 sc->disk->d_close = at45d_close; 374 sc->disk->d_strategy = at45d_strategy; 375 sc->disk->d_getattr = at45d_getattr; 376 sc->disk->d_name = "flash/at45d"; 377 sc->disk->d_drv1 = sc; 378 sc->disk->d_maxsize = DFLTPHYS; 379 sc->disk->d_sectorsize = sectorsize; 380 sc->disk->d_mediasize = pagesize * ident->pagecount; 381 sc->disk->d_unit = device_get_unit(sc->dev); 382 disk_create(sc->disk, DISK_VERSION); 383 bioq_init(&sc->bio_queue); 384 kproc_create(&at45d_task, sc, &sc->p, 0, 0, "task: at45d flash"); 385 sc->taskstate = TSTATE_RUNNING; 386 device_printf(sc->dev, 387 "%s, %d bytes per page, %d pages; %d KBytes; disk sector size %d\n", 388 ident->name, pagesize, ident->pagecount, 389 (pagesize * ident->pagecount) / 1024, sectorsize); 390 } 391 392 static int 393 at45d_open(struct disk *dp) 394 { 395 396 return (0); 397 } 398 399 static int 400 at45d_close(struct disk *dp) 401 { 402 403 return (0); 404 } 405 406 static int 407 at45d_getattr(struct bio *bp) 408 { 409 struct at45d_softc *sc; 410 411 /* 412 * This function exists to support geom_flashmap and fdt_slicer. 413 */ 414 415 if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL) 416 return (ENXIO); 417 if (strcmp(bp->bio_attribute, "SPI::device") != 0) 418 return (-1); 419 sc = bp->bio_disk->d_drv1; 420 if (bp->bio_length != sizeof(sc->dev)) 421 return (EFAULT); 422 bcopy(&sc->dev, bp->bio_data, sizeof(sc->dev)); 423 return (0); 424 } 425 426 static void 427 at45d_strategy(struct bio *bp) 428 { 429 struct at45d_softc *sc; 430 431 sc = (struct at45d_softc *)bp->bio_disk->d_drv1; 432 AT45D_LOCK(sc); 433 bioq_disksort(&sc->bio_queue, bp); 434 wakeup(sc); 435 AT45D_UNLOCK(sc); 436 } 437 438 static void 439 at45d_task(void *arg) 440 { 441 uint8_t rxBuf[8], txBuf[8]; 442 struct at45d_softc *sc; 443 struct bio *bp; 444 struct spi_command cmd; 445 device_t dev, pdev; 446 caddr_t buf; 447 u_long len, resid; 448 u_int addr, berr, err, offset, page; 449 uint8_t status; 450 451 sc = (struct at45d_softc*)arg; 452 dev = sc->dev; 453 pdev = device_get_parent(dev); 454 memset(&cmd, 0, sizeof(cmd)); 455 memset(txBuf, 0, sizeof(txBuf)); 456 memset(rxBuf, 0, sizeof(rxBuf)); 457 cmd.tx_cmd = txBuf; 458 cmd.rx_cmd = rxBuf; 459 460 for (;;) { 461 AT45D_LOCK(sc); 462 do { 463 if (sc->taskstate == TSTATE_STOPPING) { 464 sc->taskstate = TSTATE_STOPPED; 465 AT45D_UNLOCK(sc); 466 wakeup(sc); 467 kproc_exit(0); 468 } 469 bp = bioq_takefirst(&sc->bio_queue); 470 if (bp == NULL) 471 msleep(sc, &sc->sc_mtx, PRIBIO, "at45dq", 0); 472 } while (bp == NULL); 473 AT45D_UNLOCK(sc); 474 475 berr = 0; 476 buf = bp->bio_data; 477 len = resid = bp->bio_bcount; 478 page = bp->bio_offset / sc->pagesize; 479 offset = bp->bio_offset % sc->pagesize; 480 481 switch (bp->bio_cmd) { 482 case BIO_READ: 483 txBuf[0] = CONTINUOUS_ARRAY_READ; 484 cmd.tx_cmd_sz = cmd.rx_cmd_sz = 8; 485 cmd.tx_data = sc->dummybuf; 486 cmd.rx_data = buf; 487 break; 488 case BIO_WRITE: 489 cmd.tx_cmd_sz = cmd.rx_cmd_sz = 4; 490 cmd.tx_data = buf; 491 cmd.rx_data = sc->dummybuf; 492 if (resid + offset > sc->pagesize) 493 len = sc->pagesize - offset; 494 break; 495 default: 496 berr = EOPNOTSUPP; 497 goto out; 498 } 499 500 /* 501 * NB: for BIO_READ, this loop is only traversed once. 502 */ 503 while (resid > 0) { 504 if (page > sc->pagecount) { 505 berr = EINVAL; 506 goto out; 507 } 508 addr = page << sc->pageoffset; 509 if (bp->bio_cmd == BIO_WRITE) { 510 /* 511 * If writing less than a full page, transfer 512 * the existing page to the buffer, so that our 513 * PROGRAM_THROUGH_BUFFER below will preserve 514 * the parts of the page we're not writing. 515 */ 516 if (len != sc->pagesize) { 517 txBuf[0] = PAGE_TO_BUFFER_TRANSFER; 518 txBuf[1] = ((addr >> 16) & 0xff); 519 txBuf[2] = ((addr >> 8) & 0xff); 520 txBuf[3] = 0; 521 cmd.tx_data_sz = cmd.rx_data_sz = 0; 522 err = SPIBUS_TRANSFER(pdev, dev, &cmd); 523 if (err == 0) 524 err = at45d_wait_ready(dev, 525 &status); 526 if (err != 0) { 527 berr = EIO; 528 goto out; 529 } 530 } 531 txBuf[0] = PROGRAM_THROUGH_BUFFER; 532 } 533 534 addr += offset; 535 txBuf[1] = ((addr >> 16) & 0xff); 536 txBuf[2] = ((addr >> 8) & 0xff); 537 txBuf[3] = (addr & 0xff); 538 cmd.tx_data_sz = cmd.rx_data_sz = len; 539 err = SPIBUS_TRANSFER(pdev, dev, &cmd); 540 if (err == 0 && bp->bio_cmd != BIO_READ) 541 err = at45d_wait_ready(dev, &status); 542 if (err != 0) { 543 berr = EIO; 544 goto out; 545 } 546 if (bp->bio_cmd == BIO_WRITE) { 547 addr = page << sc->pageoffset; 548 txBuf[0] = PAGE_TO_BUFFER_COMPARE; 549 txBuf[1] = ((addr >> 16) & 0xff); 550 txBuf[2] = ((addr >> 8) & 0xff); 551 txBuf[3] = 0; 552 cmd.tx_data_sz = cmd.rx_data_sz = 0; 553 err = SPIBUS_TRANSFER(pdev, dev, &cmd); 554 if (err == 0) 555 err = at45d_wait_ready(dev, &status); 556 if (err != 0 || (status & STATUS_CMPFAIL)) { 557 device_printf(dev, "comparing page " 558 "%d failed (status=0x%x)\n", page, 559 status); 560 berr = EIO; 561 goto out; 562 } 563 } 564 page++; 565 buf += len; 566 offset = 0; 567 resid -= len; 568 if (resid > sc->pagesize) 569 len = sc->pagesize; 570 else 571 len = resid; 572 if (bp->bio_cmd == BIO_READ) 573 cmd.rx_data = buf; 574 else 575 cmd.tx_data = buf; 576 } 577 out: 578 if (berr != 0) { 579 bp->bio_flags |= BIO_ERROR; 580 bp->bio_error = berr; 581 } 582 bp->bio_resid = resid; 583 biodone(bp); 584 } 585 } 586 587 static device_method_t at45d_methods[] = { 588 /* Device interface */ 589 DEVMETHOD(device_probe, at45d_probe), 590 DEVMETHOD(device_attach, at45d_attach), 591 DEVMETHOD(device_detach, at45d_detach), 592 593 DEVMETHOD_END 594 }; 595 596 static driver_t at45d_driver = { 597 "at45d", 598 at45d_methods, 599 sizeof(struct at45d_softc), 600 }; 601 602 DRIVER_MODULE(at45d, spibus, at45d_driver, NULL, NULL); 603 MODULE_DEPEND(at45d, spibus, 1, 1, 1); 604 #ifdef FDT 605 MODULE_DEPEND(at45d, fdt_slicer, 1, 1, 1); 606 SPIBUS_FDT_PNP_INFO(compat_data); 607 #endif 608 609