718cf2ccb9
Mainly focus on files that use BSD 2-Clause license, however the tool I was using misidentified many licenses so this was mostly a manual - error prone - task. The Software Package Data Exchange (SPDX) group provides a specification to make it easier for automated tools to detect and summarize well known opensource licenses. We are gradually adopting the specification, noting that the tags are considered only advisory and do not, in any way, superceed or replace the license texts.
1367 lines
30 KiB
C
1367 lines
30 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* 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_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 = le16dec(¶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);
|
|
|
|
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);
|
|
|
|
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
|