diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2019-05-10 18:15:50 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2019-05-10 18:15:50 +0200 |
commit | 5fdedd222f566b61f9e5c9278e10af777384e9b4 (patch) | |
tree | 7db078612b2ab29f516bcb5bbf83fe7afc3da50d /drivers/mtd | |
parent | 6a4f6bf4493031c8d655f0506819d4788c9a7553 (diff) | |
parent | 89d1d8c9a7266d62abf0ae03513a539be2120077 (diff) | |
download | barebox-5fdedd222f566b61f9e5c9278e10af777384e9b4.tar.gz barebox-5fdedd222f566b61f9e5c9278e10af777384e9b4.tar.xz |
Merge branch 'for-next/qspi'
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/devices/Kconfig | 1 | ||||
-rw-r--r-- | drivers/mtd/devices/m25p80.c | 160 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/cadence-quadspi.c | 257 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/spi-nor.c | 672 |
4 files changed, 725 insertions, 365 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 9c3925bde1..25db10a9b2 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 8a67792125..09a8714247 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 ed5377bd4b..11e4d236dd 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 1595349c4c..43bd402f9f 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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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 = ¶ms->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 = ¶ms->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, ¶ms); + 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, ¶ms, 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); |