mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-21 06:31:31 +00:00
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
1 changed files with 141 additions and 0 deletions
|
@ -290,6 +290,126 @@ static struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
|
||||||
return mtd->priv;
|
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)
|
static int spi_nor_sr_ready(struct spi_nor *nor)
|
||||||
{
|
{
|
||||||
int sr = read_sr(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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1772,6 +1907,12 @@ int spi_nor_scan(struct spi_nor *nor)
|
||||||
|
|
||||||
if (info->addr_width) {
|
if (info->addr_width) {
|
||||||
nor->addr_width = 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 {
|
} else {
|
||||||
nor->addr_width = 3;
|
nor->addr_width = 3;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue