#define SPINAND_MFR_WINBOND 0xEF
+#define WINBOND_CFG_HFREQ BIT(0)
#define WINBOND_CFG_BUF_READ BIT(3)
#define W25N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4)
+#define W25W35NXXJW_STATUS_ECC_MULT_UNCOR (3 << 4)
#define W25N0XJW_SR4 0xD0
#define W25N0XJW_SR4_HS BIT(2)
#define W35N01JW_VCR_IO_MODE_OCTAL_DDR 0xC7
#define W35N01JW_VCR_DUMMY_CLOCK_REG 0x01
+/*
+ * Winbond chips ignore the address bytes during continuous reads, and
+ * because the dummy cycles are enough they indicate dropping the
+ * address cycles from the continuous read from cache variants. This is
+ * very poorly supported by SPI controller drivers which are "wired" to
+ * always at least provide the column. Keep using address cycles, but
+ * reduce the number of dummy cycles accordingly.
+ */
+#define WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x0b, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 1), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 1), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x8b, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 1), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x9d, 1), \
+ SPI_MEM_DTR_OP_ADDR(1, 0, 1), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 1, 1), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xcb, 1), \
+ SPI_MEM_OP_ADDR(1, 0, 8), \
+ SPI_MEM_OP_DUMMY(ndummy - 1, 8), \
+ SPI_MEM_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
+#define WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(ndummy, buf, len, freq) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9d, 8), \
+ SPI_MEM_DTR_OP_ADDR(2, 0, 8), \
+ SPI_MEM_DTR_OP_DUMMY(ndummy - 2, 8), \
+ SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \
+ SPI_MEM_OP_MAX_FREQ(freq))
+
/*
* "X2" in the core is equivalent to "dual output" in the datasheets,
* "X4" in the core is equivalent to "quad output" in the datasheets.
SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
+static SPINAND_OP_VARIANTS(cont_read_cache_octal_variants,
+ WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(24, NULL, 0, 120 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_8D_8D_8D_OP(16, NULL, 0, 86 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(3, NULL, 0, 120 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1D_8D_OP(2, NULL, 0, 105 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(20, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(16, NULL, 0, 162 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(12, NULL, 0, 124 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_8S_8S_OP(8, NULL, 0, 86 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(2, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_8S_OP(1, NULL, 0, 133 * HZ_PER_MHZ),
+ WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(1, NULL, 0, 0));
+
static SPINAND_OP_VARIANTS(write_cache_octal_variants,
SPINAND_PROG_LOAD_8D_8D_8D_OP(true, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_8S_8S_OP(true, 0, NULL, 0),
return -EINVAL;
}
+static int w25w35nxxjw_ecc_get_status(struct spinand_device *spinand, u8 status)
+{
+ switch (status & STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case STATUS_ECC_HAS_BITFLIPS:
+ return 1;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ case W25W35NXXJW_STATUS_ECC_MULT_UNCOR:
+ return -EBADMSG;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
static int w25n0xjw_hs_cfg(struct spinand_device *spinand,
enum spinand_bus_interface iface)
{
return 0;
}
+static int w35n0xjw_set_cont_read(struct spinand_device *spinand, bool enable)
+{
+ const struct spi_mem_op *cont_op = spinand->op_templates->cont_read_cache;
+ u8 mask = enable ? 0 : WINBOND_CFG_BUF_READ;
+
+ if (cont_op && enable && spinand_op_is_odtr(cont_op) &&
+ cont_op->max_freq >= 90 * HZ_PER_MHZ)
+ mask |= WINBOND_CFG_HFREQ;
+
+ return spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ | WINBOND_CFG_HFREQ, mask);
+}
+
static const struct spinand_info winbond_spinand_table[] = {
/* 512M-bit densities */
SPINAND_INFO("W25N512GW", /* 1.8V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdc, 0x21),
NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 1, 1),
NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
- &write_cache_octal_variants,
- &update_cache_octal_variants),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+ &write_cache_octal_variants,
+ &update_cache_octal_variants,
+ &cont_read_cache_octal_variants),
0,
SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
- SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
+ SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+ SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
/* 2G-bit densities */
SPINAND_INFO("W25M02GV", /* 2x1G-bit 3.3V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x22),
NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 2, 1),
NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
- &write_cache_octal_variants,
- &update_cache_octal_variants),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+ &write_cache_octal_variants,
+ &update_cache_octal_variants,
+ &cont_read_cache_octal_variants),
SPINAND_ODTR_PACKED_PAGE_READ,
SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
- SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
+ SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+ SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
/* 4G-bit densities */
SPINAND_INFO("W25N04KV", /* 3.3V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23),
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23),
NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1),
NAND_ECCREQ(1, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants,
- &write_cache_octal_variants,
- &update_cache_octal_variants),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_octal_variants,
+ &write_cache_octal_variants,
+ &update_cache_octal_variants,
+ &cont_read_cache_octal_variants),
SPINAND_ODTR_PACKED_PAGE_READ,
SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
- SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
- SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
+ SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
+ SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
+ SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
};
static int winbond_spinand_init(struct spinand_device *spinand)