summaryrefslogtreecommitdiff
path: root/sys/dev/nor
diff options
context:
space:
mode:
authorcliff <cliff@NetBSD.org>2011-07-15 19:19:55 +0000
committercliff <cliff@NetBSD.org>2011-07-15 19:19:55 +0000
commit48b433b67ee9893302eb78d2de81a172782a4e7b (patch)
treea5951bc8cbf9aa4e28bfd442804a712dd90dd10a /sys/dev/nor
parenta716412d726b7f07f673f8e19803b13b34b85be2 (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.c684
-rw-r--r--sys/dev/nor/cfi.h240
-rw-r--r--sys/dev/nor/cfi_0002.c608
-rw-r--r--sys/dev/nor/cfi_0002.h46
-rw-r--r--sys/dev/nor/files.nor4
-rw-r--r--sys/dev/nor/nor.h263
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_ */