Add support for CFI NOR, using MPC8536DS as initial example.

Only AMD/Fujitsu command set is suported so far.
This is still work in progress, be advised.
This commit is contained in:
cliff 2011-07-15 19:19:55 +00:00
parent 534ca6a0dd
commit fb19d2b789
17 changed files with 1994 additions and 215 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: MPC8536DS,v 1.6 2011/06/30 20:09:29 wiz Exp $
# $NetBSD: MPC8536DS,v 1.7 2011/07/15 19:19:55 cliff Exp $
#
# MPC8536DS -- everything that's currently supported
#
@ -7,7 +7,7 @@ include "arch/evbppc/conf/std.mpc85xx"
options INCLUDE_CONFIG_FILE # embed config file in kernel binary
ident "MPC8536DS-$Revision: 1.6 $"
ident "MPC8536DS-$Revision: 1.7 $"
maxusers 32
@ -149,6 +149,12 @@ cpu0 at cpunode?
obio0 at cpunode? # On-chip Peripheral Bus
#mkclock0 at obio0 addr 0xf8000000 size 8192
# NOR Flash
#options NOR_VERBOSE
pq3cfi0 at obio0 cs 0
nor* at pq3cfi?
flash* at nor? offset 0 size 0x8000000
e500wdog* at cpunode? # Watchdog timer
duart* at cpunode?
@ -190,9 +196,9 @@ umass* at uhub? port ?
scsibus* at umass? channel ?
sd* at scsibus? target ? lun ?
sdhc* at cpunode? # sdmmc
sdmmc* at sdhc? # SD/MMC bus
ld* at sdmmc?
#sdhc* at cpunode? # sdmmc
#sdmmc* at sdhc? # SD/MMC bus
#ld* at sdmmc?
#siisata* at pci? dev ? function ?
#atabus* at siisata? channel ?

View File

@ -0,0 +1,173 @@
/* $NetBSD: pq3cfi.c,v 1.1 2011/07/15 19:19:56 cliff Exp $ */
/*
* NOR CFI driver support for booke
*/
#include "opt_flash.h"
#include "locators.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pq3cfi.c,v 1.1 2011/07/15 19:19:56 cliff Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cdefs.h>
#include <sys/device.h>
#include <sys/endian.h>
#include <machine/bus.h>
#include <powerpc/booke/cpuvar.h>
#include <dev/nor/nor.h>
#include <dev/nor/cfi.h>
static int pq3cfi_match(device_t, cfdata_t, void *);
static void pq3cfi_attach(device_t, device_t, void *);
static int pq3cfi_detach(device_t, int);
struct pq3cfi_softc {
device_t sc_dev;
device_t sc_nordev;
struct cfi sc_cfi;
bus_addr_t sc_addr;
bus_size_t sc_size;
struct nor_interface sc_nor_if;
};
CFATTACH_DECL_NEW(pq3cfi, sizeof(struct pq3cfi_softc), pq3cfi_match,
pq3cfi_attach, pq3cfi_detach, NULL);
/*
* pq3cfi_addr - return bus address for the CFI NOR flash
*
* if the chip select not specified, use address from attach args
* otherwise use address from the chip select.
*/
static inline bus_addr_t
pq3cfi_addr(struct generic_attach_args *ga)
{
bus_addr_t addr_aa = ga->ga_addr;
if (ga->ga_cs != OBIOCF_CS_DEFAULT) {
#ifdef NOTYET
bus_addr_t addr_cs = get_addr_from_cs(ga->ga_cs);
if (addr_aa != addr_cs)
aprint_warn("%s: configured addr %#x, CS%d addr %#x\n",
__func__, addr_aa, ga->ga_cs, addr_cs);
return addr_cs;
#endif
}
return addr_aa;
}
static int
pq3cfi_match(device_t parent, cfdata_t match, void *aux)
{
struct generic_attach_args *ga = aux;
bus_size_t tmpsize = CFI_QRY_MIN_MAP_SIZE;
bus_addr_t addr;
struct cfi cfi;
int rv;
KASSERT(ga->ga_bst != NULL);
addr = pq3cfi_addr(ga);
if (addr == OBIOCF_ADDR_DEFAULT) {
aprint_error("%s: no base address\n", __func__);
return 0;
}
cfi.cfi_bst = ga->ga_bst;
int error = bus_space_map(cfi.cfi_bst, addr, tmpsize, 0, &cfi.cfi_bsh);
if (error != 0) {
aprint_error("%s: cannot map %d at offset %#x, error %d\n", __func__, tmpsize, addr, error);
return false;
}
if (! cfi_probe(&cfi)) {
aprint_debug("%s: probe addr %#x, CFI not found\n",
__func__, addr);
rv = 0;
} else {
rv = 1;
}
bus_space_unmap(cfi.cfi_bst, cfi.cfi_bsh, tmpsize);
return rv;
}
static void
pq3cfi_attach(device_t parent, device_t self, void *aux)
{
struct pq3cfi_softc *sc = device_private(self);
struct generic_attach_args *ga = aux;
struct cfi_query_data * const qryp = &sc->sc_cfi.cfi_qry_data;
const bus_size_t tmpsize = CFI_QRY_MIN_MAP_SIZE;
bool found;
int error;
aprint_normal("\n");
sc->sc_dev = self;
sc->sc_cfi.cfi_bst = ga->ga_bst;
sc->sc_addr = pq3cfi_addr(ga);
/* map enough to identify, remap later when size is known */
error = bus_space_map(sc->sc_cfi.cfi_bst, sc->sc_addr, tmpsize,
0, &sc->sc_cfi.cfi_bsh);
if (error != 0) {
aprint_error_dev(self, "could not map error %d\n", error);
return;
}
found = cfi_identify(&sc->sc_cfi);
bus_space_unmap(sc->sc_cfi.cfi_bst, sc->sc_cfi.cfi_bsh, tmpsize);
if (! found) {
/* should not happen, we already probed OK in match */
aprint_error_dev(self, "could not map error %d\n", error);
return;
}
sc->sc_size = 1 << qryp->device_size;
sc->sc_nor_if = nor_interface_cfi;
sc->sc_nor_if.private = &sc->sc_cfi;
sc->sc_nor_if.access_width = (1 << sc->sc_cfi.cfi_portwidth);
cfi_print(self, &sc->sc_cfi);
error = bus_space_map(sc->sc_cfi.cfi_bst, sc->sc_addr, sc->sc_size,
0, &sc->sc_cfi.cfi_bsh);
if (error != 0) {
aprint_error_dev(self, "could not map error %d\n", error);
return;
}
if (! pmf_device_register1(self, NULL, NULL, NULL))
aprint_error_dev(self, "couldn't establish power handler\n");
sc->sc_nordev = nor_attach_mi(&sc->sc_nor_if, self);
}
static int
pq3cfi_detach(device_t self, int flags)
{
struct pq3cfi_softc *sc = device_private(self);
int rv = 0;
pmf_device_deregister(self);
if (sc->sc_nordev != NULL)
rv = config_detach(sc->sc_nordev, flags);
bus_space_unmap(sc->sc_cfi.cfi_bst, sc->sc_cfi.cfi_bsh, sc->sc_size);
return rv;
}

View File

@ -1,4 +1,4 @@
# $NetBSD: files.booke,v 1.3 2011/05/02 01:48:05 matt Exp $
# $NetBSD: files.booke,v 1.4 2011/07/15 19:19:56 cliff Exp $
#
# PPC BookE specific configuration info
@ -58,6 +58,10 @@ device obio { [addr=-1], [size=-1], [cs=-1], [irq=-1] }
attach obio at cpunode with pq3obio
file arch/powerpc/booke/dev/pq3obio.c pq3obio
device pq3cfi: norbus
attach pq3cfi at obio
file arch/powerpc/booke/dev/pq3cfi.c nor
device nandfcm: nandbus
attach nandfcm at obio with pq3nandfcm
file arch/powerpc/booke/dev/pq3nandfcm.c pq3nandfcm

View File

@ -1,4 +1,4 @@
# $NetBSD: files,v 1.1019 2011/07/09 14:48:12 jmcneill Exp $
# $NetBSD: files,v 1.1020 2011/07/15 19:19:56 cliff Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
version 20100430
@ -1722,6 +1722,11 @@ include "dev/flash/files.flash"
#
include "dev/nand/files.nand"
#
# NOR subsytem
#
include "dev/nor/files.nor"
#
# DTV subsystem
#

View File

@ -1,4 +1,4 @@
/* $NetBSD: flash.c,v 1.7 2011/06/28 18:14:11 ahoka Exp $ */
/* $NetBSD: flash.c,v 1.8 2011/07/15 19:19:57 cliff Exp $ */
/*-
* Copyright (c) 2011 Department of Software Engineering,
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: flash.c,v 1.7 2011/06/28 18:14:11 ahoka Exp $");
__KERNEL_RCSID(0, "$NetBSD: flash.c,v 1.8 2011/07/15 19:19:57 cliff Exp $");
#include <sys/param.h>
#include <sys/types.h>
@ -81,9 +81,6 @@ bool flash_shutdown(device_t dev, int how);
int flash_nsectors(struct buf *bp);
int flash_sector(struct buf *bp);
static inline flash_off_t flash_get_part_offset(struct flash_softc *fl,
size_t poffset);
int flash_match(device_t parent, cfdata_t match, void *aux);
void flash_attach(device_t parent, device_t self, void *aux);
int flash_detach(device_t device, int flags);
@ -133,8 +130,8 @@ flash_match(device_t parent, cfdata_t match, void *aux)
void
flash_attach(device_t parent, device_t self, void *aux)
{
struct flash_softc *sc = device_private(self);
struct flash_attach_args *faa = aux;
struct flash_softc * const sc = device_private(self);
struct flash_attach_args * const faa = aux;
char pbuf[2][sizeof("9999 KB")];
sc->sc_dev = self;
@ -150,22 +147,19 @@ flash_attach(device_t parent, device_t self, void *aux)
switch (sc->flash_if->type) {
case FLASH_TYPE_NOR:
aprint_normal(": %s NOR flash\n", pbuf[0]);
aprint_normal(": NOR flash partition size %s, offset %#jx",
pbuf[0], (uintmax_t )sc->sc_partinfo.part_offset);
break;
case FLASH_TYPE_NAND:
aprint_normal(": %s NAND flash\n", pbuf[0]);
aprint_normal(": NAND flash partition size %s, offset %#jx",
pbuf[0], (uintmax_t )sc->sc_partinfo.part_offset);
break;
default:
aprint_normal(": %s unknown flash\n", pbuf[0]);
aprint_normal(": %s unknown flash", pbuf[0]);
}
aprint_normal_dev(sc->sc_dev,
"size: %#jx, offset: %#jx",
(uintmax_t )sc->sc_partinfo.part_size,
(uintmax_t )sc->sc_partinfo.part_offset);
if (sc->sc_partinfo.part_flags & FLASH_PART_READONLY) {
sc->sc_readonly = true;
aprint_normal(", read only");
@ -205,7 +199,7 @@ flash_attach(device_t parent, device_t self, void *aux)
int
flash_detach(device_t device, int flags)
{
struct flash_softc *sc = device_private(device);
struct flash_softc * const sc = device_private(device);
pmf_device_deregister(sc->sc_dev);
@ -240,7 +234,7 @@ flash_print(void *aux, const char *pnp)
}
device_t
flash_attach_mi(struct flash_interface *flash_if, device_t device)
flash_attach_mi(struct flash_interface * const flash_if, device_t device)
{
struct flash_attach_args arg;
@ -261,7 +255,7 @@ flash_attach_mi(struct flash_interface *flash_if, device_t device)
* request.
*/
int
flashopen(dev_t dev, int flags, int fmt, struct lwp *l)
flashopen(dev_t dev, int flags, int fmt, lwp_t *l)
{
int unit = minor(dev);
struct flash_softc *sc;
@ -284,7 +278,7 @@ flashopen(dev_t dev, int flags, int fmt, struct lwp *l)
* We don't have to release any resources, so just return 0.
*/
int
flashclose(dev_t dev, int flags, int fmt, struct lwp *l)
flashclose(dev_t dev, int flags, int fmt, lwp_t *l)
{
int unit = minor(dev);
struct flash_softc *sc;
@ -306,11 +300,11 @@ flashclose(dev_t dev, int flags, int fmt, struct lwp *l)
/**
* flash_read - read from character device
* This function uses the registered driver's read function to read the requested length to
* a buffer and then moves this buffer to userspace.
* This function uses the registered driver's read function to read the
* requested length to * a buffer and then moves this buffer to userspace.
*/
int
flashread(dev_t dev, struct uio *uio, int flag)
flashread(dev_t dev, struct uio * const uio, int flag)
{
return physio(flashstrategy, NULL, dev, B_READ, minphys, uio);
}
@ -322,13 +316,13 @@ flashread(dev_t dev, struct uio *uio, int flag)
* the media.
*/
int
flashwrite(dev_t dev, struct uio *uio, int flag)
flashwrite(dev_t dev, struct uio * const uio, int flag)
{
return physio(flashstrategy, NULL, dev, B_WRITE, minphys, uio);
}
void
flashstrategy(struct buf *bp)
flashstrategy(struct buf * const bp)
{
struct flash_softc *sc;
const struct flash_interface *flash_if;
@ -387,7 +381,7 @@ done:
* Handle the ioctl for the device
*/
int
flashioctl(dev_t dev, u_long command, void *data, int flags, struct lwp *l)
flashioctl(dev_t dev, u_long command, void * const data, int flags, lwp_t *l)
{
struct flash_erase_params *ep;
struct flash_info_params *ip;
@ -480,13 +474,13 @@ flashioctl(dev_t dev, u_long command, void *data, int flags, struct lwp *l)
int
flashdump(dev_t dev, daddr_t blkno, void *va, size_t size)
{
return EACCES;
return EACCES;
}
bool
flash_shutdown(device_t self, int how)
{
struct flash_softc *sc = device_private(self);
struct flash_softc * const sc = device_private(self);
if ((how & RB_NOSYNC) == 0 && !sc->sc_readonly)
flash_sync(self);
@ -532,15 +526,15 @@ flash_get_device(dev_t dev)
}
static inline flash_off_t
flash_get_part_offset(struct flash_softc *sc, size_t poffset)
flash_get_part_offset(struct flash_softc * const sc, size_t poffset)
{
return sc->sc_partinfo.part_offset + poffset;
}
int
flash_erase(device_t self, struct flash_erase_instruction *ei)
flash_erase(device_t self, struct flash_erase_instruction * const ei)
{
struct flash_softc *sc = device_private(self);
struct flash_softc * const sc = device_private(self);
KASSERT(ei != NULL);
struct flash_erase_instruction e = *ei;
@ -559,10 +553,10 @@ flash_erase(device_t self, struct flash_erase_instruction *ei)
}
int
flash_read(device_t self,
flash_off_t offset, size_t len, size_t *retlen, uint8_t *buf)
flash_read(device_t self, flash_off_t offset, size_t len, size_t * const retlen,
uint8_t * const buf)
{
struct flash_softc *sc = device_private(self);
struct flash_softc * const sc = device_private(self);
offset += sc->sc_partinfo.part_offset;
@ -575,10 +569,10 @@ flash_read(device_t self,
}
int
flash_write(device_t self,
flash_off_t offset, size_t len, size_t *retlen, const uint8_t *buf)
flash_write(device_t self, flash_off_t offset, size_t len,
size_t * const retlen, const uint8_t * const buf)
{
struct flash_softc *sc = device_private(self);
struct flash_softc * const sc = device_private(self);
if (sc->sc_readonly)
return EACCES;
@ -596,7 +590,7 @@ flash_write(device_t self,
int
flash_block_markbad(device_t self, flash_off_t offset)
{
struct flash_softc *sc = device_private(self);
struct flash_softc * const sc = device_private(self);
if (sc->sc_readonly)
return EACCES;
@ -612,9 +606,9 @@ flash_block_markbad(device_t self, flash_off_t offset)
}
int
flash_block_isbad(device_t self, flash_off_t offset, bool *bad)
flash_block_isbad(device_t self, flash_off_t offset, bool * const bad)
{
struct flash_softc *sc = device_private(self);
struct flash_softc * const sc = device_private(self);
offset += sc->sc_partinfo.part_offset;
@ -629,7 +623,7 @@ flash_block_isbad(device_t self, flash_off_t offset, bool *bad)
int
flash_sync(device_t self)
{
struct flash_softc *sc = device_private(self);
struct flash_softc * const sc = device_private(self);
if (sc->sc_readonly)
return EACCES;

View File

@ -1,4 +1,4 @@
/* $NetBSD: flash.h,v 1.5 2011/06/28 21:01:23 ahoka Exp $ */
/* $NetBSD: flash.h,v 1.6 2011/07/15 19:19:57 cliff Exp $ */
/*-
* Copyright (c) 2011 Department of Software Engineering,
@ -74,11 +74,6 @@ struct flash_attach_args {
struct flash_partition partinfo;
};
device_t flash_attach_mi(struct flash_interface *, device_t);
const struct flash_interface *flash_get_interface(dev_t);
const struct flash_softc *flash_get_softc(dev_t);
device_t flash_get_device(dev_t);
/**
* struct erase_instruction - instructions to erase a flash eraseblock
*/
@ -124,6 +119,11 @@ struct flash_cache {
uint8_t *fc_data;
};
device_t flash_attach_mi(struct flash_interface *, device_t);
const struct flash_interface *flash_get_interface(dev_t);
const struct flash_softc *flash_get_softc(dev_t);
device_t flash_get_device(dev_t);
/* flash operations should be used through these */
int flash_erase(device_t, struct flash_erase_instruction *);
int flash_read(device_t, flash_off_t, size_t, size_t *, uint8_t *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: flash_io.c,v 1.2 2011/06/28 20:58:00 ahoka Exp $ */
/* $NetBSD: flash_io.c,v 1.3 2011/07/15 19:19:57 cliff Exp $ */
/*-
* Copyright (c) 2011 Department of Software Engineering,
@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: flash_io.c,v 1.2 2011/06/28 20:58:00 ahoka Exp $");
__KERNEL_RCSID(0, "$NetBSD: flash_io.c,v 1.3 2011/07/15 19:19:57 cliff Exp $");
#include <sys/param.h>
#include <sys/buf.h>
@ -96,12 +96,14 @@ flash_io_getblock(struct flash_io *fio, struct buf *bp)
}
int
flash_sync_thread_init(struct flash_io *fio, struct flash_interface *flash_if)
flash_sync_thread_init(struct flash_io *fio, device_t dev,
struct flash_interface *flash_if)
{
int error;
FLDPRINTF(("starting flash io thread\n"));
fio->fio_dev = dev;
fio->fio_if = flash_if;
fio->fio_data = kmem_alloc(fio->fio_if->erasesize, KM_SLEEP);

View File

@ -18,7 +18,8 @@ struct flash_io {
int flash_io_submit(struct flash_io *, struct buf *);
void flash_sync_thread(void *);
int flash_sync_thread_init(struct flash_io *, struct flash_interface *);
int flash_sync_thread_init(struct flash_io *, device_t,
struct flash_interface *);
void flash_sync_thread_destroy(struct flash_io *);
#endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: nand.c,v 1.14 2011/07/01 16:46:13 ahoka Exp $ */
/* $NetBSD: nand.c,v 1.15 2011/07/15 19:19:57 cliff Exp $ */
/*-
* Copyright (c) 2010 Department of Software Engineering,
@ -34,7 +34,7 @@
/* Common driver for NAND chips implementing the ONFI 2.2 specification */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nand.c,v 1.14 2011/07/01 16:46:13 ahoka Exp $");
__KERNEL_RCSID(0, "$NetBSD: nand.c,v 1.15 2011/07/15 19:19:57 cliff Exp $");
#include "locators.h"
@ -157,7 +157,7 @@ nand_attach(device_t parent, device_t self, void *aux)
mutex_init(&sc->sc_device_lock, MUTEX_DEFAULT, IPL_NONE);
if (flash_sync_thread_init(&sc->sc_flash_io, &nand_flash_if)) {
if (flash_sync_thread_init(&sc->sc_flash_io, self, &nand_flash_if)) {
goto error;
}
@ -1037,7 +1037,7 @@ nand_default_select(device_t self, bool enable)
/* implementation of the block device API */
int
nand_flash_submit(device_t self, struct buf *bp)
nand_flash_submit(device_t self, struct buf * const bp)
{
struct nand_softc *sc = device_private(self);
@ -1386,7 +1386,7 @@ nand_flash_isbad(device_t self, flash_off_t ofs, bool *isbad)
}
if (ofs % chip->nc_block_size != 0) {
DPRINTF(("offset (0x%jx) is not the multiple of block size "
DPRINTF(("offset (0x%jx) is not a multiple of block size "
"(%ju)",
(uintmax_t)ofs, (uintmax_t)chip->nc_block_size));
return EINVAL;
@ -1415,7 +1415,7 @@ nand_flash_markbad(device_t self, flash_off_t ofs)
}
if (ofs % chip->nc_block_size != 0) {
panic("offset (%ju) is not the multiple of block size (%ju)",
panic("offset (%ju) is not a multiple of block size (%ju)",
(uintmax_t)ofs, (uintmax_t)chip->nc_block_size);
}
@ -1447,7 +1447,7 @@ nand_flash_erase(device_t self,
if (ei->ei_addr % chip->nc_block_size != 0) {
aprint_error_dev(self,
"nand_flash_erase: ei_addr (%ju) is not"
"the multiple of block size (%ju)",
" a multiple of block size (%ju)",
(uintmax_t)ei->ei_addr,
(uintmax_t)chip->nc_block_size);
return EINVAL;
@ -1456,8 +1456,8 @@ nand_flash_erase(device_t self,
if (ei->ei_len % chip->nc_block_size != 0) {
aprint_error_dev(self,
"nand_flash_erase: ei_len (%ju) is not"
"the multiple of block size (%ju)",
(uintmax_t)ei->ei_addr,
" a multiple of block size (%ju)",
(uintmax_t)ei->ei_len,
(uintmax_t)chip->nc_block_size);
return EINVAL;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: nand.h,v 1.12 2011/07/01 16:46:13 ahoka Exp $ */
/* $NetBSD: nand.h,v 1.13 2011/07/15 19:19:57 cliff Exp $ */
/*-
* Copyright (c) 2010 Department of Software Engineering,
@ -160,8 +160,7 @@ struct nand_softc {
};
/* structure holding the nand api */
struct nand_interface
{
struct nand_interface {
/* basic nand controller commands */
void (*select) (device_t, bool); /* optional */
void (*command) (device_t, uint8_t);
@ -202,7 +201,7 @@ struct nand_attach_args {
static inline void
nand_busy(device_t device)
{
struct nand_softc *sc = device_private(device);
struct nand_softc * const sc = device_private(device);
KASSERT(sc->nand_if->select != NULL);
KASSERT(sc->controller_dev != NULL);
@ -219,7 +218,7 @@ nand_busy(device_t device)
static inline void
nand_select(device_t self, bool enable)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->select != NULL);
KASSERT(sc->controller_dev != NULL);
@ -230,7 +229,7 @@ nand_select(device_t self, bool enable)
static inline void
nand_address(device_t self, uint32_t address)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->address != NULL);
KASSERT(sc->controller_dev != NULL);
@ -241,7 +240,7 @@ nand_address(device_t self, uint32_t address)
static inline void
nand_command(device_t self, uint8_t command)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->command != NULL);
KASSERT(sc->controller_dev != NULL);
@ -252,7 +251,7 @@ nand_command(device_t self, uint8_t command)
static inline void
nand_read_1(device_t self, uint8_t *data)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->read_1 != NULL);
KASSERT(sc->controller_dev != NULL);
@ -263,7 +262,7 @@ nand_read_1(device_t self, uint8_t *data)
static inline void
nand_write_1(device_t self, uint8_t data)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->write_1 != NULL);
KASSERT(sc->controller_dev != NULL);
@ -274,7 +273,7 @@ nand_write_1(device_t self, uint8_t data)
static inline void
nand_read_2(device_t self, uint16_t *data)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->read_2 != NULL);
KASSERT(sc->controller_dev != NULL);
@ -285,7 +284,7 @@ nand_read_2(device_t self, uint16_t *data)
static inline void
nand_write_2(device_t self, uint16_t data)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->write_2 != NULL);
KASSERT(sc->controller_dev != NULL);
@ -296,7 +295,7 @@ nand_write_2(device_t self, uint16_t data)
static inline void
nand_read_buf_1(device_t self, void *buf, size_t size)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->read_buf_1 != NULL);
KASSERT(sc->controller_dev != NULL);
@ -307,7 +306,7 @@ nand_read_buf_1(device_t self, void *buf, size_t size)
static inline void
nand_read_buf_2(device_t self, void *buf, size_t size)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->read_buf_2 != NULL);
KASSERT(sc->controller_dev != NULL);
@ -318,7 +317,7 @@ nand_read_buf_2(device_t self, void *buf, size_t size)
static inline void
nand_write_buf_1(device_t self, const void *buf, size_t size)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->write_buf_1 != NULL);
KASSERT(sc->controller_dev != NULL);
@ -329,7 +328,7 @@ nand_write_buf_1(device_t self, const void *buf, size_t size)
static inline void
nand_write_buf_2(device_t self, const void *buf, size_t size)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->write_buf_2 != NULL);
KASSERT(sc->controller_dev != NULL);
@ -341,7 +340,7 @@ static inline int
nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldcode,
const uint8_t *newcode)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->ecc_correct != NULL);
KASSERT(sc->controller_dev != NULL);
@ -352,7 +351,7 @@ nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldcode,
static inline void
nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *code)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->ecc_compute != NULL);
KASSERT(sc->controller_dev != NULL);
@ -363,7 +362,7 @@ nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *code)
static inline void
nand_ecc_prepare(device_t self, int mode)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->controller_dev != NULL);
@ -374,7 +373,7 @@ nand_ecc_prepare(device_t self, int mode)
static inline int
nand_program_page(device_t self, size_t offset, const uint8_t *data)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->program_page != NULL);
@ -384,7 +383,7 @@ nand_program_page(device_t self, size_t offset, const uint8_t *data)
static inline int
nand_read_page(device_t self, size_t offset, uint8_t *data)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->read_page != NULL);
@ -395,7 +394,7 @@ nand_read_page(device_t self, size_t offset, uint8_t *data)
static inline bool
nand_block_isbad(device_t self, flash_off_t block)
{
struct nand_softc *sc = device_private(self);
struct nand_softc * const sc = device_private(self);
KASSERT(sc->nand_if->block_isbad != NULL);
KASSERT(sc->controller_dev != NULL);

View File

@ -1,4 +1,4 @@
/* $NetBSD: nand_micron.c,v 1.5 2011/06/28 07:16:11 ahoka Exp $ */
/* $NetBSD: nand_micron.c,v 1.6 2011/07/15 19:19:57 cliff Exp $ */
/*-
* Copyright (c) 2011 Department of Software Engineering,
@ -39,7 +39,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nand_micron.c,v 1.5 2011/06/28 07:16:11 ahoka Exp $");
__KERNEL_RCSID(0, "$NetBSD: nand_micron.c,v 1.6 2011/07/15 19:19:57 cliff Exp $");
#include "nand.h"
#include "onfi.h"
@ -89,7 +89,7 @@ nand_micron_device_lookup(u_int8_t id)
}
int
nand_read_parameters_micron(device_t self, struct nand_chip *chip)
nand_read_parameters_micron(device_t self, struct nand_chip * const chip)
{
uint8_t mfgrid;
uint8_t devid;
@ -129,7 +129,7 @@ nand_read_parameters_micron(device_t self, struct nand_chip *chip)
}
static int
mt29fxgx_parameters(device_t self, struct nand_chip *chip,
mt29fxgx_parameters(device_t self, struct nand_chip * const chip,
u_int8_t devid, uint8_t params)
{
const struct nand_micron_devices *dp;

684
sys/dev/nor/cfi.c Normal file
View File

@ -0,0 +1,684 @@
/* $NetBSD: cfi.c,v 1.1 2011/07/15 19:19:57 cliff Exp $ */
#include "opt_nor.h"
#include "opt_flash.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cfi.c,v 1.1 2011/07/15 19:19:57 cliff Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cdefs.h>
#include <sys/device.h>
#include <sys/endian.h>
#include <machine/bus.h>
#include <dev/nor/nor.h>
#include <dev/nor/cfi.h>
#include <dev/nor/cfi_0002.h>
static bool cfi_chip_query(struct cfi * const);
static int cfi_scan_media(device_t self, struct nor_chip *chip);
static void cfi_init(device_t);
static void cfi_select(device_t, bool);
static void cfi_read_1(device_t, flash_off_t, uint8_t *);
static void cfi_read_2(device_t, flash_off_t, uint16_t *);
static void cfi_read_4(device_t, flash_off_t, uint32_t *);
static void cfi_read_buf_1(device_t, flash_off_t, uint8_t *, size_t);
static void cfi_read_buf_2(device_t, flash_off_t, uint16_t *, size_t);
static void cfi_read_buf_4(device_t, flash_off_t, uint32_t *, size_t);
static void cfi_write_1(device_t, flash_off_t, uint8_t);
static void cfi_write_2(device_t, flash_off_t, uint16_t);
static void cfi_write_4(device_t, flash_off_t, uint32_t);
static void cfi_write_buf_1(device_t, flash_off_t, const uint8_t *, size_t);
static void cfi_write_buf_2(device_t, flash_off_t, const uint16_t *, size_t);
static void cfi_write_buf_4(device_t, flash_off_t, const uint32_t *, size_t);
static bool cfi_jedec_id(struct cfi * const);
/*
* NOTE these opmode tables are informed by "Table 1. CFI Query Read"
* in Intel "Common Flash Interface (CFI) and Command Sets"
* Application Note 646, April 2000
*
* The byte ordering of the signature string here varies from that table
* because of discrepancy in observed behavior, for the case:
* - x16 device operating in 16-bit mode
* Similar discrepancy is expected (but not verified) for the case:
* - x32 device operating in 32-bit mode
* so the ordering is changed here for that case also.
*
* XXX down-sized, interleaved & multi-chip opmodes not yet supported
*/
/* 1-byte access */
static const struct cfi_opmodes cfi_opmodes_1[] = {
{ 0, 0, 0, 0x10, 3, "QRY", "x8 device operating in 8-bit mode" },
};
/* 2-byte access */
static const struct cfi_opmodes cfi_opmodes_2[] = {
{ 1, 1, 0, 0x20, 6, "\0Q\0R\0Y",
"x16 device operating in 16-bit mode" },
};
/* 4-byte access */
static const struct cfi_opmodes cfi_opmodes_4[] = {
{ 2, 2, 0, 0x40, 12, "\0\0\0Q\0\0\0R\0\0\0Y",
"x32 device operating in 32-bit mode" },
};
const struct nor_interface nor_interface_cfi = {
.scan_media = cfi_scan_media,
.init = cfi_init,
.select = cfi_select,
.read_1 = cfi_read_1,
.read_2 = cfi_read_2,
.read_4 = cfi_read_4,
.read_buf_1 = cfi_read_buf_1,
.read_buf_2 = cfi_read_buf_2,
.read_buf_4 = cfi_read_buf_4,
.write_1 = cfi_write_1,
.write_2 = cfi_write_2,
.write_4 = cfi_write_4,
.write_buf_1 = cfi_write_buf_1,
.write_buf_2 = cfi_write_buf_2,
.write_buf_4 = cfi_write_buf_4,
.read_page = NULL, /* cmdset */
.program_page = NULL, /* cmdset */
.busy = NULL,
.private = NULL,
.access_width = -1,
.part_info = NULL,
.part_num = -1,
};
/* only data[7..0] are used regardless of chip width */
#define cfi_unpack_1(n) ((n) & 0xff)
/* construct (arbitrarily big endian) uint16_t */
#define cfi_unpack_2(b0, b1) \
((cfi_unpack_1(b1) << 8) | cfi_unpack_1(b0))
/* construct (arbitrarily) big endian uint32_t */
#define cfi_unpack_4(b0, b1, b2, b3) \
((cfi_unpack_1(b3) << 24) | \
(cfi_unpack_1(b2) << 16) | \
(cfi_unpack_1(b1) << 8) | \
(cfi_unpack_1(b0)))
#define cfi_unpack_qry(qryp, data) \
do { \
(qryp)->qry[0] = cfi_unpack_1(data[0x10]); \
(qryp)->qry[1] = cfi_unpack_1(data[0x11]); \
(qryp)->qry[2] = cfi_unpack_1(data[0x12]); \
(qryp)->id_pri = be16toh(cfi_unpack_2(data[0x13], data[0x14])); \
(qryp)->addr_pri = \
be16toh(cfi_unpack_2(data[0x15], data[0x16])); \
(qryp)->id_alt = be16toh(cfi_unpack_2(data[0x17], data[0x18])); \
(qryp)->addr_alt = \
be16toh(cfi_unpack_2(data[0x19], data[0x1a])); \
(qryp)->vcc_min = cfi_unpack_1(data[0x1b]); \
(qryp)->vcc_max = cfi_unpack_1(data[0x1c]); \
(qryp)->vpp_min = cfi_unpack_1(data[0x1d]); \
(qryp)->vpp_max = cfi_unpack_1(data[0x1e]); \
(qryp)->write_word_time_typ = cfi_unpack_1(data[0x1f]); \
(qryp)->write_nbyte_time_typ = cfi_unpack_1(data[0x20]); \
(qryp)->erase_blk_time_typ = cfi_unpack_1(data[0x21]); \
(qryp)->erase_chiptime_typ = cfi_unpack_1(data[0x22]); \
(qryp)->write_word_time_max = cfi_unpack_1(data[0x23]); \
(qryp)->write_nbyte_time_max = cfi_unpack_1(data[0x24]); \
(qryp)->erase_blk_time_max = cfi_unpack_1(data[0x25]); \
(qryp)->erase_chiptime_max = cfi_unpack_1(data[0x26]); \
(qryp)->device_size = cfi_unpack_1(data[0x27]); \
(qryp)->interface_code_desc = \
be16toh(cfi_unpack_2(data[0x28], data[0x29])); \
(qryp)->write_nbyte_size_max = \
be16toh(cfi_unpack_2(data[0x2a], data[0x2b])); \
(qryp)->erase_blk_regions = cfi_unpack_1(data[0x2c]); \
u_int _i = 0x2d; \
const u_int _n = (qryp)->erase_blk_regions; \
KASSERT(_n <= 4); \
for (u_int _r = 0; _r < _n; _r++, _i+=4) { \
(qryp)->erase_blk_info[_r].y = \
be32toh(cfi_unpack_2(data[_i+0], data[_i+1])); \
(qryp)->erase_blk_info[_r].z = \
be32toh(cfi_unpack_2(data[_i+2], data[_i+3])); \
} \
} while (0)
#define cfi_unpack_pri_0002(qryp, data) \
do { \
(qryp)->pri.cmd_0002.pri[0] = cfi_unpack_1(data[0x00]); \
(qryp)->pri.cmd_0002.pri[1] = cfi_unpack_1(data[0x01]); \
(qryp)->pri.cmd_0002.pri[2] = cfi_unpack_1(data[0x02]); \
(qryp)->pri.cmd_0002.version_maj = cfi_unpack_1(data[0x03]); \
(qryp)->pri.cmd_0002.version_min = cfi_unpack_1(data[0x04]); \
(qryp)->pri.cmd_0002.asupt = cfi_unpack_1(data[0x05]); \
(qryp)->pri.cmd_0002.erase_susp = cfi_unpack_1(data[0x06]); \
(qryp)->pri.cmd_0002.sector_prot = cfi_unpack_1(data[0x07]); \
(qryp)->pri.cmd_0002.tmp_sector_unprot = \
cfi_unpack_1(data[0x08]); \
(qryp)->pri.cmd_0002.sector_prot_scheme = \
cfi_unpack_1(data[0x09]); \
(qryp)->pri.cmd_0002.simul_op = cfi_unpack_1(data[0x0a]); \
(qryp)->pri.cmd_0002.burst_mode_type = cfi_unpack_1(data[0x0b]);\
(qryp)->pri.cmd_0002.page_mode_type = cfi_unpack_1(data[0x0c]); \
(qryp)->pri.cmd_0002.acc_min = cfi_unpack_1(data[0x0d]); \
(qryp)->pri.cmd_0002.acc_max = cfi_unpack_1(data[0x0e]); \
(qryp)->pri.cmd_0002.wp_prot = cfi_unpack_1(data[0x0f]); \
/* XXX 1.3 stops here */ \
(qryp)->pri.cmd_0002.prog_susp = cfi_unpack_1(data[0x10]); \
(qryp)->pri.cmd_0002.unlock_bypass = cfi_unpack_1(data[0x11]); \
(qryp)->pri.cmd_0002.sss_size = cfi_unpack_1(data[0x12]); \
(qryp)->pri.cmd_0002.soft_feat = cfi_unpack_1(data[0x13]); \
(qryp)->pri.cmd_0002.page_size = cfi_unpack_1(data[0x14]); \
(qryp)->pri.cmd_0002.erase_susp_time_max = \
cfi_unpack_1(data[0x15]); \
(qryp)->pri.cmd_0002.prog_susp_time_max = \
cfi_unpack_1(data[0x16]); \
(qryp)->pri.cmd_0002.embhwrst_time_max = \
cfi_unpack_1(data[0x38]); \
(qryp)->pri.cmd_0002.hwrst_time_max = \
cfi_unpack_1(data[0x39]); \
} while (0)
#define CFI_QRY_UNPACK_COMMON(cfi, data, type, found) \
do { \
struct cfi_query_data * const qryp = &cfi->cfi_qry_data; \
\
memset(qryp, 0, sizeof(*qryp)); \
cfi_unpack_qry(qryp, data); \
\
switch (qryp->id_pri) { \
case 0x0002: \
if ((cfi_unpack_1(data[qryp->addr_pri + 0]) == 'P') && \
(cfi_unpack_1(data[qryp->addr_pri + 1]) == 'R') && \
(cfi_unpack_1(data[qryp->addr_pri + 2]) == 'I')) { \
type *pri_data = &data[qryp->addr_pri]; \
cfi_unpack_pri_0002(qryp, pri_data); \
found = true; \
break; \
} \
default: \
printf("%s: unsupported id_pri=%#x\n", \
__func__, qryp->id_pri); \
break; /* unknown command set */ \
} \
} while (0)
/*
* cfi_chip_query_opmode - determine operational mode based on QRY signature
*/
static bool
cfi_chip_query_opmode(struct cfi *cfi, uint8_t *data,
const struct cfi_opmodes *tab, u_int nentries)
{
for (u_int i=0; i < nentries; i++) {
if (memcmp(&data[tab[i].qsa], tab[i].sig, tab[i].len) == 0) {
cfi->cfi_opmode = &tab[i];
return true;
}
}
return false;
}
static bool
cfi_chip_query_1(struct cfi * const cfi)
{
uint8_t data[0x80];
bus_space_read_region_1(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
__arraycount(data));
bool found = cfi_chip_query_opmode(cfi, data, cfi_opmodes_1,
__arraycount(cfi_opmodes_1));
if (found) {
CFI_QRY_UNPACK_COMMON(cfi, data, uint8_t, found);
}
return found;
}
static bool
cfi_chip_query_2(struct cfi * const cfi)
{
uint16_t data[0x80];
bus_space_read_region_2(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
__arraycount(data));
bool found = cfi_chip_query_opmode(cfi, (uint8_t *)data,
cfi_opmodes_2, __arraycount(cfi_opmodes_2));
if (found) {
CFI_QRY_UNPACK_COMMON(cfi, data, uint16_t, found);
}
return found;
}
static bool
cfi_chip_query_4(struct cfi * const cfi)
{
uint32_t data[0x80];
bus_space_read_region_4(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
__arraycount(data));
bool found = cfi_chip_query_opmode(cfi, (uint8_t *)data,
cfi_opmodes_4, __arraycount(cfi_opmodes_4));
if (found) {
CFI_QRY_UNPACK_COMMON(cfi, data, uint32_t, found);
}
return found;
}
static bool
cfi_chip_query_8(struct cfi * const cfi)
{
#ifdef NOTYET
uint64_t data[0x80];
bus_space_read_region_8(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
__arraycount(data));
bool found = cfi_chip_query_opmode(cfi, (uint8_t *)data,
cfi_opmodes_8, __arraycount(cfi_opmodes_8));
if (found) {
CFI_QRY_UNPACK_COMMON(cfi, data, uint64_t, found);
}
return found;
#else
return false;
#endif
}
/*
* cfi_chip_query - detect a CFI chip
*
* fill in the struct cfi as we discover what's there
*/
static bool
cfi_chip_query(struct cfi * const cfi)
{
bool found = false;
const bus_size_t cfi_query_offset[] = {
CFI_QUERY_MODE_ADDRESS,
CFI_QUERY_MODE_ALT_ADDRESS
};
KASSERT(cfi != NULL);
KASSERT(cfi->cfi_bst != NULL);
for (int j=0; !found && j < __arraycount(cfi_query_offset); j++) {
cfi_reset_default(cfi);
cfi_cmd(cfi, cfi_query_offset[j], CFI_QUERY_DATA);
switch(cfi->cfi_portwidth) {
case 0:
found = cfi_chip_query_1(cfi);
break;
case 1:
found = cfi_chip_query_2(cfi);
break;
case 2:
found = cfi_chip_query_4(cfi);
break;
case 3:
found = cfi_chip_query_8(cfi);
break;
default:
panic("%s: bad portwidth %d\n",
__func__, cfi->cfi_portwidth);
}
}
return found;
}
/*
* cfi_probe - search for a CFI NOR trying various port & chip widths
*
* NOTE:
* striped NOR chips design not supported yet,
* so force portwidth=chipwidth for now
* eventually permute portwidth seperately
*/
bool
cfi_probe(struct cfi * const cfi)
{
bool found;
KASSERT(cfi != NULL);
for (u_int cw = 0; cw < 3; cw++) {
cfi->cfi_portwidth = /* XXX */
cfi->cfi_chipwidth = cw;
found = cfi_chip_query(cfi);
if (found)
goto out;
}
out:
cfi_reset_default(cfi); /* exit QRY mode */
return found;
}
bool
cfi_identify(struct cfi * const cfi)
{
const bus_space_tag_t bst = cfi->cfi_bst;
const bus_space_handle_t bsh = cfi->cfi_bsh;
bool found = true;
KASSERT(cfi != NULL);
KASSERT(bst != NULL);
memset(cfi, 0, sizeof(struct cfi)); /* XXX clean slate */
cfi->cfi_bst = bst; /* restore bus space */
cfi->cfi_bsh = bsh; /* " " " */
/* gather CFI PRQ and PRI data */
if (! cfi_probe(cfi)) {
aprint_debug("%s: cfi_probe failed\n", __func__);
found = false;
goto out;
}
/* gather ID data if possible */
if (! cfi_jedec_id(cfi)) {
aprint_debug("%s: cfi_jedec_id failed\n", __func__);
goto out;
}
out:
cfi_reset_default(cfi); /* exit QRY mode */
return found;
}
static int
cfi_scan_media(device_t self, struct nor_chip *chip)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc != NULL);
KASSERT(sc->sc_nor_if != NULL);
struct cfi * const cfi = (struct cfi * const)sc->sc_nor_if->private;
KASSERT(cfi != NULL);
sc->sc_nor_if->access_width = cfi->cfi_portwidth;
chip->nc_manf_id = cfi->cfi_id_data.id_mid;
chip->nc_dev_id = cfi->cfi_id_data.id_did[0]; /* XXX 3 words */
chip->nc_size = 1 << cfi->cfi_qry_data.device_size;
/* size of line for Read Buf command */
chip->nc_line_size = 1 << cfi->cfi_qry_data.pri.cmd_0002.page_size;
/*
* size of erase block
* XXX depends on erase region
*/
chip->nc_num_luns = 1;
chip->nc_lun_blocks = cfi->cfi_qry_data.erase_blk_info[0].y + 1;
chip->nc_block_size = cfi->cfi_qry_data.erase_blk_info[0].z * 256;
switch (cfi->cfi_qry_data.id_pri) {
case 0x0002:
cfi_0002_init(sc, cfi, chip);
break;
default:
return -1;
}
return 0;
}
void
cfi_init(device_t self)
{
/* nothing */
}
static void
cfi_select(device_t self, bool select)
{
/* nothing */
}
static void
cfi_read_1(device_t self, flash_off_t offset, uint8_t *datap)
{
}
static void
cfi_read_2(device_t self, flash_off_t offset, uint16_t *datap)
{
}
static void
cfi_read_4(device_t self, flash_off_t offset, uint32_t *datap)
{
}
static void
cfi_read_buf_1(device_t self, flash_off_t offset, uint8_t *datap, size_t size)
{
}
static void
cfi_read_buf_2(device_t self, flash_off_t offset, uint16_t *datap, size_t size)
{
}
static void
cfi_read_buf_4(device_t self, flash_off_t offset, uint32_t *datap, size_t size)
{
}
static void
cfi_write_1(device_t self, flash_off_t offset, uint8_t data)
{
}
static void
cfi_write_2(device_t self, flash_off_t offset, uint16_t data)
{
}
static void
cfi_write_4(device_t self, flash_off_t offset, uint32_t data)
{
}
static void
cfi_write_buf_1(device_t self, flash_off_t offset, const uint8_t *datap,
size_t size)
{
}
static void
cfi_write_buf_2(device_t self, flash_off_t offset, const uint16_t *datap,
size_t size)
{
}
static void
cfi_write_buf_4(device_t self, flash_off_t offset, const uint32_t *datap,
size_t size)
{
}
void
cfi_cmd(struct cfi * const cfi, bus_size_t off, uint32_t val)
{
const bus_space_tag_t bst = cfi->cfi_bst;
bus_space_handle_t bsh = cfi->cfi_bsh;
off <<= cfi->cfi_portwidth;
DPRINTF(("%s: %p %x %x %x\n", __func__, bst, bsh, off, val));
switch(cfi->cfi_portwidth) {
case 0:
bus_space_write_1(bst, bsh, off, (uint8_t)val);
break;
case 1:
bus_space_write_2(bst, bsh, off, val);
break;
case 2:
bus_space_write_4(bst, bsh, off, (uint32_t)val);
break;
#ifdef NOTYET
case 3:
bus_space_write_4(bst, bsh, off, (uint64_t)val);
break;
#endif
default:
panic("%s: bad portwidth %d bytes\n",
__func__, 1 << cfi->cfi_portwidth);
}
}
/*
* cfi_reset_default - when we don't know which command will work, use both
*/
void
cfi_reset_default(struct cfi * const cfi)
{
cfi_cmd(cfi, CFI_ADDRESS_ANY, CFI_RESET_DATA);
cfi_cmd(cfi, CFI_ADDRESS_ANY, CFI_ALT_RESET_DATA);
}
/*
* cfi_reset_std - use standard reset command
*/
void
cfi_reset_std(struct cfi * const cfi)
{
cfi_cmd(cfi, CFI_ADDRESS_ANY, CFI_RESET_DATA);
}
/*
* cfi_reset_alt - use "alternate" reset command
*/
void
cfi_reset_alt(struct cfi * const cfi)
{
cfi_cmd(cfi, CFI_ADDRESS_ANY, CFI_ALT_RESET_DATA);
}
static void
cfi_jedec_id_2(struct cfi * const cfi)
{
struct cfi_jedec_id_data *idp = &cfi->cfi_id_data;
uint16_t data[0x10];
bus_space_read_region_2(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
__arraycount(data));
idp->id_mid = data[0];
idp->id_did[0] = data[1];
idp->id_did[1] = data[0xe];
idp->id_did[2] = data[0xf];
idp->id_prot_state = data[2];
idp->id_indicators = data[3];
/* software bits, upper and lower
* - undefined on S29GL-P
* - defined on S29GL-S
*/
idp->id_swb_lo = data[0xc];
idp->id_swb_hi = data[0xd];
}
/*
* cfi_jedec_id - get JEDEC ID info
*
* this should be ignored altogether for CFI chips?
* JEDEC ID is superceded by CFI info except CFI is not
* a true superset of the JEDEC, so some info provided
* by JEDEC is not available via CFI QRY.
* But the JEDEC info is unreliable:
* - different chips not distinguishaable by IDs
* - some fields undefined (read as 0xff) on some chips
*/
static bool
cfi_jedec_id(struct cfi * const cfi)
{
DPRINTF(("%s\n", __func__));
cfi_cmd(cfi, 0x555, 0xaa);
cfi_cmd(cfi, 0x2aa, 0x55);
cfi_cmd(cfi, 0x555, 0x90);
switch(cfi->cfi_portwidth) {
case 1:
cfi_jedec_id_2(cfi);
break;
#ifdef NOTYET
case 0:
cfi_jedec_id_1(cfi);
break;
case 2:
cfi_jedec_id_4(cfi);
break;
case 3:
cfi_jedec_id_8(cfi);
break;
#endif
default:
panic("%s: bad portwidth %d bytes\n",
__func__, 1 << cfi->cfi_portwidth);
}
return true;
}
void
cfi_print(device_t self, struct cfi * const cfi)
{
char pbuf[sizeof("XXXX MB")];
struct cfi_query_data * const qryp = &cfi->cfi_qry_data;
format_bytes(pbuf, sizeof(pbuf), 1 << qryp->device_size);
aprint_normal_dev(self, "CFI NOR flash %s %s\n", pbuf,
cfi_interface_desc_str(qryp->interface_code_desc));
#ifdef NOR_VERBOSE
aprint_normal_dev(self, "manufacturer id %#x, device id %#x %#x %#x\n",
cfi->cfi_id_data.id_mid,
cfi->cfi_id_data.id_did[0],
cfi->cfi_id_data.id_did[1],
cfi->cfi_id_data.id_did[2]);
aprint_normal_dev(self, "%s\n", cfi->cfi_opmode->str);
aprint_normal_dev(self, "sw bits lo=%#x hi=%#x\n",
cfi->cfi_id_data.id_swb_lo,
cfi->cfi_id_data.id_swb_hi);
aprint_normal_dev(self, "max multibyte write size %d\n",
1 << qryp->write_nbyte_size_max);
aprint_normal_dev(self, "%d Erase Block Region(s)\n",
qryp->erase_blk_regions);
for (u_int r=0; r < qryp->erase_blk_regions; r++) {
size_t sz = qryp->erase_blk_info[r].z * 256;
format_bytes(pbuf, sizeof(pbuf), sz);
aprint_normal(" %d: %d blocks, size %s\n", r,
qryp->erase_blk_info[r].y + 1, pbuf);
}
#endif
switch (cfi->cfi_qry_data.id_pri) {
case 0x0002:
cfi_0002_print(self, cfi);
break;
}
}

View File

@ -1,16 +1,240 @@
/* $NetBSD: cfi.h,v 1.2 2011/07/15 19:19:57 cliff Exp $ */
#ifndef _CFI_H_
#define _CFI_H_
enum {
CFI_QUERY_MODE_ADDRESS = 0x55, /* some devices accept anything */
CFI_QUERY_DATA = 0x98,
#include <dev/nor/cfi_0002.h>
#include <machine/bus.h>
CFI_READ_ARRAY_MODE_ADDRESS = 0x00, /* could be anything */
CFI_READ_ARRAY_MODE_DATA = 0xf0, /* also can be 0xff */
/*
* minimum size to bus_space_map for probe/identify QRY:
* larget offset needed is CFI_QUERY_MODE_ALT_ADDRESS
* scaled by maximum attempted port width, so
* min >= (0x555 * sizeof(uint32_t))
*/
#define CFI_QRY_MIN_MAP_SIZE 0x2000
CFI_ADDR_IDENTIFICATION_STRING = 0x10,
CFI_ADDR_SYS_INTERFACE_INFO = 0x1b,
CFI_ADDR_DEV_GEOMETRY = 0x27
typedef enum {
CFI_STATE_DATA_ARRAY = 0,
CFI_STATE_QUERY,
/* TBD */
} cfi_state_t;
/*
* CFI Query structure
*/
struct cfi_query_data {
/* Query info */
uint8_t qry[3]; /* { 'Q', 'R', 'Y' } */
uint16_t id_pri; /* primary comand set ID */
uint16_t addr_pri; /* primary table addr */
uint16_t id_alt; /* alternate command set ID */
uint16_t addr_alt; /* alternate table addr */
/* System Interface info */
uint8_t vcc_min; /* min Vcc */
uint8_t vcc_max; /* max Vcc */
uint8_t vpp_min; /* min Vpp */
uint8_t vpp_max; /* max Vpp */
uint8_t write_word_time_typ; /* typ 1-word timeout, 1<<N usec */
uint8_t write_nbyte_time_typ; /* typ multi-byte timeout, 1<<N usec */
uint8_t erase_blk_time_typ; /* typ 1-blk erase timeout, 1<<N msec */
uint8_t erase_chiptime_typ; /* typ chip erase timeout, 1<<N msec */
uint8_t write_word_time_max; /* max 1-word timeout, typ<<N */
uint8_t write_nbyte_time_max; /* max multi-byte timeout, typ<<N */
uint8_t erase_blk_time_max; /* max 1-blk erase timeout, typ<<N */
uint8_t erase_chiptime_max; /* max chip erase timeout, typ<<N */
/* Device Geometry Definition */
uint8_t device_size; /* 1<<N bytes */
uint16_t interface_code_desc; /* JEP137 interface code description */
uint16_t write_nbyte_size_max; /* max size of multi-byte write, 1<<N */
uint8_t erase_blk_regions; /* number of erase block regions */
struct {
uint16_t z; /* Erase Blocks are z * 256 bytes */
uint16_t y; /* y+1 = #Erase Blocks in region */
} erase_blk_info[4];
/* Vendor-specific Primary command set info */
union {
struct cmdset_0002_query_data cmd_0002;
} pri;
#ifdef NOTYET
/* Vendor-specific Alternate command set info */
union {
/* some command set structure here */
} pri;
#endif
};
/*
* decode interface_code_desc
*/
static inline const char *
cfi_interface_desc_str(uint16_t icd)
{
switch(icd) {
case 0:
return "x8";
case 1:
return "x16";
case 2:
return "x8/x16";
default:
return "";
}
}
/*
* id_pri: CFI Command set and control assignments
*/
#define CFI_ID_PRI_NONE 0x0000
#define CFI_ID_PRI_INTEL_EXT 0x0001
#define CFI_ID_PRI_AMD_STD 0x0002
#define CFI_ID_PRI_INTEL_STD 0x0003
#define CFI_ID_PRI_AMD_EXT 0x0004
#define CFI_ID_PRI_WINBOND 0x0005
#define CFI_ID_PRI_ST_ADV 0x0020
#define CFI_ID_PRI_MITSU_ADV 0x0100
#define CFI_ID_PRI_MITSU_EXT 0x0101
#define CFI_ID_PRI_SST_PAGE 0x0102
#define CFI_ID_PRI_SST_OLD 0x0701
#define CFI_ID_PRI_INTEL_PERF 0x0200
#define CFI_ID_PRI_INTEL_DATA 0x0210
#define CFI_ID_PRI_RESV 0xffff /* not allowed, reserved */
/*
* JEDEC ID (autoselect) data
*/
struct cfi_jedec_id_data {
uint16_t id_mid; /* manufacturer ID */
uint16_t id_did[3]; /* device ID */
uint16_t id_prot_state;
uint16_t id_indicators;
uint8_t id_swb_lo; /* lower software bits */
uint8_t id_swb_hi; /* upper software bits */
};
/*
* table entry used to determine operating mode by QRY signature
*/
struct cfi_opmodes {
uint8_t portwidth; /* (1<<N) bytes */
uint8_t chipwidth; /* (1<<N) bytes */
uint8_t interleave; /* (1<<N) bytes */
uint8_t qsa; /* Query Start Address (in bytes) */
uint8_t len; /* signature length */
const uint8_t *sig; /* signature */
const char *str; /* descriptive string */
};
struct cfi; /* fwd ref */
struct cfi_ops {
void (*cfi_reset)(struct cfi *);
int (*cfi_busy)(struct cfi *, flash_off_t);
int (*cfi_program_word)(struct cfi *, flash_off_t);
int (*cfi_erase_sector)(struct cfi *, flash_off_t);
};
/* NOTE:
* CFI_0002_STATS are just meant temporarily for debugging
* not for long-term use. Some event counters at the flash and nor
* layers might be helpful eventually
*/
#define CFI_0002_STATS /* XXX TMP */
#ifdef CFI_0002_STATS
struct cfi_0002_stats {
u_long read_page;
u_long program_page;
u_long erase_all;
u_long erase_block;
u_long busy;
u_long busy_usec_min;
u_long busy_usec_max;
struct timeval busy_poll_tv;
struct timeval busy_yield_tv;
u_long busy_poll;
u_long busy_yield;
u_long busy_yield_hit;
u_long busy_yield_miss;
u_long busy_yield_timo;
};
extern void cfi_0002_stats_reset(struct cfi *);
extern void cfi_0002_stats_print(struct cfi *);
#define CFI_0002_STATS_INIT(dev, cfi) \
do { \
aprint_normal_dev(dev, "cfi=%p\n", cfi); \
cfi_0002_stats_reset(cfi); \
} while (0)
#define CFI_0002_STATS_INC(cfi, field) (cfi)->cfi_0002_stats.field++
#else
#define CFI_0002_STATS_INIT(cfi)
#define CFI_0002_STATS_INC(cfi, field)
#endif /* CFI_0002_STATS */
struct cfi {
bus_space_tag_t cfi_bst;
bus_space_handle_t cfi_bsh;
cfi_state_t cfi_state;
uint8_t cfi_portwidth; /* port width, 1<<N bytes */
uint8_t cfi_chipwidth; /* chip width, 1<<N bytes */
struct cfi_query_data cfi_qry_data; /* CFI Query data */
struct cfi_jedec_id_data
cfi_id_data; /* JEDEC ID data */
const struct cfi_opmodes
*cfi_opmode;
struct cfi_ops cfi_ops; /* chip dependent functions */
u_long cfi_yield_time; /* thresh. for yield in wait */
#ifdef CFI_0002_STATS
struct cfi_0002_stats cfi_0002_stats;
#endif
};
enum {
CFI_ADDRESS_ANY = 0x00, /* XXX "don't care" */
CFI_RESET_DATA = 0xf0,
CFI_ALT_RESET_DATA = 0xff,
CFI_QUERY_MODE_ADDRESS = 0x55, /* some devices accept anything */
CFI_QUERY_MODE_ALT_ADDRESS = 0x555,
CFI_QUERY_DATA = 0x98,
};
static inline void
cfi_reset(struct cfi * const cfi)
{
KASSERT(cfi->cfi_ops.cfi_reset != NULL);
cfi->cfi_ops.cfi_reset(cfi);
}
static inline int
cfi_erase_sector(struct cfi * const cfi, flash_off_t offset)
{
KASSERT(cfi->cfi_ops.cfi_erase_sector != NULL);
return cfi->cfi_ops.cfi_erase_sector(cfi, offset);
}
static inline int
cfi_program_word(struct cfi * const cfi, flash_off_t offset)
{
KASSERT(cfi->cfi_ops.cfi_program_word != NULL);
return cfi->cfi_ops.cfi_program_word(cfi, offset);
}
extern const struct nor_interface nor_interface_cfi;
extern bool cfi_probe(struct cfi * const);
extern bool cfi_identify(struct cfi * const);
extern void cfi_print(device_t, struct cfi * const);
extern void cfi_reset_default(struct cfi * const);
extern void cfi_reset_std(struct cfi * const);
extern void cfi_reset_alt(struct cfi * const);
extern void cfi_cmd(struct cfi * const, bus_size_t, uint32_t);
#endif /* _CFI_H_ */

608
sys/dev/nor/cfi_0002.c Normal file
View File

@ -0,0 +1,608 @@
/* $NetBSD: cfi_0002.c,v 1.1 2011/07/15 19:19:57 cliff Exp $ */
#include "opt_flash.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cfi_0002.c,v 1.1 2011/07/15 19:19:57 cliff Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/cdefs.h>
#include <sys/device.h>
#include <sys/endian.h>
#include <sys/time.h>
#include <machine/bus.h>
#include <dev/nor/nor.h>
#include <dev/nor/cfi.h>
#include <dev/nor/cfi_0002.h>
static void cfi_0002_version_init(struct cfi * const);
static int cfi_0002_read_page(device_t, flash_off_t, uint8_t *);
static int cfi_0002_program_page(device_t, flash_off_t, const uint8_t *);
static int cfi_0002_erase_block(device_t, flash_off_t);
static int cfi_0002_erase_all(device_t);
static int cfi_0002_busy(device_t, flash_off_t, u_long);
static int cfi_0002_busy_wait(struct cfi * const, flash_off_t, u_long);
static int cfi_0002_busy_poll(struct cfi * const, flash_off_t, u_long);
static int cfi_0002_busy_yield(struct cfi * const, flash_off_t, u_long);
static int cfi_0002_busy_dq7(struct cfi * const , flash_off_t);
#ifdef NOTYET
static int cfi_0002_busy_reg(struct cfi * const, flash_off_t);
#endif
static const char *page_mode_str[] = {
"(not supported)",
"4 word page",
"8 word page",
"16 word page",
};
static const char *wp_mode_str[] = {
"Flash device without WP Protect (No Boot)",
"Eight 8 kB Sectors at TOP and Bottom with WP (Dual Boot)",
"Bottom Boot Device with WP Protect (Bottom Boot)",
"Top Boot Device with WP Protect (Top Boot)",
"Uniform, Bottom WP Protect (Uniform Bottom Boot)",
"Uniform, Top WP Protect (Uniform Top Boot)",
"WP Protect for all sectors",
"Uniform, Top or Bottom WP Protect",
};
static inline const char *
cfi_0002_page_mode_str(uint8_t mode)
{
if (mode >= __arraycount(page_mode_str))
panic("%s: mode %d out of range", __func__, mode);
return page_mode_str[mode];
}
static inline const char *
cfi_0002_wp_mode_str(uint8_t mode)
{
if (mode >= __arraycount(wp_mode_str))
panic("%s: mode %d out of range", __func__, mode);
return wp_mode_str[mode];
}
/*
* cfi_0002_time_write_nbyte - maximum usec delay waiting for write buffer
*/
static inline u_long
cfi_0002_time_write_nbyte(struct cfi *cfi)
{
u_int shft = cfi->cfi_qry_data.write_nbyte_time_typ;
shft += cfi->cfi_qry_data.write_nbyte_time_max;
u_long usec = 1UL << shft;
return usec;
}
/*
* cfi_0002_time_erase_blk - maximum usec delay waiting for erase block
*/
static inline u_long
cfi_0002_time_erase_blk(struct cfi *cfi)
{
u_int shft = cfi->cfi_qry_data.erase_blk_time_typ;
shft += cfi->cfi_qry_data.erase_blk_time_max;
u_long usec = 1000UL << shft;
return usec;
}
/*
* cfi_0002_time_erase_all - maximum usec delay waiting for erase chip
*/
static inline u_long
cfi_0002_time_erase_all(struct cfi *cfi)
{
u_int shft = cfi->cfi_qry_data.erase_chiptime_typ;
shft += cfi->cfi_qry_data.erase_chiptime_max;
u_long usec = 1000UL << shft;
return usec;
}
/*
* cfi_0002_time_dflt - maximum usec delay to use waiting for ready
*
* use the maximum delay for chip erase function
* that should be the worst non-sick case
*/
static inline u_long
cfi_0002_time_dflt(struct cfi *cfi)
{
return cfi_0002_time_erase_all(cfi);
}
void
cfi_0002_init(struct nor_softc * const sc, struct cfi * const cfi,
struct nor_chip * const chip)
{
CFI_0002_STATS_INIT(sc->sc_dev, cfi);
cfi_0002_version_init(cfi);
cfi->cfi_ops.cfi_reset = cfi_reset_std;
cfi->cfi_yield_time = 500; /* 500 usec */
/* page size for buffered write */
chip->nc_page_size =
1 << cfi->cfi_qry_data.write_nbyte_size_max;
/* these are unused */
chip->nc_spare_size = 0;
chip->nc_badmarker_offs = 0;
/* establish command-set-specific interface ops */
sc->sc_nor_if->read_page = cfi_0002_read_page;
sc->sc_nor_if->program_page = cfi_0002_program_page;
sc->sc_nor_if->erase_block = cfi_0002_erase_block;
sc->sc_nor_if->erase_all = cfi_0002_erase_all;
sc->sc_nor_if->busy = cfi_0002_busy;
}
/*
* cfi_0002_version_init - command set version-specific initialization
*
* see "Programmer's Guide for the Spansion 65 nm GL-S MirrorBit EclipseTM
* Flash Non-Volatile Memory Family Architecture" section 5.
*/
static void
cfi_0002_version_init(struct cfi * const cfi)
{
const uint8_t major = cfi->cfi_qry_data.pri.cmd_0002.version_maj;
const uint8_t minor = cfi->cfi_qry_data.pri.cmd_0002.version_min;
if ((minor == '3') && (major == '1')) {
/* cmdset version 1.3 */
cfi->cfi_ops.cfi_busy = cfi_0002_busy_dq7;
#ifdef NOTYET
cfi->cfi_ops.cfi_erase_sector = cfi_0002_erase_sector_q;
cfi->cfi_ops.cfi_program_word = cfi_0002_program_word_ub;
} else if ((minor >= '5') && (major == '1')) {
/* cmdset version 1.5 or later */
cfi->cfi_ops.cfi_busy = cfi_0002_busy_reg;
cfi->cfi_ops.cfi_erase_sector = cfi_0002_erase_sector_1;
cfi->cfi_ops.cfi_program_word = cfi_0002_program_word_no_ub;
#endif
} else {
/* XXX this is excessive */
panic("%s: unknown cmdset version %c.%c\n",
__func__, major, minor);
}
}
void
cfi_0002_print(device_t self, struct cfi * const cfi)
{
#ifdef NOR_VERBOSE
struct cmdset_0002_query_data *pri = &cfi->cfi_qry_data.pri.cmd_0002;
aprint_normal_dev(self, "AMD/Fujitsu cmdset (0x0002) version=%c.%c\n",
pri->version_maj, pri->version_min);
aprint_normal_dev(self, "page mode type: %s\n",
cfi_0002_page_mode_str(pri->page_mode_type));
aprint_normal_dev(self, "wp protection: %s\n",
cfi_0002_wp_mode_str(pri->wp_prot));
aprint_normal_dev(self, "program suspend %ssupported\n",
(pri->prog_susp == 0) ? "not " : "");
aprint_normal_dev(self, "unlock bypass %ssupported\n",
(pri->unlock_bypass == 0) ? "not " : "");
aprint_normal_dev(self, "secure silicon sector size %#x\n",
1 << pri->sss_size);
aprint_normal_dev(self, "SW features %#x\n", pri->soft_feat);
aprint_normal_dev(self, "page size %d\n", 1 << pri->page_size);
#endif
}
static int
cfi_0002_read_page(device_t self, flash_off_t offset, uint8_t *datap)
{
struct nor_softc * const sc = device_private(self);
KASSERT(sc != NULL);
KASSERT(sc->sc_nor_if != NULL);
struct cfi *cfi = (struct cfi * const)sc->sc_nor_if->private;
KASSERT(cfi != NULL);
struct nor_chip * const chip = &sc->sc_chip;
KASSERT(chip != NULL);
KASSERT(chip->nc_page_mask != 0);
KASSERT((offset & ~chip->nc_page_mask) == 0);
KASSERT (chip->nc_page_size != 0);
KASSERT((chip->nc_page_size & ((1 << cfi->cfi_portwidth) - 1)) == 0);
CFI_0002_STATS_INC(cfi, read_page);
bus_size_t count = chip->nc_page_size >> cfi->cfi_portwidth;
/* #words/page */
int error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_dflt(cfi));
if (error != 0)
return error;
switch(cfi->cfi_portwidth) {
case 0:
bus_space_read_region_1(cfi->cfi_bst, cfi->cfi_bsh, offset,
(uint8_t *)datap, count);
break;
case 1:
bus_space_read_region_2(cfi->cfi_bst, cfi->cfi_bsh, offset,
(uint16_t *)datap, count);
break;
case 2:
bus_space_read_region_4(cfi->cfi_bst, cfi->cfi_bsh, offset,
(uint32_t *)datap, count);
break;
default:
panic("%s: bad port width %d\n", __func__, cfi->cfi_portwidth);
};
return 0;
}
static int
cfi_0002_program_page(device_t self, flash_off_t offset, const uint8_t *datap)
{
struct nor_softc * const sc = device_private(self);
KASSERT(sc != NULL);
KASSERT(sc->sc_nor_if != NULL);
struct cfi *cfi = (struct cfi * const)sc->sc_nor_if->private;
KASSERT(cfi != NULL);
struct nor_chip * const chip = &sc->sc_chip;
KASSERT(chip != NULL);
KASSERT(chip->nc_page_mask != 0);
KASSERT((offset & ~chip->nc_page_mask) == 0);
KASSERT (chip->nc_page_size != 0);
KASSERT((chip->nc_page_size & ((1 << cfi->cfi_portwidth) - 1)) == 0);
CFI_0002_STATS_INC(cfi, program_page);
bus_size_t count = chip->nc_page_size >> cfi->cfi_portwidth;
/* #words/page */
bus_size_t sa = offset >> cfi->cfi_portwidth; /* sector addr */
uint32_t wc = count - 1; /* #words - 1 */
int error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_dflt(cfi));
if (error != 0)
return ETIMEDOUT;
cfi_cmd(cfi, 0x555, 0xaa); /* unlock 1 */
cfi_cmd(cfi, 0x2aa, 0x55); /* unlock 2 */
cfi_cmd(cfi, sa, 0x25); /* Write To Buffer */
cfi_cmd(cfi, sa, wc);
switch(cfi->cfi_portwidth) {
case 0:
bus_space_write_region_1(cfi->cfi_bst, cfi->cfi_bsh, offset,
(const uint8_t *)datap, count);
break;
case 1:
bus_space_write_region_2(cfi->cfi_bst, cfi->cfi_bsh, offset,
(const uint16_t *)datap, count);
break;
case 2:
bus_space_write_region_4(cfi->cfi_bst, cfi->cfi_bsh, offset,
(const uint32_t *)datap, count);
break;
default:
panic("%s: bad port width %d\n", __func__, cfi->cfi_portwidth);
};
cfi_cmd(cfi, sa, 0x29); /* Write Buffer Program Confirm */
error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_write_nbyte(cfi));
return error;
}
static int
cfi_0002_erase_all(device_t self)
{
struct nor_softc * const sc = device_private(self);
KASSERT(sc != NULL);
KASSERT(sc->sc_nor_if != NULL);
struct cfi *cfi = (struct cfi * const)sc->sc_nor_if->private;
KASSERT(cfi != NULL);
struct nor_chip * const chip = &sc->sc_chip;
KASSERT(chip != NULL);
CFI_0002_STATS_INC(cfi, erase_all);
int error = cfi_0002_busy_wait(cfi, 0, cfi_0002_time_dflt(cfi));
if (error != 0)
return ETIMEDOUT;
cfi_cmd(cfi, 0x555, 0xaa); /* unlock 1 */
cfi_cmd(cfi, 0x2aa, 0x55); /* unlock 2 */
cfi_cmd(cfi, 0x555, 0x80); /* erase start */
cfi_cmd(cfi, 0x555, 0xaa); /* unlock 1 */
cfi_cmd(cfi, 0x2aa, 0x55); /* unlock 2 */
cfi_cmd(cfi, 0x555, 0x10); /* erase chip */
error = cfi_0002_busy_wait(cfi, 0, cfi_0002_time_erase_all(cfi));
return error;
}
static int
cfi_0002_erase_block(device_t self, flash_off_t offset)
{
struct nor_softc * const sc = device_private(self);
KASSERT(sc != NULL);
KASSERT(sc->sc_nor_if != NULL);
struct cfi *cfi = (struct cfi * const)sc->sc_nor_if->private;
KASSERT(cfi != NULL);
struct nor_chip * const chip = &sc->sc_chip;
KASSERT(chip != NULL);
KASSERT(chip->nc_block_mask != 0);
KASSERT((offset & ~chip->nc_block_mask) == 0);
KASSERT(chip->nc_block_size != 0);
KASSERT((chip->nc_block_size & ((1 << cfi->cfi_portwidth) - 1)) == 0);
CFI_0002_STATS_INC(cfi, erase_block);
/* scale sector addr by portwidth or chipwidth ? */
bus_size_t sa = offset >> cfi->cfi_portwidth;
int error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_dflt(cfi));
if (error != 0)
return ETIMEDOUT;
cfi_cmd(cfi, 0x555, 0xaa); /* unlock 1 */
cfi_cmd(cfi, 0x2aa, 0x55); /* unlock 2 */
cfi_cmd(cfi, 0x555, 0x80); /* erase start */
cfi_cmd(cfi, 0x555, 0xaa); /* unlock 1 */
cfi_cmd(cfi, 0x2aa, 0x55); /* unlock 2 */
cfi_cmd(cfi, sa, 0x30); /* erase sector */
error = cfi_0002_busy_wait(cfi, offset, cfi_0002_time_erase_blk(cfi));
return error;
}
/*
* cfi_0002_busy - nor_interface busy op
*/
static int
cfi_0002_busy(device_t self, flash_off_t offset, u_long usec)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc != NULL);
KASSERT(sc->sc_nor_if != NULL);
struct cfi * const cfi = (struct cfi * const)sc->sc_nor_if->private;
CFI_0002_STATS_INC(cfi, busy);
return cfi_0002_busy_wait(cfi, offset, usec);
}
/*
* cfi_0002_busy_wait - wait until device is not busy
*/
static int
cfi_0002_busy_wait(struct cfi * const cfi, flash_off_t offset, u_long usec)
{
int error;
#ifdef CFI_0002_STATS
struct timeval start;
struct timeval now;
struct timeval delta;
if (usec > cfi->cfi_0002_stats.busy_usec_max)
cfi->cfi_0002_stats.busy_usec_max = usec;
if (usec < cfi->cfi_0002_stats.busy_usec_min)
cfi->cfi_0002_stats.busy_usec_min = usec;
microtime(&start);
#endif
if (usec > cfi->cfi_yield_time) {
error = cfi_0002_busy_yield(cfi, offset, usec);
#ifdef CFI_0002_STATS
microtime(&now);
cfi->cfi_0002_stats.busy_yield++;
timersub(&now, &start, &delta);
timeradd(&delta,
&cfi->cfi_0002_stats.busy_yield_tv,
&cfi->cfi_0002_stats.busy_yield_tv);
#endif
} else {
error = cfi_0002_busy_poll(cfi, offset, usec);
#ifdef CFI_0002_STATS
microtime(&now);
cfi->cfi_0002_stats.busy_poll++;
timersub(&now, &start, &delta);
timeradd(&delta,
&cfi->cfi_0002_stats.busy_poll_tv,
&cfi->cfi_0002_stats.busy_poll_tv);
#endif
}
return error;
}
/*
* cfi_0002_busy_poll - poll until device is not busy
*/
static int
cfi_0002_busy_poll(struct cfi * const cfi, flash_off_t offset, u_long usec)
{
u_long count = usec >> 3;
if (count == 0)
count = 1; /* enforce minimum */
do {
if (! cfi->cfi_ops.cfi_busy(cfi, offset))
return 0; /* not busy */
DELAY(8);
} while (count-- != 0);
return ETIMEDOUT; /* busy */
}
/*
* cfi_0002_busy_yield - yield until device is not busy
*/
static int
cfi_0002_busy_yield(struct cfi * const cfi, flash_off_t offset, u_long usec)
{
struct timeval start;
struct timeval delta;
struct timeval limit;
struct timeval now;
microtime(&start);
/* try optimism */
if (! cfi->cfi_ops.cfi_busy(cfi, offset)) {
CFI_0002_STATS_INC(cfi, busy_yield_hit);
return 0; /* not busy */
}
CFI_0002_STATS_INC(cfi, busy_yield_miss);
delta.tv_sec = usec / 1000000;
delta.tv_usec = usec % 1000000;
timeradd(&start, &delta, &limit);
do {
yield();
microtime(&now);
if (! cfi->cfi_ops.cfi_busy(cfi, offset))
return 0; /* not busy */
} while (timercmp(&now, &limit, <));
CFI_0002_STATS_INC(cfi, busy_yield_timo);
return ETIMEDOUT; /* busy */
}
/*
* cfi_0002_busy_dq7 - DQ7 "toggle" method to check busy
*
* Check busy during/after erase, program, protect operation.
*
* NOTE:
* Chip manufacturers (Spansion) plan to deprecate this method.
*/
static int
cfi_0002_busy_dq7(struct cfi * const cfi, flash_off_t offset)
{
bus_space_tag_t bst = cfi->cfi_bst;
bus_space_handle_t bsh = cfi->cfi_bsh;
bool busy;
switch(cfi->cfi_portwidth) {
case 0: {
uint8_t r0 = bus_space_read_1(bst, bsh, 0) & __BIT(7);
uint8_t r1 = bus_space_read_1(bst, bsh, 0) & __BIT(7);
busy = (r0 != r1);
break;
}
case 1: {
uint16_t r0 = bus_space_read_2(bst, bsh, 0);
uint16_t r1 = bus_space_read_2(bst, bsh, 0);
busy = (r0 != r1);
break;
}
case 2: {
uint32_t r0 = bus_space_read_4(bst, bsh, 0);
uint32_t r1 = bus_space_read_4(bst, bsh, 0);
busy = (r0 != r1);
break;
}
default:
busy = true; /* appeas gcc */
panic("%s: bad port width %d\n",
__func__, cfi->cfi_portwidth);
}
return busy;
}
#ifdef NOTYET
/*
* cfi_0002_busy_reg - read and evaluate Read Status Register
*
* NOTE:
* Read Status Register not present on all chips
* use "toggle" method when Read Status Register not available.
*/
static bool
cfi_0002_busy_reg(struct cfi * const cfi, flash_off_t offset)
{
bus_space_tag_t bst = cfi->cfi_bst;
bus_space_handle_t bsh = cfi->cfi_bsh;
uint32_t r;
cfi_cmd(cfi, 0x555, 0x70); /* Status Register Read */
switch(cfi->cfi_portwidth) {
case 0:
r = bus_space_read_1(bst, bsh, 0);
break;
case 1:
r = bus_space_read_2(bst, bsh, 0);
break;
case 2:
r = bus_space_read_4(bst, bsh, 0);
break;
default:
panic("%s: bad port width %d\n",
__func__, cfi->cfi_portwidth);
}
return ((r & __BIT(7)) == 0):
}
#endif /* NOTYET */
#ifdef CFI_0002_STATS
void
cfi_0002_stats_reset(struct cfi *cfi)
{
memset(&cfi->cfi_0002_stats, 0, sizeof(struct cfi_0002_stats));
cfi->cfi_0002_stats.busy_usec_min = ~0;
}
void
cfi_0002_stats_print(struct cfi *cfi)
{
printf("read_page %lu\n", cfi->cfi_0002_stats.read_page);
printf("program_page %lu\n", cfi->cfi_0002_stats.program_page);
printf("erase_all %lu\n", cfi->cfi_0002_stats.erase_all);
printf("erase_block %lu\n", cfi->cfi_0002_stats.erase_block);
printf("busy %lu\n", cfi->cfi_0002_stats.busy);
printf("write_nbyte_time_typ %d\n",
cfi->cfi_qry_data.write_nbyte_time_typ);
printf("write_nbyte_time_max %d\n",
cfi->cfi_qry_data.write_nbyte_time_max);
printf("erase_blk_time_typ %d\n",
cfi->cfi_qry_data.erase_blk_time_typ);
printf("erase_blk_time_max %d\n",
cfi->cfi_qry_data.erase_blk_time_max);
printf("erase_chiptime_typ %d\n",
cfi->cfi_qry_data.erase_chiptime_typ);
printf("erase_chiptime_max %d\n",
cfi->cfi_qry_data.erase_chiptime_max);
printf("time_write_nbyte %lu\n", cfi_0002_time_write_nbyte(cfi));
printf("time_erase_blk %lu\n", cfi_0002_time_erase_blk(cfi));
printf("time_erase_all %lu\n", cfi_0002_time_erase_all(cfi));
printf("busy_usec_min %lu\n", cfi->cfi_0002_stats.busy_usec_min);
printf("busy_usec_max %lu\n", cfi->cfi_0002_stats.busy_usec_max);
printf("busy_poll_tv %lld.%d\n",
cfi->cfi_0002_stats.busy_poll_tv.tv_sec,
cfi->cfi_0002_stats.busy_poll_tv.tv_usec);
printf("busy_yield_tv %lld.%d\n",
cfi->cfi_0002_stats.busy_yield_tv.tv_sec,
cfi->cfi_0002_stats.busy_yield_tv.tv_usec);
printf("busy_poll %lu\n", cfi->cfi_0002_stats.busy_poll);
printf("busy_yield %lu\n", cfi->cfi_0002_stats.busy_yield);
printf("busy_yield_hit %lu\n", cfi->cfi_0002_stats.busy_yield_hit);
printf("busy_yield_miss %lu\n", cfi->cfi_0002_stats.busy_yield_miss);
printf("busy_yield_timo %lu\n", cfi->cfi_0002_stats.busy_yield_timo);
}
#endif /* CFI_0002_STATS */

46
sys/dev/nor/cfi_0002.h Normal file
View File

@ -0,0 +1,46 @@
/* $NetBSD: cfi_0002.h,v 1.1 2011/07/15 19:19:57 cliff Exp $ */
#ifndef _DEV_NOR_CFI_0002_H_
#define _DEV_NOR_CFI_0002_H_
/*
* CFI Primary Vendor-specific Extended Query structure
* AMD/Fujitsu Extended Command Set 0002
*/
struct cmdset_0002_query_data {
uint8_t pri[3]; /* { 'P', 'R', 'I' } */
uint8_t version_maj; /* major version number (ASCII) */
uint8_t version_min; /* minor version number (ASCII) */
uint8_t asupt; /* Si rev., addr-sensitive unlock */
uint8_t erase_susp; /* erase-suspend */
uint8_t sector_prot; /* sector protect */
uint8_t tmp_sector_unprot; /* temporary sector unprotect */
uint8_t sector_prot_scheme; /* sector protect scheme */
uint8_t simul_op; /* simultaneous operation */
uint8_t burst_mode_type; /* burst mode type */
uint8_t page_mode_type; /* page mode type */
uint8_t acc_min; /* Acc supply min voltage */
uint8_t acc_max; /* Acc supply max voltage */
uint8_t wp_prot; /* WP# protection */
uint8_t prog_susp; /* prpogram suspend */
uint8_t unlock_bypass; /* unlock bypass */
uint8_t sss_size; /* secured silicon sector size (1<<N) */
uint8_t soft_feat; /* software features */
uint8_t page_size; /* page size (1<<N) */
uint8_t erase_susp_time_max; /* erase susp. timeout max, 1<<N usec */
uint8_t prog_susp_time_max; /* prog. susp. timeout max, 1<<N usec */
uint8_t embhwrst_time_max; /* emb hw rst timeout max, 1<<N usec */
uint8_t hwrst_time_max; /* !emb hw rst timeout max, 1<<N usec */
};
/* forward references for prototype(s) */
struct nor_softc;
struct cfi;
struct nor_chip;
struct cfi_chip;
extern void cfi_0002_init(struct nor_softc * const, struct cfi * const,
struct nor_chip * const);
extern void cfi_0002_print(device_t, struct cfi * const);
#endif /* _DEV_NOR_CFI_0002_H_ */

View File

@ -1,4 +1,4 @@
# $NetBSD: files.nor,v 1.1 2011/06/22 21:59:15 ahoka Exp $
# $NetBSD: files.nor,v 1.2 2011/07/15 19:19:57 cliff Exp $
define norbus { }
@ -6,6 +6,8 @@ device nor: flashbus
attach nor at norbus
file dev/nor/nor.c nor
#file dev/nor/nor_io.c nor
file dev/nor/cfi.c nor
file dev/nor/cfi_0002.c nor
defflag opt_nor.h NOR_DEBUG
defflag opt_nor.h NOR_VERBOSE

View File

@ -1,4 +1,4 @@
/* $NetBSD: nor.h,v 1.1 2011/06/22 21:59:15 ahoka Exp $ */
/* $NetBSD: nor.h,v 1.2 2011/07/15 19:19:57 cliff Exp $ */
/*-
* Copyright (c) 2011 Department of Software Engineering,
@ -39,12 +39,15 @@
#include <sys/bufq.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <machine/bus.h>
#include <dev/nor/cfi.h>
#include <dev/flash/flash.h>
#include <dev/flash/flash_io.h>
#ifdef NOR_DEBUG
#define DPRINTF(x) printf x
#define DPRINTF(x) do { printf x ; } while (0)
#else
#define DPRINTF(x)
#endif
@ -59,7 +62,8 @@ struct nor_chip {
uint8_t *nc_page_cache; /* buffer for page cache */
uint8_t *nc_ecc_cache;
size_t nc_size; /* storage size in bytes */
size_t nc_page_size; /* page size in bytes */
size_t nc_page_size; /* page (write buf) size in bytes */
size_t nc_line_size; /* read line in bytes */
size_t nc_block_pages; /* block size in pages */
size_t nc_block_size; /* block size in bytes */
size_t nc_spare_size; /* spare (oob) size in bytes */
@ -79,34 +83,47 @@ struct nor_chip {
/* driver softc for nor */
struct nor_softc {
device_t sc_dev;
device_t controller_dev;
struct nor_interface *nor_if;
device_t sc_controller_dev;
struct flash_interface sc_flash_if;
struct nor_interface *sc_nor_if;
void *nor_softc;
struct nor_chip sc_chip;
size_t sc_part_offset;
size_t sc_part_size;
bus_addr_t sc_bus_addr; /* chip base address */
bus_size_t sc_bus_size; /* total NOR size */
off_t sc_part_offset; /* partition start, offset from base */
size_t sc_part_size; /* partition size */
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
kmutex_t sc_device_lock; /* serialize access to chip */
struct flash_io sc_flash_io;
};
/* structure holding the nor api */
struct nor_interface
{
struct nor_interface {
/* basic nor controller commands */
int (*scan_media)(device_t self, struct nor_chip *chip);
void (*init) (device_t);
void (*select) (device_t, bool); /* optional */
void (*read_1) (device_t, uint8_t *);
void (*read_2) (device_t, uint16_t *);
void (*read_4) (device_t, uint32_t *);
void (*read_buf_1) (device_t, void *, size_t);
void (*read_buf_2) (device_t, void *, size_t);
void (*read_buf_4) (device_t, void *, size_t);
void (*write_1) (device_t, uint8_t);
void (*write_2) (device_t, uint16_t);
void (*write_4) (device_t, uint32_t);
void (*write_buf_1) (device_t, const void *, size_t);
void (*write_buf_2) (device_t, const void *, size_t);
void (*write_buf_4) (device_t, const void *, size_t);
void (*busy) (device_t);
void (*read_1) (device_t, flash_off_t, uint8_t *);
void (*read_2) (device_t, flash_off_t, uint16_t *);
void (*read_4) (device_t, flash_off_t, uint32_t *);
void (*read_buf_1) (device_t, flash_off_t, uint8_t *, size_t);
void (*read_buf_2) (device_t, flash_off_t, uint16_t *, size_t);
void (*read_buf_4) (device_t, flash_off_t, uint32_t *, size_t);
void (*write_1) (device_t, flash_off_t, uint8_t);
void (*write_2) (device_t, flash_off_t, uint16_t);
void (*write_4) (device_t, flash_off_t, uint32_t);
void (*write_buf_1) (device_t, flash_off_t, const uint8_t *, size_t);
void (*write_buf_2) (device_t, flash_off_t, const uint16_t *, size_t);
void (*write_buf_4) (device_t, flash_off_t, const uint32_t *, size_t);
int (*busy) (device_t, flash_off_t, u_long);
int (*read_page) (device_t, flash_off_t, uint8_t *);
int (*program_page) (device_t, flash_off_t, const uint8_t *);
int (*erase_block) (device_t, flash_off_t);
int (*erase_all) (device_t);
void *private; /* where to attach e.g. struct cfi */
int access_width; /* x8/x16/x32: 1/2/4 */
/* flash partition information */
@ -119,21 +136,27 @@ struct nor_attach_args {
struct nor_interface *naa_nor_if;
};
static inline void
nor_busy(device_t device)
static inline bool
nor_busy(device_t device, flash_off_t offset, u_long usec)
{
struct nor_softc *sc = device_private(device);
bool rv;
KASSERT(sc->nor_if->select != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->select != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->select(sc->controller_dev, true);
sc->sc_nor_if->select(sc->sc_controller_dev, true);
if (sc->nor_if->busy != NULL) {
sc->nor_if->busy(sc->controller_dev);
if (sc->sc_nor_if->busy != NULL) {
rv = sc->sc_nor_if->busy(sc->sc_controller_dev, offset, usec);
} else {
DELAY(usec);
rv = false;
}
sc->nor_if->select(sc->controller_dev, false);
sc->sc_nor_if->select(sc->sc_controller_dev, false);
return rv;
}
static inline void
@ -141,142 +164,182 @@ nor_select(device_t self, bool enable)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->select != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->select != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->select(sc->controller_dev, enable);
sc->sc_nor_if->select(sc->sc_controller_dev, enable);
}
static inline void
nor_read_1(device_t self, uint8_t *data)
nor_read_1(device_t self, flash_off_t offset, uint8_t *data)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->read_1 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->read_1 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->read_1(sc->controller_dev, data);
sc->sc_nor_if->read_1(sc->sc_controller_dev, offset, data);
}
static inline void
nor_read_2(device_t self, uint16_t *data)
nor_read_2(device_t self, flash_off_t offset, uint16_t *data)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->read_2 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->read_2 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->read_2(sc->controller_dev, data);
sc->sc_nor_if->read_2(sc->sc_controller_dev, offset, data);
}
static inline void
nor_read_4(device_t self, uint16_t *data)
nor_read_4(device_t self, flash_off_t offset, uint32_t *data)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->read_4 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->read_4 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->read_4(sc->controller_dev, data);
sc->sc_nor_if->read_4(sc->sc_controller_dev, offset, data);
}
static inline void
nor_write_1(device_t self, uint8_t data)
nor_write_1(device_t self, flash_off_t offset, uint8_t data)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->write_1 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->write_1 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->write_1(sc->controller_dev, data);
sc->sc_nor_if->write_1(sc->sc_controller_dev, offset, data);
}
static inline void
nor_write_2(device_t self, uint16_t data)
nor_write_2(device_t self, flash_off_t offset, uint16_t data)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->write_2 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->write_2 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->write_2(sc->controller_dev, data);
sc->sc_nor_if->write_2(sc->sc_controller_dev, offset, data);
}
static inline void
nor_write_4(device_t self, uint16_t data)
nor_write_4(device_t self, flash_off_t offset, uint16_t data)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->write_4 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->write_4 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->write_4(sc->controller_dev, data);
sc->sc_nor_if->write_4(sc->sc_controller_dev, offset, data);
}
static inline void
nor_read_buf_1(device_t self, void *buf, size_t size)
nor_read_buf_1(device_t self, flash_off_t offset, void *buf, size_t size)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->read_buf_1 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->read_buf_1 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->read_buf_1(sc->controller_dev, buf, size);
sc->sc_nor_if->read_buf_1(sc->sc_controller_dev, offset, buf, size);
}
static inline void
nor_read_buf_2(device_t self, void *buf, size_t size)
nor_read_buf_2(device_t self, flash_off_t offset, void *buf, size_t size)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->read_buf_2 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->read_buf_2 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->read_buf_2(sc->controller_dev, buf, size);
sc->sc_nor_if->read_buf_2(sc->sc_controller_dev, offset, buf, size);
}
static inline void
nor_read_buf_4(device_t self, void *buf, size_t size)
nor_read_buf_4(device_t self, flash_off_t offset, void *buf, size_t size)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->read_buf_4 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->read_buf_4 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->read_buf_4(sc->controller_dev, buf, size);
sc->sc_nor_if->read_buf_4(sc->sc_controller_dev, offset, buf, size);
}
static inline void
nor_write_buf_1(device_t self, const void *buf, size_t size)
nor_write_buf_1(device_t self, flash_off_t offset, const void *buf, size_t size)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->write_buf_1 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->write_buf_1 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->write_buf_1(sc->controller_dev, buf, size);
sc->sc_nor_if->write_buf_1(sc->sc_controller_dev, offset, buf, size);
}
static inline void
nor_write_buf_2(device_t self, const void *buf, size_t size)
nor_write_buf_2(device_t self, flash_off_t offset, const void *buf, size_t size)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->write_buf_2 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->write_buf_2 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->write_buf_2(sc->controller_dev, buf, size);
sc->sc_nor_if->write_buf_2(sc->sc_controller_dev, offset, buf, size);
}
static inline void
nor_write_buf_4(device_t self, const void *buf, size_t size)
nor_write_buf_4(device_t self, flash_off_t offset, const void *buf, size_t size)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->nor_if->write_buf_4 != NULL);
KASSERT(sc->controller_dev != NULL);
KASSERT(sc->sc_nor_if->write_buf_4 != NULL);
KASSERT(sc->sc_controller_dev != NULL);
sc->nor_if->write_buf_4(sc->controller_dev, buf, size);
sc->sc_nor_if->write_buf_4(sc->sc_controller_dev, offset, buf, size);
}
static inline int
nor_read_page(device_t self, flash_off_t offset, uint8_t *data)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->sc_nor_if->read_page != NULL);
return sc->sc_nor_if->read_page(self, offset, data);
}
static inline int
nor_program_page(device_t self, flash_off_t offset, const uint8_t *data)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->sc_nor_if->program_page != NULL);
return sc->sc_nor_if->program_page(self, offset, data);
}
static inline int
nor_erase_all(device_t self)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->sc_nor_if->erase_all != NULL);
return sc->sc_nor_if->erase_all(self);
}
static inline int
nor_erase_block(device_t self, flash_off_t offset)
{
struct nor_softc *sc = device_private(self);
KASSERT(sc->sc_nor_if->erase_block != NULL);
return sc->sc_nor_if->erase_block(self, offset);
}
/* Manufacturer IDs defined by JEDEC */
@ -300,41 +363,9 @@ struct nor_manufacturer {
extern const struct nor_manufacturer nor_mfrs[];
/* flash interface implementation */
int nor_flash_isbad(device_t, flash_off_t, bool *);
int nor_flash_markbad(device_t, flash_off_t);
int nor_flash_write(device_t, flash_off_t, size_t, size_t *, const u_char *);
int nor_flash_read(device_t, flash_off_t, size_t, size_t *, uint8_t *);
int nor_flash_erase(device_t, struct flash_erase_instruction *);
/* nor specific functions */
int nor_erase_block(device_t, size_t);
int nor_io_submit(device_t, struct buf *);
void nor_sync_thread(void *);
int nor_sync_thread_start(device_t);
void nor_sync_thread_stop(device_t);
/* public nor specific functions */
device_t nor_attach_mi(struct nor_interface *, device_t);
void nor_init_interface(struct nor_interface *);
/*
* default functions for driver development
*/
void nor_default_select(device_t, bool);
#if 0
static inline void nor_busy(device_t);
static inline void nor_select(device_t, bool);
static inline void nor_command(device_t, uint8_t);
static inline void nor_address(device_t, uint32_t);
static inline void nor_read_buf_1(device_t, void *, size_t);
static inline void nor_read_buf_2(device_t, void *, size_t);
static inline void nor_read_1(device_t, uint8_t *);
static inline void nor_read_2(device_t, uint8_t *);
static inline void nor_write_buf_1(device_t, const void *, size_t);
static inline void nor_write_buf_2(device_t, const void *, size_t);
#endif
#endif /* _NOR_H_ */