diff options
author | Miquel Raynal <miquel.raynal@bootlin.com> | 2025-03-26 19:49:01 +0300 |
---|---|---|
committer | Miquel Raynal <miquel.raynal@bootlin.com> | 2025-03-26 19:49:01 +0300 |
commit | 59ee921ca7ec13c879ffbd101cece0049e81dac2 (patch) | |
tree | d31b4843c6894a17811165b4cb568b2dc5e21893 | |
parent | 48a29721c967da1af657279fa696d6c386bdd5ca (diff) | |
parent | eec373688d91f7f8988702e36d4a0e07b8e782b2 (diff) | |
download | linux-59ee921ca7ec13c879ffbd101cece0049e81dac2.tar.xz |
Merge tag 'spi-nor/for-6.15' into mtd/next
SPI NOR adds support for few flashes. Few cleanup patches for the core
driver, where we touched the headers inclusion list and we start using
the scope based mutex cleanup helpers.
-rw-r--r-- | drivers/mtd/spi-nor/core.c | 77 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/macronix.c | 31 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/otp.c | 1 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/swp.c | 1 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/winbond.c | 88 |
5 files changed, 146 insertions, 52 deletions
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 19eb98bd6821..ac4b960101cc 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -7,16 +7,17 @@ * Copyright (C) 2014, Freescale Semiconductor, Inc. */ -#include <linux/err.h> -#include <linux/errno.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/err.h> +#include <linux/errno.h> #include <linux/math64.h> #include <linux/module.h> #include <linux/mtd/mtd.h> #include <linux/mtd/spi-nor.h> #include <linux/mutex.h> -#include <linux/of_platform.h> +#include <linux/of.h> #include <linux/regulator/consumer.h> #include <linux/sched/task_stack.h> #include <linux/sizes.h> @@ -639,32 +640,26 @@ static bool spi_nor_use_parallel_locking(struct spi_nor *nor) static int spi_nor_rww_start_rdst(struct spi_nor *nor) { struct spi_nor_rww *rww = &nor->rww; - int ret = -EAGAIN; - mutex_lock(&nor->lock); + guard(mutex)(&nor->lock); if (rww->ongoing_io || rww->ongoing_rd) - goto busy; + return -EAGAIN; rww->ongoing_io = true; rww->ongoing_rd = true; - ret = 0; -busy: - mutex_unlock(&nor->lock); - return ret; + return 0; } static void spi_nor_rww_end_rdst(struct spi_nor *nor) { struct spi_nor_rww *rww = &nor->rww; - mutex_lock(&nor->lock); + guard(mutex)(&nor->lock); rww->ongoing_io = false; rww->ongoing_rd = false; - - mutex_unlock(&nor->lock); } static int spi_nor_lock_rdst(struct spi_nor *nor) @@ -1212,26 +1207,21 @@ static void spi_nor_offset_to_banks(u64 bank_size, loff_t start, size_t len, static bool spi_nor_rww_start_io(struct spi_nor *nor) { struct spi_nor_rww *rww = &nor->rww; - bool start = false; - mutex_lock(&nor->lock); + guard(mutex)(&nor->lock); if (rww->ongoing_io) - goto busy; + return false; rww->ongoing_io = true; - start = true; -busy: - mutex_unlock(&nor->lock); - return start; + return true; } static void spi_nor_rww_end_io(struct spi_nor *nor) { - mutex_lock(&nor->lock); + guard(mutex)(&nor->lock); nor->rww.ongoing_io = false; - mutex_unlock(&nor->lock); } static int spi_nor_lock_device(struct spi_nor *nor) @@ -1254,32 +1244,27 @@ static void spi_nor_unlock_device(struct spi_nor *nor) static bool spi_nor_rww_start_exclusive(struct spi_nor *nor) { struct spi_nor_rww *rww = &nor->rww; - bool start = false; mutex_lock(&nor->lock); if (rww->ongoing_io || rww->ongoing_rd || rww->ongoing_pe) - goto busy; + return false; rww->ongoing_io = true; rww->ongoing_rd = true; rww->ongoing_pe = true; - start = true; -busy: - mutex_unlock(&nor->lock); - return start; + return true; } static void spi_nor_rww_end_exclusive(struct spi_nor *nor) { struct spi_nor_rww *rww = &nor->rww; - mutex_lock(&nor->lock); + guard(mutex)(&nor->lock); rww->ongoing_io = false; rww->ongoing_rd = false; rww->ongoing_pe = false; - mutex_unlock(&nor->lock); } int spi_nor_prep_and_lock(struct spi_nor *nor) @@ -1316,30 +1301,26 @@ static bool spi_nor_rww_start_pe(struct spi_nor *nor, loff_t start, size_t len) { struct spi_nor_rww *rww = &nor->rww; unsigned int used_banks = 0; - bool started = false; u8 first, last; int bank; - mutex_lock(&nor->lock); + guard(mutex)(&nor->lock); if (rww->ongoing_io || rww->ongoing_rd || rww->ongoing_pe) - goto busy; + return false; spi_nor_offset_to_banks(nor->params->bank_size, start, len, &first, &last); for (bank = first; bank <= last; bank++) { if (rww->used_banks & BIT(bank)) - goto busy; + return false; used_banks |= BIT(bank); } rww->used_banks |= used_banks; rww->ongoing_pe = true; - started = true; -busy: - mutex_unlock(&nor->lock); - return started; + return true; } static void spi_nor_rww_end_pe(struct spi_nor *nor, loff_t start, size_t len) @@ -1348,15 +1329,13 @@ static void spi_nor_rww_end_pe(struct spi_nor *nor, loff_t start, size_t len) u8 first, last; int bank; - mutex_lock(&nor->lock); + guard(mutex)(&nor->lock); spi_nor_offset_to_banks(nor->params->bank_size, start, len, &first, &last); for (bank = first; bank <= last; bank++) rww->used_banks &= ~BIT(bank); rww->ongoing_pe = false; - - mutex_unlock(&nor->lock); } static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t len) @@ -1393,19 +1372,18 @@ static bool spi_nor_rww_start_rd(struct spi_nor *nor, loff_t start, size_t len) { struct spi_nor_rww *rww = &nor->rww; unsigned int used_banks = 0; - bool started = false; u8 first, last; int bank; - mutex_lock(&nor->lock); + guard(mutex)(&nor->lock); if (rww->ongoing_io || rww->ongoing_rd) - goto busy; + return false; spi_nor_offset_to_banks(nor->params->bank_size, start, len, &first, &last); for (bank = first; bank <= last; bank++) { if (rww->used_banks & BIT(bank)) - goto busy; + return false; used_banks |= BIT(bank); } @@ -1413,11 +1391,8 @@ static bool spi_nor_rww_start_rd(struct spi_nor *nor, loff_t start, size_t len) rww->used_banks |= used_banks; rww->ongoing_io = true; rww->ongoing_rd = true; - started = true; -busy: - mutex_unlock(&nor->lock); - return started; + return true; } static void spi_nor_rww_end_rd(struct spi_nor *nor, loff_t start, size_t len) @@ -1426,7 +1401,7 @@ static void spi_nor_rww_end_rd(struct spi_nor *nor, loff_t start, size_t len) u8 first, last; int bank; - mutex_lock(&nor->lock); + guard(mutex)(&nor->lock); spi_nor_offset_to_banks(nor->params->bank_size, start, len, &first, &last); for (bank = first; bank <= last; bank++) @@ -1434,8 +1409,6 @@ static void spi_nor_rww_end_rd(struct spi_nor *nor, loff_t start, size_t len) rww->ongoing_io = false; rww->ongoing_rd = false; - - mutex_unlock(&nor->lock); } static int spi_nor_prep_and_lock_rd(struct spi_nor *nor, loff_t start, size_t len) diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index 99936fd25d43..55644a3cd88c 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -45,8 +45,26 @@ mx25l25635_post_bfpt_fixups(struct spi_nor *nor, return 0; } +static int +macronix_qpp4b_post_sfdp_fixups(struct spi_nor *nor) +{ + /* PP_1_1_4_4B is supported but missing in 4BAIT. */ + struct spi_nor_flash_parameter *params = nor->params; + + params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4; + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_1_4], + SPINOR_OP_PP_1_1_4_4B, SNOR_PROTO_1_1_4); + + return 0; +} + static const struct spi_nor_fixups mx25l25635_fixups = { .post_bfpt = mx25l25635_post_bfpt_fixups, + .post_sfdp = macronix_qpp4b_post_sfdp_fixups, +}; + +static const struct spi_nor_fixups macronix_qpp4b_fixups = { + .post_sfdp = macronix_qpp4b_post_sfdp_fixups, }; static const struct flash_info macronix_nor_parts[] = { @@ -102,11 +120,17 @@ static const struct flash_info macronix_nor_parts[] = { .size = SZ_64M, .no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .fixup_flags = SPI_NOR_4B_OPCODES, + .fixups = ¯onix_qpp4b_fixups, }, { .id = SNOR_ID(0xc2, 0x20, 0x1b), .name = "mx66l1g45g", .size = SZ_128M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .fixups = ¯onix_qpp4b_fixups, + }, { + /* MX66L2G45G */ + .id = SNOR_ID(0xc2, 0x20, 0x1c), + .fixups = ¯onix_qpp4b_fixups, }, { .id = SNOR_ID(0xc2, 0x23, 0x14), .name = "mx25v8035f", @@ -148,18 +172,25 @@ static const struct flash_info macronix_nor_parts[] = { .size = SZ_64M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .fixup_flags = SPI_NOR_4B_OPCODES, + .fixups = ¯onix_qpp4b_fixups, }, { .id = SNOR_ID(0xc2, 0x25, 0x3a), .name = "mx66u51235f", .size = SZ_64M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .fixup_flags = SPI_NOR_4B_OPCODES, + .fixups = ¯onix_qpp4b_fixups, + }, { + /* MX66U1G45G */ + .id = SNOR_ID(0xc2, 0x25, 0x3b), + .fixups = ¯onix_qpp4b_fixups, }, { .id = SNOR_ID(0xc2, 0x25, 0x3c), .name = "mx66u2g45g", .size = SZ_256M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .fixup_flags = SPI_NOR_4B_OPCODES, + .fixups = ¯onix_qpp4b_fixups, }, { .id = SNOR_ID(0xc2, 0x26, 0x18), .name = "mx25l12855e", diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index 9a729aa3452d..7d0b145d78d8 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -6,6 +6,7 @@ */ #include <linux/log2.h> +#include <linux/math64.h> #include <linux/mtd/mtd.h> #include <linux/mtd/spi-nor.h> diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index e48c3cff247a..9c9328478d8a 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -5,6 +5,7 @@ * Copyright (C) 2005, Intec Automation Inc. * Copyright (C) 2014, Freescale Semiconductor, Inc. */ +#include <linux/math64.h> #include <linux/mtd/mtd.h> #include <linux/mtd/spi-nor.h> diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 8d0a00d69e12..63a93c9eb917 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -10,6 +10,7 @@ #define WINBOND_NOR_OP_RDEAR 0xc8 /* Read Extended Address Register */ #define WINBOND_NOR_OP_WREAR 0xc5 /* Write Extended Address Register */ +#define WINBOND_NOR_OP_SELDIE 0xc2 /* Select active die */ #define WINBOND_NOR_WREAR_OP(buf) \ SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0), \ @@ -17,6 +18,12 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(1, buf, 0)) +#define WINBOND_NOR_SELDIE_OP(buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_SELDIE, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(1, buf, 0)) + static int w25q128_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @@ -66,6 +73,79 @@ static const struct spi_nor_fixups w25q256_fixups = { .post_bfpt = w25q256_post_bfpt_fixups, }; +/** + * winbond_nor_select_die() - Set active die. + * @nor: pointer to 'struct spi_nor'. + * @die: die to set active. + * + * Certain Winbond chips feature more than a single die. This is mostly hidden + * to the user, except that some chips may experience time deviation when + * modifying the status bits between dies, which in some corner cases may + * produce problematic races. Being able to explicitly select a die to check its + * state in this case may be useful. + * + * Return: 0 on success, -errno otherwise. + */ +static int winbond_nor_select_die(struct spi_nor *nor, u8 die) +{ + int ret; + + nor->bouncebuf[0] = die; + + if (nor->spimem) { + struct spi_mem_op op = WINBOND_NOR_SELDIE_OP(nor->bouncebuf); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = spi_nor_controller_ops_write_reg(nor, + WINBOND_NOR_OP_SELDIE, + nor->bouncebuf, 1); + } + + if (ret) + dev_dbg(nor->dev, "error %d selecting die %d\n", ret, die); + + return ret; +} + +static int winbond_nor_multi_die_ready(struct spi_nor *nor) +{ + int ret, i; + + for (i = 0; i < nor->params->n_dice; i++) { + ret = winbond_nor_select_die(nor, i); + if (ret) + return ret; + + ret = spi_nor_sr_ready(nor); + if (ret <= 0) + return ret; + } + + return 1; +} + +static int +winbond_nor_multi_die_post_sfdp_fixups(struct spi_nor *nor) +{ + /* + * SFDP supports dice numbers, but this information is only available in + * optional additional tables which are not provided by these chips. + * Dice number has an impact though, because these devices need extra + * care when reading the busy bit. + */ + nor->params->n_dice = nor->params->size / SZ_64M; + nor->params->ready = winbond_nor_multi_die_ready; + + return 0; +} + +static const struct spi_nor_fixups winbond_nor_multi_die_fixups = { + .post_sfdp = winbond_nor_multi_die_post_sfdp_fixups, +}; + static const struct flash_info winbond_nor_parts[] = { { .id = SNOR_ID(0xef, 0x30, 0x10), @@ -147,6 +227,10 @@ static const struct flash_info winbond_nor_parts[] = { .size = SZ_64M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q01JV */ + .id = SNOR_ID(0xef, 0x40, 0x21), + .fixups = &winbond_nor_multi_die_fixups, + }, { .id = SNOR_ID(0xef, 0x50, 0x12), .name = "w25q20bw", .size = SZ_256K, @@ -222,6 +306,10 @@ static const struct flash_info winbond_nor_parts[] = { .id = SNOR_ID(0xef, 0x70, 0x19), .name = "w25q256jvm", }, { + /* W25Q02JV */ + .id = SNOR_ID(0xef, 0x70, 0x22), + .fixups = &winbond_nor_multi_die_fixups, + }, { .id = SNOR_ID(0xef, 0x71, 0x19), .name = "w25m512jv", .size = SZ_64M, |