Skip to content

Commit

Permalink
mtd: spi-nor: core: Couple the number of address bytes with the addre…
Browse files Browse the repository at this point in the history
…ss mode

Some of Infineon chips support volatile version of configuration registers
and it is recommended to update volatile registers in the field application
due to a risk of the non-volatile registers corruption by power interrupt.
Such a volatile configuration register is used to enable the Quad mode.
The register write sequence requires the number of bytes of address in
order to be programmed. As it was before, the nor->addr_width was set to 4
before calling the volatile Quad enable method. This was incorrect as the
address mode was still at default (3-byte address), which resulted in
incorrect register configuration.
Move the setting of the number of bytes of adress after the the Quad enable
method to allow reads or writes to registers that reguire the number of
address bytes to work with the default address mode. Now the number of
address bytes and the adress mode are tightly coupled, which is a natural
change.
Other (standard) Quad Enable methods are not affected, as they don't
require the number of address bytes, so no functionality changes expected.

Reported-by: Takahiro Kuwano <Takahiro.Kuwano@infineon.com>
Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
  • Loading branch information
ambarus committed Apr 21, 2022
1 parent 61d73de commit 05f20ab
Showing 1 changed file with 74 additions and 66 deletions.
140 changes: 74 additions & 66 deletions drivers/mtd/spi-nor/core.c
Expand Up @@ -2270,52 +2270,6 @@ static int spi_nor_default_setup(struct spi_nor *nor,
return 0;
}

static int spi_nor_set_addr_width(struct spi_nor *nor)
{
if (nor->flags & SNOR_F_HAS_4BAIT)
nor->addr_width = 4;

if (nor->addr_width) {
/* already configured from SFDP */
} else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) {
/*
* In 8D-8D-8D mode, one byte takes half a cycle to transfer. So
* in this protocol an odd address width cannot be used because
* then the address phase would only span a cycle and a half.
* Half a cycle would be left over. We would then have to start
* the dummy phase in the middle of a cycle and so too the data
* phase, and we will end the transaction with half a cycle left
* over.
*
* Force all 8D-8D-8D flashes to use an address width of 4 to
* avoid this situation.
*/
nor->addr_width = 4;
} else if (nor->info->addr_width) {
nor->addr_width = nor->info->addr_width;
} else {
nor->addr_width = 3;
}

if (nor->addr_width == 3 && nor->params->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
}

if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
dev_dbg(nor->dev, "address width is too large: %u\n",
nor->addr_width);
return -EINVAL;
}

/* Set 4byte opcodes when possible. */
if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES &&
!(nor->flags & SNOR_F_HAS_4BAIT))
spi_nor_set_4byte_opcodes(nor);

return 0;
}

static int spi_nor_setup(struct spi_nor *nor,
const struct spi_nor_hwcaps *hwcaps)
{
Expand All @@ -2325,10 +2279,7 @@ static int spi_nor_setup(struct spi_nor *nor,
ret = nor->params->setup(nor, hwcaps);
else
ret = spi_nor_default_setup(nor, hwcaps);
if (ret)
return ret;

return spi_nor_set_addr_width(nor);
return ret;
}

/**
Expand Down Expand Up @@ -2711,6 +2662,78 @@ static int spi_nor_quad_enable(struct spi_nor *nor)
return nor->params->quad_enable(nor);
}

static int spi_nor_set_addr_width(struct spi_nor *nor)
{
if (nor->flags & SNOR_F_HAS_4BAIT)
nor->addr_width = 4;

if (nor->addr_width) {
/* already configured from SFDP */
} else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) {
/*
* In 8D-8D-8D mode, one byte takes half a cycle to transfer. So
* in this protocol an odd address width cannot be used because
* then the address phase would only span a cycle and a half.
* Half a cycle would be left over. We would then have to start
* the dummy phase in the middle of a cycle and so too the data
* phase, and we will end the transaction with half a cycle left
* over.
*
* Force all 8D-8D-8D flashes to use an address width of 4 to
* avoid this situation.
*/
nor->addr_width = 4;
} else if (nor->info->addr_width) {
nor->addr_width = nor->info->addr_width;
} else {
nor->addr_width = 3;
}

if (nor->addr_width == 3 && nor->params->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
}

if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
dev_dbg(nor->dev, "address width is too large: %u\n",
nor->addr_width);
return -EINVAL;
}

/* Set 4byte opcodes when possible. */
if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES &&
!(nor->flags & SNOR_F_HAS_4BAIT))
spi_nor_set_4byte_opcodes(nor);

return 0;
}

static int spi_nor_set_addr_mode(struct spi_nor *nor)
{
int ret;

ret = spi_nor_set_addr_width(nor);
if (ret)
return ret;

if (nor->addr_width == 4 &&
nor->read_proto != SNOR_PROTO_8_8_8_DTR &&
!(nor->flags & SNOR_F_4B_OPCODES)) {
/*
* If the RESET# pin isn't hooked up properly, or the system
* otherwise doesn't perform a reset command in the boot
* sequence, it's impossible to 100% protect against unexpected
* reboots (e.g., crashes). Warn the user (or hopefully, system
* designer) that this is bad.
*/
WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
"enabling reset hack; may not recover from unexpected reboots\n");
nor->params->set_4byte_addr_mode(nor, true);
}

return 0;
}

static int spi_nor_init(struct spi_nor *nor)
{
int err;
Expand Down Expand Up @@ -2742,22 +2765,7 @@ static int spi_nor_init(struct spi_nor *nor)
nor->flags & SNOR_F_SWP_IS_VOLATILE))
spi_nor_try_unlock_all(nor);

if (nor->addr_width == 4 &&
nor->read_proto != SNOR_PROTO_8_8_8_DTR &&
!(nor->flags & SNOR_F_4B_OPCODES)) {
/*
* If the RESET# pin isn't hooked up properly, or the system
* otherwise doesn't perform a reset command in the boot
* sequence, it's impossible to 100% protect against unexpected
* reboots (e.g., crashes). Warn the user (or hopefully, system
* designer) that this is bad.
*/
WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
"enabling reset hack; may not recover from unexpected reboots\n");
nor->params->set_4byte_addr_mode(nor, true);
}

return 0;
return spi_nor_set_addr_mode(nor);
}

/**
Expand Down

0 comments on commit 05f20ab

Please sign in to comment.