Add a driver for the Freescale FCM module in the localbus controller.
This driver does not yet handle multiple chip selects properly. Note that the NAND infrastructure does not perform full page reads or writes, which means that this driver cannot make use of the hardware ECC that is otherwise present.
This commit is contained in:
parent
23fbc06bfc
commit
7c45c9e4b5
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=238046
@ -35,6 +35,7 @@ dev/iicbus/ad7417.c optional ad7417 powermac
|
||||
dev/iicbus/ds1775.c optional ds1775 powermac
|
||||
dev/iicbus/max6690.c optional max6690 powermac
|
||||
dev/kbd/kbd.c optional sc
|
||||
dev/nand/nfc_fsl.c optional nand mpc85xx
|
||||
dev/ofw/openfirm.c optional aim | fdt
|
||||
dev/ofw/openfirmio.c optional aim | fdt
|
||||
dev/ofw/ofw_bus_if.m optional aim | fdt
|
||||
|
716
sys/dev/nand/nfc_fsl.c
Normal file
716
sys/dev/nand/nfc_fsl.c
Normal file
@ -0,0 +1,716 @@
|
||||
/*-
|
||||
* Copyright (C) 2012 Juniper Networks, Inc.
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* TODO :
|
||||
*
|
||||
* -- test support for small pages
|
||||
* -- support for reading ONFI parameters
|
||||
* -- support for cached and interleaving commands
|
||||
* -- proper setting of AL bits in FMR
|
||||
*/
|
||||
|
||||
#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/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/kdb.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/fdt.h>
|
||||
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
|
||||
#include <powerpc/mpc85xx/lbc.h>
|
||||
|
||||
#include <dev/nand/nand.h>
|
||||
#include <dev/nand/nandbus.h>
|
||||
|
||||
#include "nfc_fsl.h"
|
||||
|
||||
#include "nfc_if.h"
|
||||
|
||||
#define LBC_READ(regname) lbc_read_reg(dev, (LBC85XX_ ## regname))
|
||||
#define LBC_WRITE(regname, val) lbc_write_reg(dev, (LBC85XX_ ## regname), val)
|
||||
|
||||
enum addr_type {
|
||||
ADDR_NONE,
|
||||
ADDR_ID,
|
||||
ADDR_ROW,
|
||||
ADDR_ROWCOL
|
||||
};
|
||||
|
||||
struct fsl_nfc_fcm {
|
||||
/* Read-only after initialization */
|
||||
uint32_t reg_fmr;
|
||||
|
||||
/* To be preserved across "start_command" */
|
||||
u_int buf_ofs;
|
||||
u_int read_ptr;
|
||||
u_int status:1;
|
||||
|
||||
/* Command state -- cleared by "start_command" */
|
||||
uint32_t fcm_startzero;
|
||||
uint32_t reg_fcr;
|
||||
uint32_t reg_fir;
|
||||
uint32_t reg_mdr;
|
||||
uint32_t reg_fbcr;
|
||||
uint32_t reg_fbar;
|
||||
uint32_t reg_fpar;
|
||||
u_int cmdnr;
|
||||
u_int opnr;
|
||||
u_int pg_ofs;
|
||||
enum addr_type addr_type;
|
||||
u_int addr_bytes;
|
||||
u_int row_addr;
|
||||
u_int column_addr;
|
||||
u_int data_fir:8;
|
||||
uint32_t fcm_endzero;
|
||||
};
|
||||
|
||||
struct fsl_nand_softc {
|
||||
struct nand_softc nand_dev;
|
||||
device_t dev;
|
||||
struct resource *res;
|
||||
int rid; /* Resourceid */
|
||||
struct lbc_devinfo *dinfo;
|
||||
struct fsl_nfc_fcm fcm;
|
||||
uint8_t col_cycles;
|
||||
uint8_t row_cycles;
|
||||
uint16_t pgsz; /* Page size */
|
||||
};
|
||||
|
||||
static int fsl_nand_attach(device_t dev);
|
||||
static int fsl_nand_probe(device_t dev);
|
||||
static int fsl_nand_detach(device_t dev);
|
||||
|
||||
static int fsl_nfc_select_cs(device_t dev, uint8_t cs);
|
||||
static int fsl_nfc_read_rnb(device_t dev);
|
||||
static int fsl_nfc_send_command(device_t dev, uint8_t command);
|
||||
static int fsl_nfc_send_address(device_t dev, uint8_t address);
|
||||
static uint8_t fsl_nfc_read_byte(device_t dev);
|
||||
static int fsl_nfc_start_command(device_t dev);
|
||||
static void fsl_nfc_read_buf(device_t dev, void *buf, uint32_t len);
|
||||
static void fsl_nfc_write_buf(device_t dev, void *buf, uint32_t len);
|
||||
|
||||
static device_method_t fsl_nand_methods[] = {
|
||||
DEVMETHOD(device_probe, fsl_nand_probe),
|
||||
DEVMETHOD(device_attach, fsl_nand_attach),
|
||||
DEVMETHOD(device_detach, fsl_nand_detach),
|
||||
|
||||
DEVMETHOD(nfc_select_cs, fsl_nfc_select_cs),
|
||||
DEVMETHOD(nfc_read_rnb, fsl_nfc_read_rnb),
|
||||
DEVMETHOD(nfc_start_command, fsl_nfc_start_command),
|
||||
DEVMETHOD(nfc_send_command, fsl_nfc_send_command),
|
||||
DEVMETHOD(nfc_send_address, fsl_nfc_send_address),
|
||||
DEVMETHOD(nfc_read_byte, fsl_nfc_read_byte),
|
||||
DEVMETHOD(nfc_read_buf, fsl_nfc_read_buf),
|
||||
DEVMETHOD(nfc_write_buf, fsl_nfc_write_buf),
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static driver_t fsl_nand_driver = {
|
||||
"nand",
|
||||
fsl_nand_methods,
|
||||
sizeof(struct fsl_nand_softc),
|
||||
};
|
||||
|
||||
static devclass_t fsl_nand_devclass;
|
||||
|
||||
DRIVER_MODULE(fsl_nand, lbc, fsl_nand_driver, fsl_nand_devclass,
|
||||
0, 0);
|
||||
|
||||
static int fsl_nand_build_address(device_t dev, uint32_t page, uint32_t column);
|
||||
static int fsl_nand_chip_preprobe(device_t dev, struct nand_id *id);
|
||||
|
||||
#ifdef NAND_DEBUG_TIMING
|
||||
static device_t fcm_devs[8];
|
||||
#endif
|
||||
|
||||
#define CMD_SHIFT(cmd_num) (24 - ((cmd_num) * 8))
|
||||
#define OP_SHIFT(op_num) (28 - ((op_num) * 4))
|
||||
|
||||
#define FSL_LARGE_PAGE_SIZE (2112)
|
||||
#define FSL_SMALL_PAGE_SIZE (528)
|
||||
|
||||
static void
|
||||
fsl_nand_init_regs(struct fsl_nand_softc *sc)
|
||||
{
|
||||
uint32_t or_v, br_v;
|
||||
device_t dev;
|
||||
|
||||
dev = sc->dev;
|
||||
|
||||
sc->fcm.reg_fmr = (15 << FMR_CWTO_SHIFT);
|
||||
|
||||
/*
|
||||
* Setup 4 row cycles and hope that chip ignores superfluous address
|
||||
* bytes.
|
||||
*/
|
||||
sc->fcm.reg_fmr |= (2 << FMR_AL_SHIFT);
|
||||
|
||||
/* Reprogram BR(x) */
|
||||
br_v = lbc_read_reg(dev, LBC85XX_BR(sc->dinfo->di_bank));
|
||||
br_v &= 0xffff8000;
|
||||
br_v |= 1 << 11; /* 8-bit port size */
|
||||
br_v |= 0 << 9; /* No ECC checking and generation */
|
||||
br_v |= 1 << 5; /* FCM machine */
|
||||
br_v |= 1; /* Valid */
|
||||
lbc_write_reg(dev, LBC85XX_BR(sc->dinfo->di_bank), br_v);
|
||||
|
||||
/* Reprogram OR(x) */
|
||||
or_v = lbc_read_reg(dev, LBC85XX_OR(sc->dinfo->di_bank));
|
||||
or_v &= 0xfffffc00;
|
||||
or_v |= 0x03AE; /* Default POR timing */
|
||||
lbc_write_reg(dev, LBC85XX_OR(sc->dinfo->di_bank), or_v);
|
||||
|
||||
if (or_v & OR_FCM_PAGESIZE) {
|
||||
sc->pgsz = FSL_LARGE_PAGE_SIZE;
|
||||
sc->col_cycles = 2;
|
||||
nand_debug(NDBG_DRV, "%s: large page NAND device at #%d",
|
||||
device_get_nameunit(dev), sc->dinfo->di_bank);
|
||||
} else {
|
||||
sc->pgsz = FSL_SMALL_PAGE_SIZE;
|
||||
sc->col_cycles = 1;
|
||||
nand_debug(NDBG_DRV, "%s: small page NAND device at #%d",
|
||||
device_get_nameunit(dev), sc->dinfo->di_bank);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_nand_probe(device_t dev)
|
||||
{
|
||||
|
||||
if (!ofw_bus_is_compatible(dev, "fsl,elbc-fcm-nand"))
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "Freescale localbus FCM Controller");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_nand_attach(device_t dev)
|
||||
{
|
||||
struct fsl_nand_softc *sc;
|
||||
struct nand_id id;
|
||||
struct nand_params *param;
|
||||
uint32_t num_pages;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->dev = dev;
|
||||
sc->dinfo = device_get_ivars(dev);
|
||||
|
||||
sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid,
|
||||
RF_ACTIVE);
|
||||
if (sc->res == NULL) {
|
||||
device_printf(dev, "could not allocate resources!\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
bzero(&sc->fcm, sizeof(sc->fcm));
|
||||
|
||||
/* Init register and check if HW ECC turned on */
|
||||
fsl_nand_init_regs(sc);
|
||||
|
||||
/* Chip is probed, so determine number of row address cycles */
|
||||
fsl_nand_chip_preprobe(dev, &id);
|
||||
param = nand_get_params(&id);
|
||||
if (param != NULL) {
|
||||
num_pages = (param->chip_size << 20) / param->page_size;
|
||||
while(num_pages) {
|
||||
sc->row_cycles++;
|
||||
num_pages >>= 8;
|
||||
}
|
||||
|
||||
sc->fcm.reg_fmr &= ~(FMR_AL);
|
||||
sc->fcm.reg_fmr |= (sc->row_cycles - 2) << FMR_AL_SHIFT;
|
||||
}
|
||||
|
||||
nand_init(&sc->nand_dev, dev, NAND_ECC_SOFT, 0, 0, NULL, NULL);
|
||||
|
||||
#ifdef NAND_DEBUG_TIMING
|
||||
fcm_devs[sc->dinfo->di_bank] = dev;
|
||||
#endif
|
||||
|
||||
return (nandbus_create(dev));
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_nand_detach(device_t dev)
|
||||
{
|
||||
struct fsl_nand_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
if (sc->res != NULL)
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_nfc_select_cs(device_t dev, uint8_t cs)
|
||||
{
|
||||
|
||||
// device_printf(dev, "%s(cs=%u)\n", __func__, cs);
|
||||
return ((cs > 0) ? EINVAL : 0);
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_nfc_read_rnb(device_t dev)
|
||||
{
|
||||
|
||||
// device_printf(dev, "%s()\n", __func__);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_nfc_send_command(device_t dev, uint8_t command)
|
||||
{
|
||||
struct fsl_nand_softc *sc;
|
||||
struct fsl_nfc_fcm *fcm;
|
||||
uint8_t fir_op;
|
||||
|
||||
// device_printf(dev, "%s(command=%u)\n", __func__, command);
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
fcm = &sc->fcm;
|
||||
|
||||
if (command == NAND_CMD_PROG_END) {
|
||||
fcm->reg_fir |= (FIR_OP_WB << OP_SHIFT(fcm->opnr));
|
||||
fcm->opnr++;
|
||||
}
|
||||
fcm->reg_fcr |= command << CMD_SHIFT(fcm->cmdnr);
|
||||
fir_op = (fcm->cmdnr == 0) ? FIR_OP_CW0 : FIR_OP_CM(fcm->cmdnr);
|
||||
fcm->cmdnr++;
|
||||
|
||||
fcm->reg_fir |= (fir_op << OP_SHIFT(fcm->opnr));
|
||||
fcm->opnr++;
|
||||
|
||||
switch (command) {
|
||||
case NAND_CMD_READ_ID:
|
||||
fcm->data_fir = FIR_OP_RBW;
|
||||
fcm->addr_type = ADDR_ID;
|
||||
break;
|
||||
case NAND_CMD_SMALLOOB:
|
||||
fcm->pg_ofs += 256;
|
||||
/*FALLTHROUGH*/
|
||||
case NAND_CMD_SMALLB:
|
||||
fcm->pg_ofs += 256;
|
||||
/*FALLTHROUGH*/
|
||||
case NAND_CMD_READ: /* NAND_CMD_SMALLA */
|
||||
fcm->data_fir = FIR_OP_RBW;
|
||||
fcm->addr_type = ADDR_ROWCOL;
|
||||
break;
|
||||
case NAND_CMD_STATUS:
|
||||
fcm->data_fir = FIR_OP_RS;
|
||||
fcm->status = 1;
|
||||
break;
|
||||
case NAND_CMD_ERASE:
|
||||
fcm->addr_type = ADDR_ROW;
|
||||
break;
|
||||
case NAND_CMD_PROG:
|
||||
fcm->addr_type = ADDR_ROWCOL;
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_nfc_send_address(device_t dev, uint8_t addr)
|
||||
{
|
||||
struct fsl_nand_softc *sc;
|
||||
struct fsl_nfc_fcm *fcm;
|
||||
uint32_t addr_bits;
|
||||
|
||||
// device_printf(dev, "%s(address=%u)\n", __func__, addr);
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
fcm = &sc->fcm;
|
||||
|
||||
KASSERT(fcm->addr_type != ADDR_NONE,
|
||||
("controller doesn't expect address cycle"));
|
||||
|
||||
addr_bits = addr;
|
||||
|
||||
if (fcm->addr_type == ADDR_ID) {
|
||||
fcm->reg_fir |= (FIR_OP_UA << OP_SHIFT(fcm->opnr));
|
||||
fcm->opnr++;
|
||||
|
||||
fcm->reg_fbcr = 5;
|
||||
fcm->reg_fbar = 0;
|
||||
fcm->reg_fpar = 0;
|
||||
fcm->reg_mdr = addr_bits;
|
||||
fcm->buf_ofs = 0;
|
||||
fcm->read_ptr = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (fcm->addr_type == ADDR_ROW) {
|
||||
addr_bits <<= fcm->addr_bytes * 8;
|
||||
fcm->row_addr |= addr_bits;
|
||||
fcm->addr_bytes++;
|
||||
if (fcm->addr_bytes < sc->row_cycles)
|
||||
return (0);
|
||||
} else {
|
||||
if (fcm->addr_bytes < sc->col_cycles) {
|
||||
addr_bits <<= fcm->addr_bytes * 8;
|
||||
fcm->column_addr |= addr_bits;
|
||||
} else {
|
||||
addr_bits <<= (fcm->addr_bytes - sc->col_cycles) * 8;
|
||||
fcm->row_addr |= addr_bits;
|
||||
}
|
||||
fcm->addr_bytes++;
|
||||
if (fcm->addr_bytes < (sc->row_cycles + sc->col_cycles))
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (fsl_nand_build_address(dev, fcm->row_addr, fcm->column_addr));
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_nand_build_address(device_t dev, uint32_t row, uint32_t column)
|
||||
{
|
||||
struct fsl_nand_softc *sc;
|
||||
struct fsl_nfc_fcm *fcm;
|
||||
uint32_t byte_count = 0;
|
||||
uint32_t block_address = 0;
|
||||
uint32_t page_address = 0;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
fcm = &sc->fcm;
|
||||
|
||||
fcm->read_ptr = 0;
|
||||
fcm->buf_ofs = 0;
|
||||
|
||||
if (fcm->addr_type == ADDR_ROWCOL) {
|
||||
fcm->reg_fir |= (FIR_OP_CA << OP_SHIFT(fcm->opnr));
|
||||
fcm->opnr++;
|
||||
|
||||
column += fcm->pg_ofs;
|
||||
fcm->pg_ofs = 0;
|
||||
|
||||
page_address |= column;
|
||||
|
||||
if (column != 0) {
|
||||
byte_count = sc->pgsz - column;
|
||||
fcm->read_ptr = column;
|
||||
}
|
||||
}
|
||||
|
||||
fcm->reg_fir |= (FIR_OP_PA << OP_SHIFT(fcm->opnr));
|
||||
fcm->opnr++;
|
||||
|
||||
if (sc->pgsz == FSL_LARGE_PAGE_SIZE) {
|
||||
block_address = row >> 6;
|
||||
page_address |= ((row << FPAR_LP_PI_SHIFT) & FPAR_LP_PI);
|
||||
fcm->buf_ofs = (row & 1) * 4096;
|
||||
} else {
|
||||
block_address = row >> 5;
|
||||
page_address |= ((row << FPAR_SP_PI_SHIFT) & FPAR_SP_PI);
|
||||
fcm->buf_ofs = (row & 7) * 1024;
|
||||
}
|
||||
|
||||
fcm->reg_fbcr = byte_count;
|
||||
fcm->reg_fbar = block_address;
|
||||
fcm->reg_fpar = page_address;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_nfc_start_command(device_t dev)
|
||||
{
|
||||
struct fsl_nand_softc *sc;
|
||||
struct fsl_nfc_fcm *fcm;
|
||||
uint32_t fmr, ltesr_v;
|
||||
int error, timeout;
|
||||
|
||||
// device_printf(dev, "%s()\n", __func__);
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
fcm = &sc->fcm;
|
||||
|
||||
fmr = fcm->reg_fmr | FMR_OP;
|
||||
|
||||
if (fcm->data_fir)
|
||||
fcm->reg_fir |= (fcm->data_fir << OP_SHIFT(fcm->opnr));
|
||||
|
||||
LBC_WRITE(FIR, fcm->reg_fir);
|
||||
LBC_WRITE(FCR, fcm->reg_fcr);
|
||||
|
||||
LBC_WRITE(FMR, fmr);
|
||||
|
||||
LBC_WRITE(FBCR, fcm->reg_fbcr);
|
||||
LBC_WRITE(FBAR, fcm->reg_fbar);
|
||||
LBC_WRITE(FPAR, fcm->reg_fpar);
|
||||
|
||||
if (fcm->addr_type == ADDR_ID)
|
||||
LBC_WRITE(MDR, fcm->reg_mdr);
|
||||
|
||||
nand_debug(NDBG_DRV, "BEFORE:\nFMR=%#x, FIR=%#x, FCR=%#x", fmr,
|
||||
fcm->reg_fir, fcm->reg_fcr);
|
||||
nand_debug(NDBG_DRV, "MDR=%#x, FBAR=%#x, FPAR=%#x, FBCR=%#x",
|
||||
LBC_READ(MDR), fcm->reg_fbar, fcm->reg_fpar, fcm->reg_fbcr);
|
||||
|
||||
LBC_WRITE(LSOR, sc->dinfo->di_bank);
|
||||
|
||||
timeout = (cold) ? FSL_FCM_WAIT_TIMEOUT : ~0;
|
||||
error = 0;
|
||||
ltesr_v = LBC_READ(LTESR);
|
||||
while (!error && (ltesr_v & LTESR_CC) == 0) {
|
||||
if (cold) {
|
||||
DELAY(1000);
|
||||
timeout--;
|
||||
if (timeout < 0)
|
||||
error = EWOULDBLOCK;
|
||||
} else
|
||||
error = tsleep(device_get_parent(sc->dev), PRIBIO,
|
||||
"nfcfsl", hz);
|
||||
ltesr_v = LBC_READ(LTESR);
|
||||
}
|
||||
if (error)
|
||||
nand_debug(NDBG_DRV, "Command complete wait timeout\n");
|
||||
|
||||
nand_debug(NDBG_DRV, "AFTER:\nLTESR=%#x, LTEDR=%#x, LTEIR=%#x,"
|
||||
" LTEATR=%#x, LTEAR=%#x, LTECCR=%#x", ltesr_v,
|
||||
LBC_READ(LTEDR), LBC_READ(LTEIR), LBC_READ(LTEATR),
|
||||
LBC_READ(LTEAR), LBC_READ(LTECCR));
|
||||
|
||||
bzero(&fcm->fcm_startzero,
|
||||
__rangeof(struct fsl_nfc_fcm, fcm_startzero, fcm_endzero));
|
||||
|
||||
if (fcm->status)
|
||||
sc->fcm.reg_mdr = LBC_READ(MDR);
|
||||
|
||||
/* Even if timeout occured, we should perform steps below */
|
||||
LBC_WRITE(LTESR, ltesr_v);
|
||||
LBC_WRITE(LTEATR, 0);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
fsl_nfc_read_byte(device_t dev)
|
||||
{
|
||||
struct fsl_nand_softc *sc = device_get_softc(dev);
|
||||
uint32_t offset;
|
||||
|
||||
// device_printf(dev, "%s()\n", __func__);
|
||||
|
||||
/*
|
||||
* LBC controller allows us to read status into a MDR instead of FCM
|
||||
* buffer. If last operation requested before read_byte() was STATUS,
|
||||
* then return MDR instead of reading a single byte from a buffer.
|
||||
*/
|
||||
if (sc->fcm.status) {
|
||||
sc->fcm.status = 0;
|
||||
return (sc->fcm.reg_mdr);
|
||||
}
|
||||
|
||||
KASSERT(sc->fcm.read_ptr < sc->pgsz,
|
||||
("Attempt to read beyond buffer %x %x", sc->fcm.read_ptr,
|
||||
sc->pgsz));
|
||||
|
||||
offset = sc->fcm.buf_ofs + sc->fcm.read_ptr;
|
||||
sc->fcm.read_ptr++;
|
||||
return (bus_read_1(sc->res, offset));
|
||||
}
|
||||
|
||||
static void
|
||||
fsl_nfc_read_buf(device_t dev, void *buf, uint32_t len)
|
||||
{
|
||||
struct fsl_nand_softc *sc = device_get_softc(dev);
|
||||
uint32_t offset;
|
||||
int bytesleft = 0;
|
||||
|
||||
// device_printf(dev, "%s(buf=%p, len=%u)\n", __func__, buf, len);
|
||||
|
||||
nand_debug(NDBG_DRV, "REQUEST OF 0x%0x B (BIB=0x%0x, NTR=0x%0x)",
|
||||
len, sc->pgsz, sc->fcm.read_ptr);
|
||||
|
||||
bytesleft = MIN((unsigned int)len, sc->pgsz - sc->fcm.read_ptr);
|
||||
|
||||
offset = sc->fcm.buf_ofs + sc->fcm.read_ptr;
|
||||
bus_read_region_1(sc->res, offset, buf, bytesleft);
|
||||
sc->fcm.read_ptr += bytesleft;
|
||||
}
|
||||
|
||||
static void
|
||||
fsl_nfc_write_buf(device_t dev, void *buf, uint32_t len)
|
||||
{
|
||||
struct fsl_nand_softc *sc = device_get_softc(dev);
|
||||
uint32_t offset;
|
||||
int bytesleft = 0;
|
||||
|
||||
// device_printf(dev, "%s(buf=%p, len=%u)\n", __func__, buf, len);
|
||||
|
||||
KASSERT(len <= sc->pgsz - sc->fcm.read_ptr,
|
||||
("Attempt to write beyond buffer"));
|
||||
|
||||
bytesleft = MIN((unsigned int)len, sc->pgsz - sc->fcm.read_ptr);
|
||||
|
||||
nand_debug(NDBG_DRV, "REQUEST TO WRITE 0x%0x (BIB=0x%0x, NTR=0x%0x)",
|
||||
bytesleft, sc->pgsz, sc->fcm.read_ptr);
|
||||
|
||||
offset = sc->fcm.buf_ofs + sc->fcm.read_ptr;
|
||||
bus_write_region_1(sc->res, offset, buf, bytesleft);
|
||||
sc->fcm.read_ptr += bytesleft;
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_nand_chip_preprobe(device_t dev, struct nand_id *id)
|
||||
{
|
||||
|
||||
if (fsl_nfc_send_command(dev, NAND_CMD_RESET) != 0)
|
||||
return (ENXIO);
|
||||
|
||||
if (fsl_nfc_start_command(dev) != 0)
|
||||
return (ENXIO);
|
||||
|
||||
DELAY(1000);
|
||||
|
||||
if (fsl_nfc_send_command(dev, NAND_CMD_READ_ID))
|
||||
return (ENXIO);
|
||||
|
||||
if (fsl_nfc_send_address(dev, 0))
|
||||
return (ENXIO);
|
||||
|
||||
if (fsl_nfc_start_command(dev) != 0)
|
||||
return (ENXIO);
|
||||
|
||||
DELAY(25);
|
||||
|
||||
id->man_id = fsl_nfc_read_byte(dev);
|
||||
id->dev_id = fsl_nfc_read_byte(dev);
|
||||
|
||||
nand_debug(NDBG_DRV, "manufacturer id: %x chip id: %x",
|
||||
id->man_id, id->dev_id);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef NAND_DEBUG_TIMING
|
||||
|
||||
static SYSCTL_NODE(_debug, OID_AUTO, fcm, CTLFLAG_RD, 0, "FCM timing");
|
||||
|
||||
static u_int csct = 1; /* 22: Chip select to command time (trlx). */
|
||||
SYSCTL_UINT(_debug_fcm, OID_AUTO, csct, CTLFLAG_RW, &csct, 1,
|
||||
"Chip select to command time: determines how far in advance -LCSn is "
|
||||
"asserted prior to any bus activity during a NAND Flash access handled "
|
||||
"by the FCM. This helps meet chip-select setup times for slow memories.");
|
||||
|
||||
static u_int cst = 1; /* 23: Command setup time (trlx). */
|
||||
SYSCTL_UINT(_debug_fcm, OID_AUTO, cst, CTLFLAG_RW, &cst, 1,
|
||||
"Command setup time: determines the delay of -LFWE assertion relative to "
|
||||
"the command, address, or data change when the external memory access "
|
||||
"is handled by the FCM.");
|
||||
|
||||
static u_int cht = 1; /* 24: Command hold time (trlx). */
|
||||
SYSCTL_UINT(_debug_fcm, OID_AUTO, cht, CTLFLAG_RW, &cht, 1,
|
||||
"Command hold time: determines the -LFWE negation prior to the command, "
|
||||
"address, or data change when the external memory access is handled by "
|
||||
"the FCM.");
|
||||
|
||||
static u_int scy = 2; /* 25-27: Cycle length in bus clocks */
|
||||
SYSCTL_UINT(_debug_fcm, OID_AUTO, scy, CTLFLAG_RW, &scy, 2,
|
||||
"Cycle length in bus clocks: see RM");
|
||||
|
||||
static u_int rst = 1; /* 28: Read setup time (trlx). */
|
||||
SYSCTL_UINT(_debug_fcm, OID_AUTO, rst, CTLFLAG_RW, &rst, 1,
|
||||
"Read setup time: determines the delay of -LFRE assertion relative to "
|
||||
"sampling of read data when the external memory access is handled by "
|
||||
"the FCM.");
|
||||
|
||||
static u_int trlx = 1; /* 29: Timing relaxed. */
|
||||
SYSCTL_UINT(_debug_fcm, OID_AUTO, trlx, CTLFLAG_RW, &trlx, 1,
|
||||
"Timing relaxed: modifies the settings of timing parameters for slow "
|
||||
"memories. See RM");
|
||||
|
||||
static u_int ehtr = 1; /* 30: Extended hold time on read accesses. */
|
||||
SYSCTL_UINT(_debug_fcm, OID_AUTO, ehtr, CTLFLAG_RW, &ehtr, 1,
|
||||
"Extended hold time on read accesses: indicates with TRLX how many "
|
||||
"cycles are inserted between a read access from the current bank and "
|
||||
"the next access.");
|
||||
|
||||
static u_int
|
||||
fsl_nand_get_timing(void)
|
||||
{
|
||||
u_int timing;
|
||||
|
||||
timing = ((csct & 1) << 9) | ((cst & 1) << 8) | ((cht & 1) << 7) |
|
||||
((scy & 7) << 4) | ((rst & 1) << 3) | ((trlx & 1) << 2) |
|
||||
((ehtr & 1) << 1);
|
||||
|
||||
printf("nfc_fsl: timing = %u\n", timing);
|
||||
return (timing);
|
||||
}
|
||||
|
||||
static int
|
||||
fsl_sysctl_program(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct fsl_nand_softc *sc;
|
||||
int error, i;
|
||||
device_t dev;
|
||||
uint32_t or_v;
|
||||
|
||||
error = sysctl_wire_old_buffer(req, sizeof(int));
|
||||
if (error == 0) {
|
||||
i = 0;
|
||||
error = sysctl_handle_int(oidp, &i, 0, req);
|
||||
}
|
||||
if (error != 0 || req->newptr == NULL)
|
||||
return (error);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
dev = fcm_devs[i];
|
||||
if (dev == NULL)
|
||||
continue;
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
/* Reprogram OR(x) */
|
||||
or_v = lbc_read_reg(dev, LBC85XX_OR(sc->dinfo->di_bank));
|
||||
or_v &= 0xfffffc00;
|
||||
or_v |= fsl_nand_get_timing();
|
||||
lbc_write_reg(dev, LBC85XX_OR(sc->dinfo->di_bank), or_v);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
SYSCTL_PROC(_debug_fcm, OID_AUTO, program, CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
|
||||
fsl_sysctl_program, "I", "write to program FCM with current values");
|
||||
|
||||
#endif /* NAND_DEBUG_TIMING */
|
97
sys/dev/nand/nfc_fsl.h
Normal file
97
sys/dev/nand/nfc_fsl.h
Normal file
@ -0,0 +1,97 @@
|
||||
/*-
|
||||
* Copyright (C) 2012 Juniper Networks, Inc.
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _NAND_NFC_FSL_H_
|
||||
#define _NAND_NFC_FSL_H_
|
||||
|
||||
/* LBC BR/OR Registers layout definitions */
|
||||
#define BR_V 0x00000001
|
||||
#define BR_V_SHIFT 0
|
||||
#define BR_MSEL 0x000000E0
|
||||
#define BR_MSEL_SHIFT 5
|
||||
#define BR_DECC_CHECK_MODE 0x00000600
|
||||
#define BR_DECC_CHECK_GEN 0x00000400
|
||||
|
||||
#define OR_FCM_PAGESIZE 0x00000400
|
||||
|
||||
/* Options definitions */
|
||||
#define NAND_OPT_ECC_MODE_HW 1
|
||||
#define NAND_OPT_ECC_MODE_SOFT (1 << 1)
|
||||
|
||||
/* FMR - Flash Mode Register */
|
||||
#define FMR_CWTO 0xF000
|
||||
#define FMR_CWTO_SHIFT 12
|
||||
#define FMR_BOOT 0x0800
|
||||
#define FMR_ECCM 0x0100
|
||||
#define FMR_AL 0x0030
|
||||
#define FMR_AL_SHIFT 4
|
||||
#define FMR_OP 0x0003
|
||||
#define FMR_OP_SHIFT 0
|
||||
|
||||
#define FIR_OP_NOP 0x0 /* No operation and end of sequence */
|
||||
#define FIR_OP_CA 0x1 /* Issue current column address */
|
||||
#define FIR_OP_PA 0x2 /* Issue current block+page address */
|
||||
#define FIR_OP_UA 0x3 /* Issue user defined address */
|
||||
#define FIR_OP_CM(x) (4 + (x)) /* Issue command from FCR[CMD(x)] */
|
||||
#define FIR_OP_WB 0x8 /* Write FBCR bytes from FCM buffer */
|
||||
#define FIR_OP_WS 0x9 /* Write 1 or 2 bytes from MDR[AS] */
|
||||
#define FIR_OP_RB 0xA /* Read FBCR bytes to FCM buffer */
|
||||
#define FIR_OP_RS 0xB /* Read 1 or 2 bytes to MDR[AS] */
|
||||
#define FIR_OP_CW0 0xC /* Wait then issue FCR[CMD0] */
|
||||
#define FIR_OP_CW1 0xD /* Wait then issue FCR[CMD1] */
|
||||
#define FIR_OP_RBW 0xE /* Wait then read FBCR bytes */
|
||||
#define FIR_OP_RSW 0xF /* Wait then read 1 or 2 bytes */
|
||||
|
||||
/* LTESR - Transfer Error Status Register */
|
||||
#define LTESR_BM 0x80000000
|
||||
#define LTESR_FCT 0x40000000
|
||||
#define LTESR_PAR 0x20000000
|
||||
#define LTESR_WP 0x04000000
|
||||
#define LTESR_ATMW 0x00800000
|
||||
#define LTESR_ATMR 0x00400000
|
||||
#define LTESR_CS 0x00080000
|
||||
#define LTESR_CC 0x00000001
|
||||
|
||||
#define LTESR_NAND_MASK (LTESR_FCT | LTESR_CC | LTESR_CS)
|
||||
|
||||
/* FPAR - Flash Page Address Register */
|
||||
#define FPAR_SP_PI 0x00007C00
|
||||
#define FPAR_SP_PI_SHIFT 10
|
||||
#define FPAR_SP_MS 0x00000200
|
||||
#define FPAR_SP_CI 0x000001FF
|
||||
#define FPAR_SP_CI_SHIFT 0
|
||||
#define FPAR_LP_PI 0x0003F000
|
||||
#define FPAR_LP_PI_SHIFT 12
|
||||
#define FPAR_LP_MS 0x00000800
|
||||
#define FPAR_LP_CI 0x000007FF
|
||||
#define FPAR_LP_CI_SHIFT 0
|
||||
|
||||
#define FSL_FCM_WAIT_TIMEOUT 10
|
||||
|
||||
#endif /* _NAND_NFC_FSL_H_ */
|
Loading…
Reference in New Issue
Block a user