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.
This commit is contained in:
Miquel Raynal
2025-03-26 17:49:01 +01:00
5 changed files with 146 additions and 52 deletions
+25 -52
View File
@@ -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)
+31
View File
@@ -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(&params->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 = &macronix_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 = &macronix_qpp4b_fixups,
}, {
/* MX66L2G45G */
.id = SNOR_ID(0xc2, 0x20, 0x1c),
.fixups = &macronix_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 = &macronix_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 = &macronix_qpp4b_fixups,
}, {
/* MX66U1G45G */
.id = SNOR_ID(0xc2, 0x25, 0x3b),
.fixups = &macronix_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 = &macronix_qpp4b_fixups,
}, {
.id = SNOR_ID(0xc2, 0x26, 0x18),
.name = "mx25l12855e",
+1
View File
@@ -6,6 +6,7 @@
*/
#include <linux/log2.h>
#include <linux/math64.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/spi-nor.h>
+1
View File
@@ -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>
+88
View File
@@ -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),
@@ -146,6 +226,10 @@ static const struct flash_info winbond_nor_parts[] = {
.name = "w25q512jvq",
.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",
@@ -221,6 +305,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",