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 120static int 121at45d_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 140static int 141at45d_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 162static int 163at45d_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 180static int 181at45d_probe(device_t dev) 182{ 183 184 device_set_desc(dev, "AT45D Flash Family"); 185 return (0); 186} 187 188static int 189at45d_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 205static int 206at45d_detach(device_t dev) 207{ 208 209 return (EBUSY) /* XXX */; 210} 211 212static void 213at45d_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 272static int 273at45d_open(struct disk *dp) 274{ 275 276 return (0); 277} 278 279static int 280at45d_close(struct disk *dp) 281{ 282 283 return (0); 284} 285 286static void 287at45d_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 298static void 299at45d_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 431static devclass_t at45d_devclass; 432 433static 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 442static driver_t at45d_driver = { 443 "at45d", 444 at45d_methods, 445 sizeof(struct at45d_softc), 446}; 447 448DRIVER_MODULE(at45d, spibus, at45d_driver, at45d_devclass, NULL, NULL);
| 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 120static int 121at45d_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 140static int 141at45d_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 162static int 163at45d_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 180static int 181at45d_probe(device_t dev) 182{ 183 184 device_set_desc(dev, "AT45D Flash Family"); 185 return (0); 186} 187 188static int 189at45d_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 205static int 206at45d_detach(device_t dev) 207{ 208 209 return (EBUSY) /* XXX */; 210} 211 212static void 213at45d_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 272static int 273at45d_open(struct disk *dp) 274{ 275 276 return (0); 277} 278 279static int 280at45d_close(struct disk *dp) 281{ 282 283 return (0); 284} 285 286static void 287at45d_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 298static void 299at45d_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 431static devclass_t at45d_devclass; 432 433static 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 442static driver_t at45d_driver = { 443 "at45d", 444 at45d_methods, 445 sizeof(struct at45d_softc), 446}; 447 448DRIVER_MODULE(at45d, spibus, at45d_driver, at45d_devclass, NULL, NULL);
|