freebsd-dev/sys/dev/nand/nand.c
Ian Lepore 774b082ca9 Rework the routine that returns a pointer to the table of software ECC
byte positions within the OOB area to support chips with unusual OOB
sizes such as 218 or 224 bytes.

The table for 128 byte OOB works for these but it assumes 3 bytes of ECC
per 256 byte block, and in the case of an ONFI chip the params page may
ask for something different.  In other words, this is better but not
yet perfect.
2013-11-15 23:48:51 +00:00

834 lines
19 KiB
C

/*-
* Copyright (C) 2009-2012 Semihalf
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/callout.h>
#include <sys/sysctl.h>
#include <dev/nand/nand.h>
#include <dev/nand/nandbus.h>
#include <dev/nand/nand_ecc_pos.h>
#include "nfc_if.h"
#include "nand_if.h"
#include "nandbus_if.h"
#include <machine/stdarg.h>
#define NAND_RESET_DELAY 1000 /* tRST */
#define NAND_ERASE_DELAY 3000 /* tBERS */
#define NAND_PROG_DELAY 700 /* tPROG */
#define NAND_READ_DELAY 50 /* tR */
#define BIT0(x) ((x) & 0x1)
#define BIT1(x) (BIT0(x >> 1))
#define BIT2(x) (BIT0(x >> 2))
#define BIT3(x) (BIT0(x >> 3))
#define BIT4(x) (BIT0(x >> 4))
#define BIT5(x) (BIT0(x >> 5))
#define BIT6(x) (BIT0(x >> 6))
#define BIT7(x) (BIT0(x >> 7))
#define SOFTECC_SIZE 256
#define SOFTECC_BYTES 3
int nand_debug_flag = 0;
SYSCTL_INT(_debug, OID_AUTO, nand_debug, CTLFLAG_RW, &nand_debug_flag, 0,
"NAND subsystem debug flag");
static void
nand_tunable_init(void *arg)
{
TUNABLE_INT_FETCH("debug.nand", &nand_debug_flag);
}
SYSINIT(nand_tunables, SI_SUB_VFS, SI_ORDER_ANY, nand_tunable_init, NULL);
MALLOC_DEFINE(M_NAND, "NAND", "NAND dynamic data");
static void calculate_ecc(const uint8_t *, uint8_t *);
static int correct_ecc(uint8_t *, uint8_t *, uint8_t *);
void
nand_debug(int level, const char *fmt, ...)
{
va_list ap;
if (!(nand_debug_flag & level))
return;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("\n");
}
void
nand_init(struct nand_softc *nand, device_t dev, int ecc_mode,
int ecc_bytes, int ecc_size, uint16_t *eccposition, char *cdev_name)
{
nand->ecc.eccmode = ecc_mode;
nand->chip_cdev_name = cdev_name;
if (ecc_mode == NAND_ECC_SOFT) {
nand->ecc.eccbytes = SOFTECC_BYTES;
nand->ecc.eccsize = SOFTECC_SIZE;
} else if (ecc_mode != NAND_ECC_NONE) {
nand->ecc.eccbytes = ecc_bytes;
nand->ecc.eccsize = ecc_size;
if (eccposition)
nand->ecc.eccpositions = eccposition;
}
}
void
nand_onfi_set_params(struct nand_chip *chip, struct onfi_chip_params *params)
{
struct chip_geom *cg;
cg = &chip->chip_geom;
init_chip_geom(cg, params->luns, params->blocks_per_lun,
params->pages_per_block, params->bytes_per_page,
params->spare_bytes_per_page);
chip->t_bers = params->t_bers;
chip->t_prog = params->t_prog;
chip->t_r = params->t_r;
chip->t_ccs = params->t_ccs;
if (params->features & ONFI_FEAT_16BIT)
chip->flags |= NAND_16_BIT;
}
void
nand_set_params(struct nand_chip *chip, struct nand_params *params)
{
struct chip_geom *cg;
uint32_t blocks_per_chip;
cg = &chip->chip_geom;
blocks_per_chip = (params->chip_size << 20) /
(params->page_size * params->pages_per_block);
init_chip_geom(cg, 1, blocks_per_chip,
params->pages_per_block, params->page_size,
params->oob_size);
chip->t_bers = NAND_ERASE_DELAY;
chip->t_prog = NAND_PROG_DELAY;
chip->t_r = NAND_READ_DELAY;
chip->t_ccs = 0;
if (params->flags & NAND_16_BIT)
chip->flags |= NAND_16_BIT;
}
int
nand_init_stat(struct nand_chip *chip)
{
struct block_stat *blk_stat;
struct page_stat *pg_stat;
struct chip_geom *cg;
uint32_t blks, pgs;
cg = &chip->chip_geom;
blks = cg->blks_per_lun * cg->luns;
blk_stat = malloc(sizeof(struct block_stat) * blks, M_NAND,
M_WAITOK | M_ZERO);
if (!blk_stat)
return (ENOMEM);
pgs = blks * cg->pgs_per_blk;
pg_stat = malloc(sizeof(struct page_stat) * pgs, M_NAND,
M_WAITOK | M_ZERO);
if (!pg_stat) {
free(blk_stat, M_NAND);
return (ENOMEM);
}
chip->blk_stat = blk_stat;
chip->pg_stat = pg_stat;
return (0);
}
void
nand_destroy_stat(struct nand_chip *chip)
{
free(chip->pg_stat, M_NAND);
free(chip->blk_stat, M_NAND);
}
int
init_chip_geom(struct chip_geom *cg, uint32_t luns, uint32_t blks_per_lun,
uint32_t pgs_per_blk, uint32_t pg_size, uint32_t oob_size)
{
int shift;
if (!cg)
return (-1);
cg->luns = luns;
cg->blks_per_lun = blks_per_lun;
cg->blks_per_chip = blks_per_lun * luns;
cg->pgs_per_blk = pgs_per_blk;
cg->page_size = pg_size;
cg->oob_size = oob_size;
cg->block_size = cg->page_size * cg->pgs_per_blk;
cg->chip_size = cg->block_size * cg->blks_per_chip;
shift = fls(cg->pgs_per_blk - 1);
cg->pg_mask = (1 << shift) - 1;
cg->blk_shift = shift;
if (cg->blks_per_lun > 0) {
shift = fls(cg->blks_per_lun - 1);
cg->blk_mask = ((1 << shift) - 1) << cg->blk_shift;
} else {
shift = 0;
cg->blk_mask = 0;
}
cg->lun_shift = shift + cg->blk_shift;
shift = fls(cg->luns - 1);
cg->lun_mask = ((1 << shift) - 1) << cg->lun_shift;
nand_debug(NDBG_NAND, "Masks: lun 0x%x blk 0x%x page 0x%x\n"
"Shifts: lun %d blk %d",
cg->lun_mask, cg->blk_mask, cg->pg_mask,
cg->lun_shift, cg->blk_shift);
return (0);
}
int
nand_row_to_blkpg(struct chip_geom *cg, uint32_t row, uint32_t *lun,
uint32_t *blk, uint32_t *pg)
{
if (!cg || !lun || !blk || !pg)
return (-1);
if (row & ~(cg->lun_mask | cg->blk_mask | cg->pg_mask)) {
nand_debug(NDBG_NAND,"Address out of bounds\n");
return (-1);
}
*lun = (row & cg->lun_mask) >> cg->lun_shift;
*blk = (row & cg->blk_mask) >> cg->blk_shift;
*pg = (row & cg->pg_mask);
nand_debug(NDBG_NAND,"address %x-%x-%x\n", *lun, *blk, *pg);
return (0);
}
int page_to_row(struct chip_geom *cg, uint32_t page, uint32_t *row)
{
uint32_t lun, block, pg_in_blk;
if (!cg || !row)
return (-1);
block = page / cg->pgs_per_blk;
pg_in_blk = page % cg->pgs_per_blk;
lun = block / cg->blks_per_lun;
block = block % cg->blks_per_lun;
*row = (lun << cg->lun_shift) & cg->lun_mask;
*row |= ((block << cg->blk_shift) & cg->blk_mask);
*row |= (pg_in_blk & cg->pg_mask);
return (0);
}
int
nand_check_page_boundary(struct nand_chip *chip, uint32_t page)
{
struct chip_geom* cg;
cg = &chip->chip_geom;
if (page >= (cg->pgs_per_blk * cg->blks_per_lun * cg->luns)) {
nand_debug(NDBG_GEN,"%s: page number too big %#x\n",
__func__, page);
return (1);
}
return (0);
}
void
nand_get_chip_param(struct nand_chip *chip, struct chip_param_io *param)
{
struct chip_geom *cg;
cg = &chip->chip_geom;
param->page_size = cg->page_size;
param->oob_size = cg->oob_size;
param->blocks = cg->blks_per_lun * cg->luns;
param->pages_per_block = cg->pgs_per_blk;
}
static uint16_t *
default_software_ecc_positions(struct nand_chip *chip)
{
/* If positions have been set already, use them. */
if (chip->nand->ecc.eccpositions)
return (chip->nand->ecc.eccpositions);
/*
* XXX Note that the following logic isn't really sufficient, especially
* in the ONFI case where the number of ECC bytes can be dictated by
* values in the parameters page, and that could lead to needing more
* byte positions than exist within the tables of software-ecc defaults.
*/
if (chip->chip_geom.oob_size >= 128)
return (default_software_ecc_positions_128);
if (chip->chip_geom.oob_size >= 64)
return (default_software_ecc_positions_64);
else if (chip->chip_geom.oob_size >= 16)
return (default_software_ecc_positions_16);
return (NULL);
}
static void
calculate_ecc(const uint8_t *buf, uint8_t *ecc)
{
uint8_t p8, byte;
int i;
memset(ecc, 0, 3);
for (i = 0; i < 256; i++) {
byte = buf[i];
ecc[0] ^= (BIT0(byte) ^ BIT2(byte) ^ BIT4(byte) ^
BIT6(byte)) << 2;
ecc[0] ^= (BIT1(byte) ^ BIT3(byte) ^ BIT5(byte) ^
BIT7(byte)) << 3;
ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT4(byte) ^
BIT5(byte)) << 4;
ecc[0] ^= (BIT2(byte) ^ BIT3(byte) ^ BIT6(byte) ^
BIT7(byte)) << 5;
ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^
BIT3(byte)) << 6;
ecc[0] ^= (BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^
BIT7(byte)) << 7;
p8 = BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^
BIT3(byte) ^ BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^
BIT7(byte);
if (p8) {
ecc[2] ^= (0x1 << BIT0(i));
ecc[2] ^= (0x4 << BIT1(i));
ecc[2] ^= (0x10 << BIT2(i));
ecc[2] ^= (0x40 << BIT3(i));
ecc[1] ^= (0x1 << BIT4(i));
ecc[1] ^= (0x4 << BIT5(i));
ecc[1] ^= (0x10 << BIT6(i));
ecc[1] ^= (0x40 << BIT7(i));
}
}
ecc[0] = ~ecc[0];
ecc[1] = ~ecc[1];
ecc[2] = ~ecc[2];
ecc[0] |= 3;
}
static int
correct_ecc(uint8_t *buf, uint8_t *calc_ecc, uint8_t *read_ecc)
{
uint8_t ecc0, ecc1, ecc2, onesnum, bit, byte;
uint16_t addr = 0;
ecc0 = calc_ecc[0] ^ read_ecc[0];
ecc1 = calc_ecc[1] ^ read_ecc[1];
ecc2 = calc_ecc[2] ^ read_ecc[2];
if (!ecc0 && !ecc1 && !ecc2)
return (ECC_OK);
addr = BIT3(ecc0) | (BIT5(ecc0) << 1) | (BIT7(ecc0) << 2);
addr |= (BIT1(ecc2) << 3) | (BIT3(ecc2) << 4) |
(BIT5(ecc2) << 5) | (BIT7(ecc2) << 6);
addr |= (BIT1(ecc1) << 7) | (BIT3(ecc1) << 8) |
(BIT5(ecc1) << 9) | (BIT7(ecc1) << 10);
onesnum = 0;
while (ecc0 || ecc1 || ecc2) {
if (ecc0 & 1)
onesnum++;
if (ecc1 & 1)
onesnum++;
if (ecc2 & 1)
onesnum++;
ecc0 >>= 1;
ecc1 >>= 1;
ecc2 >>= 1;
}
if (onesnum == 11) {
/* Correctable error */
bit = addr & 7;
byte = addr >> 3;
buf[byte] ^= (1 << bit);
return (ECC_CORRECTABLE);
} else if (onesnum == 1) {
/* ECC error */
return (ECC_ERROR_ECC);
} else {
/* Uncorrectable error */
return (ECC_UNCORRECTABLE);
}
return (0);
}
int
nand_softecc_get(device_t dev, uint8_t *buf, int pagesize, uint8_t *ecc)
{
int steps = pagesize / SOFTECC_SIZE;
int i = 0, j = 0;
for (; i < (steps * SOFTECC_BYTES);
i += SOFTECC_BYTES, j += SOFTECC_SIZE) {
calculate_ecc(&buf[j], &ecc[i]);
}
return (0);
}
int
nand_softecc_correct(device_t dev, uint8_t *buf, int pagesize,
uint8_t *readecc, uint8_t *calcecc)
{
int steps = pagesize / SOFTECC_SIZE;
int i = 0, j = 0, ret = 0;
for (i = 0; i < (steps * SOFTECC_BYTES);
i += SOFTECC_BYTES, j += SOFTECC_SIZE) {
ret += correct_ecc(&buf[j], &calcecc[i], &readecc[i]);
if (ret < 0)
return (ret);
}
return (ret);
}
static int
offset_to_page(struct chip_geom *cg, uint32_t offset)
{
return (offset / cg->page_size);
}
int
nand_read_pages(struct nand_chip *chip, uint32_t offset, void *buf,
uint32_t len)
{
struct chip_geom *cg;
struct nand_ecc_data *eccd;
struct page_stat *pg_stat;
device_t nandbus;
void *oob = NULL;
uint8_t *ptr;
uint16_t *eccpos = NULL;
uint32_t page, num, steps = 0;
int i, retval = 0, needwrite;
nand_debug(NDBG_NAND,"%p read page %x[%x]", chip, offset, len);
cg = &chip->chip_geom;
eccd = &chip->nand->ecc;
page = offset_to_page(cg, offset);
num = len / cg->page_size;
if (eccd->eccmode != NAND_ECC_NONE) {
steps = cg->page_size / eccd->eccsize;
eccpos = default_software_ecc_positions(chip);
oob = malloc(cg->oob_size, M_NAND, M_WAITOK);
}
nandbus = device_get_parent(chip->dev);
NANDBUS_LOCK(nandbus);
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
ptr = (uint8_t *)buf;
while (num--) {
pg_stat = &(chip->pg_stat[page]);
if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
retval = ENXIO;
break;
}
if (eccd->eccmode != NAND_ECC_NONE) {
if (NAND_GET_ECC(chip->dev, ptr, eccd->ecccalculated,
&needwrite)) {
retval = ENXIO;
break;
}
nand_debug(NDBG_ECC,"%s: ECC calculated:",
__func__);
if (nand_debug_flag & NDBG_ECC)
for (i = 0; i < (eccd->eccbytes * steps); i++)
printf("%x ", eccd->ecccalculated[i]);
nand_debug(NDBG_ECC,"\n");
if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size,
0)) {
retval = ENXIO;
break;
}
for (i = 0; i < (eccd->eccbytes * steps); i++)
eccd->eccread[i] = ((uint8_t *)oob)[eccpos[i]];
nand_debug(NDBG_ECC,"%s: ECC read:", __func__);
if (nand_debug_flag & NDBG_ECC)
for (i = 0; i < (eccd->eccbytes * steps); i++)
printf("%x ", eccd->eccread[i]);
nand_debug(NDBG_ECC,"\n");
retval = NAND_CORRECT_ECC(chip->dev, ptr, eccd->eccread,
eccd->ecccalculated);
nand_debug(NDBG_ECC, "NAND_CORRECT_ECC() returned %d",
retval);
if (retval == 0)
pg_stat->ecc_stat.ecc_succeded++;
else if (retval > 0) {
pg_stat->ecc_stat.ecc_corrected += retval;
retval = ECC_CORRECTABLE;
} else {
pg_stat->ecc_stat.ecc_failed++;
break;
}
}
pg_stat->page_read++;
page++;
ptr += cg->page_size;
}
NANDBUS_UNLOCK(nandbus);
if (oob)
free(oob, M_NAND);
return (retval);
}
int
nand_read_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
uint32_t len)
{
struct chip_geom *cg;
device_t nandbus;
uint8_t *ptr;
uint32_t page, num, end, begin = 0, begin_off;
int retval = 0;
cg = &chip->chip_geom;
page = offset_to_page(cg, offset);
begin_off = offset - page * cg->page_size;
if (begin_off) {
begin = cg->page_size - begin_off;
len -= begin;
}
num = len / cg->page_size;
end = len % cg->page_size;
nandbus = device_get_parent(chip->dev);
NANDBUS_LOCK(nandbus);
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
ptr = (uint8_t *)buf;
if (begin_off) {
if (NAND_READ_PAGE(chip->dev, page, ptr, begin, begin_off)) {
NANDBUS_UNLOCK(nandbus);
return (ENXIO);
}
page++;
ptr += begin;
}
while (num--) {
if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
NANDBUS_UNLOCK(nandbus);
return (ENXIO);
}
page++;
ptr += cg->page_size;
}
if (end)
if (NAND_READ_PAGE(chip->dev, page, ptr, end, 0)) {
NANDBUS_UNLOCK(nandbus);
return (ENXIO);
}
NANDBUS_UNLOCK(nandbus);
return (retval);
}
int
nand_prog_pages(struct nand_chip *chip, uint32_t offset, uint8_t *buf,
uint32_t len)
{
struct chip_geom *cg;
struct page_stat *pg_stat;
struct nand_ecc_data *eccd;
device_t nandbus;
uint32_t page, num;
uint8_t *oob = NULL;
uint16_t *eccpos = NULL;
int steps = 0, i, needwrite, err = 0;
nand_debug(NDBG_NAND,"%p prog page %x[%x]", chip, offset, len);
eccd = &chip->nand->ecc;
cg = &chip->chip_geom;
page = offset_to_page(cg, offset);
num = len / cg->page_size;
if (eccd->eccmode != NAND_ECC_NONE) {
steps = cg->page_size / eccd->eccsize;
oob = malloc(cg->oob_size, M_NAND, M_WAITOK);
eccpos = default_software_ecc_positions(chip);
}
nandbus = device_get_parent(chip->dev);
NANDBUS_LOCK(nandbus);
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
while (num--) {
if (NAND_PROGRAM_PAGE(chip->dev, page, buf, cg->page_size, 0)) {
err = ENXIO;
break;
}
if (eccd->eccmode != NAND_ECC_NONE) {
if (NAND_GET_ECC(chip->dev, buf, &eccd->ecccalculated,
&needwrite)) {
err = ENXIO;
break;
}
nand_debug(NDBG_ECC,"ECC calculated:");
if (nand_debug_flag & NDBG_ECC)
for (i = 0; i < (eccd->eccbytes * steps); i++)
printf("%x ", eccd->ecccalculated[i]);
nand_debug(NDBG_ECC,"\n");
if (needwrite) {
if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size,
0)) {
err = ENXIO;
break;
}
for (i = 0; i < (eccd->eccbytes * steps); i++)
oob[eccpos[i]] = eccd->ecccalculated[i];
if (NAND_PROGRAM_OOB(chip->dev, page, oob,
cg->oob_size, 0)) {
err = ENXIO;
break;
}
}
}
pg_stat = &(chip->pg_stat[page]);
pg_stat->page_written++;
page++;
buf += cg->page_size;
}
NANDBUS_UNLOCK(nandbus);
if (oob)
free(oob, M_NAND);
return (err);
}
int
nand_prog_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
uint32_t len)
{
struct chip_geom *cg;
device_t nandbus;
uint8_t *ptr;
uint32_t page, num, end, begin = 0, begin_off;
int retval = 0;
cg = &chip->chip_geom;
page = offset_to_page(cg, offset);
begin_off = offset - page * cg->page_size;
if (begin_off) {
begin = cg->page_size - begin_off;
len -= begin;
}
num = len / cg->page_size;
end = len % cg->page_size;
nandbus = device_get_parent(chip->dev);
NANDBUS_LOCK(nandbus);
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
ptr = (uint8_t *)buf;
if (begin_off) {
if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, begin, begin_off)) {
NANDBUS_UNLOCK(nandbus);
return (ENXIO);
}
page++;
ptr += begin;
}
while (num--) {
if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
NANDBUS_UNLOCK(nandbus);
return (ENXIO);
}
page++;
ptr += cg->page_size;
}
if (end)
retval = NAND_PROGRAM_PAGE(chip->dev, page, ptr, end, 0);
NANDBUS_UNLOCK(nandbus);
return (retval);
}
int
nand_read_oob(struct nand_chip *chip, uint32_t page, void *buf,
uint32_t len)
{
device_t nandbus;
int retval = 0;
nandbus = device_get_parent(chip->dev);
NANDBUS_LOCK(nandbus);
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
retval = NAND_READ_OOB(chip->dev, page, buf, len, 0);
NANDBUS_UNLOCK(nandbus);
return (retval);
}
int
nand_prog_oob(struct nand_chip *chip, uint32_t page, void *buf,
uint32_t len)
{
device_t nandbus;
int retval = 0;
nandbus = device_get_parent(chip->dev);
NANDBUS_LOCK(nandbus);
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
retval = NAND_PROGRAM_OOB(chip->dev, page, buf, len, 0);
NANDBUS_UNLOCK(nandbus);
return (retval);
}
int
nand_erase_blocks(struct nand_chip *chip, off_t offset, size_t len)
{
device_t nandbus;
struct chip_geom *cg;
uint32_t block, num_blocks;
int err = 0;
cg = &chip->chip_geom;
if ((offset % cg->block_size) || (len % cg->block_size))
return (EINVAL);
block = offset / cg->block_size;
num_blocks = len / cg->block_size;
nand_debug(NDBG_NAND,"%p erase blocks %d[%d]", chip, block, num_blocks);
nandbus = device_get_parent(chip->dev);
NANDBUS_LOCK(nandbus);
NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
while (num_blocks--) {
if (!nand_check_bad_block(chip, block)) {
if (NAND_ERASE_BLOCK(chip->dev, block)) {
nand_debug(NDBG_NAND,"%p erase blocks %d error",
chip, block);
nand_mark_bad_block(chip, block);
err = ENXIO;
}
} else
err = ENXIO;
block++;
};
NANDBUS_UNLOCK(nandbus);
if (err)
nand_update_bbt(chip);
return (err);
}
MODULE_VERSION(nand, 1);