summaryrefslogtreecommitdiffstats
path: root/sys/dev/nand/nand.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/nand/nand.c')
-rw-r--r--sys/dev/nand/nand.c832
1 files changed, 832 insertions, 0 deletions
diff --git a/sys/dev/nand/nand.c b/sys/dev/nand/nand.c
new file mode 100644
index 0000000..ad5bc40
--- /dev/null
+++ b/sys/dev/nand/nand.c
@@ -0,0 +1,832 @@
+/*-
+ * 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_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)
+{
+ struct nand_ecc_data *eccd;
+
+ eccd = &chip->nand->ecc;
+
+ if (eccd->eccpositions)
+ return (eccd->eccpositions);
+
+ switch (chip->chip_geom.oob_size) {
+ case 16:
+ return ((uint16_t *)&default_software_ecc_positions_16);
+ case 64:
+ return ((uint16_t *)&default_software_ecc_positions_64);
+ case 128:
+ return ((uint16_t *)&default_software_ecc_positions_128);
+ default:
+ return (NULL); /* No ecc bytes positions defs available */
+ }
+
+ 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);
+}
OpenPOWER on IntegriCloud