summaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/devices/Kconfig1
-rw-r--r--drivers/mtd/devices/m25p80.c160
-rw-r--r--drivers/mtd/spi-nor/cadence-quadspi.c257
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c672
4 files changed, 725 insertions, 365 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 9c3925b..25db10a 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -23,6 +23,7 @@ config MTD_M25P80
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
depends on SPI
select MTD_SPI_NOR
+ select SPI_MEM
help
This enables access to most modern SPI flash chips, used for
program and data storage. Series supported include Atmel AT26DF,
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 8a67792..09a8714 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -27,12 +27,13 @@
#include <clock.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/spi-nor.h>
+#include <linux/spi/spi-mem.h>
#include <linux/mod_devicetable.h>
#define MAX_CMD_SIZE 6
struct m25p {
- struct spi_device *spi;
+ struct spi_mem *spimem;
struct spi_nor spi_nor;
struct mtd_info mtd;
u8 command[MAX_CMD_SIZE];
@@ -41,71 +42,60 @@ struct m25p {
static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
{
struct m25p *flash = nor->priv;
- struct spi_device *spi = flash->spi;
+ struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_IN(len, val, 1));
int ret;
- ret = spi_write_then_read(spi, &code, 1, val, len);
+ ret = spi_mem_exec_op(flash->spimem, &op);
if (ret < 0)
- dev_err(&spi->dev, "error %d reading %x\n", ret, code);
+ dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
+ code);
return ret;
}
-static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
-{
- /* opcode is in cmd[0] */
- cmd[1] = addr >> (nor->addr_width * 8 - 8);
- cmd[2] = addr >> (nor->addr_width * 8 - 16);
- cmd[3] = addr >> (nor->addr_width * 8 - 24);
- cmd[4] = addr >> (nor->addr_width * 8 - 32);
-}
-
-static int m25p_cmdsz(struct spi_nor *nor)
-{
- return 1 + nor->addr_width;
-}
-
-static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
- int wr_en)
+static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
struct m25p *flash = nor->priv;
- struct spi_device *spi = flash->spi;
-
- flash->command[0] = opcode;
- if (buf)
- memcpy(&flash->command[1], buf, len);
+ struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(len, buf, 1));
- return spi_write(spi, flash->command, len + 1);
+ return spi_mem_exec_op(flash->spimem, &op);
}
static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct m25p *flash = nor->priv;
- struct spi_device *spi = flash->spi;
- struct spi_transfer t[2] = {};
- struct spi_message m;
- int cmd_sz = m25p_cmdsz(nor);
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
+ SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(len, buf, 1));
+ int ret;
- spi_message_init(&m);
+ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
+ op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
+ op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
- cmd_sz = 1;
-
- flash->command[0] = nor->program_opcode;
- m25p_addr2cmd(nor, to, flash->command);
+ op.addr.nbytes = 0;
- t[0].tx_buf = flash->command;
- t[0].len = cmd_sz;
- spi_message_add_tail(&t[0], &m);
+ ret = spi_mem_adjust_op_size(flash->spimem, &op);
+ if (ret)
+ return;
- t[1].tx_buf = buf;
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
+ op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
- spi_sync(spi, &m);
+ ret = spi_mem_exec_op(flash->spimem, &op);
+ if (ret)
+ return;
- *retlen += m.actual_length - cmd_sz;
+ *retlen = op.data.nbytes;
}
/*
@@ -116,46 +106,35 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct m25p *flash = nor->priv;
- struct spi_device *spi = flash->spi;
- struct spi_transfer t[2];
- struct spi_message m;
- unsigned int dummy = nor->read_dummy;
-
- /* convert the dummy cycles to the number of bytes */
- dummy /= 8;
-
- spi_message_init(&m);
- memset(t, 0, (sizeof t));
-
- flash->command[0] = nor->read_opcode;
- m25p_addr2cmd(nor, from, flash->command);
-
- t[0].tx_buf = flash->command;
- t[0].len = m25p_cmdsz(nor) + dummy;
- spi_message_add_tail(&t[0], &m);
-
- t[1].rx_buf = buf;
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
-
- spi_sync(spi, &m);
-
- *retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
- return 0;
-}
-
-static int m25p80_erase(struct spi_nor *nor, loff_t offset)
-{
- struct m25p *flash = nor->priv;
-
- dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
- flash->mtd.erasesize / 1024, (u32)offset);
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
+ SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
+ SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
+ SPI_MEM_OP_DATA_IN(len, buf, 1));
+ size_t remaining = len;
+ int ret;
- /* Set up command buffer. */
- flash->command[0] = nor->erase_opcode;
- m25p_addr2cmd(nor, offset, flash->command);
+ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
+ op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
+ op.dummy.buswidth = op.addr.buswidth;
+ op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
+
+ op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
+
+ while (remaining) {
+ op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+ ret = spi_mem_adjust_op_size(flash->spimem, &op);
+ if (ret)
+ return ret;
+ ret = spi_mem_exec_op(flash->spimem, &op);
+ if (ret)
+ return ret;
+ op.addr.val += op.data.nbytes;
+ remaining -= op.data.nbytes;
+ op.data.buf.in += op.data.nbytes;
+ }
- spi_write(flash->spi, flash->command, m25p_cmdsz(nor));
+ *retlen = len;
return 0;
}
@@ -229,10 +208,15 @@ static const struct platform_device_id m25p_ids[] = {
static int m25p_probe(struct device_d *dev)
{
struct spi_device *spi = (struct spi_device *)dev->type_data;
+ struct spi_mem *spimem = spi->mem;
struct flash_platform_data *data;
struct m25p *flash;
struct spi_nor *nor;
- enum read_mode mode = SPI_NOR_NORMAL;
+ struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_PP,
+ };
const char *flash_name = NULL;
int device_id;
bool use_large_blocks;
@@ -247,17 +231,21 @@ static int m25p_probe(struct device_d *dev)
/* install the hooks */
nor->read = m25p80_read;
nor->write = m25p80_write;
- nor->erase = m25p80_erase;
nor->write_reg = m25p80_write_reg;
nor->read_reg = m25p80_read_reg;
- nor->dev = dev;
+ nor->dev = &spimem->spi->dev;
nor->mtd = &flash->mtd;
nor->priv = flash;
flash->mtd.priv = nor;
flash->mtd.parent = &spi->dev;
- flash->spi = spi;
+ flash->spimem = spimem;
+
+ if (spi->mode & SPI_RX_QUAD)
+ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+ else if (spi->mode & SPI_RX_DUAL)
+ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
dev->priv = (void *)flash;
@@ -276,7 +264,7 @@ static int m25p_probe(struct device_d *dev)
use_large_blocks = of_property_read_bool(dev->device_node,
"use-large-blocks");
- ret = spi_nor_scan(nor, flash_name, mode, use_large_blocks);
+ ret = spi_nor_scan(nor, flash_name, &hwcaps, use_large_blocks);
if (ret)
return ret;
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index ed5377b..11e4d23 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -45,6 +45,9 @@ struct cqspi_flash_pdata {
unsigned int tsd2d_ns;
unsigned int tchsh_ns;
unsigned int tslch_ns;
+ u8 inst_width;
+ u8 addr_width;
+ u8 data_width;
};
struct cqspi_st {
@@ -287,9 +290,10 @@ static unsigned int cqspi_calc_rdreg(struct spi_nor *nor, u8 opcode)
f_pdata = &cqspi->f_pdata[cqspi->current_cs];
- if (nor->flash_read == SPI_NOR_QUAD)
- rdreg |= (CQSPI_INST_TYPE_QUAD
- << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB);
+ rdreg |= f_pdata->inst_width << CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB;
+ rdreg |= f_pdata->addr_width << CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB;
+ rdreg |= f_pdata->data_width << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
+
return rdreg;
}
@@ -427,6 +431,7 @@ static int cqspi_command_write_addr(struct spi_nor *nor,
static int cqspi_indirect_read_setup(struct spi_nor *nor,
unsigned int from_addr)
{
+ struct cqspi_flash_pdata *f_pdata;
struct cqspi_st *cqspi = nor->priv;
unsigned int ahb_base = (unsigned int) cqspi->ahb_base;
void __iomem *reg_base = cqspi->iobase;
@@ -437,6 +442,7 @@ static int cqspi_indirect_read_setup(struct spi_nor *nor,
writel(ahb_base & CQSPI_INDIRECTTRIGGER_ADDR_MASK,
reg_base + CQSPI_REG_INDIRECTTRIGGER);
writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
+ f_pdata = &cqspi->f_pdata[cqspi->current_cs];
reg = nor->read_opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB;
reg |= cqspi_calc_rdreg(nor, nor->read_opcode);
@@ -444,14 +450,10 @@ static int cqspi_indirect_read_setup(struct spi_nor *nor,
/* Setup dummy clock cycles */
dummy_bytes = nor->read_dummy / 8;
- if (dummy_bytes) {
- struct cqspi_flash_pdata *f_pdata;
-
- f_pdata = &cqspi->f_pdata[cqspi->current_cs];
-
- if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX)
- dummy_bytes = CQSPI_DUMMY_BYTES_MAX;
+ if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX)
+ dummy_bytes = CQSPI_DUMMY_BYTES_MAX;
+ if (dummy_bytes) {
reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB);
/* Set mode bits high to ensure chip doesn't enter XIP */
writel(0xFF, reg_base + CQSPI_REG_MODE_BIT);
@@ -459,7 +461,8 @@ static int cqspi_indirect_read_setup(struct spi_nor *nor,
/* Convert to clock cycles. */
dummy_clk = dummy_bytes * CQSPI_DUMMY_CLKS_PER_BYTE;
/* Need to subtract the mode byte (8 clocks). */
- dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE;
+ if (f_pdata->inst_width != CQSPI_INST_TYPE_QUAD)
+ dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE;
if (dummy_clk)
reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK)
@@ -676,51 +679,53 @@ failwr:
return ret;
}
-static void cqspi_write(struct spi_nor *nor, loff_t to,
- size_t len, size_t *retlen, const u_char *buf)
+static void cqspi_controller_enable(struct cqspi_st *cqspi)
{
- int ret;
-
- if (!IS_ENABLED(CONFIG_MTD_WRITE))
- return;
+ void __iomem *reg_base = cqspi->iobase;
+ unsigned int reg;
- ret = cqspi_indirect_write_setup(nor, to);
- if (ret == 0) {
- ret = cqspi_indirect_write_execute(nor, buf, len);
- if (ret == 0)
- *retlen += len;
- }
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ reg |= CQSPI_REG_CONFIG_ENABLE_MASK;
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
}
-static int cqspi_read(struct spi_nor *nor, loff_t from,
- size_t len, size_t *retlen, u_char *buf)
+static void cqspi_controller_disable(struct cqspi_st *cqspi)
{
- int ret;
+ void __iomem *reg_base = cqspi->iobase;
+ unsigned int reg;
- ret = cqspi_indirect_read_setup(nor, from);
- if (ret == 0) {
- ret = cqspi_indirect_read_execute(nor, buf, len);
- if (ret == 0)
- *retlen += len;
- }
- return ret;
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK;
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
}
-static int cqspi_erase(struct spi_nor *nor, loff_t offs)
+static void cqspi_chipselect(struct cqspi_st *cqspi,
+ unsigned int chip_select,
+ unsigned int decoder_enable)
{
- int ret;
+ void __iomem *reg_base = cqspi->iobase;
+ unsigned int reg;
- /* Send write enable, then erase commands. */
- ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
- if (ret)
- return ret;
+ reg = readl(reg_base + CQSPI_REG_CONFIG);
+ if (decoder_enable) {
+ reg |= CQSPI_REG_CONFIG_DECODE_MASK;
+ } else {
+ reg &= ~CQSPI_REG_CONFIG_DECODE_MASK;
- /* Set up command buffer. */
- ret = cqspi_command_write_addr(nor, nor->erase_opcode, offs);
- if (ret)
- return ret;
+ /* Convert CS if without decoder.
+ * CS0 to 4b'1110
+ * CS1 to 4b'1101
+ * CS2 to 4b'1011
+ * CS3 to 4b'0111
+ */
+ chip_select = 0xF & ~(1 << chip_select);
+ }
- return 0;
+ reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK
+ << CQSPI_REG_CONFIG_CHIPSELECT_LSB);
+ reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK)
+ << CQSPI_REG_CONFIG_CHIPSELECT_LSB;
+ writel(reg, reg_base + CQSPI_REG_CONFIG);
}
static unsigned int calculate_ticks_for_ns(unsigned int ref_clk_hz,
@@ -834,55 +839,6 @@ static void cqspi_readdata_capture(struct cqspi_st *cqspi,
writel(reg, reg_base + CQSPI_REG_READCAPTURE);
}
-static void cqspi_chipselect(struct cqspi_st *cqspi,
- unsigned int chip_select,
- unsigned int decoder_enable)
-{
- void __iomem *reg_base = cqspi->iobase;
- unsigned int reg;
-
- reg = readl(reg_base + CQSPI_REG_CONFIG);
- if (decoder_enable) {
- reg |= CQSPI_REG_CONFIG_DECODE_MASK;
- } else {
- reg &= ~CQSPI_REG_CONFIG_DECODE_MASK;
-
- /* Convert CS if without decoder.
- * CS0 to 4b'1110
- * CS1 to 4b'1101
- * CS2 to 4b'1011
- * CS3 to 4b'0111
- */
- chip_select = 0xF & ~(1 << chip_select);
- }
-
- reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK
- << CQSPI_REG_CONFIG_CHIPSELECT_LSB);
- reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK)
- << CQSPI_REG_CONFIG_CHIPSELECT_LSB;
- writel(reg, reg_base + CQSPI_REG_CONFIG);
-}
-
-static void cqspi_controller_enable(struct cqspi_st *cqspi)
-{
- void __iomem *reg_base = cqspi->iobase;
- unsigned int reg;
-
- reg = readl(reg_base + CQSPI_REG_CONFIG);
- reg |= CQSPI_REG_CONFIG_ENABLE_MASK;
- writel(reg, reg_base + CQSPI_REG_CONFIG);
-}
-
-static void cqspi_controller_disable(struct cqspi_st *cqspi)
-{
- void __iomem *reg_base = cqspi->iobase;
- unsigned int reg;
-
- reg = readl(reg_base + CQSPI_REG_CONFIG);
- reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK;
- writel(reg, reg_base + CQSPI_REG_CONFIG);
-}
-
static void cqspi_switch_cs(struct cqspi_st *cqspi, unsigned int cs)
{
unsigned int reg;
@@ -908,7 +864,7 @@ static void cqspi_switch_cs(struct cqspi_st *cqspi, unsigned int cs)
cqspi_controller_enable(cqspi);
}
-static int cqspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
+static int cqspi_configure(struct spi_nor *nor)
{
struct cqspi_st *cqspi = nor->priv;
int cs = cqspi_find_chipselect(nor);
@@ -936,27 +892,119 @@ static int cqspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
return 0;
}
+static int cqspi_set_protocol(struct spi_nor *nor, const int read)
+{
+ struct cqspi_st *cqspi = nor->priv;
+ struct cqspi_flash_pdata *f_pdata;
+
+ f_pdata = &cqspi->f_pdata[cqspi->current_cs];
+
+ f_pdata->inst_width = CQSPI_INST_TYPE_SINGLE;
+ f_pdata->addr_width = CQSPI_INST_TYPE_SINGLE;
+ f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
+
+ if (read) {
+ switch (nor->read_proto) {
+ case SNOR_PROTO_1_1_1:
+ f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
+ break;
+ case SNOR_PROTO_1_1_2:
+ f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
+ break;
+ case SNOR_PROTO_1_1_4:
+ f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ cqspi_configure(nor);
+
+ return 0;
+}
+
+static void cqspi_write(struct spi_nor *nor, loff_t to,
+ size_t len, size_t *retlen, const u_char *buf)
+{
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_MTD_WRITE))
+ return;
+
+ ret = cqspi_set_protocol(nor, 0);
+ if (ret)
+ return;
+
+ ret = cqspi_indirect_write_setup(nor, to);
+ if (ret == 0) {
+ ret = cqspi_indirect_write_execute(nor, buf, len);
+ if (ret == 0)
+ *retlen += len;
+ }
+}
+
+static int cqspi_read(struct spi_nor *nor, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+{
+ int ret;
+
+ ret = cqspi_set_protocol(nor, 1);
+ if (ret)
+ return ret;
+
+ ret = cqspi_indirect_read_setup(nor, from);
+ if (ret == 0) {
+ ret = cqspi_indirect_read_execute(nor, buf, len);
+ if (ret == 0)
+ *retlen += len;
+ }
+ return ret;
+}
+
+static int cqspi_erase(struct spi_nor *nor, loff_t offs)
+{
+ int ret;
+
+ ret = cqspi_set_protocol(nor, 0);
+ if (ret)
+ return ret;
+
+ /* Send write enable, then erase commands. */
+ ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
+ if (ret)
+ return ret;
+
+ /* Set up command buffer. */
+ ret = cqspi_command_write_addr(nor, nor->erase_opcode, offs);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int cqspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
int ret;
- cqspi_prep(nor, SPI_NOR_OPS_READ);
+ ret = cqspi_set_protocol(nor, 0);
+ if (!ret)
+ ret = cqspi_command_read(nor, &opcode, 1, buf, len);
- ret = cqspi_command_read(nor, &opcode, 1, buf, len);
return ret;
}
-static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
- int write_enable)
+static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
int ret = 0;
if (!IS_ENABLED(CONFIG_MTD_WRITE))
return -ENOTSUPP;
- cqspi_prep(nor, SPI_NOR_OPS_WRITE);
+ ret = cqspi_set_protocol(nor, 0);
+ if (!ret)
+ ret = cqspi_command_write(nor, opcode, buf, len);
- ret = cqspi_command_write(nor, opcode, buf, len);
return ret;
}
@@ -1034,6 +1082,13 @@ static int cqspi_setup_flash(struct device_d *dev,
struct cqspi_flash_pdata *f_pdata,
struct device_node *np)
{
+ const struct spi_nor_hwcaps hwcaps = {
+ .mask = SNOR_HWCAPS_READ |
+ SNOR_HWCAPS_READ_FAST |
+ SNOR_HWCAPS_READ_1_1_2 |
+ SNOR_HWCAPS_READ_1_1_4 |
+ SNOR_HWCAPS_PP,
+ };
struct cqspi_st *cqspi = dev->priv;
struct mtd_info *mtd;
struct spi_nor *nor;
@@ -1075,7 +1130,7 @@ static int cqspi_setup_flash(struct device_d *dev,
nor->write = cqspi_write;
nor->erase = cqspi_erase;
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD, false);
+ ret = spi_nor_scan(nor, NULL, &hwcaps, false);
if (ret)
goto probe_failed;
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 1595349..43bd402 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -25,6 +25,7 @@
#include <spi/flash.h>
#define SPI_NOR_MAX_ID_LEN 6
+#define SPI_NOR_MAX_ADDR_WIDTH 4
/*
* For everything but full-chip erase; probably could be much smaller, but kept
@@ -57,14 +58,85 @@ struct flash_info {
u16 addr_width;
u16 flags;
-#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */
-#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */
-#define SST_WRITE 0x04 /* use SST byte programming */
-#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */
-#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */
-#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */
-#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */
-#define USE_FSR 0x80 /* use flag status register */
+#define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */
+#define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */
+#define SST_WRITE BIT(2) /* use SST byte programming */
+#define SPI_NOR_NO_FR BIT(3) /* Can't do fastread */
+#define SECT_4K_PMC BIT(4) /* SPINOR_OP_BE_4K_PMC works uniformly */
+#define SPI_NOR_DUAL_READ BIT(5) /* Flash supports Dual Read */
+#define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */
+#define USE_FSR BIT(7) /* use flag status register */
+#define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */
+#define SPI_NOR_HAS_TB BIT(9) /*
+ * Flash SR has Top/Bottom (TB) protect
+ * bit. Must be used with
+ * SPI_NOR_HAS_LOCK.
+ */
+#define SPI_S3AN BIT(10) /*
+ * Xilinx Spartan 3AN In-System Flash
+ * (MFR cannot be used for probing
+ * because it has the same value as
+ * ATMEL flashes)
+ */
+#define SPI_NOR_4B_OPCODES BIT(11) /*
+ * Use dedicated 4byte address op codes
+ * to support memory size above 128Mib.
+ */
+#define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */
+#define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */
+#define USE_CLSR BIT(14) /* use CLSR command */
+#define SPI_NOR_OCTAL_READ BIT(15) /* Flash supports Octal Read */
+};
+
+enum spi_nor_read_command_index {
+ SNOR_CMD_READ,
+ SNOR_CMD_READ_FAST,
+
+ /* Dual SPI */
+ SNOR_CMD_READ_1_1_2,
+ SNOR_CMD_READ_1_2_2,
+ SNOR_CMD_READ_2_2_2,
+
+ /* Quad SPI */
+ SNOR_CMD_READ_1_1_4,
+ SNOR_CMD_READ_1_4_4,
+ SNOR_CMD_READ_4_4_4,
+
+ SNOR_CMD_READ_MAX
+};
+
+struct spi_nor_read_command {
+ u8 num_mode_clocks;
+ u8 num_wait_states;
+ u8 opcode;
+ enum spi_nor_protocol proto;
+};
+
+struct spi_nor_pp_command {
+ u8 opcode;
+ enum spi_nor_protocol proto;
+};
+
+enum spi_nor_pp_command_index {
+ SNOR_CMD_PP,
+
+ /* Quad SPI */
+ SNOR_CMD_PP_1_1_4,
+ SNOR_CMD_PP_1_4_4,
+ SNOR_CMD_PP_4_4_4,
+
+ SNOR_CMD_PP_MAX
+};
+
+struct spi_nor_flash_parameter {
+ u64 size;
+ u32 page_size;
+
+ struct spi_nor_hwcaps hwcaps;
+ struct spi_nor_read_command reads[SNOR_CMD_READ_MAX];
+ struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX];
+
+ int (*quad_enable)(struct spi_nor *nor);
};
#define JEDEC_MFR(info) ((info)->id[0])
@@ -129,31 +201,13 @@ static int read_cr(struct spi_nor *nor)
}
/*
- * Dummy Cycle calculation for different type of read.
- * It can be used to support more commands with
- * different dummy cycle requirements.
- */
-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
-{
- switch (nor->flash_read) {
- case SPI_NOR_FAST:
- case SPI_NOR_DUAL:
- case SPI_NOR_QUAD:
- return 8;
- case SPI_NOR_NORMAL:
- return 0;
- }
- return 0;
-}
-
-/*
* Write status register 1 byte
* Returns negative if error occurred.
*/
static inline int write_sr(struct spi_nor *nor, u8 val)
{
nor->cmd_buf[0] = val;
- return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
+ return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
}
/*
@@ -162,7 +216,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val)
*/
static inline int write_enable(struct spi_nor *nor)
{
- return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
+ return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
}
/*
@@ -170,7 +224,7 @@ static inline int write_enable(struct spi_nor *nor)
*/
static inline int write_disable(struct spi_nor *nor)
{
- return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0);
+ return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0);
}
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
@@ -178,6 +232,81 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
return mtd->priv;
}
+static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (table[i][0] == opcode)
+ return table[i][1];
+
+ /* No conversion found, keep input op code. */
+ return opcode;
+}
+
+static u8 spi_nor_convert_3to4_read(u8 opcode)
+{
+ static const u8 spi_nor_3to4_read[][2] = {
+ { SPINOR_OP_READ, SPINOR_OP_READ_4B },
+ { SPINOR_OP_READ_FAST, SPINOR_OP_READ_FAST_4B },
+ { SPINOR_OP_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B },
+ { SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B },
+ { SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B },
+ { SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B },
+
+ { SPINOR_OP_READ_1_1_1_DTR, SPINOR_OP_READ_1_1_1_DTR_4B },
+ { SPINOR_OP_READ_1_2_2_DTR, SPINOR_OP_READ_1_2_2_DTR_4B },
+ { SPINOR_OP_READ_1_4_4_DTR, SPINOR_OP_READ_1_4_4_DTR_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
+ ARRAY_SIZE(spi_nor_3to4_read));
+}
+
+static u8 spi_nor_convert_3to4_program(u8 opcode)
+{
+ static const u8 spi_nor_3to4_program[][2] = {
+ { SPINOR_OP_PP, SPINOR_OP_PP_4B },
+ { SPINOR_OP_PP_1_1_4, SPINOR_OP_PP_1_1_4_4B },
+ { SPINOR_OP_PP_1_4_4, SPINOR_OP_PP_1_4_4_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_program,
+ ARRAY_SIZE(spi_nor_3to4_program));
+}
+
+static u8 spi_nor_convert_3to4_erase(u8 opcode)
+{
+ static const u8 spi_nor_3to4_erase[][2] = {
+ { SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B },
+ { SPINOR_OP_BE_32K, SPINOR_OP_BE_32K_4B },
+ { SPINOR_OP_SE, SPINOR_OP_SE_4B },
+ };
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase,
+ ARRAY_SIZE(spi_nor_3to4_erase));
+}
+
+static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
+{
+ /* Do some manufacturer fixups first */
+ switch (JEDEC_MFR(nor->info)) {
+ case SNOR_MFR_SPANSION:
+ /* No small sector erase for 4-byte command set */
+ nor->erase_opcode = SPINOR_OP_SE;
+ nor->mtd->erasesize = nor->info->sector_size;
+ break;
+
+ default:
+ break;
+ }
+
+ nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
+ nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
+ nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
+}
+
+
/* Enable/disable 4-byte addressing mode. */
static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
int enable)
@@ -196,7 +325,7 @@ static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
write_enable(nor);
cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
- status = nor->write_reg(nor, cmd, NULL, 0, 0);
+ status = nor->write_reg(nor, cmd, NULL, 0);
if (need_wren)
write_disable(nor);
@@ -204,7 +333,7 @@ static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
default:
/* Spansion style */
nor->cmd_buf[0] = enable << 7;
- return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
+ return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
}
}
static inline int spi_nor_sr_ready(struct spi_nor *nor)
@@ -279,7 +408,7 @@ static int erase_chip(struct spi_nor *nor)
{
dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
- return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
+ return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
}
static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
@@ -307,6 +436,29 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
}
/*
+ * Initiate the erasure of a single sector
+ */
+static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
+{
+ u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
+ int i;
+
+ if (nor->erase)
+ return nor->erase(nor, addr);
+
+ /*
+ * Default implementation, if driver doesn't have a specialized HW
+ * control
+ */
+ for (i = nor->addr_width - 1; i >= 0; i--) {
+ buf[i] = addr & 0xff;
+ addr >>= 8;
+ }
+
+ return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
+}
+
+/*
* Erase an address range on the nor chip. The address range may extend
* one or more erase sectors. Return an error is there is a problem erasing.
*/
@@ -370,10 +522,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
while (len) {
write_enable(nor);
- if (nor->erase(nor, addr)) {
- ret = -EIO;
+ ret = spi_nor_erase_sector(nor, addr);
+ if (ret)
goto erase_err;
- }
addr += mtd->erasesize;
len -= mtd->erasesize;
@@ -540,6 +691,9 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
+ { "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ { "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
/* Micron */
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
@@ -785,7 +939,8 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
- u32 page_offset, page_size, i;
+ size_t page_offset, page_remain, i;
+ size_t retval;
int ret;
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
@@ -794,32 +949,23 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
if (ret)
return ret;
- write_enable(nor);
-
- page_offset = to & (nor->page_size - 1);
-
- /* do all the bytes fit onto one page? */
- if (page_offset + len <= nor->page_size) {
- nor->write(nor, to, len, retlen, buf);
- } else {
- /* the size of data remaining on the first page */
- page_size = nor->page_size - page_offset;
- nor->write(nor, to, page_size, retlen, buf);
+ for (i = 0; i < len; ) {
+ ssize_t written;
- /* write everything in nor->page_size chunks */
- for (i = page_size; i < len; i += page_size) {
- page_size = len - i;
- if (page_size > nor->page_size)
- page_size = nor->page_size;
+ page_offset = (to + i) & (nor->page_size - 1);
+ page_remain = min_t(size_t, nor->page_size - page_offset,
+ len - i);
- ret = spi_nor_wait_till_ready(nor);
- if (ret)
- goto write_err;
+ write_enable(nor);
+ nor->write(nor, to + i, page_remain, &retval, buf + i);
+ written = retval;
- write_enable(nor);
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto write_err;
- nor->write(nor, to + i, page_size, retlen, buf + i);
- }
+ *retlen += written;
+ i += written;
}
ret = spi_nor_wait_till_ready(nor);
@@ -828,28 +974,6 @@ write_err:
return ret;
}
-static int macronix_quad_enable(struct spi_nor *nor)
-{
- int ret, val;
-
- val = read_sr(nor);
- write_enable(nor);
-
- nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
- nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
-
- if (spi_nor_wait_till_ready(nor))
- return 1;
-
- ret = read_sr(nor);
- if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
- dev_err(nor->dev, "Macronix Quad bit not set\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
/*
* Write status Register and configuration register with 2 bytes
* The first byte will be written to the status register, while the
@@ -861,7 +985,7 @@ static int write_sr_cr(struct spi_nor *nor, u16 val)
nor->cmd_buf[0] = val & 0xff;
nor->cmd_buf[1] = (val >> 8);
- return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0);
+ return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2);
}
static int spansion_quad_enable(struct spi_nor *nor)
@@ -888,42 +1012,272 @@ static int spansion_quad_enable(struct spi_nor *nor)
return 0;
}
-static int set_quad_mode(struct spi_nor *nor, struct flash_info *info)
+static int spi_nor_check(struct spi_nor *nor)
{
- int status;
+ if (!nor->dev || !nor->read || !nor->write ||
+ !nor->read_reg || !nor->write_reg) {
+ pr_err("spi-nor: please fill all the necessary fields!\n");
+ return -EINVAL;
+ }
- switch (JEDEC_MFR(info)) {
- case CFI_MFR_MACRONIX:
- status = macronix_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Macronix quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
- default:
- status = spansion_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Spansion quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
+ return 0;
+}
+
+static void
+spi_nor_set_read_settings(struct spi_nor_read_command *read,
+ u8 num_mode_clocks,
+ u8 num_wait_states,
+ u8 opcode,
+ enum spi_nor_protocol proto)
+{
+ read->num_mode_clocks = num_mode_clocks;
+ read->num_wait_states = num_wait_states;
+ read->opcode = opcode;
+ read->proto = proto;
+}
+
+static void
+spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
+ u8 opcode,
+ enum spi_nor_protocol proto)
+{
+ pp->opcode = opcode;
+ pp->proto = proto;
+}
+
+static int spi_nor_init_params(struct spi_nor *nor,
+ const struct flash_info *info,
+ struct spi_nor_flash_parameter *params)
+{
+ /* Set legacy flash parameters as default. */
+ memset(params, 0, sizeof(*params));
+
+ /* Set SPI NOR sizes. */
+ params->size = info->sector_size * info->n_sectors;
+ params->page_size = info->page_size;
+
+ /* (Fast) Read settings. */
+ params->hwcaps.mask |= SNOR_HWCAPS_READ;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
+ 0, 0, SPINOR_OP_READ,
+ SNOR_PROTO_1_1_1);
+
+ if (!(info->flags & SPI_NOR_NO_FR)) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
+ 0, 8, SPINOR_OP_READ_FAST,
+ SNOR_PROTO_1_1_1);
+ }
+
+ if (info->flags & SPI_NOR_DUAL_READ) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
+ 0, 8, SPINOR_OP_READ_1_1_2,
+ SNOR_PROTO_1_1_2);
}
+
+ if (info->flags & SPI_NOR_QUAD_READ) {
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
+ 0, 8, SPINOR_OP_READ_1_1_4,
+ SNOR_PROTO_1_1_4);
+ }
+
+ /* Page Program settings. */
+ params->hwcaps.mask |= SNOR_HWCAPS_PP;
+ spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
+ SPINOR_OP_PP, SNOR_PROTO_1_1_1);
+
+ /* Select the procedure to set the Quad Enable bit. */
+ if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
+ SNOR_HWCAPS_PP_QUAD))
+ params->quad_enable = spansion_quad_enable;
+
+ return 0;
}
-static int spi_nor_check(struct spi_nor *nor)
+static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size)
{
- if (!nor->dev || !nor->read || !nor->write ||
- !nor->read_reg || !nor->write_reg || !nor->erase) {
- pr_err("spi-nor: please fill all the necessary fields!\n");
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (table[i][0] == (int)hwcaps)
+ return table[i][1];
+
+ return -EINVAL;
+}
+
+static int spi_nor_hwcaps_read2cmd(u32 hwcaps)
+{
+ static const int hwcaps_read2cmd[][2] = {
+ { SNOR_HWCAPS_READ, SNOR_CMD_READ },
+ { SNOR_HWCAPS_READ_FAST, SNOR_CMD_READ_FAST },
+ { SNOR_HWCAPS_READ_1_1_2, SNOR_CMD_READ_1_1_2 },
+ { SNOR_HWCAPS_READ_1_2_2, SNOR_CMD_READ_1_2_2 },
+ { SNOR_HWCAPS_READ_2_2_2, SNOR_CMD_READ_2_2_2 },
+ { SNOR_HWCAPS_READ_1_1_4, SNOR_CMD_READ_1_1_4 },
+ { SNOR_HWCAPS_READ_1_4_4, SNOR_CMD_READ_1_4_4 },
+ { SNOR_HWCAPS_READ_4_4_4, SNOR_CMD_READ_4_4_4 },
+ };
+
+ return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd,
+ ARRAY_SIZE(hwcaps_read2cmd));
+}
+
+static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
+{
+ static const int hwcaps_pp2cmd[][2] = {
+ { SNOR_HWCAPS_PP, SNOR_CMD_PP },
+ { SNOR_HWCAPS_PP_1_1_4, SNOR_CMD_PP_1_1_4 },
+ { SNOR_HWCAPS_PP_1_4_4, SNOR_CMD_PP_1_4_4 },
+ { SNOR_HWCAPS_PP_4_4_4, SNOR_CMD_PP_4_4_4 },
+ };
+
+ return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd,
+ ARRAY_SIZE(hwcaps_pp2cmd));
+}
+
+static int spi_nor_select_read(struct spi_nor *nor,
+ const struct spi_nor_flash_parameter *params,
+ u32 shared_hwcaps)
+{
+ int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
+ const struct spi_nor_read_command *read;
+
+ if (best_match < 0)
+ return -EINVAL;
+
+ cmd = spi_nor_hwcaps_read2cmd(BIT(best_match));
+ if (cmd < 0)
+ return -EINVAL;
+
+ read = &params->reads[cmd];
+ nor->read_opcode = read->opcode;
+ nor->read_proto = read->proto;
+
+ /*
+ * In the spi-nor framework, we don't need to make the difference
+ * between mode clock cycles and wait state clock cycles.
+ * Indeed, the value of the mode clock cycles is used by a QSPI
+ * flash memory to know whether it should enter or leave its 0-4-4
+ * (Continuous Read / XIP) mode.
+ * eXecution In Place is out of the scope of the mtd sub-system.
+ * Hence we choose to merge both mode and wait state clock cycles
+ * into the so called dummy clock cycles.
+ */
+ nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
+ return 0;
+}
+
+static int spi_nor_select_pp(struct spi_nor *nor,
+ const struct spi_nor_flash_parameter *params,
+ u32 shared_hwcaps)
+{
+ int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
+ const struct spi_nor_pp_command *pp;
+
+ if (best_match < 0)
+ return -EINVAL;
+
+ cmd = spi_nor_hwcaps_pp2cmd(BIT(best_match));
+ if (cmd < 0)
return -EINVAL;
+
+ pp = &params->page_programs[cmd];
+ nor->program_opcode = pp->opcode;
+ nor->write_proto = pp->proto;
+ return 0;
+}
+
+static int spi_nor_select_erase(struct spi_nor *nor,
+ const struct flash_info *info)
+{
+ struct mtd_info *mtd = nor->mtd;
+
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+ /* prefer "small sector" erase if possible */
+ if (info->flags & SECT_4K) {
+ nor->erase_opcode = SPINOR_OP_BE_4K;
+ mtd->erasesize = 4096;
+ } else if (info->flags & SECT_4K_PMC) {
+ nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
+ mtd->erasesize = 4096;
+ } else
+#endif
+ {
+ nor->erase_opcode = SPINOR_OP_SE;
+ mtd->erasesize = info->sector_size;
+ }
+ return 0;
+}
+
+static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
+ const struct spi_nor_flash_parameter *params,
+ const struct spi_nor_hwcaps *hwcaps)
+{
+ u32 ignored_mask, shared_mask;
+ bool enable_quad_io;
+ int err;
+
+ /*
+ * Keep only the hardware capabilities supported by both the SPI
+ * controller and the SPI flash memory.
+ */
+ shared_mask = hwcaps->mask & params->hwcaps.mask;
+
+ /* SPI n-n-n protocols are not supported yet. */
+ ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
+ SNOR_HWCAPS_READ_4_4_4 |
+ SNOR_HWCAPS_PP_4_4_4);
+ if (shared_mask & ignored_mask) {
+ dev_dbg(nor->dev,
+ "SPI n-n-n protocols are not supported yet.\n");
+ shared_mask &= ~ignored_mask;
+ }
+
+ /* Select the (Fast) Read command. */
+ err = spi_nor_select_read(nor, params, shared_mask);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select read settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ /* Select the Page Program command. */
+ err = spi_nor_select_pp(nor, params, shared_mask);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select write settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ /* Select the Sector Erase command. */
+ err = spi_nor_select_erase(nor, info);
+ if (err) {
+ dev_err(nor->dev,
+ "can't select erase settings supported by both the SPI controller and memory.\n");
+ return err;
+ }
+
+ /* Enable Quad I/O if needed. */
+ enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
+ spi_nor_get_protocol_width(nor->write_proto) == 4);
+ if (enable_quad_io && params->quad_enable) {
+ err = params->quad_enable(nor);
+ if (err) {
+ dev_err(nor->dev, "quad mode not supported\n");
+ return err;
+ }
}
return 0;
}
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+ const struct spi_nor_hwcaps *hwcaps,
bool use_large_blocks)
{
+ struct spi_nor_flash_parameter params;
const struct spi_device_id *id = NULL;
struct flash_info *info;
struct device_d *dev = nor->dev;
@@ -936,6 +1290,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
if (ret)
return ret;
+ /* Reset SPI protocol for all commands. */
+ nor->reg_proto = SNOR_PROTO_1_1_1;
+ nor->read_proto = SNOR_PROTO_1_1_1;
+ nor->write_proto = SNOR_PROTO_1_1_1;
+
/* Try to auto-detect if chip name wasn't specified */
if (!name)
id = spi_nor_read_id(nor);
@@ -971,6 +1330,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
}
}
+ nor->info = info;
+
mutex_init(&nor->lock);
/*
@@ -985,12 +1346,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
write_sr(nor, 0);
}
+ /* Parse the Serial Flash Discoverable Parameters table. */
+ ret = spi_nor_init_params(nor, info, &params);
+ if (ret)
+ return ret;
+
if (!mtd->name)
mtd->name = (char *) dev_name(dev);
mtd->type = MTD_NORFLASH;
mtd->writesize = 1;
mtd->flags = MTD_CAP_NORFLASH;
- mtd->size = info->sector_size * info->n_sectors;
+ mtd->size = params.size;
mtd->erase = spi_nor_erase;
mtd->read = spi_nor_read;
@@ -1009,107 +1375,57 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
if (info->flags & USE_FSR)
nor->flags |= SNOR_F_USE_FSR;
-#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
- /* prefer "small sector" erase if possible */
- if (info->flags & SECT_4K && !use_large_blocks) {
- nor->erase_opcode = SPINOR_OP_BE_4K;
- mtd->erasesize = 4096;
- } else if (info->flags & SECT_4K_PMC && !use_large_blocks) {
- nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
- mtd->erasesize = 4096;
- } else
-#endif
- {
- nor->erase_opcode = SPINOR_OP_SE;
- mtd->erasesize = info->sector_size;
- }
-
if (info->flags & SPI_NOR_NO_ERASE)
mtd->flags |= MTD_NO_ERASE;
- nor->page_size = info->page_size;
+ nor->page_size = params.page_size;
mtd->writebufsize = nor->page_size;
if (np) {
/* If we were instantiated by DT, use it */
if (of_property_read_bool(np, "m25p,fast-read"))
- nor->flash_read = SPI_NOR_FAST;
+ params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
else
- nor->flash_read = SPI_NOR_NORMAL;
+ params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
} else {
/* If we weren't instantiated by DT, default to fast-read */
- nor->flash_read = SPI_NOR_FAST;
+ params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
}
/* Some devices cannot do fast-read, no matter what DT tells us */
if (info->flags & SPI_NOR_NO_FR)
- nor->flash_read = SPI_NOR_NORMAL;
+ params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
- /* Quad/Dual-read mode takes precedence over fast/normal */
- if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
- ret = set_quad_mode(nor, info);
- if (ret) {
- dev_err(dev, "quad mode not supported\n");
- return ret;
- }
- nor->flash_read = SPI_NOR_QUAD;
- } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
- nor->flash_read = SPI_NOR_DUAL;
- }
-
- /* Default commands */
- switch (nor->flash_read) {
- case SPI_NOR_QUAD:
- nor->read_opcode = SPINOR_OP_READ_1_1_4;
- break;
- case SPI_NOR_DUAL:
- nor->read_opcode = SPINOR_OP_READ_1_1_2;
- break;
- case SPI_NOR_FAST:
- nor->read_opcode = SPINOR_OP_READ_FAST;
- break;
- case SPI_NOR_NORMAL:
- nor->read_opcode = SPINOR_OP_READ;
- break;
- default:
- dev_err(dev, "No Read opcode defined\n");
- return -EINVAL;
- }
-
- nor->program_opcode = SPINOR_OP_PP;
+ /*
+ * Configure the SPI memory:
+ * - select op codes for (Fast) Read, Page Program and Sector Erase.
+ * - set the number of dummy cycles (mode cycles + wait states).
+ * - set the SPI protocols for register and memory accesses.
+ * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
+ */
+ ret = spi_nor_setup(nor, info, &params, hwcaps);
+ if (ret)
+ return ret;
if (info->addr_width)
nor->addr_width = info->addr_width;
else if (mtd->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
- if (JEDEC_MFR(info) == CFI_MFR_AMD) {
- /* Dedicated 4-byte command set */
- switch (nor->flash_read) {
- case SPI_NOR_QUAD:
- nor->read_opcode = SPINOR_OP_READ4_1_1_4;
- break;
- case SPI_NOR_DUAL:
- nor->read_opcode = SPINOR_OP_READ4_1_1_2;
- break;
- case SPI_NOR_FAST:
- nor->read_opcode = SPINOR_OP_READ4_FAST;
- break;
- case SPI_NOR_NORMAL:
- nor->read_opcode = SPINOR_OP_READ4;
- break;
- }
- nor->program_opcode = SPINOR_OP_PP_4B;
- /* No small sector erase for 4-byte command set */
- nor->erase_opcode = SPINOR_OP_SE_4B;
- mtd->erasesize = info->sector_size;
- } else
+ if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+ info->flags & SPI_NOR_4B_OPCODES)
+ spi_nor_set_4byte_opcodes(nor);
+ else
set_4byte(nor, info, 1);
} else {
nor->addr_width = 3;
}
- nor->read_dummy = spi_nor_read_dummy_cycles(nor);
+ if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
+ dev_err(dev, "address width is too large: %u\n",
+ nor->addr_width);
+ return -EINVAL;
+ }
dev_info(dev, "%s (%lld Kbytes)\n", id->name,
(long long)mtd->size >> 10);