1235537Sgber/*- 2235537Sgber * Copyright (C) 2009-2012 Semihalf 3235537Sgber * All rights reserved. 4235537Sgber * 5235537Sgber * Redistribution and use in source and binary forms, with or without 6235537Sgber * modification, are permitted provided that the following conditions 7235537Sgber * are met: 8235537Sgber * 1. Redistributions of source code must retain the above copyright 9235537Sgber * notice, this list of conditions and the following disclaimer. 10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright 11235537Sgber * notice, this list of conditions and the following disclaimer in the 12235537Sgber * documentation and/or other materials provided with the distribution. 13235537Sgber * 14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17235537Sgber * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24235537Sgber * SUCH DAMAGE. 25235537Sgber */ 26235537Sgber 27235537Sgber#include <sys/cdefs.h> 28235537Sgber__FBSDID("$FreeBSD: stable/10/sys/dev/nand/nand_geom.c 313525 2017-02-10 05:35:30Z ngie $"); 29235537Sgber 30235537Sgber#include <sys/param.h> 31235537Sgber#include <sys/systm.h> 32235537Sgber#include <sys/conf.h> 33235537Sgber#include <sys/bus.h> 34235537Sgber#include <sys/malloc.h> 35235537Sgber#include <sys/uio.h> 36235537Sgber#include <sys/bio.h> 37235537Sgber#include <geom/geom.h> 38235537Sgber#include <geom/geom_disk.h> 39235537Sgber 40235537Sgber#include <dev/nand/nand.h> 41235537Sgber#include <dev/nand/nandbus.h> 42235537Sgber#include <dev/nand/nand_dev.h> 43235537Sgber#include "nand_if.h" 44235537Sgber#include "nandbus_if.h" 45235537Sgber 46235537Sgber#define BIO_NAND_STD ((void *)1) 47235537Sgber#define BIO_NAND_RAW ((void *)2) 48235537Sgber 49235537Sgberstatic disk_ioctl_t nand_ioctl; 50235537Sgberstatic disk_getattr_t nand_getattr; 51235537Sgberstatic disk_strategy_t nand_strategy; 52235537Sgberstatic disk_strategy_t nand_strategy_raw; 53235537Sgber 54235537Sgberstatic int 55235537Sgbernand_read(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len) 56235537Sgber{ 57235537Sgber 58235537Sgber nand_debug(NDBG_GEOM, "Read from chip %d [%p] at %d", chip->num, chip, 59235537Sgber offset); 60235537Sgber 61235537Sgber return (nand_read_pages(chip, offset, buf, len)); 62235537Sgber} 63235537Sgber 64235537Sgberstatic int 65235537Sgbernand_write(struct nand_chip *chip, uint32_t offset, void* buf, uint32_t len) 66235537Sgber{ 67235537Sgber 68235537Sgber nand_debug(NDBG_GEOM, "Write to chip %d [%p] at %d", chip->num, chip, 69235537Sgber offset); 70235537Sgber 71235537Sgber return (nand_prog_pages(chip, offset, buf, len)); 72235537Sgber} 73235537Sgber 74235537Sgberstatic int 75235537Sgbernand_read_raw(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len) 76235537Sgber{ 77235537Sgber nand_debug(NDBG_GEOM, "Raw read from chip %d [%p] at %d", chip->num, 78235537Sgber chip, offset); 79235537Sgber 80235537Sgber return (nand_read_pages_raw(chip, offset, buf, len)); 81235537Sgber} 82235537Sgber 83235537Sgberstatic int 84235537Sgbernand_write_raw(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len) 85235537Sgber{ 86235537Sgber 87235537Sgber nand_debug(NDBG_GEOM, "Raw write to chip %d [%p] at %d", chip->num, 88235537Sgber chip, offset); 89235537Sgber 90235537Sgber return (nand_prog_pages_raw(chip, offset, buf, len)); 91235537Sgber} 92235537Sgber 93235537Sgberstatic void 94235537Sgbernand_strategy(struct bio *bp) 95235537Sgber{ 96235537Sgber struct nand_chip *chip; 97235537Sgber 98235537Sgber chip = (struct nand_chip *)bp->bio_disk->d_drv1; 99235537Sgber 100235537Sgber bp->bio_driver1 = BIO_NAND_STD; 101235537Sgber 102235537Sgber nand_debug(NDBG_GEOM, "Strategy %s on chip %d [%p]", 103235537Sgber (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" : 104235537Sgber ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE ? "WRITE" : 105235537Sgber ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE ? "DELETE" : "UNKNOWN")), 106235537Sgber chip->num, chip); 107235537Sgber 108235537Sgber mtx_lock(&chip->qlock); 109235537Sgber bioq_insert_tail(&chip->bioq, bp); 110235537Sgber mtx_unlock(&chip->qlock); 111235537Sgber taskqueue_enqueue(chip->tq, &chip->iotask); 112235537Sgber} 113235537Sgber 114235537Sgberstatic void 115235537Sgbernand_strategy_raw(struct bio *bp) 116235537Sgber{ 117235537Sgber struct nand_chip *chip; 118235537Sgber 119235537Sgber chip = (struct nand_chip *)bp->bio_disk->d_drv1; 120235537Sgber 121235537Sgber /* Inform taskqueue that it's a raw access */ 122235537Sgber bp->bio_driver1 = BIO_NAND_RAW; 123235537Sgber 124235537Sgber nand_debug(NDBG_GEOM, "Strategy %s on chip %d [%p]", 125235537Sgber (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" : 126235537Sgber ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE ? "WRITE" : 127235537Sgber ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE ? "DELETE" : "UNKNOWN")), 128235537Sgber chip->num, chip); 129235537Sgber 130235537Sgber mtx_lock(&chip->qlock); 131235537Sgber bioq_insert_tail(&chip->bioq, bp); 132235537Sgber mtx_unlock(&chip->qlock); 133235537Sgber taskqueue_enqueue(chip->tq, &chip->iotask); 134235537Sgber} 135235537Sgber 136235537Sgberstatic int 137235537Sgbernand_oob_access(struct nand_chip *chip, uint32_t page, uint32_t offset, 138235537Sgber uint32_t len, uint8_t *data, uint8_t write) 139235537Sgber{ 140235537Sgber struct chip_geom *cg; 141235537Sgber int ret = 0; 142235537Sgber 143235537Sgber cg = &chip->chip_geom; 144235537Sgber 145235537Sgber if (!write) 146235537Sgber ret = nand_read_oob(chip, page, data, cg->oob_size); 147235537Sgber else 148235537Sgber ret = nand_prog_oob(chip, page, data, cg->oob_size); 149235537Sgber 150235537Sgber return (ret); 151235537Sgber} 152235537Sgber 153235537Sgberstatic int 154235537Sgbernand_getattr(struct bio *bp) 155235537Sgber{ 156235537Sgber struct nand_chip *chip; 157235537Sgber struct chip_geom *cg; 158235537Sgber device_t dev; 159251651Smav int val; 160235537Sgber 161235537Sgber if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL) 162235537Sgber return (ENXIO); 163235537Sgber 164235537Sgber chip = (struct nand_chip *)bp->bio_disk->d_drv1; 165235537Sgber cg = &(chip->chip_geom); 166235537Sgber 167235537Sgber dev = device_get_parent(chip->dev); 168235537Sgber dev = device_get_parent(dev); 169235537Sgber 170251651Smav if (strcmp(bp->bio_attribute, "NAND::device") == 0) { 171251651Smav if (bp->bio_length != sizeof(dev)) 172251651Smav return (EFAULT); 173251651Smav bcopy(&dev, bp->bio_data, sizeof(dev)); 174251651Smav } else { 175251651Smav if (strcmp(bp->bio_attribute, "NAND::oobsize") == 0) 176251651Smav val = cg->oob_size; 177251651Smav else if (strcmp(bp->bio_attribute, "NAND::pagesize") == 0) 178251651Smav val = cg->page_size; 179251651Smav else if (strcmp(bp->bio_attribute, "NAND::blocksize") == 0) 180251651Smav val = cg->block_size; 181251651Smav else 182251651Smav return (-1); 183251651Smav if (bp->bio_length != sizeof(val)) 184251651Smav return (EFAULT); 185251651Smav bcopy(&val, bp->bio_data, sizeof(val)); 186251651Smav } 187251651Smav bp->bio_completed = bp->bio_length; 188251651Smav return (0); 189235537Sgber} 190235537Sgber 191235537Sgberstatic int 192235537Sgbernand_ioctl(struct disk *ndisk, u_long cmd, void *data, int fflag, 193235537Sgber struct thread *td) 194235537Sgber{ 195235537Sgber struct nand_chip *chip; 196258554Sgber struct chip_geom *cg; 197235537Sgber struct nand_oob_rw *oob_rw = NULL; 198235537Sgber struct nand_raw_rw *raw_rw = NULL; 199235537Sgber device_t nandbus; 200258554Sgber size_t bufsize = 0, len = 0; 201258554Sgber size_t raw_size; 202258554Sgber off_t off; 203235537Sgber uint8_t *buf = NULL; 204235537Sgber int ret = 0; 205235537Sgber uint8_t status; 206235537Sgber 207235537Sgber chip = (struct nand_chip *)ndisk->d_drv1; 208258554Sgber cg = &chip->chip_geom; 209235537Sgber nandbus = device_get_parent(chip->dev); 210235537Sgber 211235537Sgber if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) { 212235537Sgber raw_rw = (struct nand_raw_rw *)data; 213258554Sgber raw_size = cg->pgs_per_blk * (cg->page_size + cg->oob_size); 214258554Sgber 215258554Sgber /* Check if len is not bigger than chip size */ 216258554Sgber if (raw_rw->len > raw_size) 217258554Sgber return (EFBIG); 218258554Sgber 219258554Sgber /* 220258554Sgber * Do not ask for too much memory, in case of large transfers 221258554Sgber * read/write in 16-pages chunks 222258554Sgber */ 223258554Sgber bufsize = 16 * (cg->page_size + cg->oob_size); 224258554Sgber if (raw_rw->len < bufsize) 225258554Sgber bufsize = raw_rw->len; 226258554Sgber 227258554Sgber buf = malloc(bufsize, M_NAND, M_WAITOK); 228258554Sgber len = raw_rw->len; 229258554Sgber off = 0; 230235537Sgber } 231258554Sgber 232235537Sgber switch (cmd) { 233235537Sgber case NAND_IO_ERASE: 234235537Sgber ret = nand_erase_blocks(chip, ((off_t *)data)[0], 235235537Sgber ((off_t *)data)[1]); 236235537Sgber break; 237235537Sgber 238235537Sgber case NAND_IO_OOB_READ: 239235537Sgber oob_rw = (struct nand_oob_rw *)data; 240235537Sgber ret = nand_oob_access(chip, oob_rw->page, 0, 241235537Sgber oob_rw->len, oob_rw->data, 0); 242235537Sgber break; 243235537Sgber 244235537Sgber case NAND_IO_OOB_PROG: 245235537Sgber oob_rw = (struct nand_oob_rw *)data; 246235537Sgber ret = nand_oob_access(chip, oob_rw->page, 0, 247235537Sgber oob_rw->len, oob_rw->data, 1); 248235537Sgber break; 249235537Sgber 250235537Sgber case NAND_IO_GET_STATUS: 251235537Sgber NANDBUS_LOCK(nandbus); 252235537Sgber ret = NANDBUS_GET_STATUS(nandbus, &status); 253235537Sgber if (ret == 0) 254235537Sgber *(uint8_t *)data = status; 255235537Sgber NANDBUS_UNLOCK(nandbus); 256235537Sgber break; 257235537Sgber 258235537Sgber case NAND_IO_RAW_PROG: 259258554Sgber while (len > 0) { 260258554Sgber if (len < bufsize) 261258554Sgber bufsize = len; 262258554Sgber 263258554Sgber ret = copyin(raw_rw->data + off, buf, bufsize); 264258554Sgber if (ret) 265258554Sgber break; 266258554Sgber ret = nand_prog_pages_raw(chip, raw_rw->off + off, buf, 267258554Sgber bufsize); 268258554Sgber if (ret) 269258554Sgber break; 270258554Sgber len -= bufsize; 271258554Sgber off += bufsize; 272258554Sgber } 273235537Sgber break; 274235537Sgber 275235537Sgber case NAND_IO_RAW_READ: 276258554Sgber while (len > 0) { 277258554Sgber if (len < bufsize) 278258554Sgber bufsize = len; 279258554Sgber 280258554Sgber ret = nand_read_pages_raw(chip, raw_rw->off + off, buf, 281258554Sgber bufsize); 282258554Sgber if (ret) 283258554Sgber break; 284258554Sgber 285258554Sgber ret = copyout(buf, raw_rw->data + off, bufsize); 286258554Sgber if (ret) 287258554Sgber break; 288258554Sgber len -= bufsize; 289258554Sgber off += bufsize; 290258554Sgber } 291235537Sgber break; 292235537Sgber 293235537Sgber case NAND_IO_GET_CHIP_PARAM: 294235537Sgber nand_get_chip_param(chip, (struct chip_param_io *)data); 295235537Sgber break; 296235537Sgber 297235537Sgber default: 298235537Sgber printf("Unknown nand_ioctl request \n"); 299235537Sgber ret = EIO; 300235537Sgber } 301235537Sgber 302235537Sgber if (buf) 303235537Sgber free(buf, M_NAND); 304235537Sgber 305235537Sgber return (ret); 306235537Sgber} 307235537Sgber 308235537Sgberstatic void 309235537Sgbernand_io_proc(void *arg, int pending) 310235537Sgber{ 311235537Sgber struct nand_chip *chip = arg; 312235537Sgber struct bio *bp; 313235537Sgber int err = 0; 314235537Sgber 315235537Sgber for (;;) { 316235537Sgber mtx_lock(&chip->qlock); 317235537Sgber bp = bioq_takefirst(&chip->bioq); 318235537Sgber mtx_unlock(&chip->qlock); 319235537Sgber if (bp == NULL) 320235537Sgber break; 321235537Sgber 322235537Sgber if (bp->bio_driver1 == BIO_NAND_STD) { 323235537Sgber if ((bp->bio_cmd & BIO_READ) == BIO_READ) { 324235537Sgber err = nand_read(chip, 325235537Sgber bp->bio_offset & 0xffffffff, 326235537Sgber bp->bio_data, bp->bio_bcount); 327235537Sgber } else if ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE) { 328235537Sgber err = nand_write(chip, 329235537Sgber bp->bio_offset & 0xffffffff, 330235537Sgber bp->bio_data, bp->bio_bcount); 331235537Sgber } 332235537Sgber } else if (bp->bio_driver1 == BIO_NAND_RAW) { 333235537Sgber if ((bp->bio_cmd & BIO_READ) == BIO_READ) { 334235537Sgber err = nand_read_raw(chip, 335235537Sgber bp->bio_offset & 0xffffffff, 336235537Sgber bp->bio_data, bp->bio_bcount); 337235537Sgber } else if ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE) { 338235537Sgber err = nand_write_raw(chip, 339235537Sgber bp->bio_offset & 0xffffffff, 340235537Sgber bp->bio_data, bp->bio_bcount); 341235537Sgber } 342235537Sgber } else 343235537Sgber panic("Unknown access type in bio->bio_driver1\n"); 344235537Sgber 345235537Sgber if ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE) { 346235537Sgber nand_debug(NDBG_GEOM, "Delete on chip%d offset %lld " 347235537Sgber "length %ld\n", chip->num, bp->bio_offset, 348235537Sgber bp->bio_bcount); 349235537Sgber err = nand_erase_blocks(chip, 350235537Sgber bp->bio_offset & 0xffffffff, 351235537Sgber bp->bio_bcount); 352235537Sgber } 353235537Sgber 354235537Sgber if (err == 0 || err == ECC_CORRECTABLE) 355235537Sgber bp->bio_resid = 0; 356235537Sgber else { 357235537Sgber nand_debug(NDBG_GEOM,"nand_[read|write|erase_blocks] " 358235537Sgber "error: %d\n", err); 359235537Sgber 360235537Sgber bp->bio_error = EIO; 361235537Sgber bp->bio_flags |= BIO_ERROR; 362235537Sgber bp->bio_resid = bp->bio_bcount; 363235537Sgber } 364235537Sgber biodone(bp); 365235537Sgber } 366235537Sgber} 367235537Sgber 368235537Sgberint 369235537Sgbercreate_geom_disk(struct nand_chip *chip) 370235537Sgber{ 371235537Sgber struct disk *ndisk, *rdisk; 372235537Sgber 373235537Sgber /* Create the disk device */ 374235537Sgber ndisk = disk_alloc(); 375235537Sgber ndisk->d_strategy = nand_strategy; 376235537Sgber ndisk->d_ioctl = nand_ioctl; 377235537Sgber ndisk->d_getattr = nand_getattr; 378235537Sgber ndisk->d_name = "gnand"; 379235537Sgber ndisk->d_drv1 = chip; 380235537Sgber ndisk->d_maxsize = chip->chip_geom.block_size; 381235537Sgber ndisk->d_sectorsize = chip->chip_geom.page_size; 382235537Sgber ndisk->d_mediasize = chip->chip_geom.chip_size; 383235537Sgber ndisk->d_unit = chip->num + 384235537Sgber 10 * device_get_unit(device_get_parent(chip->dev)); 385235537Sgber 386235537Sgber /* 387235537Sgber * When using BBT, make two last blocks of device unavailable 388235537Sgber * to user (because those are used to store BBT table). 389235537Sgber */ 390235537Sgber if (chip->bbt != NULL) 391235537Sgber ndisk->d_mediasize -= (2 * chip->chip_geom.block_size); 392235537Sgber 393235537Sgber ndisk->d_flags = DISKFLAG_CANDELETE; 394235537Sgber 395235537Sgber snprintf(ndisk->d_ident, sizeof(ndisk->d_ident), 396235537Sgber "nand: Man:0x%02x Dev:0x%02x", chip->id.man_id, chip->id.dev_id); 397312406Smav ndisk->d_rotation_rate = DISK_RR_NON_ROTATING; 398235537Sgber 399235537Sgber disk_create(ndisk, DISK_VERSION); 400235537Sgber 401235537Sgber /* Create the RAW disk device */ 402235537Sgber rdisk = disk_alloc(); 403235537Sgber rdisk->d_strategy = nand_strategy_raw; 404235537Sgber rdisk->d_ioctl = nand_ioctl; 405235537Sgber rdisk->d_getattr = nand_getattr; 406235537Sgber rdisk->d_name = "gnand.raw"; 407235537Sgber rdisk->d_drv1 = chip; 408235537Sgber rdisk->d_maxsize = chip->chip_geom.block_size; 409235537Sgber rdisk->d_sectorsize = chip->chip_geom.page_size; 410235537Sgber rdisk->d_mediasize = chip->chip_geom.chip_size; 411235537Sgber rdisk->d_unit = chip->num + 412235537Sgber 10 * device_get_unit(device_get_parent(chip->dev)); 413235537Sgber 414235537Sgber rdisk->d_flags = DISKFLAG_CANDELETE; 415235537Sgber 416235537Sgber snprintf(rdisk->d_ident, sizeof(rdisk->d_ident), 417235537Sgber "nand_raw: Man:0x%02x Dev:0x%02x", chip->id.man_id, 418235537Sgber chip->id.dev_id); 419313525Sngie rdisk->d_rotation_rate = DISK_RR_NON_ROTATING; 420235537Sgber 421235537Sgber disk_create(rdisk, DISK_VERSION); 422235537Sgber 423235537Sgber chip->ndisk = ndisk; 424235537Sgber chip->rdisk = rdisk; 425235537Sgber 426235537Sgber mtx_init(&chip->qlock, "NAND I/O lock", NULL, MTX_DEF); 427235537Sgber bioq_init(&chip->bioq); 428235537Sgber 429235537Sgber TASK_INIT(&chip->iotask, 0, nand_io_proc, chip); 430235537Sgber chip->tq = taskqueue_create("nand_taskq", M_WAITOK, 431235537Sgber taskqueue_thread_enqueue, &chip->tq); 432235537Sgber taskqueue_start_threads(&chip->tq, 1, PI_DISK, "nand taskq"); 433235537Sgber 434235537Sgber if (bootverbose) 435235537Sgber device_printf(chip->dev, "Created gnand%d for chip [0x%0x, " 436235537Sgber "0x%0x]\n", ndisk->d_unit, chip->id.man_id, 437235537Sgber chip->id.dev_id); 438235537Sgber 439235537Sgber return (0); 440235537Sgber} 441235537Sgber 442235537Sgbervoid 443235537Sgberdestroy_geom_disk(struct nand_chip *chip) 444235537Sgber{ 445235537Sgber struct bio *bp; 446235537Sgber 447235537Sgber taskqueue_free(chip->tq); 448235537Sgber disk_destroy(chip->ndisk); 449235537Sgber disk_destroy(chip->rdisk); 450235537Sgber 451235537Sgber mtx_lock(&chip->qlock); 452235537Sgber for (;;) { 453235537Sgber bp = bioq_takefirst(&chip->bioq); 454235537Sgber if (bp == NULL) 455235537Sgber break; 456235537Sgber bp->bio_error = EIO; 457235537Sgber bp->bio_flags |= BIO_ERROR; 458235537Sgber bp->bio_resid = bp->bio_bcount; 459235537Sgber 460235537Sgber biodone(bp); 461235537Sgber } 462235537Sgber mtx_unlock(&chip->qlock); 463235537Sgber 464235537Sgber mtx_destroy(&chip->qlock); 465235537Sgber} 466