mtd: spi-nor: Rework hwcaps selection for the spi-mem case
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() when nor->spimem != NULL. Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com> Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
This commit is contained in:
parent
b35b9a1036
commit
c76f508979
|
@ -2951,6 +2951,129 @@ static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_spimem_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_spimem_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->spimem, 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->spimem, op))
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_spimem_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_spimem_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_spimem_check_op(nor, &op);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_spimem_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_spimem_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_spimem_check_op(nor, &op);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_spimem_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
|
||||
*/
|
||||
static void
|
||||
spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor,
|
||||
const struct spi_nor_flash_parameter *params,
|
||||
u32 *hwcaps)
|
||||
{
|
||||
unsigned int cap;
|
||||
|
||||
/* 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_spimem_check_readop(nor, ¶ms->reads[rdidx]))
|
||||
*hwcaps &= ~BIT(cap);
|
||||
|
||||
ppidx = spi_nor_hwcaps_pp2cmd(BIT(cap));
|
||||
if (ppidx < 0)
|
||||
continue;
|
||||
|
||||
if (spi_nor_spimem_check_pp(nor,
|
||||
¶ms->page_programs[ppidx]))
|
||||
*hwcaps &= ~BIT(cap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
|
@ -4360,16 +4483,25 @@ static int spi_nor_setup(struct spi_nor *nor,
|
|||
*/
|
||||
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;
|
||||
if (nor->spimem) {
|
||||
/*
|
||||
* When called from spi_nor_probe(), all caps are set and we
|
||||
* need to discard some of them based on what the SPI
|
||||
* controller actually supports (using spi_mem_supports_op()).
|
||||
*/
|
||||
spi_nor_spimem_adjust_hwcaps(nor, params, &shared_mask);
|
||||
} else {
|
||||
/*
|
||||
* SPI n-n-n protocols are not supported when the SPI
|
||||
* controller directly implements the spi_nor interface.
|
||||
* Yet another reason to switch to spi-mem.
|
||||
*/
|
||||
ignored_mask = SNOR_HWCAPS_X_X_X;
|
||||
if (shared_mask & ignored_mask) {
|
||||
dev_dbg(nor->dev,
|
||||
"SPI n-n-n protocols are not supported.\n");
|
||||
shared_mask &= ~ignored_mask;
|
||||
}
|
||||
}
|
||||
|
||||
/* Select the (Fast) Read command. */
|
||||
|
@ -4712,11 +4844,11 @@ static int spi_nor_probe(struct spi_mem *spimem)
|
|||
struct spi_device *spi = spimem->spi;
|
||||
struct flash_platform_data *data = dev_get_platdata(&spi->dev);
|
||||
struct spi_nor *nor;
|
||||
struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ |
|
||||
SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
/*
|
||||
* Enable all caps by default. The core will mask them after
|
||||
* checking what's really supported using spi_mem_supports_op().
|
||||
*/
|
||||
const struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_ALL };
|
||||
char *flash_name;
|
||||
int ret;
|
||||
|
||||
|
@ -4730,27 +4862,6 @@ static int spi_nor_probe(struct spi_mem *spimem)
|
|||
|
||||
spi_mem_set_drvdata(spimem, nor);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (data && data->name)
|
||||
nor->mtd.name = data->name;
|
||||
|
||||
|
|
|
@ -524,6 +524,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)
|
||||
|
||||
/**
|
||||
* spi_nor_scan() - scan the SPI NOR
|
||||
* @nor: the spi_nor structure
|
||||
|
|
Loading…
Reference in New Issue