- add Macronix Octal flash (JaimeLiao)
This commit is contained in:
Tom Rini 2022-07-18 15:37:38 -04:00
commit 905e779b9e
6 changed files with 219 additions and 18 deletions

View file

@ -294,6 +294,12 @@ static int do_spi_flash_read_write(int argc, char *const argv[])
return 1;
}
if (strncmp(argv[0], "read", 4) != 0 && flash->flash_is_unlocked &&
!flash->flash_is_unlocked(flash, offset, len)) {
printf("ERROR: flash area is locked\n");
return 1;
}
buf = map_physmem(addr, len, MAP_WRBACK);
if (!buf && addr) {
puts("Failed to map physical memory\n");
@ -350,6 +356,12 @@ static int do_spi_flash_erase(int argc, char *const argv[])
return 1;
}
if (flash->flash_is_unlocked &&
!flash->flash_is_unlocked(flash, offset, len)) {
printf("ERROR: flash area is locked\n");
return 1;
}
ret = spi_flash_erase(flash, offset, size);
printf("SF: %zu bytes @ %#x Erased: ", (size_t)size, (u32)offset);
if (ret)

View file

@ -172,6 +172,11 @@ static int spl_spi_load_image(struct spl_image_info *spl_image,
spl_image->size,
(void *)spl_image->load_addr);
}
if (IS_ENABLED(CONFIG_SPI_FLASH_SOFT_RESET)) {
err = spi_nor_remove(flash);
if (err)
return err;
}
}
return err;

View file

@ -97,6 +97,13 @@ config SPI_FLASH_SMART_HWCAPS
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_NOR_BOOT_SOFT_RESET_EXT_INVERT
bool "Command extension type is INVERT for Software Reset on boot"
default n
help
Because of SFDP information can not be get before boot.
So define command extension type is INVERT when Software Reset on boot only.
config SPI_FLASH_SOFT_RESET
bool "Software Reset support for SPI NOR flashes"
help

View file

@ -65,6 +65,10 @@ struct sfdp_parameter_header {
#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */
#define SFDP_SST_ID 0x01bf /* Manufacturer specific Table */
#define SFDP_PROFILE1_ID 0xff05 /* xSPI Profile 1.0 Table */
#define SFDP_SCCR_MAP_ID 0xff87 /*
* Status, Control and Configuration
* Register Map.
*/
#define SFDP_SIGNATURE 0x50444653U
#define SFDP_JESD216_MAJOR 1
@ -174,6 +178,9 @@ struct sfdp_header {
#define PROFILE1_DWORD5_DUMMY_100MHZ GENMASK(11, 7)
#define PROFILE1_DUMMY_DEFAULT 20
/* Status, Control and Configuration Register Map(SCCR) */
#define SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE BIT(31)
struct sfdp_bfpt {
u32 dwords[BFPT_DWORD_MAX];
};
@ -1308,13 +1315,13 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
}
/*
* Check if a region of the flash is (completely) locked. See stm_lock() for
* Check if a region of the flash is (completely) unlocked. See stm_lock() for
* more info.
*
* Returns 1 if entire region is locked, 0 if any portion is unlocked, and
* Returns 1 if entire region is unlocked, 0 if any portion is locked, and
* negative on errors.
*/
static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
static int stm_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len)
{
int status;
@ -1322,7 +1329,7 @@ static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
if (status < 0)
return status;
return stm_is_locked_sr(nor, ofs, len, status);
return stm_is_unlocked_sr(nor, ofs, len, status);
}
#endif /* CONFIG_SPI_FLASH_STMICRO */
@ -1555,16 +1562,16 @@ static int sst26_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
}
/*
* Returns EACCES (positive value) if region is locked, 0 if region is unlocked,
* and negative on errors.
* Returns EACCES (positive value) if region is (partially) locked, 0 if region
* is completely unlocked, and negative on errors.
*/
static int sst26_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
static int sst26_is_unlocked(struct spi_nor *nor, loff_t ofs, uint64_t len)
{
/*
* is_locked function is used for check before reading or erasing flash
* region, so offset and length might be not 64k allighned, so adjust
* them to be 64k allighned as sst26_lock_ctl works only with 64k
* allighned regions.
* is_unlocked function is used for check before reading or erasing
* flash region, so offset and length might be not 64k aligned, so
* adjust them to be 64k aligned as sst26_lock_ctl works only with 64k
* aligned regions.
*/
ofs -= ofs & (SZ_64K - 1);
len = len & (SZ_64K - 1) ? (len & ~(SZ_64K - 1)) + SZ_64K : len;
@ -2456,6 +2463,44 @@ out:
return ret;
}
/**
* spi_nor_parse_sccr() - Parse the Status, Control and Configuration Register
* Map.
* @nor: pointer to a 'struct spi_nor'
* @sccr_header: pointer to the 'struct sfdp_parameter_header' describing
* the SCCR Map table length and version.
*
* Return: 0 on success, -errno otherwise.
*/
static int spi_nor_parse_sccr(struct spi_nor *nor,
const struct sfdp_parameter_header *sccr_header)
{
u32 *table, addr;
size_t len;
int ret, i;
len = sccr_header->length * sizeof(*table);
table = kmalloc(len, GFP_KERNEL);
if (!table)
return -ENOMEM;
addr = SFDP_PARAM_HEADER_PTP(sccr_header);
ret = spi_nor_read_sfdp(nor, addr, len, table);
if (ret)
goto out;
/* Fix endianness of the table DWORDs. */
for (i = 0; i < sccr_header->length; i++)
table[i] = le32_to_cpu(table[i]);
if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE, table[22]))
nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
out:
kfree(table);
return ret;
}
/**
* spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
* @nor: pointer to a 'struct spi_nor'
@ -2562,6 +2607,10 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
err = spi_nor_parse_profile1(nor, param_header, params);
break;
case SFDP_SCCR_MAP_ID:
err = spi_nor_parse_sccr(nor, param_header);
break;
default:
break;
}
@ -3526,6 +3575,84 @@ static struct spi_nor_fixups mt35xu512aba_fixups = {
};
#endif /* CONFIG_SPI_FLASH_MT35XU */
#if CONFIG_IS_ENABLED(SPI_FLASH_MACRONIX)
/**
* spi_nor_macronix_octal_dtr_enable() - Enable octal DTR on Macronix flashes.
* @nor: pointer to a 'struct spi_nor'
*
* Set Macronix max dummy cycles 20 to allow the flash to run at fastest frequency.
*
* Return: 0 on success, -errno otherwise.
*/
static int spi_nor_macronix_octal_dtr_enable(struct spi_nor *nor)
{
struct spi_mem_op op;
int ret;
u8 buf;
ret = write_enable(nor);
if (ret)
return ret;
buf = SPINOR_REG_MXIC_DC_20;
op = (struct spi_mem_op)
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_CR2, 1),
SPI_MEM_OP_ADDR(4, SPINOR_REG_MXIC_CR2_DC, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, &buf, 1));
ret = spi_mem_exec_op(nor->spi, &op);
if (ret)
return ret;
ret = spi_nor_wait_till_ready(nor);
if (ret)
return ret;
nor->read_dummy = MXIC_MAX_DC;
ret = write_enable(nor);
if (ret)
return ret;
buf = SPINOR_REG_MXIC_OPI_DTR_EN;
op = (struct spi_mem_op)
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_CR2, 1),
SPI_MEM_OP_ADDR(4, SPINOR_REG_MXIC_CR2_MODE, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(1, &buf, 1));
ret = spi_mem_exec_op(nor->spi, &op);
if (ret) {
dev_err(nor->dev, "Failed to enable octal DTR mode\n");
return ret;
}
nor->reg_proto = SNOR_PROTO_8_8_8_DTR;
return 0;
}
static void macronix_octal_default_init(struct spi_nor *nor)
{
nor->octal_dtr_enable = spi_nor_macronix_octal_dtr_enable;
}
static void macronix_octal_post_sfdp_fixup(struct spi_nor *nor,
struct spi_nor_flash_parameter *params)
{
/*
* Adding SNOR_HWCAPS_PP_8_8_8_DTR in hwcaps.mask when
* SPI_NOR_OCTAL_DTR_READ flag exists.
*/
if (params->hwcaps.mask & SNOR_HWCAPS_READ_8_8_8_DTR)
params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR;
}
static struct spi_nor_fixups macronix_octal_fixups = {
.default_init = macronix_octal_default_init,
.post_sfdp = macronix_octal_post_sfdp_fixup,
};
#endif /* CONFIG_SPI_FLASH_MACRONIX */
/** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed
* @nor: pointer to a 'struct spi_nor'
*
@ -3542,6 +3669,9 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor)
nor->write_proto == SNOR_PROTO_8_8_8_DTR))
return 0;
if (!(nor->flags & SNOR_F_IO_MODE_EN_VOLATILE))
return 0;
ret = nor->octal_dtr_enable(nor);
if (ret)
return ret;
@ -3619,7 +3749,12 @@ static int spi_nor_soft_reset(struct spi_nor *nor)
enum spi_nor_cmd_ext ext;
ext = nor->cmd_ext_type;
nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
if (nor->cmd_ext_type == SPI_NOR_EXT_NONE) {
nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
#if CONFIG_IS_ENABLED(SPI_NOR_BOOT_SOFT_RESET_EXT_INVERT)
nor->cmd_ext_type = SPI_NOR_EXT_INVERT;
#endif /* SPI_NOR_BOOT_SOFT_RESET_EXT_INVERT */
}
op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 0),
SPI_MEM_OP_NO_DUMMY,
@ -3696,6 +3831,10 @@ void spi_nor_set_fixups(struct spi_nor *nor)
if (!strcmp(nor->info->name, "mt35xu512aba"))
nor->fixups = &mt35xu512aba_fixups;
#endif
#if CONFIG_IS_ENABLED(SPI_FLASH_MACRONIX)
nor->fixups = &macronix_octal_fixups;
#endif /* SPI_FLASH_MACRONIX */
}
int spi_nor_scan(struct spi_nor *nor)
@ -3785,7 +3924,7 @@ int spi_nor_scan(struct spi_nor *nor)
info->flags & SPI_NOR_HAS_LOCK) {
nor->flash_lock = stm_lock;
nor->flash_unlock = stm_unlock;
nor->flash_is_locked = stm_is_locked;
nor->flash_is_unlocked = stm_is_unlocked;
}
#endif
@ -3797,7 +3936,7 @@ int spi_nor_scan(struct spi_nor *nor)
if (info->flags & SPI_NOR_HAS_SST26LOCK) {
nor->flash_lock = sst26_lock;
nor->flash_unlock = sst26_unlock;
nor->flash_is_locked = sst26_is_locked;
nor->flash_is_unlocked = sst26_is_unlocked;
}
#endif

View file

@ -198,7 +198,24 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("mx66l2g45g", 0xc2201c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25l1633e", 0xc22415, 0, 64 * 1024, 32, SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | SECT_4K) },
{ INFO("mx25r6435f", 0xc22817, 0, 64 * 1024, 128, SECT_4K) },
{ INFO("mx66uw2g345g", 0xc2943c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx66uw2g345gx0", 0xc2943c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx66lm1g45g", 0xc2853b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25lm51245g", 0xc2853a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25lw51245g", 0xc2863a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25lm25645g", 0xc28539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx66uw2g345g", 0xc2843c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx66um1g45g", 0xc2803b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx66uw1g45g", 0xc2813b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25uw51245g", 0xc2813a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25uw51345g", 0xc2843a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25um25645g", 0xc28039, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25uw25645g", 0xc28139, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25um25345g", 0xc28339, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25uw25345g", 0xc28439, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25uw12845g", 0xc28138, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25uw12345g", 0xc28438, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25uw6445g", 0xc28137, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25uw6345g", 0xc28437, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
#endif
#ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */
@ -397,6 +414,16 @@ const struct flash_info spi_nor_ids[] = {
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
INFO("w25q512nwq", 0xef6020, 0, 64 * 1024, 1024,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
INFO("w25q512nwm", 0xef8020, 0, 64 * 1024, 1024,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
INFO("w25q01jv", 0xef4021, 0, 64 * 1024, 2048,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |

View file

@ -119,6 +119,16 @@
/* Used for Macronix and Winbond flashes. */
#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
#define SPINOR_OP_RD_CR2 0x71 /* Read configuration register 2 */
#define SPINOR_OP_WR_CR2 0x72 /* Write configuration register 2 */
#define SPINOR_OP_MXIC_DTR_RD 0xee /* Fast Read opcode in DTR mode */
#define SPINOR_REG_MXIC_CR2_MODE 0x00000000 /* For setting octal DTR mode */
#define SPINOR_REG_MXIC_OPI_DTR_EN 0x2 /* Enable Octal DTR */
#define SPINOR_REG_MXIC_CR2_DC 0x00000300 /* For setting dummy cycles */
#define SPINOR_REG_MXIC_DC_20 0x0 /* Setting dummy cycles to 20 */
#define MXIC_MAX_DC 20 /* Maximum value of dummy cycles */
/* Used for Spansion flashes only. */
#define SPINOR_OP_BRWR 0x17 /* Bank register write */
@ -280,6 +290,7 @@ enum spi_nor_option_flags {
SNOR_F_USE_CLSR = BIT(5),
SNOR_F_BROKEN_RESET = BIT(6),
SNOR_F_SOFT_RESET = BIT(7),
SNOR_F_IO_MODE_EN_VOLATILE = BIT(8),
};
struct spi_nor;
@ -506,8 +517,8 @@ struct spi_flash {
* spi-nor will send the erase opcode via write_reg()
* @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR
* @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR
* @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is
* completely locked
* @flash_is_unlocked: [FLASH-SPECIFIC] check if a region of the SPI NOR is
* completely unlocked
* @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode
* @octal_dtr_enable: [FLASH-SPECIFIC] enables SPI NOR octal DTR mode.
* @ready: [FLASH-SPECIFIC] check if the flash is ready
@ -556,7 +567,7 @@ struct spi_nor {
int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*flash_is_unlocked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*quad_enable)(struct spi_nor *nor);
int (*octal_dtr_enable)(struct spi_nor *nor);
int (*ready)(struct spi_nor *nor);