c3c04faa3a
to native endianness. We must also pay attention to unaligned accesses. Copy the interesting parameters to a new struct so the rest of the code can forget about these problems. Submitted by: Kristof Provost <kristof@sigsegv.be> (cleanup) and me (orig).
1375 lines
30 KiB
C
1375 lines
30 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.
|
|
*/
|
|
|
|
/* Generic NAND driver */
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/endian.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/module.h>
|
|
#include <sys/rman.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/time.h>
|
|
#include <sys/malloc.h>
|
|
|
|
#include <dev/nand/nand.h>
|
|
#include <dev/nand/nandbus.h>
|
|
#include "nfc_if.h"
|
|
#include "nand_if.h"
|
|
#include "nandbus_if.h"
|
|
|
|
|
|
static int onfi_nand_probe(device_t dev);
|
|
static int large_nand_probe(device_t dev);
|
|
static int small_nand_probe(device_t dev);
|
|
static int generic_nand_attach(device_t dev);
|
|
static int generic_nand_detach(device_t dev);
|
|
|
|
static int generic_erase_block(device_t, uint32_t);
|
|
static int generic_erase_block_intlv(device_t, uint32_t);
|
|
static int generic_read_page (device_t, uint32_t, void *, uint32_t, uint32_t);
|
|
static int generic_read_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
|
|
static int generic_program_page(device_t, uint32_t, void *, uint32_t, uint32_t);
|
|
static int generic_program_page_intlv(device_t, uint32_t, void *, uint32_t,
|
|
uint32_t);
|
|
static int generic_program_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
|
|
static int generic_is_blk_bad(device_t, uint32_t, uint8_t *);
|
|
static int generic_get_ecc(device_t, void *, void *, int *);
|
|
static int generic_correct_ecc(device_t, void *, void *, void *);
|
|
|
|
static int small_read_page(device_t, uint32_t, void *, uint32_t, uint32_t);
|
|
static int small_read_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
|
|
static int small_program_page(device_t, uint32_t, void *, uint32_t, uint32_t);
|
|
static int small_program_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
|
|
|
|
static int onfi_is_blk_bad(device_t, uint32_t, uint8_t *);
|
|
static int onfi_read_parameter(struct nand_chip *, struct onfi_chip_params *);
|
|
|
|
static int nand_send_address(device_t, int32_t, int32_t, int8_t);
|
|
|
|
static device_method_t onand_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, onfi_nand_probe),
|
|
DEVMETHOD(device_attach, generic_nand_attach),
|
|
DEVMETHOD(device_detach, generic_nand_detach),
|
|
|
|
DEVMETHOD(nand_read_page, generic_read_page),
|
|
DEVMETHOD(nand_program_page, generic_program_page),
|
|
DEVMETHOD(nand_program_page_intlv, generic_program_page_intlv),
|
|
DEVMETHOD(nand_read_oob, generic_read_oob),
|
|
DEVMETHOD(nand_program_oob, generic_program_oob),
|
|
DEVMETHOD(nand_erase_block, generic_erase_block),
|
|
DEVMETHOD(nand_erase_block_intlv, generic_erase_block_intlv),
|
|
|
|
DEVMETHOD(nand_is_blk_bad, onfi_is_blk_bad),
|
|
DEVMETHOD(nand_get_ecc, generic_get_ecc),
|
|
DEVMETHOD(nand_correct_ecc, generic_correct_ecc),
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static device_method_t lnand_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, large_nand_probe),
|
|
DEVMETHOD(device_attach, generic_nand_attach),
|
|
DEVMETHOD(device_detach, generic_nand_detach),
|
|
|
|
DEVMETHOD(nand_read_page, generic_read_page),
|
|
DEVMETHOD(nand_program_page, generic_program_page),
|
|
DEVMETHOD(nand_read_oob, generic_read_oob),
|
|
DEVMETHOD(nand_program_oob, generic_program_oob),
|
|
DEVMETHOD(nand_erase_block, generic_erase_block),
|
|
|
|
DEVMETHOD(nand_is_blk_bad, generic_is_blk_bad),
|
|
DEVMETHOD(nand_get_ecc, generic_get_ecc),
|
|
DEVMETHOD(nand_correct_ecc, generic_correct_ecc),
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static device_method_t snand_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, small_nand_probe),
|
|
DEVMETHOD(device_attach, generic_nand_attach),
|
|
DEVMETHOD(device_detach, generic_nand_detach),
|
|
|
|
DEVMETHOD(nand_read_page, small_read_page),
|
|
DEVMETHOD(nand_program_page, small_program_page),
|
|
DEVMETHOD(nand_read_oob, small_read_oob),
|
|
DEVMETHOD(nand_program_oob, small_program_oob),
|
|
DEVMETHOD(nand_erase_block, generic_erase_block),
|
|
|
|
DEVMETHOD(nand_is_blk_bad, generic_is_blk_bad),
|
|
DEVMETHOD(nand_get_ecc, generic_get_ecc),
|
|
DEVMETHOD(nand_correct_ecc, generic_correct_ecc),
|
|
{ 0, 0 }
|
|
};
|
|
|
|
devclass_t onand_devclass;
|
|
devclass_t lnand_devclass;
|
|
devclass_t snand_devclass;
|
|
|
|
driver_t onand_driver = {
|
|
"onand",
|
|
onand_methods,
|
|
sizeof(struct nand_chip)
|
|
};
|
|
|
|
driver_t lnand_driver = {
|
|
"lnand",
|
|
lnand_methods,
|
|
sizeof(struct nand_chip)
|
|
};
|
|
|
|
driver_t snand_driver = {
|
|
"snand",
|
|
snand_methods,
|
|
sizeof(struct nand_chip)
|
|
};
|
|
|
|
DRIVER_MODULE(onand, nandbus, onand_driver, onand_devclass, 0, 0);
|
|
DRIVER_MODULE(lnand, nandbus, lnand_driver, lnand_devclass, 0, 0);
|
|
DRIVER_MODULE(snand, nandbus, snand_driver, snand_devclass, 0, 0);
|
|
|
|
static int
|
|
onfi_nand_probe(device_t dev)
|
|
{
|
|
struct nandbus_ivar *ivar;
|
|
|
|
ivar = device_get_ivars(dev);
|
|
if (ivar && ivar->is_onfi) {
|
|
device_set_desc(dev, "ONFI compliant NAND");
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
return (ENODEV);
|
|
}
|
|
|
|
static int
|
|
large_nand_probe(device_t dev)
|
|
{
|
|
struct nandbus_ivar *ivar;
|
|
|
|
ivar = device_get_ivars(dev);
|
|
if (ivar && !ivar->is_onfi && ivar->params->page_size >= 512) {
|
|
device_set_desc(dev, ivar->params->name);
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
return (ENODEV);
|
|
}
|
|
|
|
static int
|
|
small_nand_probe(device_t dev)
|
|
{
|
|
struct nandbus_ivar *ivar;
|
|
|
|
ivar = device_get_ivars(dev);
|
|
if (ivar && !ivar->is_onfi && ivar->params->page_size == 512) {
|
|
device_set_desc(dev, ivar->params->name);
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
return (ENODEV);
|
|
}
|
|
|
|
static int
|
|
generic_nand_attach(device_t dev)
|
|
{
|
|
struct nand_chip *chip;
|
|
struct nandbus_ivar *ivar;
|
|
struct onfi_chip_params *onfi_chip_params;
|
|
device_t nandbus, nfc;
|
|
int err;
|
|
|
|
chip = device_get_softc(dev);
|
|
chip->dev = dev;
|
|
|
|
ivar = device_get_ivars(dev);
|
|
chip->id.man_id = ivar->man_id;
|
|
chip->id.dev_id = ivar->dev_id;
|
|
chip->num = ivar->cs;
|
|
|
|
/* TODO remove when HW ECC supported */
|
|
nandbus = device_get_parent(dev);
|
|
nfc = device_get_parent(nandbus);
|
|
|
|
chip->nand = device_get_softc(nfc);
|
|
|
|
if (ivar->is_onfi) {
|
|
onfi_chip_params = malloc(sizeof(struct onfi_chip_params),
|
|
M_NAND, M_WAITOK | M_ZERO);
|
|
if (onfi_chip_params == NULL)
|
|
return (ENOMEM);
|
|
|
|
if (onfi_read_parameter(chip, onfi_chip_params)) {
|
|
nand_debug(NDBG_GEN,"Could not read parameter page!\n");
|
|
free(onfi_chip_params, M_NAND);
|
|
return (ENXIO);
|
|
}
|
|
|
|
nand_onfi_set_params(chip, onfi_chip_params);
|
|
/* Set proper column and row cycles */
|
|
ivar->cols = (onfi_chip_params->address_cycles >> 4) & 0xf;
|
|
ivar->rows = onfi_chip_params->address_cycles & 0xf;
|
|
free(onfi_chip_params, M_NAND);
|
|
|
|
} else {
|
|
nand_set_params(chip, ivar->params);
|
|
}
|
|
|
|
err = nand_init_stat(chip);
|
|
if (err) {
|
|
generic_nand_detach(dev);
|
|
return (err);
|
|
}
|
|
|
|
err = nand_init_bbt(chip);
|
|
if (err) {
|
|
generic_nand_detach(dev);
|
|
return (err);
|
|
}
|
|
|
|
err = nand_make_dev(chip);
|
|
if (err) {
|
|
generic_nand_detach(dev);
|
|
return (err);
|
|
}
|
|
|
|
err = create_geom_disk(chip);
|
|
if (err) {
|
|
generic_nand_detach(dev);
|
|
return (err);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
generic_nand_detach(device_t dev)
|
|
{
|
|
struct nand_chip *chip;
|
|
|
|
chip = device_get_softc(dev);
|
|
|
|
nand_destroy_bbt(chip);
|
|
destroy_geom_disk(chip);
|
|
nand_destroy_dev(chip);
|
|
nand_destroy_stat(chip);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
can_write(device_t nandbus)
|
|
{
|
|
uint8_t status;
|
|
|
|
if (NANDBUS_WAIT_READY(nandbus, &status))
|
|
return (0);
|
|
|
|
if (!(status & NAND_STATUS_WP)) {
|
|
nand_debug(NDBG_GEN,"Chip is write-protected");
|
|
return (0);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
check_fail(device_t nandbus)
|
|
{
|
|
uint8_t status;
|
|
|
|
NANDBUS_WAIT_READY(nandbus, &status);
|
|
if (status & NAND_STATUS_FAIL) {
|
|
nand_debug(NDBG_GEN,"Status failed %x", status);
|
|
return (ENXIO);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static uint16_t
|
|
onfi_crc(const void *buf, size_t buflen)
|
|
{
|
|
int i, j;
|
|
uint16_t crc;
|
|
const uint8_t *bufptr;
|
|
|
|
bufptr = buf;
|
|
crc = 0x4f4e;
|
|
for (j = 0; j < buflen; j++) {
|
|
crc ^= *bufptr++ << 8;
|
|
for (i = 0; i < 8; i++)
|
|
if (crc & 0x8000)
|
|
crc = (crc << 1) ^ 0x8005;
|
|
else
|
|
crc <<= 1;
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
static int
|
|
onfi_read_parameter(struct nand_chip *chip, struct onfi_chip_params *chip_params)
|
|
{
|
|
device_t nandbus;
|
|
struct onfi_params params;
|
|
int found, sigcount, trycopy;
|
|
|
|
nand_debug(NDBG_GEN,"read parameter");
|
|
|
|
nandbus = device_get_parent(chip->dev);
|
|
|
|
NANDBUS_SELECT_CS(nandbus, chip->num);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_READ_PARAMETER))
|
|
return (ENXIO);
|
|
|
|
if (nand_send_address(chip->dev, -1, -1, PAGE_PARAMETER_DEF))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_START_COMMAND(nandbus))
|
|
return (ENXIO);
|
|
|
|
/*
|
|
* XXX Bogus DELAY, we really need a nandbus_wait_ready() here, but it's
|
|
* not accessible from here (static to nandbus).
|
|
*/
|
|
DELAY(1000);
|
|
|
|
/*
|
|
* The ONFI spec mandates a minimum of three copies of the parameter
|
|
* data, so loop up to 3 times trying to find good data. Each copy is
|
|
* validated by a signature of "ONFI" and a crc. There is a very strange
|
|
* rule that the signature is valid if any 2 of the 4 bytes are correct.
|
|
*/
|
|
for (found= 0, trycopy = 0; !found && trycopy < 3; trycopy++) {
|
|
NANDBUS_READ_BUFFER(nandbus, ¶ms, sizeof(struct onfi_params));
|
|
sigcount = params.signature[0] == 'O';
|
|
sigcount += params.signature[1] == 'N';
|
|
sigcount += params.signature[2] == 'F';
|
|
sigcount += params.signature[3] == 'I';
|
|
if (sigcount < 2)
|
|
continue;
|
|
if (onfi_crc(¶ms, 254) != params.crc)
|
|
continue;
|
|
found = 1;
|
|
}
|
|
if (!found)
|
|
return (ENXIO);
|
|
|
|
chip_params->luns = params.luns;
|
|
chip_params->blocks_per_lun = le32dec(¶ms.blocks_per_lun);
|
|
chip_params->pages_per_block = le32dec(¶ms.pages_per_block);
|
|
chip_params->bytes_per_page = le32dec(¶ms.bytes_per_page);
|
|
chip_params->spare_bytes_per_page = le32dec(¶ms.spare_bytes_per_page);
|
|
chip_params->t_bers = le16dec(¶ms.t_bers);
|
|
chip_params->t_prog = le16dec(¶ms.t_prog);
|
|
chip_params->t_r = le16dec(¶ms.t_r);
|
|
chip_params->t_ccs = le16dec(¶ms.t_ccs);
|
|
chip_params->features = le16dec(¶ms.features);
|
|
chip_params->address_cycles = params.address_cycles;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
send_read_page(device_t nand, uint8_t start_command, uint8_t end_command,
|
|
uint32_t row, uint32_t column)
|
|
{
|
|
device_t nandbus = device_get_parent(nand);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, start_command))
|
|
return (ENXIO);
|
|
|
|
if (nand_send_address(nand, row, column, -1))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, end_command))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_START_COMMAND(nandbus))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
generic_read_page(device_t nand, uint32_t page, void *buf, uint32_t len,
|
|
uint32_t offset)
|
|
{
|
|
struct nand_chip *chip;
|
|
struct page_stat *pg_stat;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN,"%p raw read page %x[%x] at %x", nand, page, len, offset);
|
|
chip = device_get_softc(nand);
|
|
nandbus = device_get_parent(nand);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_END, row,
|
|
offset))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_r);
|
|
|
|
NANDBUS_READ_BUFFER(nandbus, buf, len);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
pg_stat = &(chip->pg_stat[page]);
|
|
pg_stat->page_raw_read++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
generic_read_oob(device_t nand, uint32_t page, void* buf, uint32_t len,
|
|
uint32_t offset)
|
|
{
|
|
struct nand_chip *chip;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN,"%p raw read oob %x[%x] at %x", nand, page, len, offset);
|
|
chip = device_get_softc(nand);
|
|
nandbus = device_get_parent(nand);
|
|
|
|
if (nand_check_page_boundary(chip, page)) {
|
|
nand_debug(NDBG_GEN,"page boundary check failed: %08x\n", page);
|
|
return (ENXIO);
|
|
}
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
offset += chip->chip_geom.page_size;
|
|
|
|
if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_END, row,
|
|
offset))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_r);
|
|
|
|
NANDBUS_READ_BUFFER(nandbus, buf, len);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
send_start_program_page(device_t nand, uint32_t row, uint32_t column)
|
|
{
|
|
device_t nandbus = device_get_parent(nand);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_PROG))
|
|
return (ENXIO);
|
|
|
|
if (nand_send_address(nand, row, column, -1))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
send_end_program_page(device_t nandbus, uint8_t end_command)
|
|
{
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, end_command))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_START_COMMAND(nandbus))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
generic_program_page(device_t nand, uint32_t page, void *buf, uint32_t len,
|
|
uint32_t offset)
|
|
{
|
|
struct nand_chip *chip;
|
|
struct page_stat *pg_stat;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN,"%p raw prog page %x[%x] at %x", nand, page, len,
|
|
offset);
|
|
chip = device_get_softc(nand);
|
|
nandbus = device_get_parent(nand);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (!can_write(nandbus))
|
|
return (ENXIO);
|
|
|
|
if (send_start_program_page(nand, row, offset))
|
|
return (ENXIO);
|
|
|
|
NANDBUS_WRITE_BUFFER(nandbus, buf, len);
|
|
|
|
if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_prog);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
pg_stat = &(chip->pg_stat[page]);
|
|
pg_stat->page_raw_written++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
generic_program_page_intlv(device_t nand, uint32_t page, void *buf,
|
|
uint32_t len, uint32_t offset)
|
|
{
|
|
struct nand_chip *chip;
|
|
struct page_stat *pg_stat;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN,"%p raw prog page %x[%x] at %x", nand, page, len, offset);
|
|
chip = device_get_softc(nand);
|
|
nandbus = device_get_parent(nand);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (!can_write(nandbus))
|
|
return (ENXIO);
|
|
|
|
if (send_start_program_page(nand, row, offset))
|
|
return (ENXIO);
|
|
|
|
NANDBUS_WRITE_BUFFER(nandbus, buf, len);
|
|
|
|
if (send_end_program_page(nandbus, NAND_CMD_PROG_INTLV))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_prog);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
pg_stat = &(chip->pg_stat[page]);
|
|
pg_stat->page_raw_written++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
generic_program_oob(device_t nand, uint32_t page, void* buf, uint32_t len,
|
|
uint32_t offset)
|
|
{
|
|
struct nand_chip *chip;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN,"%p raw prog oob %x[%x] at %x", nand, page, len,
|
|
offset);
|
|
chip = device_get_softc(nand);
|
|
nandbus = device_get_parent(nand);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
offset += chip->chip_geom.page_size;
|
|
|
|
if (!can_write(nandbus))
|
|
return (ENXIO);
|
|
|
|
if (send_start_program_page(nand, row, offset))
|
|
return (ENXIO);
|
|
|
|
NANDBUS_WRITE_BUFFER(nandbus, buf, len);
|
|
|
|
if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_prog);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
send_erase_block(device_t nand, uint32_t row, uint8_t second_command)
|
|
{
|
|
device_t nandbus = device_get_parent(nand);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_ERASE))
|
|
return (ENXIO);
|
|
|
|
if (nand_send_address(nand, row, -1, -1))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, second_command))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_START_COMMAND(nandbus))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
generic_erase_block(device_t nand, uint32_t block)
|
|
{
|
|
struct block_stat *blk_stat;
|
|
struct nand_chip *chip;
|
|
device_t nandbus;
|
|
int row;
|
|
|
|
nand_debug(NDBG_GEN,"%p erase block %x", nand, block);
|
|
nandbus = device_get_parent(nand);
|
|
chip = device_get_softc(nand);
|
|
|
|
if (block >= (chip->chip_geom.blks_per_lun * chip->chip_geom.luns))
|
|
return (ENXIO);
|
|
|
|
row = (block << chip->chip_geom.blk_shift) &
|
|
chip->chip_geom.blk_mask;
|
|
|
|
nand_debug(NDBG_GEN,"%p erase block row %x", nand, row);
|
|
|
|
if (!can_write(nandbus))
|
|
return (ENXIO);
|
|
|
|
send_erase_block(nand, row, NAND_CMD_ERASE_END);
|
|
|
|
DELAY(chip->t_bers);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
blk_stat = &(chip->blk_stat[block]);
|
|
blk_stat->block_erased++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
generic_erase_block_intlv(device_t nand, uint32_t block)
|
|
{
|
|
struct block_stat *blk_stat;
|
|
struct nand_chip *chip;
|
|
device_t nandbus;
|
|
int row;
|
|
|
|
nand_debug(NDBG_GEN,"%p erase block %x", nand, block);
|
|
nandbus = device_get_parent(nand);
|
|
chip = device_get_softc(nand);
|
|
|
|
if (block >= (chip->chip_geom.blks_per_lun * chip->chip_geom.luns))
|
|
return (ENXIO);
|
|
|
|
row = (block << chip->chip_geom.blk_shift) &
|
|
chip->chip_geom.blk_mask;
|
|
|
|
if (!can_write(nandbus))
|
|
return (ENXIO);
|
|
|
|
send_erase_block(nand, row, NAND_CMD_ERASE_INTLV);
|
|
|
|
DELAY(chip->t_bers);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
blk_stat = &(chip->blk_stat[block]);
|
|
blk_stat->block_erased++;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
static int
|
|
onfi_is_blk_bad(device_t device, uint32_t block_number, uint8_t *bad)
|
|
{
|
|
struct nand_chip *chip;
|
|
int page_number, i, j, err;
|
|
uint8_t *oob;
|
|
|
|
chip = device_get_softc(device);
|
|
|
|
oob = malloc(chip->chip_geom.oob_size, M_NAND, M_WAITOK);
|
|
if (!oob) {
|
|
device_printf(device, "%s: cannot allocate oob\n", __func__);
|
|
return (ENOMEM);
|
|
}
|
|
|
|
page_number = block_number * chip->chip_geom.pgs_per_blk;
|
|
*bad = 0;
|
|
/* Check OOB of first and last page */
|
|
for (i = 0; i < 2; i++, page_number+= chip->chip_geom.pgs_per_blk - 1) {
|
|
err = generic_read_oob(device, page_number, oob,
|
|
chip->chip_geom.oob_size, 0);
|
|
if (err) {
|
|
device_printf(device, "%s: cannot allocate oob\n",
|
|
__func__);
|
|
free(oob, M_NAND);
|
|
return (ENOMEM);
|
|
}
|
|
|
|
for (j = 0; j < chip->chip_geom.oob_size; j++) {
|
|
if (!oob[j]) {
|
|
*bad = 1;
|
|
free(oob, M_NAND);
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
|
|
free(oob, M_NAND);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
send_small_read_page(device_t nand, uint8_t start_command,
|
|
uint32_t row, uint32_t column)
|
|
{
|
|
device_t nandbus = device_get_parent(nand);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, start_command))
|
|
return (ENXIO);
|
|
|
|
if (nand_send_address(nand, row, column, -1))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_START_COMMAND(nandbus))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
static int
|
|
small_read_page(device_t nand, uint32_t page, void *buf, uint32_t len,
|
|
uint32_t offset)
|
|
{
|
|
struct nand_chip *chip;
|
|
struct page_stat *pg_stat;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN,"%p small read page %x[%x] at %x", nand, page, len, offset);
|
|
chip = device_get_softc(nand);
|
|
nandbus = device_get_parent(nand);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (offset < 256) {
|
|
if (send_small_read_page(nand, NAND_CMD_SMALLA, row, offset))
|
|
return (ENXIO);
|
|
} else {
|
|
offset -= 256;
|
|
if (send_small_read_page(nandbus, NAND_CMD_SMALLB, row, offset))
|
|
return (ENXIO);
|
|
}
|
|
|
|
DELAY(chip->t_r);
|
|
|
|
NANDBUS_READ_BUFFER(nandbus, buf, len);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
pg_stat = &(chip->pg_stat[page]);
|
|
pg_stat->page_raw_read++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
small_read_oob(device_t nand, uint32_t page, void *buf, uint32_t len,
|
|
uint32_t offset)
|
|
{
|
|
struct nand_chip *chip;
|
|
struct page_stat *pg_stat;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN,"%p small read oob %x[%x] at %x", nand, page, len, offset);
|
|
chip = device_get_softc(nand);
|
|
nandbus = device_get_parent(nand);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (send_small_read_page(nand, NAND_CMD_SMALLOOB, row, 0))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_r);
|
|
|
|
NANDBUS_READ_BUFFER(nandbus, buf, len);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
pg_stat = &(chip->pg_stat[page]);
|
|
pg_stat->page_raw_read++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
small_program_page(device_t nand, uint32_t page, void* buf, uint32_t len,
|
|
uint32_t offset)
|
|
{
|
|
struct nand_chip *chip;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN,"%p small prog page %x[%x] at %x", nand, page, len, offset);
|
|
chip = device_get_softc(nand);
|
|
nandbus = device_get_parent(nand);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (!can_write(nandbus))
|
|
return (ENXIO);
|
|
|
|
if (offset < 256) {
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLA))
|
|
return (ENXIO);
|
|
} else {
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLB))
|
|
return (ENXIO);
|
|
}
|
|
|
|
if (send_start_program_page(nand, row, offset))
|
|
return (ENXIO);
|
|
|
|
NANDBUS_WRITE_BUFFER(nandbus, buf, len);
|
|
|
|
if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_prog);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
small_program_oob(device_t nand, uint32_t page, void* buf, uint32_t len,
|
|
uint32_t offset)
|
|
{
|
|
struct nand_chip *chip;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN,"%p small prog oob %x[%x] at %x", nand, page, len, offset);
|
|
chip = device_get_softc(nand);
|
|
nandbus = device_get_parent(nand);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (!can_write(nandbus))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLOOB))
|
|
return (ENXIO);
|
|
|
|
if (send_start_program_page(nand, row, offset))
|
|
return (ENXIO);
|
|
|
|
NANDBUS_WRITE_BUFFER(nandbus, buf, len);
|
|
|
|
if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_prog);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nand_send_address(device_t nand, int32_t row, int32_t col, int8_t id)
|
|
{
|
|
struct nandbus_ivar *ivar;
|
|
device_t nandbus;
|
|
uint8_t addr;
|
|
int err = 0;
|
|
int i;
|
|
|
|
nandbus = device_get_parent(nand);
|
|
ivar = device_get_ivars(nand);
|
|
|
|
if (id != -1) {
|
|
nand_debug(NDBG_GEN,"send_address: send id %02x", id);
|
|
err = NANDBUS_SEND_ADDRESS(nandbus, id);
|
|
}
|
|
|
|
if (!err && col != -1) {
|
|
for (i = 0; i < ivar->cols; i++, col >>= 8) {
|
|
addr = (uint8_t)(col & 0xff);
|
|
nand_debug(NDBG_GEN,"send_address: send address column "
|
|
"%02x", addr);
|
|
err = NANDBUS_SEND_ADDRESS(nandbus, addr);
|
|
if (err)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!err && row != -1) {
|
|
for (i = 0; i < ivar->rows; i++, row >>= 8) {
|
|
addr = (uint8_t)(row & 0xff);
|
|
nand_debug(NDBG_GEN,"send_address: send address row "
|
|
"%02x", addr);
|
|
err = NANDBUS_SEND_ADDRESS(nandbus, addr);
|
|
if (err)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
|
|
static int
|
|
generic_is_blk_bad(device_t dev, uint32_t block, uint8_t *bad)
|
|
{
|
|
struct nand_chip *chip;
|
|
int page_number, err, i;
|
|
uint8_t *oob;
|
|
|
|
chip = device_get_softc(dev);
|
|
|
|
oob = malloc(chip->chip_geom.oob_size, M_NAND, M_WAITOK);
|
|
if (!oob) {
|
|
device_printf(dev, "%s: cannot allocate OOB\n", __func__);
|
|
return (ENOMEM);
|
|
}
|
|
|
|
page_number = block * chip->chip_geom.pgs_per_blk;
|
|
*bad = 0;
|
|
|
|
/* Check OOB of first and second page */
|
|
for (i = 0; i < 2; i++) {
|
|
err = NAND_READ_OOB(dev, page_number + i, oob,
|
|
chip->chip_geom.oob_size, 0);
|
|
if (err) {
|
|
device_printf(dev, "%s: cannot allocate OOB\n",
|
|
__func__);
|
|
free(oob, M_NAND);
|
|
return (ENOMEM);
|
|
}
|
|
|
|
if (!oob[0]) {
|
|
*bad = 1;
|
|
free(oob, M_NAND);
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
free(oob, M_NAND);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
generic_get_ecc(device_t dev, void *buf, void *ecc, int *needwrite)
|
|
{
|
|
struct nand_chip *chip = device_get_softc(dev);
|
|
struct chip_geom *cg = &chip->chip_geom;
|
|
|
|
return (NANDBUS_GET_ECC(device_get_parent(dev), buf, cg->page_size,
|
|
ecc, needwrite));
|
|
}
|
|
|
|
static int
|
|
generic_correct_ecc(device_t dev, void *buf, void *readecc, void *calcecc)
|
|
{
|
|
struct nand_chip *chip = device_get_softc(dev);
|
|
struct chip_geom *cg = &chip->chip_geom;
|
|
|
|
return (NANDBUS_CORRECT_ECC(device_get_parent(dev), buf,
|
|
cg->page_size, readecc, calcecc));
|
|
}
|
|
|
|
|
|
#if 0
|
|
int
|
|
nand_chng_read_col(device_t nand, uint32_t col, void *buf, size_t len)
|
|
{
|
|
struct nand_chip *chip;
|
|
device_t nandbus;
|
|
|
|
chip = device_get_softc(nand);
|
|
nandbus = device_get_parent(nand);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_SEND_ADDRESS(nandbus, -1, col, -1))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL_END))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_START_COMMAND(nandbus))
|
|
return (ENXIO);
|
|
|
|
if (buf != NULL && len > 0)
|
|
NANDBUS_READ_BUFFER(nandbus, buf, len);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nand_chng_write_col(device_t dev, uint32_t col, void *buf,
|
|
size_t len)
|
|
{
|
|
struct nand_chip *chip;
|
|
device_t nandbus;
|
|
|
|
chip = device_get_softc(dev);
|
|
nandbus = device_get_parent(dev);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_WRITE_COL))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_SEND_ADDRESS(nandbus, -1, col, -1))
|
|
return (ENXIO);
|
|
|
|
if (buf != NULL && len > 0)
|
|
NANDBUS_WRITE_BUFFER(nandbus, buf, len);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL_END))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_START_COMMAND(nandbus))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nand_copyback_read(device_t dev, uint32_t page, uint32_t col,
|
|
void *buf, size_t len)
|
|
{
|
|
struct nand_chip *chip;
|
|
struct page_stat *pg_stat;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN," raw read page %x[%x] at %x", page, col, len);
|
|
chip = device_get_softc(dev);
|
|
nandbus = device_get_parent(dev);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_CPBK, row, 0))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_r);
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
if (buf != NULL && len > 0)
|
|
NANDBUS_READ_BUFFER(nandbus, buf, len);
|
|
|
|
pg_stat = &(chip->pg_stat[page]);
|
|
pg_stat->page_raw_read++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nand_copyback_prog(device_t dev, uint32_t page, uint32_t col,
|
|
void *buf, size_t len)
|
|
{
|
|
struct nand_chip *chip;
|
|
struct page_stat *pg_stat;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN,"copyback prog page %x[%x]", page, len);
|
|
chip = device_get_softc(dev);
|
|
nandbus = device_get_parent(dev);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (!can_write(nandbus))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_WRITE_COL))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_SEND_ADDRESS(nandbus, row, col, -1))
|
|
return (ENXIO);
|
|
|
|
if (buf != NULL && len > 0)
|
|
NANDBUS_WRITE_BUFFER(nandbus, buf, len);
|
|
|
|
if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_prog);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
pg_stat = &(chip->pg_stat[page]);
|
|
pg_stat->page_raw_written++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nand_copyback_prog_intlv(device_t dev, uint32_t page)
|
|
{
|
|
struct nand_chip *chip;
|
|
struct page_stat *pg_stat;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
|
|
nand_debug(NDBG_GEN,"cache prog page %x", page);
|
|
chip = device_get_softc(dev);
|
|
nandbus = device_get_parent(dev);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (!can_write(nandbus))
|
|
return (ENXIO);
|
|
|
|
if (send_start_program_page(nand, row, 0))
|
|
return (ENXIO);
|
|
|
|
if (send_end_program_page(nandbus, NAND_CMD_PROG_INTLV))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_prog);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
pg_stat = &(chip->pg_stat[page]);
|
|
pg_stat->page_raw_written++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nand_prog_cache(device_t dev, uint32_t page, uint32_t col,
|
|
void *buf, size_t len, uint8_t end)
|
|
{
|
|
struct nand_chip *chip;
|
|
struct page_stat *pg_stat;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
uint8_t command;
|
|
|
|
nand_debug(NDBG_GEN,"cache prog page %x[%x]", page, len);
|
|
chip = device_get_softc(dev);
|
|
nandbus = device_get_parent(dev);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (!can_write(nandbus))
|
|
return (ENXIO);
|
|
|
|
if (send_start_program_page(dev, row, 0))
|
|
return (ENXIO);
|
|
|
|
NANDBUS_WRITE_BUFFER(nandbus, buf, len);
|
|
|
|
if (end)
|
|
command = NAND_CMD_PROG_END;
|
|
else
|
|
command = NAND_CMD_PROG_CACHE;
|
|
|
|
if (send_end_program_page(nandbus, command))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_prog);
|
|
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
pg_stat = &(chip->pg_stat[page]);
|
|
pg_stat->page_raw_written++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nand_read_cache(device_t dev, uint32_t page, uint32_t col,
|
|
void *buf, size_t len, uint8_t end)
|
|
{
|
|
struct nand_chip *chip;
|
|
struct page_stat *pg_stat;
|
|
device_t nandbus;
|
|
uint32_t row;
|
|
uint8_t command;
|
|
|
|
nand_debug(NDBG_GEN,"cache read page %x[%x] ", page, len);
|
|
chip = device_get_softc(dev);
|
|
nandbus = device_get_parent(dev);
|
|
|
|
if (nand_check_page_boundary(chip, page))
|
|
return (ENXIO);
|
|
|
|
page_to_row(&chip->chip_geom, page, &row);
|
|
|
|
if (page != -1) {
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_READ))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_SEND_ADDRESS(nandbus, row, col, -1))
|
|
return (ENXIO);
|
|
}
|
|
|
|
if (end)
|
|
command = NAND_CMD_READ_CACHE_END;
|
|
else
|
|
command = NAND_CMD_READ_CACHE;
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, command))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_START_COMMAND(nandbus))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_r);
|
|
if (check_fail(nandbus))
|
|
return (ENXIO);
|
|
|
|
if (buf != NULL && len > 0)
|
|
NANDBUS_READ_BUFFER(nandbus, buf, len);
|
|
|
|
pg_stat = &(chip->pg_stat[page]);
|
|
pg_stat->page_raw_read++;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nand_get_feature(device_t dev, uint8_t feat, void *buf)
|
|
{
|
|
struct nand_chip *chip;
|
|
device_t nandbus;
|
|
|
|
nand_debug(NDBG_GEN,"nand get feature");
|
|
|
|
chip = device_get_softc(dev);
|
|
nandbus = device_get_parent(dev);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_GET_FEATURE))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_SEND_ADDRESS(nandbus, -1, -1, feat))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_START_COMMAND(nandbus))
|
|
return (ENXIO);
|
|
|
|
DELAY(chip->t_r);
|
|
NANDBUS_READ_BUFFER(nandbus, buf, 4);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
nand_set_feature(device_t dev, uint8_t feat, void *buf)
|
|
{
|
|
struct nand_chip *chip;
|
|
device_t nandbus;
|
|
|
|
nand_debug(NDBG_GEN,"nand set feature");
|
|
|
|
chip = device_get_softc(dev);
|
|
nandbus = device_get_parent(dev);
|
|
|
|
if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SET_FEATURE))
|
|
return (ENXIO);
|
|
|
|
if (NANDBUS_SEND_ADDRESS(nandbus, -1, -1, feat))
|
|
return (ENXIO);
|
|
|
|
NANDBUS_WRITE_BUFFER(nandbus, buf, 4);
|
|
|
|
if (NANDBUS_START_COMMAND(nandbus))
|
|
return (ENXIO);
|
|
|
|
return (0);
|
|
}
|
|
#endif
|