This is an automated email from Gerrit. "Andreas Bolsch <hyphen0br...@gmail.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7359
-- gerrit commit c8fb583686358ce455f89f7aec87f11c25740b0e Author: Andreas Bolsch <hyphen0br...@gmail.com> Date: Thu Nov 17 22:50:43 2022 +0100 Extend SFDP parser in particular regarding octal devices Change-Id: I270835b782f9f94c9b1b9fe39ba0ddcf88483ed4 Signed-off-by: Andreas Bolsch <hyphen0br...@gmail.com> diff --git a/src/flash/nor/sfdp.c b/src/flash/nor/sfdp.c index 5bfb541946..08533aef15 100644 --- a/src/flash/nor/sfdp.c +++ b/src/flash/nor/sfdp.c @@ -8,55 +8,172 @@ #include "config.h" #endif +#include "helper/bits.h" #include "imp.h" #include "spi.h" #include "sfdp.h" -#define SFDP_MAGIC 0x50444653 -#define SFDP_ACCESS_PROT 0xFF -#define SFDP_BASIC_FLASH 0xFF00 -#define SFDP_4BYTE_ADDR 0xFF84 +#define SFDP_MAGIC 0x50444653 + +#define SFDP_ACCESS_OCT_3_8 0xFC +#define SFDP_ACCESS_OCT_4_20 0xFD +#define SFDP_ACCESS_OCT_4_8 0xFE +#define SFDP_ACCESS_QUAD_3_8 0xFF + +#define SFDP_BASIC_FLASH 0xFF00 +#define SFDP_XSPI_1_0 0xFF05 +#define SFDP_XSPI_2_0 0xFF06 +#define SFDP_STAT_CTRL_CONF_2_0 0xFF09 +#define SFDP_CMD_OCTAL_DTR 0xFF0A +#define SFDP_X4_QUAD_IO 0xFF0C +#define SFDP_SECTOR_MAP 0xFF81 +#define SFDP_4BYTE_ADDR 0xFF84 +#define SFDP_STAT_CTRL_CONF 0xFF87 +#define SFDP_CMD_QUAD_DTR 0xFF8D static const char *sfdp_name = "sfdp"; struct sfdp_hdr { - uint32_t signature; - uint32_t revision; + uint32_t signature; + uint32_t revision; }; struct sfdp_phdr { - uint32_t revision; - uint32_t ptr; + uint32_t revision; + uint32_t ptr; }; struct sfdp_basic_flash_param { - uint32_t fast_addr; /* 01: fast read and 3/4 address bytes */ - uint32_t density; /* 02: memory density */ - uint32_t fast_1x4; /* 03: 1-1-4 and 1-4-4 fast read */ - uint32_t fast_1x2; /* 04: 1-2-2 and 1-1-2 fast read */ - uint32_t fast_444; /* 05: 4-4-4 and 2-2-2 fast read */ - uint32_t read_222; /* 06: 2-2-2 fast read instr and dummy */ - uint32_t read_444; /* 07: 4-4-4 fast read instr and dummy */ - uint32_t erase_t12; /* 08: erase types 1, 2 */ - uint32_t erase_t34; /* 09: erase types 3, 4 */ - uint32_t erase_time; /* 10: erase times for types 1 - 4 */ - uint32_t chip_byte; /* 11: chip erase time, byte prog time, page prog */ - uint32_t susp_time; /* 12: suspend and resume times */ - uint32_t susp_instr; /* 13: suspend and resume instr */ - uint32_t pwrd_instr; /* 14: powerdown instr */ - uint32_t quad_req; /* 15: quad enable requirements */ - uint32_t addr_reset; /* 16: 3-/4-byte addressing and reset */ - uint32_t read_1x8; /* 17: 1-1-8 and 1-8-8 fast read instr and dummy */ - uint32_t dtr_drive; /* 18: dtr modes and drive strength */ - uint32_t octal_req; /* 19: octal enable requirements */ - uint32_t speed_888; /* 20: speed in 8-8-8 modes */ + uint32_t fast_addr; /* 01: fast read and 3/4 address bytes */ + uint32_t density; /* 02: memory density */ + uint32_t fast_1x4; /* 03: 1-1-4 and 1-4-4 fast read */ + uint32_t fast_1x2; /* 04: 1-2-2 and 1-1-2 fast read */ + uint32_t fast_444; /* 05: 4-4-4 and 2-2-2 fast read */ + uint32_t read_222; /* 06: 2-2-2 fast read instr and dummy */ + uint32_t read_444; /* 07: 4-4-4 fast read instr and dummy */ + uint32_t erase_t12; /* 08: erase types 1, 2 */ + uint32_t erase_t34; /* 09: erase types 3, 4 */ + uint32_t erase_time; /* 10: erase times for types 1 - 4 */ + uint32_t chip_byte; /* 11: chip erase time, byte prog time, page prog */ + uint32_t susp_time; /* 12: suspend and resume times */ + uint32_t susp_instr; /* 13: suspend and resume instr */ + uint32_t pwrd_instr; /* 14: powerdown instr */ + uint32_t quad_req; /* 15: quad enable requirements */ + uint32_t addr_reset; /* 16: 3-/4-byte addressing and reset */ + uint32_t read_1x8; /* 17: 1-1-8 and 1-8-8 fast read instr and dummy */ + uint32_t dtr_drive; /* 18: dtr modes and drive strength */ + uint32_t octal_req; /* 19: octal enable requirements */ + uint32_t speed_888; /* 20: speed in 8-8-8 modes */ +}; + +struct sfdp_xspi_1_0_param { + uint32_t status_cmds; /* 01: commands for status reg. in 8-8-8 mode */ + uint32_t reg_cmds; /* 02: commands for (non-) volatile registers in 8-8-8 mode */ + uint32_t mem_cmds; /* 03: commands for memory accesses in 8-8-8 mode */ + uint32_t dummy_200; /* 04: dummy cycles for 200 MHz */ + uint32_t dummy_166_100; /* 05: dummy cycles for 100 to 166 MHz */ +}; + +struct sfdp_xspi_2_0_param { + uint32_t opi_cmds; /* 01: commands in 8-8-8 mode */ + uint32_t dummy_200; /* 02: dummy cycles for 200 MHz */ + uint32_t dummy_166_100; /* 03: dummy cycles for 100 to 166 MHz */ +}; + +struct sfdp_sector_map { + uint32_t conf_descr_1; /* 01: conf descriptor 1 */ + uint32_t conf_descr_2; /* 02: conf descriptor 1 */ }; struct sfdp_4byte_addr_param { - uint32_t flags; /* 01: various flags */ - uint32_t erase_t1234; /* 02: erase commands */ + uint32_t flags; /* 01: various flags */ + uint32_t erase_t1234; /* 02: erase commands */ +}; + +struct sfdp_stat_ctrl_conf_param { + uint32_t addr_offs_vol; /* 01: address offset for volatile register */ + uint32_t addr_offs_nvol; /* 02: address offset for non-volatile register */ + uint32_t stat_ctrl_vol; /* 03: volatile status/control registers */ + uint32_t stat_ctrl_nvol; /* 04: non-volatile status/control registers */ + uint32_t wip_bit; /* 05: WIP bit */ + uint32_t wel_bit; /* 06: WIP bit */ + uint32_t pe_bit; /* 07: PE bit */ + uint32_t ee_bit; /* 08: EE bit */ + uint32_t dummy_vol; /* 09: volatile dummy cycles */ + uint32_t dummy_nvol; /* 10: non-volatile dummy cycles */ + uint32_t dummy_bit_30_22; /* 11: bit patterns for 30 to 22 dummy cycles */ + uint32_t dummy_bit_20_12; /* 12: bit patterns for 20 to 12 dummy cycles */ + uint32_t dummy_bit_10_02; /* 13: bit patterns for 10 to 02 dummy cycles */ + uint32_t qpi_vol; /* 14: volatile QPI mode enable */ + uint32_t qpi_nvol; /* 15: non-volatile QPI mode enable */ + uint32_t opi_vol; /* 16: volatile OPI mode enable */ + uint32_t opi_nvol; /* 17: non-volatile OPI mode enable */ + uint32_t str_dtr_vol; /* 18: volatile STR/DTR select */ + uint32_t str_dtr_nvol; /* 19: non-volatile STR/DTR mode select */ + uint32_t str_octal_vol; /* 20: volatile STR octal mode enable */ + uint32_t str_octal_nvol; /* 21: non-volatile STR octal mode enable */ + uint32_t dtr_octal_vol; /* 22: volatile DTR octal mode enable */ + uint32_t dtr_octal_nvol; /* 23: non-volatile DTR octal mode enable */ + uint32_t dpd_status; /* 24: deep power-down status */ + uint32_t udpd_status; /* 25: ultra-deep power-down status */ + uint32_t drv_str_vol; /* 26: volatile output driver stregth */ + uint32_t drv_str_nvol; /* 27: non-volatile output driver stregth */ + uint32_t drv_str_bit; /* 28: bit patterns for output drive strength */ +}; + +struct sfdp_stat_ctrl_conf_2_0_param { + uint32_t addr_offs_vol; /* 01: address offset for volatile register */ + uint32_t addr_offs_nvol; /* 02: address offset for non-volatile register */ + uint32_t stat_ctrl; /* 03: generic status/control registers */ + uint32_t pgm_err; /* 04: program error */ + uint32_t erase_err; /* 05: erase error */ + uint32_t dummy_vol; /* 06: volatile dummy cycles */ + uint32_t dummy_nvol; /* 07: non-volatile dummy cycles */ + uint32_t dummy_bit_30_22; /* 08: bit patterns for 30 to 22 dummy cycles */ + uint32_t dummy_bit_20_12; /* 09: bit patterns for 20 to 12 dummy cycles */ + uint32_t dummy_bit_10_02; /* 10: bit patterns for 10 to 02 dummy cycles */ + uint32_t drv_str_vol; /* 11: volatile output driver stregth */ + uint32_t drv_str_nvol; /* 12: non-volatile output driver stregth */ + uint32_t drv_str_bit; /* 13: bit patterns for output drive strength */ }; +static void wip_wle_access(uint32_t offset, int addr_bytes, uint32_t word, const char *name) +{ + if (word & BIT(31)) { + if (word & BIT(28)) { + uint32_t addr = (((word >> 16) & 0xFFU) << ((word >> 27) & 0x1)) + offset; + LOG_INFO("SFDP %s: %s, Write (%s) 0x%02" PRIx8 ", Read 0x%02" PRIx8 ", addr 0x%0*" PRIx32 + ", bit %" PRIx8, name, (word & BIT(30)) ? "neg" : "pos", (word & BIT(29)) ? "direct" : "bit", + word & 0xFFU, (word >> 8) & 0xFFU, addr_bytes << 1, addr, (word >> 24) & 0x7U); + } else { + LOG_INFO("SFDP %s: %s, Write (%s) 0x%02" PRIx8 ", Read 0x%02" PRIx8 ", bit %" PRIx8 ", %" PRIx8 + " dummy cycles", name, (word & BIT(30)) ? "neg" : "pos", (word & BIT(29)) ? "direct" : "bit", + word & 0xFFU, (word >> 8) & 0xFFU, (word >> 24) & 0x7U, (word >> 16) & 0xFU); + } + } else { + LOG_DEBUG("SFDP %s: not supported", name); + } +} + +static void qpi_opi_access(uint32_t offset, int addr_bytes, uint32_t word, const char *name) +{ + if (word & BIT(31)) { + if (word & BIT(28)) { + uint32_t addr = (((word >> 16) & 0xFFU) << ((word >> 27) & 0x1)) + offset; + LOG_INFO("SFDP %s: %s, Write %s0x%02" PRIx8 ", Read 0x%02" PRIx8 ", addr 0x%0*" PRIx32 + ", bit %" PRIx8, name, (word & BIT(30)) ? "neg" : "pos", (word & BIT(29)) ? "(OTP) " : "", + word & 0xFFU, (word >> 8) & 0xFFU, addr_bytes << 1, addr, + (word >> 24) & 0x7); + } else { + LOG_INFO("SFDP %s: %s, Write %s0x%02" PRIx8 ", Read 0x%02" PRIx8 ", bit %" PRIx8 ", %" PRIx8 + " dummy cycles", name, (word & BIT(30)) ? "neg" : "pos", (word & BIT(29)) ? "(OTP) " : "", + word & 0xFFU, (word >> 8) & 0xFFU, (word >> 24) & 0x7, (word >> 16) & 0xFU); + } + } else { + LOG_DEBUG("SFDP %s: not supported", name); + } +} + /* Try to get parameters from flash via SFDP */ int spi_sfdp(struct flash_bank *bank, struct flash_device *dev, read_sfdp_block_t read_sfdp_block) @@ -66,6 +183,7 @@ int spi_sfdp(struct flash_bank *bank, struct flash_device *dev, uint32_t *ptable = NULL; unsigned int j, k, nph; int retval, erase_type = 0; + uint8_t opi_read_fast_cmd = 0; memset(dev, 0, sizeof(struct flash_device)); @@ -76,11 +194,22 @@ int spi_sfdp(struct flash_bank *bank, struct flash_device *dev, return retval; LOG_DEBUG("header 0x%08" PRIx32 " 0x%08" PRIx32, header.signature, header.revision); if (header.signature != SFDP_MAGIC) { - LOG_INFO("no SDFP found"); + LOG_INFO("Read SFDP: no header found"); return ERROR_FLASH_BANK_NOT_PROBED; } - if (((header.revision >> 24) & 0xFF) != SFDP_ACCESS_PROT) { - LOG_ERROR("access protocol 0x%02x not implemented", + + uint8_t sfdp_access = (header.revision >> 24) & 0xFFU; + + if (sfdp_access == SFDP_ACCESS_QUAD_3_8) { + LOG_INFO("SFDP access protocol S/D/Q, 3-byte, 8 dummy"); + } else if (sfdp_access == SFDP_ACCESS_OCT_3_8) { + LOG_INFO("SFDP access protocol Octal, 3-byte, 8 dummy"); + } else if (sfdp_access == SFDP_ACCESS_OCT_4_20) { + LOG_INFO("SFDP access protocol Octal, 4-byte, 20 dummy"); + } else if (sfdp_access == SFDP_ACCESS_OCT_4_8) { + LOG_INFO("SFDP access protocol Octal, 4-byte, 8 dummy"); + } else { + LOG_ERROR("SFDP access protocol 0x%02" PRIx8 " unknown", (header.revision >> 24) & 0xFFU); return ERROR_FLASH_BANK_NOT_PROBED; } @@ -100,9 +229,9 @@ int spi_sfdp(struct flash_bank *bank, struct flash_device *dev, goto err; for (k = 0; k < nph; k++) { - uint8_t words = (pheaders[k].revision >> 24) & 0xFF; - uint16_t id = (((pheaders[k].ptr) >> 16) & 0xFF00) | (pheaders[k].revision & 0xFF); - uint32_t ptr = pheaders[k].ptr & 0xFFFFFF; + uint8_t words = (pheaders[k].revision >> 24) & 0xFFU; + uint16_t id = (((pheaders[k].ptr) >> 16) & 0xFF00U) | (pheaders[k].revision & 0xFFU); + uint32_t ptr = pheaders[k].ptr & 0xFFFFFFU; LOG_DEBUG("pheader %d len=0x%02" PRIx8 " id=0x%04" PRIx16 " ptr=0x%06" PRIx32, k, words, id, ptr); @@ -119,19 +248,26 @@ int spi_sfdp(struct flash_bank *bank, struct flash_device *dev, goto err; for (j = 0; j < words; j++) - LOG_DEBUG("word %02d 0x%08X", j + 1, ptable[j]); + LOG_DEBUG("word %02d 0x%08" PRIx32, j + 1, ptable[j]); if (id == SFDP_BASIC_FLASH) { struct sfdp_basic_flash_param *table = (struct sfdp_basic_flash_param *)ptable; uint16_t erase; if (words < 9) { - LOG_ERROR("id=0x%04" PRIx16 " invalid length %d", id, words); + LOG_ERROR("id=0x%04" PRIx16 " invalid length %" PRIu8, id, words); retval = ERROR_FLASH_BANK_NOT_PROBED; goto err; } - LOG_DEBUG("basic flash parameter table"); + LOG_DEBUG("SFDP basic flash parameter table, length %" PRIu8, words); + if (((offsetof(struct sfdp_basic_flash_param, chip_byte) >> 2) < words) && + (table->chip_byte == ~0U)) { + /* ATXP032 indicates length 20 DWORDS but last 10 apparently empty */ + LOG_WARNING("SFDP basic flash 11th word invalid, ignoring this and all following info"); + words = (offsetof(struct sfdp_basic_flash_param, chip_byte) >> 2) - 1; + } + /* dummy device name */ dev->name = sfdp_name; @@ -146,35 +282,47 @@ int spi_sfdp(struct flash_bank *bank, struct flash_device *dev, else dev->size_in_bytes = (table->density + 1) >> 3; - /* 2-2-2 read instruction, not used */ - if (table->fast_444 & (1UL << 0)) - dev->qread_cmd = (table->read_222 >> 24) & 0xFF; - - /* 4-4-4 read instruction */ - if (table->fast_444 & (1UL << 4)) - dev->qread_cmd = (table->read_444 >> 24) & 0xFF; + if ((table->fast_444 & (1UL << 4)) && + ((table->read_444 & 0xFFFFU) == 0xFFFFU)) { + /* 4-4-4 read instruction */ + /* ATXP032 contains bogus data, check for bits 15 to 0 all '1' */ + dev->qread_cmd = (table->read_444 >> 24) & 0xFFU; + } else if ((table->fast_444 & (1UL << 0)) && + ((table->read_222 & 0xFFFFU) == 0xFFFFU)) { + /* fallback: 2-2-2 read instruction */ + dev->qread_cmd = (table->read_222 >> 24) & 0xFFU; + } else if ((offsetof(struct sfdp_basic_flash_param, read_1x8) >> 2) < words) { + /* if no fast read so far, try the octal variants */ + if ((table->read_1x8 >> 8) & 0xFFU) { + /* fallback: 1-8-8 read instruction */ + dev->qread_cmd = (table->read_1x8 >> 8) & 0xFFU; + } else if ((table->read_1x8 >> 24) & 0xFFU) { + /* fallback: 1-1-8 read instruction */ + dev->qread_cmd = (table->read_1x8 >> 24) & 0xFFU; + } + } /* find the largest erase block size and instruction */ - erase = (table->erase_t12 >> 0) & 0xFFFF; + erase = (table->erase_t12 >> 0) & 0xFFFFU; erase_type = 1; - if (((table->erase_t12 >> 16) & 0xFF) > (erase & 0xFF)) { - erase = (table->erase_t12 >> 16) & 0xFFFF; + if (((table->erase_t12 >> 16) & 0xFFU) > (erase & 0xFFU)) { + erase = (table->erase_t12 >> 16) & 0xFFFFU; erase_type = 2; } - if (((table->erase_t34 >> 0) & 0xFF) > (erase & 0xFF)) { - erase = (table->erase_t34 >> 0) & 0xFFFF; + if (((table->erase_t34 >> 0) & 0xFFU) > (erase & 0xFFU)) { + erase = (table->erase_t34 >> 0) & 0xFFFFU; erase_type = 3; } - if (((table->erase_t34 >> 16) & 0xFF) > (erase & 0xFF)) { - erase = (table->erase_t34 >> 16) & 0xFFFF; + if (((table->erase_t34 >> 16) & 0xFFU) > (erase & 0xFFU)) { + erase = (table->erase_t34 >> 16) & 0xFFFFU; erase_type = 4; } - dev->erase_cmd = (erase >> 8) & 0xFF; - dev->sectorsize = 1UL << (erase & 0xFF); + dev->erase_cmd = (erase >> 8) & 0xFFU; + dev->sectorsize = 1UL << (erase & 0xFFU); if ((offsetof(struct sfdp_basic_flash_param, chip_byte) >> 2) < words) { /* get Program Page Size, if chip_byte present, that's optional */ - dev->pagesize = 1UL << ((table->chip_byte >> 4) & 0x0F); + dev->pagesize = 1UL << ((table->chip_byte >> 4) & 0xFU); } else { /* no explicit page size specified ... */ if (table->fast_addr & (1UL << 2)) { @@ -187,8 +335,8 @@ int spi_sfdp(struct flash_bank *bank, struct flash_device *dev, } if (dev->size_in_bytes > (1UL << 24)) { - if (((table->fast_addr >> 17) & 0x3) == 0x0) - LOG_ERROR("device needs paging - not implemented"); + if (((table->fast_addr >> 17) & 0x3U) == 0x0U) + LOG_ERROR("SFDP device needs paging - not implemented"); /* 4-byte addresses needed if more than 16 MBytes */ if (((offsetof(struct sfdp_basic_flash_param, addr_reset) >> 2) < words) && @@ -199,44 +347,368 @@ int spi_sfdp(struct flash_bank *bank, struct flash_device *dev, dev->read_cmd = 0x13; dev->pprog_cmd = 0x12; dev->erase_cmd = 0xDC; - if (dev->qread_cmd != 0) + if (dev->qread_cmd == 0xEB) dev->qread_cmd = 0xEC; - } else if (((table->fast_addr >> 17) & 0x3) == 0x1) - LOG_INFO("device has to be switched to 4-byte addresses"); + } else if (((table->fast_addr >> 17) & 0x3) == 0x1U) + LOG_INFO("SFDP device has to be switched to 4-byte addresses"); + } + + if ((offsetof(struct sfdp_basic_flash_param, quad_req) >> 2) < words) { + switch ((table->quad_req >> 20) & 0x7U) { + case 0: + LOG_INFO("SFDP QER: none"); + break; + + case 1: + LOG_INFO("SFDP QER: Write Status Reg., QE is *second* data byte bit 1 " + "(one data byte only implies second 0x00"); + break; + + case 2: + LOG_INFO("SFDP QER: Write Status Reg., QE is bit 6"); + break; + + case 3: + LOG_INFO("SFDP QER: Write Status Reg. 2, 0x3E, QE is bit 7"); + break; + + case 4: + LOG_INFO("SFDP QER: Write Status Reg., QE is *second* data byte bit 1 " + "(one data byte only has no effect on second"); + break; + + case 5: + LOG_INFO("SFDP QER: Write Status, 0x01, QE is *second* data byte bit 1 " + "(second byte read with 0x35"); + break; + + case 6: + LOG_INFO("SFDP QER: Write Status Reg. 2, 0x31, QE is bit 1, (read with 0x35"); + break; + + case 7: + LOG_INFO("SFDP QER: reserved"); + break; + } + + if (table->quad_req & BIT(4)) + LOG_INFO("SFDP QPI enter: set QE, then 0x38"); + if (table->quad_req & BIT(5)) + LOG_INFO("SFDP QPI enter: 0x38"); + if (table->quad_req & BIT(6)) + LOG_INFO("SFDP QPI enter: 0x35"); + if (table->quad_req & BIT(7)) + LOG_INFO("SFDP QPI enter: read by 0x65, addr 0x800003, set bit 6, write by 0x71, addr 0x800003"); + if (table->quad_req & BIT(8)) + LOG_INFO("SFDP QPI enter/exit: read by 0x65, clear/set bit 7, write by 0x61"); + if (table->quad_req & BIT(0)) + LOG_INFO("SFDP QPI exit: 0xFF"); + if (table->quad_req & BIT(1)) + LOG_INFO("SFDP QPI exit: 0xF5"); + if (table->quad_req & BIT(2)) + LOG_INFO("SFDP QPI exit: read by 0x65, addr 0x800003, clear bit 6, write by 0x71, addr 0x800003"); + if (table->quad_req & BIT(3)) + LOG_INFO("SFDP QPI exit: 0x66, 0x99"); + } + + if ((offsetof(struct sfdp_basic_flash_param, addr_reset) >> 2) < words) { + if (table->addr_reset & BIT(24)) + LOG_INFO("SFDP 4-byte enter: 0xB7"); + if (table->addr_reset & BIT(25)) + LOG_INFO("SFDP 4-byte enter: 0x06, 0xB7"); + if (table->addr_reset & BIT(26)) + LOG_INFO("SFDP 4-byte: read address extension by 0xC8, write by 0xC5"); + if (table->addr_reset & BIT(27)) + LOG_INFO("SFDP 4-byte enter/exit: read address extension by 0x16, " + "write by 0x17, bit 7 is 4-byte mode"); + if (table->addr_reset & BIT(28)) + LOG_INFO("SFDP 4-byte enter/exit: read 16-bit non-vol. reg. by 0xB5, write by 0xB1, 4-byte mode" + " is bit 0 in *second* data byte"); + if (table->addr_reset & BIT(29)) + LOG_INFO("SFDP 4-byte: dedicated 4-byte instructions"); + if (table->addr_reset & BIT(30)) + LOG_INFO("SFDP 4-byte: always"); + + if (table->addr_reset & BIT(14)) + LOG_INFO("SFDP 4-byte enter: 0xE9"); + if (table->addr_reset & BIT(15)) + LOG_INFO("SFDP 4-byte enter: 0x06, 0xE9"); + if (table->addr_reset & BIT(16)) + LOG_INFO("SFDP 4-byte: read address extension by 0xC8, write by 0xC5"); + if (table->addr_reset & BIT(17)) + LOG_INFO("SFDP 4-byte enter/exit: read address extension by 0x16, " + "write by 0x17, bit 7 is 4-byte mode"); + if (table->addr_reset & BIT(18)) + LOG_INFO("SFDP 4-byte enter/exit: read 16-bit non-vol. reg. by 0xB5, write by 0xB1, 4-byte mode" + " is bit 0 in *second* data byte"); + if (table->addr_reset & BIT(19)) + LOG_INFO("SFDP 4-byte exit: hardware reset"); + if (table->addr_reset & BIT(20)) + LOG_INFO("SFDP 4-byte exit: software reset"); + if (table->addr_reset & BIT(21)) + LOG_INFO("SFDP 4-byte exit: power cycle"); + } + + if ((offsetof(struct sfdp_basic_flash_param, octal_req) >> 2) < words) { + switch ((table->octal_req >> 20) & 0x7U) { + case 0: + LOG_INFO("SFDP OER: none"); + break; + + case 1: + LOG_INFO("SFDP OER: set by 0x31, one data byte, OE is bit 3, clear by 0x3E, " + "one data byte, read by 0x65, addr 0x02, one dummy"); + break; + + default: + LOG_INFO("SFDP OER: reserved"); + break; + } + + if (table->octal_req & BIT(5)) + LOG_INFO("SFDP OPI enter: 0x06, 0xE8"); + if (table->octal_req & BIT(6)) + LOG_INFO("SFDP OPI enter: 0x06, 0x72, addr 0x000000, data 0x01 (STR) or data 0x02 (DTR)"); + if (table->octal_req & BIT(0)) + LOG_INFO("SFDP OPI exit: 0x06, 0xFF"); + if (table->octal_req & BIT(3)) + LOG_INFO("SFDP OPI exit: 0x66, 0x99"); + } + + } else if ((id == SFDP_XSPI_1_0) || (id == SFDP_X4_QUAD_IO)) { + struct sfdp_xspi_1_0_param *table = (struct sfdp_xspi_1_0_param *)ptable; + + if (words < sizeof(struct sfdp_xspi_1_0_param) / sizeof(uint32_t)) { + LOG_ERROR("id=0x%04" PRIx16 " invalid length %" PRIu8, id, words); + retval = ERROR_FLASH_BANK_NOT_PROBED; + goto err; + } + + const char *mode = (id == SFDP_XSPI_1_0) ? "OPI" : "QPI"; + if (id == SFDP_XSPI_1_0) + LOG_DEBUG("xSPI profile 1.0 parameter table, length %" PRIu8, words); + else + LOG_DEBUG("x4 Quad IO parameter table, length %" PRIu8, words); + + if (table->status_cmds == ~0U) { + /* ATXP032 has all 0xFFFFFFFF ... */ + LOG_WARNING("SFDP %s 1st word invalid, ignoring this table", + (id == SFDP_XSPI_1_0) ? "xSPI profile 1.0" : "x4 Quad IO"); + goto skip; + } + + LOG_INFO("%s: %d-byte address and %d dummy cycles for SFDP", mode, + (table->status_cmds & BIT(31)) ? 3 : 4, + (table->status_cmds & BIT(30)) ? 20 : 8); + LOG_INFO("%s: latency %d, modifier %d for RDSR", mode, + (table->status_cmds & BIT(28)) ? 8 : 4, + (table->status_cmds & BIT(29)) ? 4 : 0); + LOG_INFO("%s: latency %d, modifier %d for Read Vol. Reg.", mode, + (table->status_cmds & BIT(26)) ? 8 : 4, + (table->status_cmds & BIT(27)) ? 4 : 1); + LOG_INFO("%s: latency %d, modifier %d for Read Non-Vol. Reg.", mode, + (table->status_cmds & BIT(25)) ? 8 : 4, + (table->status_cmds & BIT(27)) ? 4 : 1); + LOG_INFO("%s: modifier bytes %d for WRSR, %d for Write Reg. " + "with %d data bytes", mode, (table->status_cmds & BIT(24)) ? 4 : 0, + (table->status_cmds & BIT(23)) ? 4 : 1, + (table->status_cmds & BIT(22)) ? 2 : 1); + opi_read_fast_cmd = (table->status_cmds >> 8) & 0xFFU; + LOG_INFO("%s: Read Fast 0x%02" PRIx8 ", Read Fast Wrapped 0x%02" PRIx8, mode, + opi_read_fast_cmd, (table->status_cmds >> 0) & 0xFFU); + if (table->mem_cmds & BIT(22)) + LOG_INFO("%s: Read Vol. Reg. 0x%02" PRIx8, mode, + (table->reg_cmds >> 24) & 0xFFU); + if (table->mem_cmds & BIT(21)) + LOG_INFO("%s: Read Non-Vol. Reg. 0x%02" PRIx8, mode, + (table->reg_cmds >> 16) & 0xFFU); + if (table->mem_cmds & BIT(17)) + LOG_INFO("%s: Write Vol. Reg. 0x%02" PRIx8, mode, + (table->reg_cmds >> 8) & 0xFFU); + if (table->mem_cmds & BIT(16)) + LOG_INFO("%s: Write Non-Vol. Reg. 0x%02" PRIx8, mode, + (table->reg_cmds >> 0) & 0xFFU); + LOG_INFO("%s: %sRead SFDP, %sErase 4KBytes, %sErase 32kBytes, %sErase Chip", mode, + (table->mem_cmds & BIT(31)) ? "" : "*NO* ", + (table->mem_cmds & BIT(28)) ? "" : "*NO* ", + (table->mem_cmds & BIT(27)) ? "" : "*NO* ", + (table->mem_cmds & BIT(26)) ? "" : "*NO* "); + LOG_INFO("%s: %sRead Conf. Reg., %sRead Reg., %sWrite Conf. Reg., %sWrite Reg.", mode, + (table->mem_cmds & BIT(25)) ? "" : "*NO* ", + (table->mem_cmds & BIT(23)) ? "" : "*NO* ", + (table->mem_cmds & BIT(20)) ? "" : "*NO* ", + (table->mem_cmds & BIT(18)) ? "" : "*NO* "); + } else if (id == SFDP_XSPI_2_0) { + struct sfdp_xspi_2_0_param *table = (struct sfdp_xspi_2_0_param *)ptable; + + if (words < sizeof(struct sfdp_xspi_2_0_param) / sizeof(uint32_t)) { + LOG_ERROR("id=0x%04" PRIx16 " invalid length %" PRIu8, id, words); + retval = ERROR_FLASH_BANK_NOT_PROBED; + goto err; } + + LOG_DEBUG("xSPI profile 2.0 parameter table, length %" PRIu8, words); + + if (table->opi_cmds & BIT(31)) { + if (table->opi_cmds & BIT(19)) + LOG_INFO("OPI: Status Reg. Read"); + if (table->opi_cmds & BIT(13)) + LOG_INFO("OPI: Sector Erase"); + if (table->opi_cmds & BIT(12)) + LOG_INFO("OPI: Chip Erase"); + if (table->opi_cmds & BIT(5)) + LOG_INFO("OPI: Enter SPI"); + } else { + LOG_DEBUG("OPI DTR profile not implemented"); + } + } else if (id == SFDP_SECTOR_MAP) { + struct sfdp_sector_map *table = (struct sfdp_sector_map *)ptable; + + if (words < sizeof(struct sfdp_sector_map) / sizeof(uint32_t)) { + LOG_ERROR("id=0x%04" PRIx16 " invalid length %" PRIu8, id, words); + goto skip; + } + + LOG_DEBUG("sector map table, length %" PRIu8, words); + (void)table->conf_descr_1; } else if (id == SFDP_4BYTE_ADDR) { struct sfdp_4byte_addr_param *table = (struct sfdp_4byte_addr_param *)ptable; if (words >= (offsetof(struct sfdp_4byte_addr_param, erase_t1234) + sizeof(table->erase_t1234)) >> 2) { - LOG_INFO("4-byte address parameter table"); + LOG_INFO("SFDP 4-byte address parameter table"); /* read and page program instructions */ if (table->flags & (1UL << 0)) dev->read_cmd = 0x13; + if (table->flags & (1UL << 5)) dev->qread_cmd = 0xEC; + else if (table->flags & (1UL << 1)) + dev->qread_cmd = 0x0C; + else if (table->flags & (1UL << 2)) + dev->qread_cmd = 0x3C; + else if (table->flags & (1UL << 3)) + dev->qread_cmd = 0xBC; + else if (table->flags & (1UL << 4)) + dev->qread_cmd = 0x6C; + else if (table->flags & (1UL << 5)) + dev->qread_cmd = 0xEC; + if (table->flags & (1UL << 6)) dev->pprog_cmd = 0x12; + else if (table->flags & (1UL << 7)) + dev->pprog_cmd = 0x34; + else if (table->flags & (1UL << 8)) + dev->pprog_cmd = 0x3E; /* erase instructions */ if ((erase_type == 1) && (table->flags & (1UL << 9))) - dev->erase_cmd = (table->erase_t1234 >> 0) & 0xFF; + dev->erase_cmd = (table->erase_t1234 >> 0) & 0xFFU; else if ((erase_type == 2) && (table->flags & (1UL << 10))) - dev->erase_cmd = (table->erase_t1234 >> 8) & 0xFF; + dev->erase_cmd = (table->erase_t1234 >> 8) & 0xFFU; else if ((erase_type == 3) && (table->flags & (1UL << 11))) - dev->erase_cmd = (table->erase_t1234 >> 16) & 0xFF; + dev->erase_cmd = (table->erase_t1234 >> 16) & 0xFFU; else if ((erase_type == 4) && (table->flags & (1UL << 12))) - dev->erase_cmd = (table->erase_t1234 >> 24) & 0xFF; + dev->erase_cmd = (table->erase_t1234 >> 24) & 0xFFU; } else - LOG_ERROR("parameter table id=0x%04" PRIx16 " invalid length %d", id, words); + LOG_ERROR("parameter table id=0x%04" PRIx16 " invalid length %" PRIu8, id, words); + } else if (id == SFDP_STAT_CTRL_CONF) { + struct sfdp_stat_ctrl_conf_param *table = (struct sfdp_stat_ctrl_conf_param *)ptable; + + if ((offsetof(struct sfdp_stat_ctrl_conf_param, wel_bit) >> 2) >= words) { + LOG_ERROR("id=0x%04" PRIx16 " invalid length %" PRIu8, id, words); + goto skip; + } + + LOG_DEBUG("status, control and configuration register map table, length %" PRIu8, words); + + if (table->wip_bit == ~0U) { + /* ATXP032 has all 0xFFFFFFFF ... */ + LOG_WARNING("SFDP Status, Control and Configuration Register Map " + "5th word invalid, ignoring this table"); + goto skip; + } + + const unsigned int vol_addr = ((table->stat_ctrl_vol >> 28) & 0x3U) + 1; + LOG_INFO("SFDP: %d-byte addr for volatile", vol_addr); + + const unsigned int nvol_addr = ((table->stat_ctrl_nvol >> 28) & 0x3U) + 1; + LOG_INFO("SFDP: %d-byte addr for non-volatile", nvol_addr); + + wip_wle_access(table->addr_offs_vol, vol_addr, table->wip_bit, "WIP"); + wip_wle_access(table->addr_offs_vol, vol_addr, table->wel_bit, "WEL"); + if ((offsetof(struct sfdp_stat_ctrl_conf_param, qpi_nvol) >> 2) < words) { + qpi_opi_access(table->addr_offs_vol, vol_addr, table->qpi_vol, "vol. QPI"); + qpi_opi_access(table->addr_offs_nvol, nvol_addr, table->qpi_nvol, "non-vol. QPI"); + } + if ((offsetof(struct sfdp_stat_ctrl_conf_param, opi_nvol) >> 2) < words) { + qpi_opi_access(table->addr_offs_vol, vol_addr, table->opi_vol, "vol. OPI"); + qpi_opi_access(table->addr_offs_nvol, nvol_addr, table->opi_nvol, "non-vol. OPI"); + } + if ((offsetof(struct sfdp_stat_ctrl_conf_param, str_dtr_nvol) >> 2) < words) { + qpi_opi_access(table->addr_offs_vol, vol_addr, table->str_dtr_vol, "vol. STR/DTR"); + qpi_opi_access(table->addr_offs_nvol, nvol_addr, table->str_dtr_nvol, "non-vol. STR/DTR"); + } + if ((offsetof(struct sfdp_stat_ctrl_conf_param, str_octal_nvol) >> 2) < words) { + qpi_opi_access(table->addr_offs_vol, vol_addr, table->str_octal_vol, "vol. STR Octal"); + qpi_opi_access(table->addr_offs_nvol, nvol_addr, table->str_octal_nvol, "non-vol. STR Octal"); + } + if ((offsetof(struct sfdp_stat_ctrl_conf_param, dtr_octal_nvol) >> 2) < words) { + qpi_opi_access(table->addr_offs_vol, vol_addr, table->dtr_octal_vol, "vol. DTR Octal"); + qpi_opi_access(table->addr_offs_nvol, nvol_addr, table->dtr_octal_nvol, "non-vol. DTR Octal"); + } + } else if (id == SFDP_STAT_CTRL_CONF_2_0) { + struct sfdp_stat_ctrl_conf_2_0_param *table = (struct sfdp_stat_ctrl_conf_2_0_param *)ptable; + + if ((offsetof(struct sfdp_stat_ctrl_conf_2_0_param, stat_ctrl) >> 2) >= words) { + LOG_ERROR("id=0x%04" PRIx16 " invalid length %" PRIu8, id, words); + goto skip; + } + + LOG_DEBUG("status, control and configuration register map 2.0 table, length %" PRIu8, words); + + if (((table->stat_ctrl >> 28) & 0x3U) != 0x0) { + LOG_ERROR("SFDP: reserved number of address bytes"); + goto skip; + } + + LOG_INFO("SFDP: addr offset for volatile %0x08" PRIx32, table->addr_offs_vol); + LOG_INFO("SFDP: addr offset for non-volatile %0x08" PRIx32, table->addr_offs_nvol); + + if (table->stat_ctrl & BIT(31)) + LOG_INFO("SFDP: generic addressable read for all registers, %d bytes", 6); + if (table->stat_ctrl & BIT(30)) + LOG_INFO("SFDP: generic addressable write for all registers, %d bytes", 6); + LOG_INFO("SFDP: %" PRIu8 " dummy for vol. registers, %" PRIu8 " dummy for non-vol. registers", + (table->stat_ctrl >> 23) & 0x1FU, (table->stat_ctrl >> 18) & 0x1FU); + if (table->stat_ctrl & BIT(17)) { + uint32_t addr = (((table->stat_ctrl >> 3) & 0xFFU) << ((table->stat_ctrl >> 15) & 0x1)) + + table->addr_offs_vol; + LOG_INFO("SFDP WIP: Read 0x%02" PRIx8 ", addr 0x%0*" PRIx32 ", bit %" PRIu8, + 0, 8, addr, (table->stat_ctrl >> 11) & 0xFU); + } } else - LOG_DEBUG("unimplemented parameter table id=0x%04" PRIx16, id); + if (((id >> 8) >= 0x1U) && (id >> 8) <= 0x7FU) { + /* vendor owned */ + LOG_DEBUG("unimplemented vendor owned parameter table id=0x%04" PRIx16, id); + } else { + /* function specific */ + LOG_DEBUG("unimplemented function specific parameter table id=0x%04" PRIx16, id); + } +skip: free(ptable); ptable = NULL; } + if (opi_read_fast_cmd != 0 && dev->qread_cmd != opi_read_fast_cmd) { + /* OPI Read Fast takes precedence for octal-capable devices */ + LOG_WARNING("OPI: Read Fast 0x%02" PRIx8 " overrides default Read Fast 0x%02" PRIx8, + opi_read_fast_cmd, dev->qread_cmd); + dev->qread_cmd = opi_read_fast_cmd; + } + if (erase_type != 0) { LOG_INFO("valid SFDP detected"); retval = ERROR_OK; --