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:
parent
534ca6a0dd
commit
fb19d2b789
|
@ -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 ?
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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_ */
|
||||
|
|
|
@ -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 */
|
|
@ -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_ */
|
|
@ -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
|
||||
|
|
|
@ -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_ */
|
||||
|
|
Loading…
Reference in New Issue