diff options
| author | cliff <cliff@NetBSD.org> | 2011-07-15 19:19:55 +0000 |
|---|---|---|
| committer | cliff <cliff@NetBSD.org> | 2011-07-15 19:19:55 +0000 |
| commit | 48b433b67ee9893302eb78d2de81a172782a4e7b (patch) | |
| tree | a5951bc8cbf9aa4e28bfd442804a712dd90dd10a /sys/dev/nor | |
| parent | a716412d726b7f07f673f8e19803b13b34b85be2 (diff) | |
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.
Diffstat (limited to 'sys/dev/nor')
| -rw-r--r-- | sys/dev/nor/cfi.c | 684 | ||||
| -rw-r--r-- | sys/dev/nor/cfi.h | 240 | ||||
| -rw-r--r-- | sys/dev/nor/cfi_0002.c | 608 | ||||
| -rw-r--r-- | sys/dev/nor/cfi_0002.h | 46 | ||||
| -rw-r--r-- | sys/dev/nor/files.nor | 4 | ||||
| -rw-r--r-- | sys/dev/nor/nor.h | 263 |
6 files changed, 1720 insertions, 125 deletions
diff --git a/sys/dev/nor/cfi.c b/sys/dev/nor/cfi.c new file mode 100644 index 00000000000..4f5bdf8e6cb --- /dev/null +++ b/sys/dev/nor/cfi.c @@ -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; + } +} diff --git a/sys/dev/nor/cfi.h b/sys/dev/nor/cfi.h index 8cfc1fcfd7b..af7b75a32ed 100644 --- a/sys/dev/nor/cfi.h +++ b/sys/dev/nor/cfi.h @@ -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> + +/* + * 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 + + +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 */ +}; - CFI_READ_ARRAY_MODE_ADDRESS = 0x00, /* could be anything */ - CFI_READ_ARRAY_MODE_DATA = 0xf0, /* also can be 0xff */ +/* + * 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); +}; - CFI_ADDR_IDENTIFICATION_STRING = 0x10, - CFI_ADDR_SYS_INTERFACE_INFO = 0x1b, - CFI_ADDR_DEV_GEOMETRY = 0x27 +/* 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_ */ diff --git a/sys/dev/nor/cfi_0002.c b/sys/dev/nor/cfi_0002.c new file mode 100644 index 00000000000..246ace6bd12 --- /dev/null +++ b/sys/dev/nor/cfi_0002.c @@ -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 */ diff --git a/sys/dev/nor/cfi_0002.h b/sys/dev/nor/cfi_0002.h new file mode 100644 index 00000000000..3c6db83079d --- /dev/null +++ b/sys/dev/nor/cfi_0002.h @@ -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_ */ diff --git a/sys/dev/nor/files.nor b/sys/dev/nor/files.nor index 5646e8ab310..6b29b3c4314 100644 --- a/sys/dev/nor/files.nor +++ b/sys/dev/nor/files.nor @@ -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 diff --git a/sys/dev/nor/nor.h b/sys/dev/nor/nor.h index de860cdeb81..f1ae9d993d9 100644 --- a/sys/dev/nor/nor.h +++ b/sys/dev/nor/nor.h @@ -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->sc_nor_if->write_buf_4 != NULL); + KASSERT(sc->sc_controller_dev != NULL); + + 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->nor_if->write_buf_4 != NULL); - KASSERT(sc->controller_dev != NULL); + KASSERT(sc->sc_nor_if->read_page != NULL); - sc->nor_if->write_buf_4(sc->controller_dev, buf, size); + 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_ */ |
