mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 23:24:38 +00:00
mtd: spi-nor-core: Rework hwcaps selection
The spi-mem layer provides a spi_mem_supports_op() function to check whether a specific operation is supported by the controller or not. This is much more accurate than the hwcaps selection logic based on SPI_{RX,TX}_ flags. Rework the hwcaps selection logic to use spi_mem_supports_op(). To make sure the build doesn't break for boards not using CONFIG_DM_SPI, add a simple SPI_{RX,TX}_ based hwcaps selection logic in spi-mem-nodm similar to spi_mem_default_supports_op(). This change is only compile-tested. To avoid SPL size problems on the x530 board, the old hwcaps selection is still kept around. Leaving the code in-place was getting difficult to read and understand, so the code is restructured to have it all in one isolated function. As a result of this, the parameter hwcaps to spi_nor_setup() is no longer needed. Remove it. Based on the Linux commit c76f5089796a (mtd: spi-nor: Rework hwcaps selection for the spi-mem case, 2019-08-06) Signed-off-by: Pratyush Yadav <p.yadav@ti.com> Reviewed-by: Jagan Teki <jagan@amarulasolutions.com>
This commit is contained in:
parent
8702188ce5
commit
71025f013c
4 changed files with 280 additions and 52 deletions
|
@ -88,6 +88,15 @@ config SPI_FLASH_SFDP_SUPPORT
|
|||
SPI NOR flashes using Serial Flash Discoverable Parameters (SFDP)
|
||||
tables as per JESD216 standard.
|
||||
|
||||
config SPI_FLASH_SMART_HWCAPS
|
||||
bool "Smart hardware capability detection based on SPI MEM supports_op() hook"
|
||||
default y
|
||||
help
|
||||
Enable support for smart hardware capability detection based on SPI
|
||||
MEM supports_op() hook that lets controllers express whether they
|
||||
can support a type of operation in a much more refined way compared
|
||||
to using flags like SPI_RX_DUAL, SPI_TX_QUAD, etc.
|
||||
|
||||
config SPI_FLASH_BAR
|
||||
bool "SPI flash Bank/Extended address register support"
|
||||
help
|
||||
|
|
|
@ -2299,6 +2299,194 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
|
|||
ARRAY_SIZE(hwcaps_pp2cmd));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI_FLASH_SMART_HWCAPS
|
||||
/**
|
||||
* spi_nor_check_op - check if the operation is supported by controller
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @op: pointer to op template to be checked
|
||||
*
|
||||
* Returns 0 if operation is supported, -ENOTSUPP otherwise.
|
||||
*/
|
||||
static int spi_nor_check_op(struct spi_nor *nor,
|
||||
struct spi_mem_op *op)
|
||||
{
|
||||
/*
|
||||
* First test with 4 address bytes. The opcode itself might be a 3B
|
||||
* addressing opcode but we don't care, because SPI controller
|
||||
* implementation should not check the opcode, but just the sequence.
|
||||
*/
|
||||
op->addr.nbytes = 4;
|
||||
if (!spi_mem_supports_op(nor->spi, op)) {
|
||||
if (nor->mtd.size > SZ_16M)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* If flash size <= 16MB, 3 address bytes are sufficient */
|
||||
op->addr.nbytes = 3;
|
||||
if (!spi_mem_supports_op(nor->spi, op))
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_check_readop - check if the read op is supported by controller
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @read: pointer to op template to be checked
|
||||
*
|
||||
* Returns 0 if operation is supported, -ENOTSUPP otherwise.
|
||||
*/
|
||||
static int spi_nor_check_readop(struct spi_nor *nor,
|
||||
const struct spi_nor_read_command *read)
|
||||
{
|
||||
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 1),
|
||||
SPI_MEM_OP_ADDR(3, 0, 1),
|
||||
SPI_MEM_OP_DUMMY(0, 1),
|
||||
SPI_MEM_OP_DATA_IN(0, NULL, 1));
|
||||
|
||||
op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(read->proto);
|
||||
op.addr.buswidth = spi_nor_get_protocol_addr_nbits(read->proto);
|
||||
op.data.buswidth = spi_nor_get_protocol_data_nbits(read->proto);
|
||||
op.dummy.buswidth = op.addr.buswidth;
|
||||
op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) *
|
||||
op.dummy.buswidth / 8;
|
||||
|
||||
return spi_nor_check_op(nor, &op);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_check_pp - check if the page program op is supported by controller
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @pp: pointer to op template to be checked
|
||||
*
|
||||
* Returns 0 if operation is supported, -ENOTSUPP otherwise.
|
||||
*/
|
||||
static int spi_nor_check_pp(struct spi_nor *nor,
|
||||
const struct spi_nor_pp_command *pp)
|
||||
{
|
||||
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 1),
|
||||
SPI_MEM_OP_ADDR(3, 0, 1),
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_OUT(0, NULL, 1));
|
||||
|
||||
op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(pp->proto);
|
||||
op.addr.buswidth = spi_nor_get_protocol_addr_nbits(pp->proto);
|
||||
op.data.buswidth = spi_nor_get_protocol_data_nbits(pp->proto);
|
||||
|
||||
return spi_nor_check_op(nor, &op);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_adjust_hwcaps - Find optimal Read/Write protocol based on SPI
|
||||
* controller capabilities
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @params: pointer to the 'struct spi_nor_flash_parameter'
|
||||
* representing SPI NOR flash capabilities
|
||||
* @hwcaps: pointer to resulting capabilities after adjusting
|
||||
* according to controller and flash's capability
|
||||
*
|
||||
* Discard caps based on what the SPI controller actually supports (using
|
||||
* spi_mem_supports_op()).
|
||||
*/
|
||||
static void
|
||||
spi_nor_adjust_hwcaps(struct spi_nor *nor,
|
||||
const struct spi_nor_flash_parameter *params,
|
||||
u32 *hwcaps)
|
||||
{
|
||||
unsigned int cap;
|
||||
|
||||
/*
|
||||
* Enable all caps by default. We will mask them after checking what's
|
||||
* really supported using spi_mem_supports_op().
|
||||
*/
|
||||
*hwcaps = SNOR_HWCAPS_ALL;
|
||||
|
||||
/* DTR modes are not supported yet, mask them all. */
|
||||
*hwcaps &= ~SNOR_HWCAPS_DTR;
|
||||
|
||||
/* X-X-X modes are not supported yet, mask them all. */
|
||||
*hwcaps &= ~SNOR_HWCAPS_X_X_X;
|
||||
|
||||
for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) {
|
||||
int rdidx, ppidx;
|
||||
|
||||
if (!(*hwcaps & BIT(cap)))
|
||||
continue;
|
||||
|
||||
rdidx = spi_nor_hwcaps_read2cmd(BIT(cap));
|
||||
if (rdidx >= 0 &&
|
||||
spi_nor_check_readop(nor, ¶ms->reads[rdidx]))
|
||||
*hwcaps &= ~BIT(cap);
|
||||
|
||||
ppidx = spi_nor_hwcaps_pp2cmd(BIT(cap));
|
||||
if (ppidx < 0)
|
||||
continue;
|
||||
|
||||
if (spi_nor_check_pp(nor, ¶ms->page_programs[ppidx]))
|
||||
*hwcaps &= ~BIT(cap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* spi_nor_adjust_hwcaps - Find optimal Read/Write protocol based on SPI
|
||||
* controller capabilities
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
* @params: pointer to the 'struct spi_nor_flash_parameter'
|
||||
* representing SPI NOR flash capabilities
|
||||
* @hwcaps: pointer to resulting capabilities after adjusting
|
||||
* according to controller and flash's capability
|
||||
*
|
||||
* Select caps based on what the SPI controller and SPI flash both support.
|
||||
*/
|
||||
static void
|
||||
spi_nor_adjust_hwcaps(struct spi_nor *nor,
|
||||
const struct spi_nor_flash_parameter *params,
|
||||
u32 *hwcaps)
|
||||
{
|
||||
struct spi_slave *spi = nor->spi;
|
||||
u32 ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
|
||||
SNOR_HWCAPS_READ_4_4_4 |
|
||||
SNOR_HWCAPS_READ_8_8_8 |
|
||||
SNOR_HWCAPS_PP_4_4_4 |
|
||||
SNOR_HWCAPS_PP_8_8_8);
|
||||
u32 spi_hwcaps = (SNOR_HWCAPS_READ | SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_PP);
|
||||
|
||||
/* Get the hardware capabilities the SPI controller supports. */
|
||||
if (spi->mode & SPI_RX_OCTAL) {
|
||||
spi_hwcaps |= SNOR_HWCAPS_READ_1_1_8;
|
||||
|
||||
if (spi->mode & SPI_TX_OCTAL)
|
||||
spi_hwcaps |= (SNOR_HWCAPS_READ_1_8_8 |
|
||||
SNOR_HWCAPS_PP_1_1_8 |
|
||||
SNOR_HWCAPS_PP_1_8_8);
|
||||
} else if (spi->mode & SPI_RX_QUAD) {
|
||||
spi_hwcaps |= SNOR_HWCAPS_READ_1_1_4;
|
||||
|
||||
if (spi->mode & SPI_TX_QUAD)
|
||||
spi_hwcaps |= (SNOR_HWCAPS_READ_1_4_4 |
|
||||
SNOR_HWCAPS_PP_1_1_4 |
|
||||
SNOR_HWCAPS_PP_1_4_4);
|
||||
} else if (spi->mode & SPI_RX_DUAL) {
|
||||
spi_hwcaps |= SNOR_HWCAPS_READ_1_1_2;
|
||||
|
||||
if (spi->mode & SPI_TX_DUAL)
|
||||
spi_hwcaps |= SNOR_HWCAPS_READ_1_2_2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep only the hardware capabilities supported by both the SPI
|
||||
* controller and the SPI flash memory.
|
||||
*/
|
||||
*hwcaps = spi_hwcaps & params->hwcaps.mask;
|
||||
if (*hwcaps & ignored_mask) {
|
||||
dev_dbg(nor->dev,
|
||||
"SPI n-n-n protocols are not supported yet.\n");
|
||||
*hwcaps &= ~ignored_mask;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_SPI_FLASH_SMART_HWCAPS */
|
||||
|
||||
static int spi_nor_select_read(struct spi_nor *nor,
|
||||
const struct spi_nor_flash_parameter *params,
|
||||
u32 shared_hwcaps)
|
||||
|
@ -2379,30 +2567,13 @@ static int spi_nor_select_erase(struct spi_nor *nor,
|
|||
|
||||
static int spi_nor_default_setup(struct spi_nor *nor,
|
||||
const struct flash_info *info,
|
||||
const struct spi_nor_flash_parameter *params,
|
||||
const struct spi_nor_hwcaps *hwcaps)
|
||||
const struct spi_nor_flash_parameter *params)
|
||||
{
|
||||
u32 ignored_mask, shared_mask;
|
||||
u32 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_READ_8_8_8 |
|
||||
SNOR_HWCAPS_PP_4_4_4 |
|
||||
SNOR_HWCAPS_PP_8_8_8);
|
||||
if (shared_mask & ignored_mask) {
|
||||
dev_dbg(nor->dev,
|
||||
"SPI n-n-n protocols are not supported yet.\n");
|
||||
shared_mask &= ~ignored_mask;
|
||||
}
|
||||
spi_nor_adjust_hwcaps(nor, params, &shared_mask);
|
||||
|
||||
/* Select the (Fast) Read command. */
|
||||
err = spi_nor_select_read(nor, params, shared_mask);
|
||||
|
@ -2440,13 +2611,12 @@ static int spi_nor_default_setup(struct spi_nor *nor,
|
|||
}
|
||||
|
||||
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)
|
||||
const struct spi_nor_flash_parameter *params)
|
||||
{
|
||||
if (!nor->setup)
|
||||
return 0;
|
||||
|
||||
return nor->setup(nor, info, params, hwcaps);
|
||||
return nor->setup(nor, info, params);
|
||||
}
|
||||
|
||||
static int spi_nor_init(struct spi_nor *nor)
|
||||
|
@ -2502,11 +2672,6 @@ int spi_nor_scan(struct spi_nor *nor)
|
|||
struct spi_nor_flash_parameter params;
|
||||
const struct flash_info *info = NULL;
|
||||
struct mtd_info *mtd = &nor->mtd;
|
||||
struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ |
|
||||
SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
struct spi_slave *spi = nor->spi;
|
||||
int ret;
|
||||
|
||||
|
@ -2521,27 +2686,6 @@ int spi_nor_scan(struct spi_nor *nor)
|
|||
|
||||
nor->setup = spi_nor_default_setup;
|
||||
|
||||
if (spi->mode & SPI_RX_OCTAL) {
|
||||
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8;
|
||||
|
||||
if (spi->mode & SPI_TX_OCTAL)
|
||||
hwcaps.mask |= (SNOR_HWCAPS_READ_1_8_8 |
|
||||
SNOR_HWCAPS_PP_1_1_8 |
|
||||
SNOR_HWCAPS_PP_1_8_8);
|
||||
} else if (spi->mode & SPI_RX_QUAD) {
|
||||
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
|
||||
|
||||
if (spi->mode & SPI_TX_QUAD)
|
||||
hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |
|
||||
SNOR_HWCAPS_PP_1_1_4 |
|
||||
SNOR_HWCAPS_PP_1_4_4);
|
||||
} else if (spi->mode & SPI_RX_DUAL) {
|
||||
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
|
||||
|
||||
if (spi->mode & SPI_TX_DUAL)
|
||||
hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;
|
||||
}
|
||||
|
||||
info = spi_nor_read_id(nor);
|
||||
if (IS_ERR_OR_NULL(info))
|
||||
return -ENOENT;
|
||||
|
@ -2616,7 +2760,7 @@ int spi_nor_scan(struct spi_nor *nor)
|
|||
* - 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);
|
||||
ret = spi_nor_setup(nor, info, ¶ms);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -105,3 +105,65 @@ int spi_mem_adjust_op_size(struct spi_slave *slave,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_check_buswidth_req(struct spi_slave *slave, u8 buswidth, bool tx)
|
||||
{
|
||||
u32 mode = slave->mode;
|
||||
|
||||
switch (buswidth) {
|
||||
case 1:
|
||||
return 0;
|
||||
|
||||
case 2:
|
||||
if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
|
||||
(!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
|
||||
return 0;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if ((tx && (mode & SPI_TX_QUAD)) ||
|
||||
(!tx && (mode & SPI_RX_QUAD)))
|
||||
return 0;
|
||||
|
||||
break;
|
||||
case 8:
|
||||
if ((tx && (mode & SPI_TX_OCTAL)) ||
|
||||
(!tx && (mode & SPI_RX_OCTAL)))
|
||||
return 0;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
bool spi_mem_supports_op(struct spi_slave *slave, const struct spi_mem_op *op)
|
||||
{
|
||||
if (spi_check_buswidth_req(slave, op->cmd.buswidth, true))
|
||||
return false;
|
||||
|
||||
if (op->addr.nbytes &&
|
||||
spi_check_buswidth_req(slave, op->addr.buswidth, true))
|
||||
return false;
|
||||
|
||||
if (op->dummy.nbytes &&
|
||||
spi_check_buswidth_req(slave, op->dummy.buswidth, true))
|
||||
return false;
|
||||
|
||||
if (op->data.nbytes &&
|
||||
spi_check_buswidth_req(slave, op->data.buswidth,
|
||||
op->data.dir == SPI_MEM_DATA_OUT))
|
||||
return false;
|
||||
|
||||
if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
|
||||
return false;
|
||||
|
||||
if (op->cmd.nbytes != 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -312,6 +312,20 @@ struct spi_nor_hwcaps {
|
|||
#define SNOR_HWCAPS_PP_1_8_8 BIT(21)
|
||||
#define SNOR_HWCAPS_PP_8_8_8 BIT(22)
|
||||
|
||||
#define SNOR_HWCAPS_X_X_X (SNOR_HWCAPS_READ_2_2_2 | \
|
||||
SNOR_HWCAPS_READ_4_4_4 | \
|
||||
SNOR_HWCAPS_READ_8_8_8 | \
|
||||
SNOR_HWCAPS_PP_4_4_4 | \
|
||||
SNOR_HWCAPS_PP_8_8_8)
|
||||
|
||||
#define SNOR_HWCAPS_DTR (SNOR_HWCAPS_READ_1_1_1_DTR | \
|
||||
SNOR_HWCAPS_READ_1_2_2_DTR | \
|
||||
SNOR_HWCAPS_READ_1_4_4_DTR | \
|
||||
SNOR_HWCAPS_READ_1_8_8_DTR)
|
||||
|
||||
#define SNOR_HWCAPS_ALL (SNOR_HWCAPS_READ_MASK | \
|
||||
SNOR_HWCAPS_PP_MASK)
|
||||
|
||||
struct spi_nor_read_command {
|
||||
u8 num_mode_clocks;
|
||||
u8 num_wait_states;
|
||||
|
@ -461,8 +475,7 @@ struct spi_nor {
|
|||
struct spi_nor_fixups *fixups;
|
||||
|
||||
int (*setup)(struct spi_nor *nor, const struct flash_info *info,
|
||||
const struct spi_nor_flash_parameter *params,
|
||||
const struct spi_nor_hwcaps *hwcaps);
|
||||
const struct spi_nor_flash_parameter *params);
|
||||
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
|
||||
|
|
Loading…
Reference in a new issue