mtd: spi: spi-nor-core: Add 4 Byte addressing support
Sync changes from Linux SPI NOR framework to add 4 byte addressing support. This is required in order to support flashes like MT35x that no longer support legacy Bank Address Register(BAR) way of accessing >16MB region. Signed-off-by: Vignesh R <vigneshr@ti.com> Tested-by: Simon Goldschmidt <simon.k.r.goldschmidt@gmail.com> Tested-by: Stefan Roese <sr@denx.de> Tested-by: Horatiu Vultur <horatiu.vultur@microchip.com> Reviewed-by: Jagan Teki <jagan@openedev.com> Tested-by: Jagan Teki <jagan@amarulasolutions.com> #zynq-microzed
This commit is contained in:
parent
492e65b29b
commit
61059bc55a
@ -290,6 +290,126 @@ static struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
|
||||
return mtd->priv;
|
||||
}
|
||||
|
||||
static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
if (table[i][0] == opcode)
|
||||
return table[i][1];
|
||||
|
||||
/* No conversion found, keep input op code. */
|
||||
return opcode;
|
||||
}
|
||||
|
||||
static u8 spi_nor_convert_3to4_read(u8 opcode)
|
||||
{
|
||||
static const u8 spi_nor_3to4_read[][2] = {
|
||||
{ SPINOR_OP_READ, SPINOR_OP_READ_4B },
|
||||
{ SPINOR_OP_READ_FAST, SPINOR_OP_READ_FAST_4B },
|
||||
{ SPINOR_OP_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B },
|
||||
{ SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B },
|
||||
{ SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B },
|
||||
{ SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B },
|
||||
|
||||
{ SPINOR_OP_READ_1_1_1_DTR, SPINOR_OP_READ_1_1_1_DTR_4B },
|
||||
{ SPINOR_OP_READ_1_2_2_DTR, SPINOR_OP_READ_1_2_2_DTR_4B },
|
||||
{ SPINOR_OP_READ_1_4_4_DTR, SPINOR_OP_READ_1_4_4_DTR_4B },
|
||||
};
|
||||
|
||||
return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
|
||||
ARRAY_SIZE(spi_nor_3to4_read));
|
||||
}
|
||||
|
||||
static u8 spi_nor_convert_3to4_program(u8 opcode)
|
||||
{
|
||||
static const u8 spi_nor_3to4_program[][2] = {
|
||||
{ SPINOR_OP_PP, SPINOR_OP_PP_4B },
|
||||
{ SPINOR_OP_PP_1_1_4, SPINOR_OP_PP_1_1_4_4B },
|
||||
{ SPINOR_OP_PP_1_4_4, SPINOR_OP_PP_1_4_4_4B },
|
||||
};
|
||||
|
||||
return spi_nor_convert_opcode(opcode, spi_nor_3to4_program,
|
||||
ARRAY_SIZE(spi_nor_3to4_program));
|
||||
}
|
||||
|
||||
static u8 spi_nor_convert_3to4_erase(u8 opcode)
|
||||
{
|
||||
static const u8 spi_nor_3to4_erase[][2] = {
|
||||
{ SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B },
|
||||
{ SPINOR_OP_BE_32K, SPINOR_OP_BE_32K_4B },
|
||||
{ SPINOR_OP_SE, SPINOR_OP_SE_4B },
|
||||
};
|
||||
|
||||
return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase,
|
||||
ARRAY_SIZE(spi_nor_3to4_erase));
|
||||
}
|
||||
|
||||
static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
|
||||
const struct flash_info *info)
|
||||
{
|
||||
/* Do some manufacturer fixups first */
|
||||
switch (JEDEC_MFR(info)) {
|
||||
case SNOR_MFR_SPANSION:
|
||||
/* No small sector erase for 4-byte command set */
|
||||
nor->erase_opcode = SPINOR_OP_SE;
|
||||
nor->mtd.erasesize = info->sector_size;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
|
||||
nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
|
||||
nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
|
||||
}
|
||||
|
||||
/* Enable/disable 4-byte addressing mode. */
|
||||
static int set_4byte(struct spi_nor *nor, const struct flash_info *info,
|
||||
int enable)
|
||||
{
|
||||
int status;
|
||||
bool need_wren = false;
|
||||
u8 cmd;
|
||||
|
||||
switch (JEDEC_MFR(info)) {
|
||||
case SNOR_MFR_ST:
|
||||
case SNOR_MFR_MICRON:
|
||||
/* Some Micron need WREN command; all will accept it */
|
||||
need_wren = true;
|
||||
case SNOR_MFR_MACRONIX:
|
||||
case SNOR_MFR_WINBOND:
|
||||
if (need_wren)
|
||||
write_enable(nor);
|
||||
|
||||
cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
|
||||
status = nor->write_reg(nor, cmd, NULL, 0);
|
||||
if (need_wren)
|
||||
write_disable(nor);
|
||||
|
||||
if (!status && !enable &&
|
||||
JEDEC_MFR(info) == SNOR_MFR_WINBOND) {
|
||||
/*
|
||||
* On Winbond W25Q256FV, leaving 4byte mode causes
|
||||
* the Extended Address Register to be set to 1, so all
|
||||
* 3-byte-address reads come from the second 16M.
|
||||
* We must clear the register to enable normal behavior.
|
||||
*/
|
||||
write_enable(nor);
|
||||
nor->cmd_buf[0] = 0;
|
||||
nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1);
|
||||
write_disable(nor);
|
||||
}
|
||||
|
||||
return status;
|
||||
default:
|
||||
/* Spansion style */
|
||||
nor->cmd_buf[0] = enable << 7;
|
||||
return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int spi_nor_sr_ready(struct spi_nor *nor)
|
||||
{
|
||||
int sr = read_sr(nor);
|
||||
@ -1663,6 +1783,21 @@ static int spi_nor_init(struct spi_nor *nor)
|
||||
}
|
||||
}
|
||||
|
||||
if (nor->addr_width == 4 &&
|
||||
(JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) &&
|
||||
!(nor->info->flags & SPI_NOR_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.
|
||||
*/
|
||||
if (nor->flags & SNOR_F_BROKEN_RESET)
|
||||
printf("enabling reset hack; may not recover from unexpected reboots\n");
|
||||
set_4byte(nor, nor->info, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1772,6 +1907,12 @@ int spi_nor_scan(struct spi_nor *nor)
|
||||
|
||||
if (info->addr_width) {
|
||||
nor->addr_width = info->addr_width;
|
||||
} else if (mtd->size > 0x1000000) {
|
||||
/* enable 4-byte addressing if the device exceeds 16MiB */
|
||||
nor->addr_width = 4;
|
||||
if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
|
||||
info->flags & SPI_NOR_4B_OPCODES)
|
||||
spi_nor_set_4byte_opcodes(nor, info);
|
||||
} else {
|
||||
nor->addr_width = 3;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user