summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/spi/winbond.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/spi/winbond.c')
-rw-r--r--drivers/mtd/nand/spi/winbond.c129
1 files changed, 95 insertions, 34 deletions
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 4870b2d5edb2..6dfd0dcc8ee7 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -22,7 +22,7 @@
#define W25N0XJW_SR4 0xD0
#define W25N0XJW_SR4_HS BIT(2)
-#define W35N01JW_VCR_IO_MODE 0x00
+#define W35N01JW_VCR_IO_MODE_REG 0x00
#define W35N01JW_VCR_IO_MODE_SINGLE_SDR 0xFF
#define W35N01JW_VCR_IO_MODE_OCTAL_SDR 0xDF
#define W35N01JW_VCR_IO_MODE_OCTAL_DDR_DS 0xE7
@@ -36,6 +36,8 @@
*/
static SPINAND_OP_VARIANTS(read_cache_octal_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 24, NULL, 0, 120 * HZ_PER_MHZ),
+ SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 16, NULL, 0, 86 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 3, NULL, 0, 120 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 2, NULL, 0, 105 * HZ_PER_MHZ),
SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 20, NULL, 0, 0),
@@ -48,11 +50,13 @@ static SPINAND_OP_VARIANTS(read_cache_octal_variants,
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 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),
SPINAND_PROG_LOAD_1S_1S_8S_OP(0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
static SPINAND_OP_VARIANTS(update_cache_octal_variants,
+ SPINAND_PROG_LOAD_8D_8D_8D_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_8S_8S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
@@ -87,6 +91,47 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
+#define SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, buf) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x81, 1), \
+ SPI_MEM_OP_ADDR(3, reg, 1), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_DATA_OUT(1, buf, 1))
+
+#define SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, buf) \
+ SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x81, 8), \
+ SPI_MEM_DTR_OP_ADDR(4, reg, 8), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_DTR_OP_DATA_OUT(2, buf, 8))
+
+static SPINAND_OP_VARIANTS(winbond_w35_ops,
+ SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(0, NULL),
+ SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(0, NULL));
+
+static struct spi_mem_op
+spinand_fill_winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, void *valptr)
+{
+ return (spinand->bus_iface == SSDR) ?
+ (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, valptr) :
+ (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, valptr);
+}
+
+#define SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(buf) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_DATA_OUT(1, buf, 1))
+
+static SPINAND_OP_VARIANTS(winbond_w25_ops,
+ SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(NULL));
+
+static struct spi_mem_op
+spinand_fill_winbond_select_target_op(struct spinand_device *spinand, void *valptr)
+{
+ WARN_ON_ONCE(spinand->bus_iface != SSDR);
+
+ return (struct spi_mem_op)SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(valptr);
+}
+
static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
@@ -119,12 +164,8 @@ static const struct mtd_ooblayout_ops w25m02gv_ooblayout = {
static int w25m02gv_select_target(struct spinand_device *spinand,
unsigned int target)
{
- struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1),
- SPI_MEM_OP_NO_ADDR,
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_DATA_OUT(1,
- spinand->scratchbuf,
- 1));
+ struct spi_mem_op op = SPINAND_OP(spinand, winbond_select_target,
+ spinand->scratchbuf);
*spinand->scratchbuf = target;
return spi_mem_exec_op(spinand->spimem, &op);
@@ -251,7 +292,8 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
{
struct nand_device *nand = spinand_to_nand(spinand);
u8 mbf = 0;
- struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(0x30, spinand->scratchbuf);
+ struct spi_mem_op op = SPINAND_OP(spinand, get_feature,
+ 0x30, spinand->scratchbuf);
switch (status & STATUS_ECC_MASK) {
case STATUS_ECC_NO_BITFLIPS:
@@ -284,14 +326,18 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
return -EINVAL;
}
-static int w25n0xjw_hs_cfg(struct spinand_device *spinand)
+static int w25n0xjw_hs_cfg(struct spinand_device *spinand,
+ enum spinand_bus_interface iface)
{
const struct spi_mem_op *op;
bool hs;
u8 sr4;
int ret;
- op = spinand->op_templates.read_cache;
+ if (iface != SSDR)
+ return -EOPNOTSUPP;
+
+ op = spinand->op_templates->read_cache;
if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
hs = false;
else if (op->cmd.buswidth == 1 && op->addr.buswidth == 1 &&
@@ -320,11 +366,8 @@ static int w25n0xjw_hs_cfg(struct spinand_device *spinand)
static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val)
{
- struct spi_mem_op op =
- SPI_MEM_OP(SPI_MEM_OP_CMD(0x81, 1),
- SPI_MEM_OP_ADDR(3, reg, 1),
- SPI_MEM_OP_NO_DUMMY,
- SPI_MEM_OP_DATA_OUT(1, spinand->scratchbuf, 1));
+ struct spi_mem_op op = SPINAND_OP(spinand, winbond_write_vcr,
+ reg, spinand->scratchbuf);
int ret;
*spinand->scratchbuf = val;
@@ -347,32 +390,28 @@ static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val)
return 0;
}
-static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
+static int w35n0xjw_vcr_cfg(struct spinand_device *spinand,
+ enum spinand_bus_interface iface)
{
- const struct spi_mem_op *op;
+ const struct spi_mem_op *ref_op;
unsigned int dummy_cycles;
bool dtr, single;
u8 io_mode;
int ret;
- op = spinand->op_templates.read_cache;
-
- single = (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && op->data.buswidth == 1);
- dtr = (op->cmd.dtr || op->addr.dtr || op->data.dtr);
- if (single && !dtr)
- io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR;
- else if (!single && !dtr)
- io_mode = W35N01JW_VCR_IO_MODE_OCTAL_SDR;
- else if (!single && dtr)
- io_mode = W35N01JW_VCR_IO_MODE_OCTAL_DDR;
- else
- return -EINVAL;
-
- ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE, io_mode);
- if (ret)
- return ret;
+ switch (iface) {
+ case SSDR:
+ ref_op = spinand->ssdr_op_templates.read_cache;
+ break;
+ case ODTR:
+ ref_op = spinand->odtr_op_templates.read_cache;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
- dummy_cycles = ((op->dummy.nbytes * 8) / op->dummy.buswidth) / (op->dummy.dtr ? 2 : 1);
+ dummy_cycles = ((ref_op->dummy.nbytes * 8) / ref_op->dummy.buswidth) /
+ (ref_op->dummy.dtr ? 2 : 1);
switch (dummy_cycles) {
case 8:
case 12:
@@ -384,10 +423,28 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
default:
return -EINVAL;
}
+
ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_DUMMY_CLOCK_REG, dummy_cycles);
if (ret)
return ret;
+ single = (ref_op->cmd.buswidth == 1 &&
+ ref_op->addr.buswidth == 1 &&
+ ref_op->data.buswidth == 1);
+ dtr = (ref_op->cmd.dtr && ref_op->addr.dtr && ref_op->data.dtr);
+ if (single && !dtr)
+ io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR;
+ else if (!single && !dtr)
+ io_mode = W35N01JW_VCR_IO_MODE_OCTAL_SDR;
+ else if (!single && dtr)
+ io_mode = W35N01JW_VCR_IO_MODE_OCTAL_DDR;
+ else
+ return -EINVAL;
+
+ ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE_REG, io_mode);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -448,6 +505,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_octal_variants,
&update_cache_octal_variants),
0,
+ SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
SPINAND_INFO("W35N02JW", /* 1.8V */
@@ -458,6 +516,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_octal_variants,
&update_cache_octal_variants),
0,
+ SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
SPINAND_INFO("W35N04JW", /* 1.8V */
@@ -468,6 +527,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_octal_variants,
&update_cache_octal_variants),
0,
+ SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops),
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
/* 2G-bit densities */
@@ -479,6 +539,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
0,
+ SPINAND_INFO_VENDOR_OPS(&winbond_w25_ops),
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
SPINAND_SELECT_TARGET(w25m02gv_select_target)),
SPINAND_INFO("W25N02JW", /* high-speed 1.8V */