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$"); 29235537Sgber 30235537Sgber#include <sys/param.h> 31235537Sgber#include <sys/systm.h> 32235537Sgber#include <sys/kernel.h> 33235537Sgber#include <sys/socket.h> 34235537Sgber#include <sys/malloc.h> 35235537Sgber#include <sys/module.h> 36235537Sgber#include <sys/bus.h> 37235537Sgber#include <sys/lock.h> 38235537Sgber#include <sys/mutex.h> 39235537Sgber#include <sys/callout.h> 40235537Sgber#include <sys/sysctl.h> 41235537Sgber 42235537Sgber#include <dev/nand/nand.h> 43235537Sgber#include <dev/nand/nandbus.h> 44235537Sgber#include <dev/nand/nand_ecc_pos.h> 45235537Sgber#include "nfc_if.h" 46235537Sgber#include "nand_if.h" 47235537Sgber#include "nandbus_if.h" 48235537Sgber#include <machine/stdarg.h> 49235537Sgber 50235537Sgber#define NAND_RESET_DELAY 1000 /* tRST */ 51235537Sgber#define NAND_ERASE_DELAY 3000 /* tBERS */ 52235537Sgber#define NAND_PROG_DELAY 700 /* tPROG */ 53235537Sgber#define NAND_READ_DELAY 50 /* tR */ 54235537Sgber 55235537Sgber#define BIT0(x) ((x) & 0x1) 56235537Sgber#define BIT1(x) (BIT0(x >> 1)) 57235537Sgber#define BIT2(x) (BIT0(x >> 2)) 58235537Sgber#define BIT3(x) (BIT0(x >> 3)) 59235537Sgber#define BIT4(x) (BIT0(x >> 4)) 60235537Sgber#define BIT5(x) (BIT0(x >> 5)) 61235537Sgber#define BIT6(x) (BIT0(x >> 6)) 62235537Sgber#define BIT7(x) (BIT0(x >> 7)) 63235537Sgber 64235537Sgber#define SOFTECC_SIZE 256 65235537Sgber#define SOFTECC_BYTES 3 66235537Sgber 67235537Sgberint nand_debug_flag = 0; 68235537SgberSYSCTL_INT(_debug, OID_AUTO, nand_debug, CTLFLAG_RW, &nand_debug_flag, 0, 69235537Sgber "NAND subsystem debug flag"); 70235537Sgber 71235537Sgberstatic void 72235537Sgbernand_tunable_init(void *arg) 73235537Sgber{ 74235537Sgber 75235537Sgber TUNABLE_INT_FETCH("debug.nand", &nand_debug_flag); 76235537Sgber} 77235537Sgber 78235537SgberSYSINIT(nand_tunables, SI_SUB_VFS, SI_ORDER_ANY, nand_tunable_init, NULL); 79235537Sgber 80235537SgberMALLOC_DEFINE(M_NAND, "NAND", "NAND dynamic data"); 81235537Sgber 82235537Sgberstatic void calculate_ecc(const uint8_t *, uint8_t *); 83235537Sgberstatic int correct_ecc(uint8_t *, uint8_t *, uint8_t *); 84235537Sgber 85235537Sgbervoid 86235537Sgbernand_debug(int level, const char *fmt, ...) 87235537Sgber{ 88235537Sgber va_list ap; 89235537Sgber 90235537Sgber if (!(nand_debug_flag & level)) 91235537Sgber return; 92235537Sgber va_start(ap, fmt); 93235537Sgber vprintf(fmt, ap); 94235537Sgber va_end(ap); 95235537Sgber printf("\n"); 96235537Sgber} 97235537Sgber 98235537Sgbervoid 99235537Sgbernand_init(struct nand_softc *nand, device_t dev, int ecc_mode, 100235537Sgber int ecc_bytes, int ecc_size, uint16_t *eccposition, char *cdev_name) 101235537Sgber{ 102235537Sgber 103235537Sgber nand->ecc.eccmode = ecc_mode; 104235537Sgber nand->chip_cdev_name = cdev_name; 105235537Sgber 106235537Sgber if (ecc_mode == NAND_ECC_SOFT) { 107235537Sgber nand->ecc.eccbytes = SOFTECC_BYTES; 108235537Sgber nand->ecc.eccsize = SOFTECC_SIZE; 109235537Sgber } else if (ecc_mode != NAND_ECC_NONE) { 110235537Sgber nand->ecc.eccbytes = ecc_bytes; 111235537Sgber nand->ecc.eccsize = ecc_size; 112235537Sgber if (eccposition) 113235537Sgber nand->ecc.eccpositions = eccposition; 114235537Sgber } 115235537Sgber} 116235537Sgber 117235537Sgbervoid 118235537Sgbernand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params) 119235537Sgber{ 120235537Sgber struct chip_geom *cg; 121235537Sgber 122235537Sgber cg = &chip->chip_geom; 123235537Sgber 124235537Sgber init_chip_geom(cg, params->luns, params->blocks_per_lun, 125235537Sgber params->pages_per_block, params->bytes_per_page, 126235537Sgber params->spare_bytes_per_page); 127235537Sgber chip->t_bers = params->t_bers; 128235537Sgber chip->t_prog = params->t_prog; 129235537Sgber chip->t_r = params->t_r; 130235537Sgber chip->t_ccs = params->t_ccs; 131235537Sgber 132235537Sgber if (params->features & ONFI_FEAT_16BIT) 133235537Sgber chip->flags |= NAND_16_BIT; 134235537Sgber} 135235537Sgber 136235537Sgbervoid 137235537Sgbernand_set_params(struct nand_chip *chip, struct nand_params *params) 138235537Sgber{ 139235537Sgber struct chip_geom *cg; 140235537Sgber uint32_t blocks_per_chip; 141235537Sgber 142235537Sgber cg = &chip->chip_geom; 143235537Sgber blocks_per_chip = (params->chip_size << 20) / 144235537Sgber (params->page_size * params->pages_per_block); 145235537Sgber 146235537Sgber init_chip_geom(cg, 1, blocks_per_chip, 147235537Sgber params->pages_per_block, params->page_size, 148235537Sgber params->oob_size); 149235537Sgber 150235537Sgber chip->t_bers = NAND_ERASE_DELAY; 151235537Sgber chip->t_prog = NAND_PROG_DELAY; 152235537Sgber chip->t_r = NAND_READ_DELAY; 153235537Sgber chip->t_ccs = 0; 154235537Sgber 155235537Sgber if (params->flags & NAND_16_BIT) 156235537Sgber chip->flags |= NAND_16_BIT; 157235537Sgber} 158235537Sgber 159235537Sgberint 160235537Sgbernand_init_stat(struct nand_chip *chip) 161235537Sgber{ 162235537Sgber struct block_stat *blk_stat; 163235537Sgber struct page_stat *pg_stat; 164235537Sgber struct chip_geom *cg; 165235537Sgber uint32_t blks, pgs; 166235537Sgber 167235537Sgber cg = &chip->chip_geom; 168235537Sgber blks = cg->blks_per_lun * cg->luns; 169235537Sgber blk_stat = malloc(sizeof(struct block_stat) * blks, M_NAND, 170235537Sgber M_WAITOK | M_ZERO); 171235537Sgber if (!blk_stat) 172235537Sgber return (ENOMEM); 173235537Sgber 174235537Sgber pgs = blks * cg->pgs_per_blk; 175235537Sgber pg_stat = malloc(sizeof(struct page_stat) * pgs, M_NAND, 176235537Sgber M_WAITOK | M_ZERO); 177235537Sgber if (!pg_stat) { 178235537Sgber free(blk_stat, M_NAND); 179235537Sgber return (ENOMEM); 180235537Sgber } 181235537Sgber 182235537Sgber chip->blk_stat = blk_stat; 183235537Sgber chip->pg_stat = pg_stat; 184235537Sgber 185235537Sgber return (0); 186235537Sgber} 187235537Sgber 188235537Sgbervoid 189235537Sgbernand_destroy_stat(struct nand_chip *chip) 190235537Sgber{ 191235537Sgber 192235537Sgber free(chip->pg_stat, M_NAND); 193235537Sgber free(chip->blk_stat, M_NAND); 194235537Sgber} 195235537Sgber 196235537Sgberint 197235537Sgberinit_chip_geom(struct chip_geom *cg, uint32_t luns, uint32_t blks_per_lun, 198235537Sgber uint32_t pgs_per_blk, uint32_t pg_size, uint32_t oob_size) 199235537Sgber{ 200235537Sgber int shift; 201235537Sgber 202235537Sgber if (!cg) 203235537Sgber return (-1); 204235537Sgber 205235537Sgber cg->luns = luns; 206235537Sgber cg->blks_per_lun = blks_per_lun; 207235537Sgber cg->blks_per_chip = blks_per_lun * luns; 208235537Sgber cg->pgs_per_blk = pgs_per_blk; 209235537Sgber 210235537Sgber cg->page_size = pg_size; 211235537Sgber cg->oob_size = oob_size; 212235537Sgber cg->block_size = cg->page_size * cg->pgs_per_blk; 213235537Sgber cg->chip_size = cg->block_size * cg->blks_per_chip; 214235537Sgber 215235537Sgber shift = fls(cg->pgs_per_blk - 1); 216235537Sgber cg->pg_mask = (1 << shift) - 1; 217235537Sgber cg->blk_shift = shift; 218235537Sgber 219235537Sgber if (cg->blks_per_lun > 0) { 220235537Sgber shift = fls(cg->blks_per_lun - 1); 221235537Sgber cg->blk_mask = ((1 << shift) - 1) << cg->blk_shift; 222235537Sgber } else { 223235537Sgber shift = 0; 224235537Sgber cg->blk_mask = 0; 225235537Sgber } 226235537Sgber 227235537Sgber cg->lun_shift = shift + cg->blk_shift; 228235537Sgber shift = fls(cg->luns - 1); 229235537Sgber cg->lun_mask = ((1 << shift) - 1) << cg->lun_shift; 230235537Sgber 231235537Sgber nand_debug(NDBG_NAND, "Masks: lun 0x%x blk 0x%x page 0x%x\n" 232235537Sgber "Shifts: lun %d blk %d", 233235537Sgber cg->lun_mask, cg->blk_mask, cg->pg_mask, 234235537Sgber cg->lun_shift, cg->blk_shift); 235235537Sgber 236235537Sgber return (0); 237235537Sgber} 238235537Sgber 239235537Sgberint 240235537Sgbernand_row_to_blkpg(struct chip_geom *cg, uint32_t row, uint32_t *lun, 241235537Sgber uint32_t *blk, uint32_t *pg) 242235537Sgber{ 243235537Sgber 244235537Sgber if (!cg || !lun || !blk || !pg) 245235537Sgber return (-1); 246235537Sgber 247235537Sgber if (row & ~(cg->lun_mask | cg->blk_mask | cg->pg_mask)) { 248235537Sgber nand_debug(NDBG_NAND,"Address out of bounds\n"); 249235537Sgber return (-1); 250235537Sgber } 251235537Sgber 252235537Sgber *lun = (row & cg->lun_mask) >> cg->lun_shift; 253235537Sgber *blk = (row & cg->blk_mask) >> cg->blk_shift; 254235537Sgber *pg = (row & cg->pg_mask); 255235537Sgber 256235537Sgber nand_debug(NDBG_NAND,"address %x-%x-%x\n", *lun, *blk, *pg); 257235537Sgber 258235537Sgber return (0); 259235537Sgber} 260235537Sgber 261235537Sgberint page_to_row(struct chip_geom *cg, uint32_t page, uint32_t *row) 262235537Sgber{ 263235537Sgber uint32_t lun, block, pg_in_blk; 264235537Sgber 265235537Sgber if (!cg || !row) 266235537Sgber return (-1); 267235537Sgber 268235537Sgber block = page / cg->pgs_per_blk; 269235537Sgber pg_in_blk = page % cg->pgs_per_blk; 270235537Sgber 271235537Sgber lun = block / cg->blks_per_lun; 272235537Sgber block = block % cg->blks_per_lun; 273235537Sgber 274235537Sgber *row = (lun << cg->lun_shift) & cg->lun_mask; 275235537Sgber *row |= ((block << cg->blk_shift) & cg->blk_mask); 276235537Sgber *row |= (pg_in_blk & cg->pg_mask); 277235537Sgber 278235537Sgber return (0); 279235537Sgber} 280235537Sgber 281235537Sgberint 282235537Sgbernand_check_page_boundary(struct nand_chip *chip, uint32_t page) 283235537Sgber{ 284235537Sgber struct chip_geom* cg; 285235537Sgber 286235537Sgber cg = &chip->chip_geom; 287235537Sgber if (page >= (cg->pgs_per_blk * cg->blks_per_lun * cg->luns)) { 288235537Sgber nand_debug(NDBG_GEN,"%s: page number too big %#x\n", 289235537Sgber __func__, page); 290235537Sgber return (1); 291235537Sgber } 292235537Sgber 293235537Sgber return (0); 294235537Sgber} 295235537Sgber 296235537Sgbervoid 297235537Sgbernand_get_chip_param(struct nand_chip *chip, struct chip_param_io *param) 298235537Sgber{ 299235537Sgber struct chip_geom *cg; 300235537Sgber 301235537Sgber cg = &chip->chip_geom; 302235537Sgber param->page_size = cg->page_size; 303235537Sgber param->oob_size = cg->oob_size; 304235537Sgber 305235537Sgber param->blocks = cg->blks_per_lun * cg->luns; 306235537Sgber param->pages_per_block = cg->pgs_per_blk; 307235537Sgber} 308235537Sgber 309235537Sgberstatic uint16_t * 310235537Sgberdefault_software_ecc_positions(struct nand_chip *chip) 311235537Sgber{ 312235537Sgber struct nand_ecc_data *eccd; 313235537Sgber 314235537Sgber eccd = &chip->nand->ecc; 315235537Sgber 316235537Sgber if (eccd->eccpositions) 317235537Sgber return (eccd->eccpositions); 318235537Sgber 319235537Sgber switch (chip->chip_geom.oob_size) { 320235537Sgber case 16: 321235537Sgber return ((uint16_t *)&default_software_ecc_positions_16); 322235537Sgber case 64: 323235537Sgber return ((uint16_t *)&default_software_ecc_positions_64); 324235537Sgber case 128: 325235537Sgber return ((uint16_t *)&default_software_ecc_positions_128); 326235537Sgber default: 327235537Sgber return (NULL); /* No ecc bytes positions defs available */ 328235537Sgber } 329235537Sgber 330235537Sgber return (NULL); 331235537Sgber} 332235537Sgber 333235537Sgberstatic void 334235537Sgbercalculate_ecc(const uint8_t *buf, uint8_t *ecc) 335235537Sgber{ 336235537Sgber uint8_t p8, byte; 337235537Sgber int i; 338235537Sgber 339235537Sgber memset(ecc, 0, 3); 340235537Sgber 341235537Sgber for (i = 0; i < 256; i++) { 342235537Sgber byte = buf[i]; 343235537Sgber ecc[0] ^= (BIT0(byte) ^ BIT2(byte) ^ BIT4(byte) ^ 344235537Sgber BIT6(byte)) << 2; 345235537Sgber ecc[0] ^= (BIT1(byte) ^ BIT3(byte) ^ BIT5(byte) ^ 346235537Sgber BIT7(byte)) << 3; 347235537Sgber ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT4(byte) ^ 348235537Sgber BIT5(byte)) << 4; 349235537Sgber ecc[0] ^= (BIT2(byte) ^ BIT3(byte) ^ BIT6(byte) ^ 350235537Sgber BIT7(byte)) << 5; 351235537Sgber ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^ 352235537Sgber BIT3(byte)) << 6; 353235537Sgber ecc[0] ^= (BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^ 354235537Sgber BIT7(byte)) << 7; 355235537Sgber 356235537Sgber p8 = BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^ 357235537Sgber BIT3(byte) ^ BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^ 358235537Sgber BIT7(byte); 359235537Sgber 360235537Sgber if (p8) { 361235537Sgber ecc[2] ^= (0x1 << BIT0(i)); 362235537Sgber ecc[2] ^= (0x4 << BIT1(i)); 363235537Sgber ecc[2] ^= (0x10 << BIT2(i)); 364235537Sgber ecc[2] ^= (0x40 << BIT3(i)); 365235537Sgber 366235537Sgber ecc[1] ^= (0x1 << BIT4(i)); 367235537Sgber ecc[1] ^= (0x4 << BIT5(i)); 368235537Sgber ecc[1] ^= (0x10 << BIT6(i)); 369235537Sgber ecc[1] ^= (0x40 << BIT7(i)); 370235537Sgber } 371235537Sgber } 372235537Sgber ecc[0] = ~ecc[0]; 373235537Sgber ecc[1] = ~ecc[1]; 374235537Sgber ecc[2] = ~ecc[2]; 375235537Sgber ecc[0] |= 3; 376235537Sgber} 377235537Sgber 378235537Sgberstatic int 379235537Sgbercorrect_ecc(uint8_t *buf, uint8_t *calc_ecc, uint8_t *read_ecc) 380235537Sgber{ 381235537Sgber uint8_t ecc0, ecc1, ecc2, onesnum, bit, byte; 382235537Sgber uint16_t addr = 0; 383235537Sgber 384235537Sgber ecc0 = calc_ecc[0] ^ read_ecc[0]; 385235537Sgber ecc1 = calc_ecc[1] ^ read_ecc[1]; 386235537Sgber ecc2 = calc_ecc[2] ^ read_ecc[2]; 387235537Sgber 388235537Sgber if (!ecc0 && !ecc1 && !ecc2) 389235537Sgber return (ECC_OK); 390235537Sgber 391235537Sgber addr = BIT3(ecc0) | (BIT5(ecc0) << 1) | (BIT7(ecc0) << 2); 392235537Sgber addr |= (BIT1(ecc2) << 3) | (BIT3(ecc2) << 4) | 393235537Sgber (BIT5(ecc2) << 5) | (BIT7(ecc2) << 6); 394235537Sgber addr |= (BIT1(ecc1) << 7) | (BIT3(ecc1) << 8) | 395235537Sgber (BIT5(ecc1) << 9) | (BIT7(ecc1) << 10); 396235537Sgber 397235537Sgber onesnum = 0; 398235537Sgber while (ecc0 || ecc1 || ecc2) { 399235537Sgber if (ecc0 & 1) 400235537Sgber onesnum++; 401235537Sgber if (ecc1 & 1) 402235537Sgber onesnum++; 403235537Sgber if (ecc2 & 1) 404235537Sgber onesnum++; 405235537Sgber 406235537Sgber ecc0 >>= 1; 407235537Sgber ecc1 >>= 1; 408235537Sgber ecc2 >>= 1; 409235537Sgber } 410235537Sgber 411235537Sgber if (onesnum == 11) { 412235537Sgber /* Correctable error */ 413235537Sgber bit = addr & 7; 414235537Sgber byte = addr >> 3; 415235537Sgber buf[byte] ^= (1 << bit); 416235537Sgber return (ECC_CORRECTABLE); 417235537Sgber } else if (onesnum == 1) { 418235537Sgber /* ECC error */ 419235537Sgber return (ECC_ERROR_ECC); 420235537Sgber } else { 421235537Sgber /* Uncorrectable error */ 422235537Sgber return (ECC_UNCORRECTABLE); 423235537Sgber } 424235537Sgber 425235537Sgber return (0); 426235537Sgber} 427235537Sgber 428235537Sgberint 429235537Sgbernand_softecc_get(device_t dev, uint8_t *buf, int pagesize, uint8_t *ecc) 430235537Sgber{ 431235537Sgber int steps = pagesize / SOFTECC_SIZE; 432235537Sgber int i = 0, j = 0; 433235537Sgber 434235537Sgber for (; i < (steps * SOFTECC_BYTES); 435235537Sgber i += SOFTECC_BYTES, j += SOFTECC_SIZE) { 436235537Sgber calculate_ecc(&buf[j], &ecc[i]); 437235537Sgber } 438235537Sgber 439235537Sgber return (0); 440235537Sgber} 441235537Sgber 442235537Sgberint 443235537Sgbernand_softecc_correct(device_t dev, uint8_t *buf, int pagesize, 444235537Sgber uint8_t *readecc, uint8_t *calcecc) 445235537Sgber{ 446235537Sgber int steps = pagesize / SOFTECC_SIZE; 447235537Sgber int i = 0, j = 0, ret = 0; 448235537Sgber 449235537Sgber for (i = 0; i < (steps * SOFTECC_BYTES); 450235537Sgber i += SOFTECC_BYTES, j += SOFTECC_SIZE) { 451235537Sgber ret += correct_ecc(&buf[j], &calcecc[i], &readecc[i]); 452235537Sgber if (ret < 0) 453235537Sgber return (ret); 454235537Sgber } 455235537Sgber 456235537Sgber return (ret); 457235537Sgber} 458235537Sgber 459235537Sgberstatic int 460235537Sgberoffset_to_page(struct chip_geom *cg, uint32_t offset) 461235537Sgber{ 462235537Sgber 463235537Sgber return (offset / cg->page_size); 464235537Sgber} 465235537Sgber 466235537Sgberint 467235537Sgbernand_read_pages(struct nand_chip *chip, uint32_t offset, void *buf, 468235537Sgber uint32_t len) 469235537Sgber{ 470235537Sgber struct chip_geom *cg; 471235537Sgber struct nand_ecc_data *eccd; 472235537Sgber struct page_stat *pg_stat; 473235537Sgber device_t nandbus; 474235537Sgber void *oob = NULL; 475235537Sgber uint8_t *ptr; 476235537Sgber uint16_t *eccpos = NULL; 477235537Sgber uint32_t page, num, steps = 0; 478235537Sgber int i, retval = 0, needwrite; 479235537Sgber 480235537Sgber nand_debug(NDBG_NAND,"%p read page %x[%x]", chip, offset, len); 481235537Sgber cg = &chip->chip_geom; 482235537Sgber eccd = &chip->nand->ecc; 483235537Sgber page = offset_to_page(cg, offset); 484235537Sgber num = len / cg->page_size; 485235537Sgber 486235537Sgber if (eccd->eccmode != NAND_ECC_NONE) { 487235537Sgber steps = cg->page_size / eccd->eccsize; 488235537Sgber eccpos = default_software_ecc_positions(chip); 489235537Sgber oob = malloc(cg->oob_size, M_NAND, M_WAITOK); 490235537Sgber } 491235537Sgber 492235537Sgber nandbus = device_get_parent(chip->dev); 493235537Sgber NANDBUS_LOCK(nandbus); 494235537Sgber NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); 495235537Sgber 496235537Sgber ptr = (uint8_t *)buf; 497235537Sgber while (num--) { 498235537Sgber pg_stat = &(chip->pg_stat[page]); 499235537Sgber 500235537Sgber if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) { 501235537Sgber retval = ENXIO; 502235537Sgber break; 503235537Sgber } 504235537Sgber 505235537Sgber if (eccd->eccmode != NAND_ECC_NONE) { 506235537Sgber if (NAND_GET_ECC(chip->dev, ptr, eccd->ecccalculated, 507235537Sgber &needwrite)) { 508235537Sgber retval = ENXIO; 509235537Sgber break; 510235537Sgber } 511235537Sgber nand_debug(NDBG_ECC,"%s: ECC calculated:", 512235537Sgber __func__); 513235537Sgber if (nand_debug_flag & NDBG_ECC) 514235537Sgber for (i = 0; i < (eccd->eccbytes * steps); i++) 515235537Sgber printf("%x ", eccd->ecccalculated[i]); 516235537Sgber 517235537Sgber nand_debug(NDBG_ECC,"\n"); 518235537Sgber 519235537Sgber if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size, 520235537Sgber 0)) { 521235537Sgber retval = ENXIO; 522235537Sgber break; 523235537Sgber } 524235537Sgber for (i = 0; i < (eccd->eccbytes * steps); i++) 525235537Sgber eccd->eccread[i] = ((uint8_t *)oob)[eccpos[i]]; 526235537Sgber 527235537Sgber nand_debug(NDBG_ECC,"%s: ECC read:", __func__); 528235537Sgber if (nand_debug_flag & NDBG_ECC) 529235537Sgber for (i = 0; i < (eccd->eccbytes * steps); i++) 530235537Sgber printf("%x ", eccd->eccread[i]); 531235537Sgber nand_debug(NDBG_ECC,"\n"); 532235537Sgber 533235537Sgber retval = NAND_CORRECT_ECC(chip->dev, ptr, eccd->eccread, 534235537Sgber eccd->ecccalculated); 535235537Sgber 536235537Sgber nand_debug(NDBG_ECC, "NAND_CORRECT_ECC() returned %d", 537235537Sgber retval); 538235537Sgber 539235537Sgber if (retval == 0) 540235537Sgber pg_stat->ecc_stat.ecc_succeded++; 541235537Sgber else if (retval > 0) { 542235537Sgber pg_stat->ecc_stat.ecc_corrected += retval; 543235537Sgber retval = ECC_CORRECTABLE; 544235537Sgber } else { 545235537Sgber pg_stat->ecc_stat.ecc_failed++; 546235537Sgber break; 547235537Sgber } 548235537Sgber } 549235537Sgber 550235537Sgber pg_stat->page_read++; 551235537Sgber page++; 552235537Sgber ptr += cg->page_size; 553235537Sgber } 554235537Sgber 555235537Sgber NANDBUS_UNLOCK(nandbus); 556235537Sgber 557235537Sgber if (oob) 558235537Sgber free(oob, M_NAND); 559235537Sgber 560235537Sgber return (retval); 561235537Sgber} 562235537Sgber 563235537Sgberint 564235537Sgbernand_read_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf, 565235537Sgber uint32_t len) 566235537Sgber{ 567235537Sgber struct chip_geom *cg; 568235537Sgber device_t nandbus; 569235537Sgber uint8_t *ptr; 570235537Sgber uint32_t page, num, end, begin = 0, begin_off; 571235537Sgber int retval = 0; 572235537Sgber 573235537Sgber cg = &chip->chip_geom; 574235537Sgber page = offset_to_page(cg, offset); 575235537Sgber begin_off = offset - page * cg->page_size; 576235537Sgber if (begin_off) { 577235537Sgber begin = cg->page_size - begin_off; 578235537Sgber len -= begin; 579235537Sgber } 580235537Sgber num = len / cg->page_size; 581235537Sgber end = len % cg->page_size; 582235537Sgber 583235537Sgber nandbus = device_get_parent(chip->dev); 584235537Sgber NANDBUS_LOCK(nandbus); 585235537Sgber NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); 586235537Sgber 587235537Sgber ptr = (uint8_t *)buf; 588235537Sgber if (begin_off) { 589235537Sgber if (NAND_READ_PAGE(chip->dev, page, ptr, begin, begin_off)) { 590235537Sgber NANDBUS_UNLOCK(nandbus); 591235537Sgber return (ENXIO); 592235537Sgber } 593235537Sgber 594235537Sgber page++; 595235537Sgber ptr += begin; 596235537Sgber } 597235537Sgber 598235537Sgber while (num--) { 599235537Sgber if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) { 600235537Sgber NANDBUS_UNLOCK(nandbus); 601235537Sgber return (ENXIO); 602235537Sgber } 603235537Sgber 604235537Sgber page++; 605235537Sgber ptr += cg->page_size; 606235537Sgber } 607235537Sgber 608235537Sgber if (end) 609235537Sgber if (NAND_READ_PAGE(chip->dev, page, ptr, end, 0)) { 610235537Sgber NANDBUS_UNLOCK(nandbus); 611235537Sgber return (ENXIO); 612235537Sgber } 613235537Sgber 614235537Sgber NANDBUS_UNLOCK(nandbus); 615235537Sgber 616235537Sgber return (retval); 617235537Sgber} 618235537Sgber 619235537Sgber 620235537Sgberint 621235537Sgbernand_prog_pages(struct nand_chip *chip, uint32_t offset, uint8_t *buf, 622235537Sgber uint32_t len) 623235537Sgber{ 624235537Sgber struct chip_geom *cg; 625235537Sgber struct page_stat *pg_stat; 626235537Sgber struct nand_ecc_data *eccd; 627235537Sgber device_t nandbus; 628235537Sgber uint32_t page, num; 629235537Sgber uint8_t *oob = NULL; 630235537Sgber uint16_t *eccpos = NULL; 631235537Sgber int steps = 0, i, needwrite, err = 0; 632235537Sgber 633235537Sgber nand_debug(NDBG_NAND,"%p prog page %x[%x]", chip, offset, len); 634235537Sgber 635235537Sgber eccd = &chip->nand->ecc; 636235537Sgber cg = &chip->chip_geom; 637235537Sgber page = offset_to_page(cg, offset); 638235537Sgber num = len / cg->page_size; 639235537Sgber 640235537Sgber if (eccd->eccmode != NAND_ECC_NONE) { 641235537Sgber steps = cg->page_size / eccd->eccsize; 642235537Sgber oob = malloc(cg->oob_size, M_NAND, M_WAITOK); 643235537Sgber eccpos = default_software_ecc_positions(chip); 644235537Sgber } 645235537Sgber 646235537Sgber nandbus = device_get_parent(chip->dev); 647235537Sgber NANDBUS_LOCK(nandbus); 648235537Sgber NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); 649235537Sgber 650235537Sgber while (num--) { 651235537Sgber if (NAND_PROGRAM_PAGE(chip->dev, page, buf, cg->page_size, 0)) { 652235537Sgber err = ENXIO; 653235537Sgber break; 654235537Sgber } 655235537Sgber 656235537Sgber if (eccd->eccmode != NAND_ECC_NONE) { 657235537Sgber if (NAND_GET_ECC(chip->dev, buf, &eccd->ecccalculated, 658235537Sgber &needwrite)) { 659235537Sgber err = ENXIO; 660235537Sgber break; 661235537Sgber } 662235537Sgber nand_debug(NDBG_ECC,"ECC calculated:"); 663235537Sgber if (nand_debug_flag & NDBG_ECC) 664235537Sgber for (i = 0; i < (eccd->eccbytes * steps); i++) 665235537Sgber printf("%x ", eccd->ecccalculated[i]); 666235537Sgber 667235537Sgber nand_debug(NDBG_ECC,"\n"); 668235537Sgber 669235537Sgber if (needwrite) { 670235537Sgber if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size, 671235537Sgber 0)) { 672235537Sgber err = ENXIO; 673235537Sgber break; 674235537Sgber } 675235537Sgber 676235537Sgber for (i = 0; i < (eccd->eccbytes * steps); i++) 677235537Sgber oob[eccpos[i]] = eccd->ecccalculated[i]; 678235537Sgber 679235537Sgber if (NAND_PROGRAM_OOB(chip->dev, page, oob, 680235537Sgber cg->oob_size, 0)) { 681235537Sgber err = ENXIO; 682235537Sgber break; 683235537Sgber } 684235537Sgber } 685235537Sgber } 686235537Sgber 687235537Sgber pg_stat = &(chip->pg_stat[page]); 688235537Sgber pg_stat->page_written++; 689235537Sgber 690235537Sgber page++; 691235537Sgber buf += cg->page_size; 692235537Sgber } 693235537Sgber 694235537Sgber NANDBUS_UNLOCK(nandbus); 695235537Sgber 696235537Sgber if (oob) 697235537Sgber free(oob, M_NAND); 698235537Sgber 699235537Sgber return (err); 700235537Sgber} 701235537Sgber 702235537Sgberint 703235537Sgbernand_prog_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf, 704235537Sgber uint32_t len) 705235537Sgber{ 706235537Sgber struct chip_geom *cg; 707235537Sgber device_t nandbus; 708235537Sgber uint8_t *ptr; 709235537Sgber uint32_t page, num, end, begin = 0, begin_off; 710235537Sgber int retval = 0; 711235537Sgber 712235537Sgber cg = &chip->chip_geom; 713235537Sgber page = offset_to_page(cg, offset); 714235537Sgber begin_off = offset - page * cg->page_size; 715235537Sgber if (begin_off) { 716235537Sgber begin = cg->page_size - begin_off; 717235537Sgber len -= begin; 718235537Sgber } 719235537Sgber num = len / cg->page_size; 720235537Sgber end = len % cg->page_size; 721235537Sgber 722235537Sgber nandbus = device_get_parent(chip->dev); 723235537Sgber NANDBUS_LOCK(nandbus); 724235537Sgber NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); 725235537Sgber 726235537Sgber ptr = (uint8_t *)buf; 727235537Sgber if (begin_off) { 728235537Sgber if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, begin, begin_off)) { 729235537Sgber NANDBUS_UNLOCK(nandbus); 730235537Sgber return (ENXIO); 731235537Sgber } 732235537Sgber 733235537Sgber page++; 734235537Sgber ptr += begin; 735235537Sgber } 736235537Sgber 737235537Sgber while (num--) { 738235537Sgber if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, cg->page_size, 0)) { 739235537Sgber NANDBUS_UNLOCK(nandbus); 740235537Sgber return (ENXIO); 741235537Sgber } 742235537Sgber 743235537Sgber page++; 744235537Sgber ptr += cg->page_size; 745235537Sgber } 746235537Sgber 747235537Sgber if (end) 748235537Sgber retval = NAND_PROGRAM_PAGE(chip->dev, page, ptr, end, 0); 749235537Sgber 750235537Sgber NANDBUS_UNLOCK(nandbus); 751235537Sgber 752235537Sgber return (retval); 753235537Sgber} 754235537Sgber 755235537Sgberint 756235537Sgbernand_read_oob(struct nand_chip *chip, uint32_t page, void *buf, 757235537Sgber uint32_t len) 758235537Sgber{ 759235537Sgber device_t nandbus; 760235537Sgber int retval = 0; 761235537Sgber 762235537Sgber nandbus = device_get_parent(chip->dev); 763235537Sgber NANDBUS_LOCK(nandbus); 764235537Sgber NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); 765235537Sgber 766235537Sgber retval = NAND_READ_OOB(chip->dev, page, buf, len, 0); 767235537Sgber 768235537Sgber NANDBUS_UNLOCK(nandbus); 769235537Sgber 770235537Sgber return (retval); 771235537Sgber} 772235537Sgber 773235537Sgber 774235537Sgberint 775235537Sgbernand_prog_oob(struct nand_chip *chip, uint32_t page, void *buf, 776235537Sgber uint32_t len) 777235537Sgber{ 778235537Sgber device_t nandbus; 779235537Sgber int retval = 0; 780235537Sgber 781235537Sgber nandbus = device_get_parent(chip->dev); 782235537Sgber NANDBUS_LOCK(nandbus); 783235537Sgber NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); 784235537Sgber 785235537Sgber retval = NAND_PROGRAM_OOB(chip->dev, page, buf, len, 0); 786235537Sgber 787235537Sgber NANDBUS_UNLOCK(nandbus); 788235537Sgber 789235537Sgber return (retval); 790235537Sgber} 791235537Sgber 792235537Sgberint 793235537Sgbernand_erase_blocks(struct nand_chip *chip, off_t offset, size_t len) 794235537Sgber{ 795235537Sgber device_t nandbus; 796235537Sgber struct chip_geom *cg; 797235537Sgber uint32_t block, num_blocks; 798235537Sgber int err = 0; 799235537Sgber 800235537Sgber cg = &chip->chip_geom; 801235537Sgber if ((offset % cg->block_size) || (len % cg->block_size)) 802235537Sgber return (EINVAL); 803235537Sgber 804235537Sgber block = offset / cg->block_size; 805235537Sgber num_blocks = len / cg->block_size; 806235537Sgber nand_debug(NDBG_NAND,"%p erase blocks %d[%d]", chip, block, num_blocks); 807235537Sgber 808235537Sgber nandbus = device_get_parent(chip->dev); 809235537Sgber NANDBUS_LOCK(nandbus); 810235537Sgber NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); 811235537Sgber 812235537Sgber while (num_blocks--) { 813235537Sgber if (!nand_check_bad_block(chip, block)) { 814235537Sgber if (NAND_ERASE_BLOCK(chip->dev, block)) { 815235537Sgber nand_debug(NDBG_NAND,"%p erase blocks %d error", 816235537Sgber chip, block); 817235537Sgber nand_mark_bad_block(chip, block); 818235537Sgber err = ENXIO; 819235537Sgber } 820235537Sgber } else 821235537Sgber err = ENXIO; 822235537Sgber 823235537Sgber block++; 824235537Sgber }; 825235537Sgber 826235537Sgber NANDBUS_UNLOCK(nandbus); 827235537Sgber 828235537Sgber if (err) 829235537Sgber nand_update_bbt(chip); 830235537Sgber 831235537Sgber return (err); 832235537Sgber} 833237605Stakawata 834237605StakawataMODULE_VERSION(nand, 1); 835