@@ -470,46 +470,15 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
/* Enable/disable 4-byte addressing mode. */
static int set_4byte(struct spi_nor *nor, bool enable)
{
- int status;
- bool need_wren = false;
- u8 cmd;
+ if (nor->set_4byte)
+ return nor->set_4byte(nor, enable);
- switch (JEDEC_MFR(nor->info)) {
- case SNOR_MFR_ST:
- case SNOR_MFR_MICRON:
- /* Some Micron need WREN command; all will accept it */
- need_wren = true;
- /* fall through */
- case SNOR_MFR_MACRONIX:
- case SNOR_MFR_WINBOND:
- if (need_wren)
- write_enable(nor);
-
- cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
- status = nor->write_reg(nor, cmd, NULL, 0);
- if (need_wren)
- write_disable(nor);
-
- if (!status && !enable &&
- JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) {
- /*
- * On Winbond W25Q256FV, leaving 4byte mode causes
- * the Extended Address Register to be set to 1, so all
- * 3-byte-address reads come from the second 16M.
- * We must clear the register to enable normal behavior.
- */
- write_enable(nor);
- nor->cmd_buf[0] = 0;
- nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1);
- write_disable(nor);
- }
-
- return status;
- default:
- /* Spansion style */
- nor->cmd_buf[0] = enable << 7;
- return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
- }
+ /*
+ * Spansion style. Should work for all NORs that do not have their own
+ * ->set_4byte() implementation.
+ */
+ nor->cmd_buf[0] = enable << 7;
+ return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
}
static int s3an_sr_ready(struct spi_nor *nor)
@@ -3666,13 +3635,98 @@ void spi_nor_restore(struct spi_nor *nor)
}
EXPORT_SYMBOL_GPL(spi_nor_restore);
+static int macronix_set_4byte(struct spi_nor *nor, bool enable)
+{
+ return nor->write_reg(nor, enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B,
+ NULL, 0);
+}
+
+static int st_micron_set_4byte(struct spi_nor *nor, bool enable)
+{
+ int ret;
+
+ write_enable(nor);
+ ret = macronix_set_4byte(nor, enable);
+ write_disable(nor);
+
+ return ret;
+}
+
+static int winbond_set_4byte(struct spi_nor *nor, bool enable)
+{
+ int ret;
+
+ ret = macronix_set_4byte(nor, enable);
+ if (ret || enable)
+ return ret;
+
+ /*
+ * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address
+ * Register to be set to 1, so all 3-byte-address reads come from the
+ * second 16M.
+ * We must clear the register to enable normal behavior.
+ */
+ write_enable(nor);
+ nor->cmd_buf[0] = 0;
+ nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1);
+ write_disable(nor);
+
+ return ret;
+}
+
+static void st_micron_post_sfdp_fixups(struct spi_nor *nor)
+{
+ nor->set_4byte = st_micron_set_4byte;
+}
+
+static void macronix_post_sfdp_fixups(struct spi_nor *nor)
+{
+ nor->set_4byte = macronix_set_4byte;
+}
+
+static void winbond_post_sfdp_fixups(struct spi_nor *nor)
+{
+ nor->set_4byte = winbond_set_4byte;
+}
+
+static int
+spi_nor_manufacturer_post_sfdp_fixups(struct spi_nor *nor,
+ struct spi_nor_flash_parameter *params)
+{
+ switch (JEDEC_MFR(nor->info)) {
+ case SNOR_MFR_ST:
+ case SNOR_MFR_MICRON:
+ st_micron_post_sfdp_fixups(nor);
+ break;
+
+ case SNOR_MFR_MACRONIX:
+ macronix_post_sfdp_fixups(nor);
+ break;
+
+ case SNOR_MFR_WINBOND:
+ winbond_post_sfdp_fixups(nor);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int spi_nor_post_sfdp_fixups(struct spi_nor *nor,
struct spi_nor_flash_parameter *params)
{
- if (nor->info->fixups && nor->info->fixups->post_sfdp)
- return nor->info->fixups->post_sfdp(nor, params);
+ int ret;
- return 0;
+ ret = spi_nor_manufacturer_post_sfdp_fixups(nor, params);
+ if (ret)
+ return ret;
+
+ if (nor->info->fixups && nor->info->fixups->post_sfdp)
+ ret = nor->info->fixups->post_sfdp(nor, params);
+
+ return ret;
}
static const struct flash_info *spi_nor_match_id(const char *name)
@@ -365,6 +365,8 @@ struct flash_info;
* @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is
* @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode
* completely locked
+ * @set_4byte: [FLASH-SPECIFIC] put the SPI NOR in 4 byte addressing
+ * mode
* @priv: the private data
*/
struct spi_nor {
@@ -401,6 +403,7 @@ struct spi_nor {
int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*quad_enable)(struct spi_nor *nor);
+ int (*set_4byte)(struct spi_nor *nor, bool enable);
void *priv;
};
The procedure used to enable 4 byte addressing mode depends on the NOR device, so let's provide a hook so that manufacturer specific handling can be implemented in a sane way. This is also an opportunity to create a per-manufacturer post SFDP fixups function where we'll put all the manufacturer specific tweaks. Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> --- drivers/mtd/spi-nor/spi-nor.c | 138 +++++++++++++++++++++++----------- include/linux/mtd/spi-nor.h | 3 + 2 files changed, 99 insertions(+), 42 deletions(-)