Parcing table of SFDP Command Sequence to change to Octal DDR(8D-8D-8D) mode(CSODDR) and following SPI protocol to judge opcode, address, dummy and data. Enabling Octal DDR mode when nor->octal_dtr_enable didn't hook.
Signed-off-by: JaimeLiao <jaimeliao...@gmail.com> --- drivers/mtd/spi/spi-nor-core.c | 114 ++++++++++++++++++++++++++++++++- include/linux/mtd/spi-nor.h | 8 ++- 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index d5d905fa5a..058dd8e889 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -63,6 +63,9 @@ 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_CSODDR_ID 0xff0a /* Command Sequences to Change to Octal + * DDR (8D-8D-8D) mode + */ #define SFDP_SIGNATURE 0x50444653U #define SFDP_JESD216_MAJOR 1 @@ -172,6 +175,11 @@ struct sfdp_header { #define PROFILE1_DWORD5_DUMMY_100MHZ GENMASK(11, 7) #define PROFILE1_DUMMY_DEFAULT 20 +/* Command Sequences to Change to Octal DDR (8D-8D-8D) mode */ +#define CSODDR_LENGTH_OF_CMD GENMASK(31, 24) +#define CSODDR_DWORD1_OF_CMD GENMASK(31, 0) +#define CSODDR_DWORD2_OF_CMD GENMASK(31, 0) + struct sfdp_bfpt { u32 dwords[BFPT_DWORD_MAX]; }; @@ -2434,6 +2442,49 @@ out: return ret; } +/** + * spi_nor_parse_csoddr() - Parse Command Sequences to Change to Octal DDR (8D-8D-8D) + * mode. + * @nor: pointer to a 'struct spi_nor' + * @csoddr_header: pointer to the 'struct sfdp_parameter_header' describing + * the CSODDR table length and version. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_csoddr(struct spi_nor *nor, + const struct sfdp_parameter_header *csoddr_header) +{ + u32 *table, addr; + size_t len; + int ret, i; + + len = csoddr_header->length * sizeof(*table); + table = kmalloc(len, GFP_KERNEL); + if (!table) + return -ENOMEM; + + addr = SFDP_PARAM_HEADER_PTP(csoddr_header); + ret = spi_nor_read_sfdp(nor, addr, len, table); + if (ret) + goto out; + + /* Fix endianness of the table DWORDs. */ + for (i = 0; i < csoddr_header->length; i++) + table[i] = le32_to_cpu(table[i]); + + /* Each command sequence consists of 2DWORDs(8 bytes) */ + for(i = 0; i < csoddr_header->length/2; i++) { + if(FIELD_GET(CSODDR_LENGTH_OF_CMD, table[2*i])) { + memcpy(&nor->csoddr_cmd[i].cmd_byte[0], &table[2*i], 4); + memcpy(&nor->csoddr_cmd[i].cmd_byte[4], &table[2*i+1], 4); + } + } + +out: + kfree(table); + return ret; +} + /** * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. * @nor: pointer to a 'struct spi_nor' @@ -2540,6 +2591,10 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, err = spi_nor_parse_profile1(nor, param_header, params); break; + case SFDP_CSODDR_ID: + err = spi_nor_parse_csoddr(nor, param_header); + break; + default: break; } @@ -3489,6 +3544,63 @@ static struct spi_nor_fixups mt35xu512aba_fixups = { }; #endif /* CONFIG_SPI_FLASH_MT35XU */ +/** + * spi_nor_generall_octal_dtr_enable() - execute command sequence to enable octal dtr mode. + * @nor: pointer to a 'struct spi_nor' + * + * Command Sequence to change to Octal DTR mode is parcing from SFDP table. + * Executing commands to enable Octal DTR mode. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_generall_octal_dtr_enable(struct spi_nor *nor) +{ + struct spi_mem_op op; + int ret, i; + u8 opcode, buf, word_buf[2]; + u32 addr; + + /* According SPI protocol for judging combination of commands */ + for(i = 0; i < MAX_COMMAND_SEQUENCE_DTR; i++) { + if(nor->csoddr_cmd[i].cmd_byte[3]) { + if(nor->csoddr_cmd[i].cmd_byte[3] == 1) { + opcode = nor->csoddr_cmd[i].cmd_byte[2]; + ret = nor->write_reg(nor, opcode, NULL, 0); + } else if(nor->csoddr_cmd[i].cmd_byte[3] == 2 ) { + opcode = nor->csoddr_cmd[i].cmd_byte[2]; + buf = nor->csoddr_cmd[i].cmd_byte[1]; + ret = nor->write_reg(nor, opcode, &buf, 1); + } else if(nor->csoddr_cmd[i].cmd_byte[3] == 3 ) { + opcode = nor->csoddr_cmd[i].cmd_byte[2]; + word_buf[0] = nor->csoddr_cmd[i].cmd_byte[1]; + word_buf[1] = nor->csoddr_cmd[i].cmd_byte[0]; + ret = nor->write_reg(nor, opcode, word_buf, 2); + } else if(nor->csoddr_cmd[i].cmd_byte[3] > 5) { + opcode = nor->csoddr_cmd[i].cmd_byte[2]; + addr = nor->csoddr_cmd[i].cmd_byte[1] << 24 | nor->csoddr_cmd[i].cmd_byte[0] << 16 | + nor->csoddr_cmd[i].cmd_byte[7] << 8 | nor->csoddr_cmd[i].cmd_byte[7]; + buf = nor->csoddr_cmd[i].cmd_byte[5]; + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1), + SPI_MEM_OP_ADDR(4, addr, 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; +} + /** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed * @nor: pointer to a 'struct spi_nor' * @@ -3499,7 +3611,7 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor) int ret; if (!nor->octal_dtr_enable) - return 0; + nor->octal_dtr_enable = spi_nor_generall_octal_dtr_enable; if (!(nor->read_proto == SNOR_PROTO_8_8_8_DTR && nor->write_proto == SNOR_PROTO_8_8_8_DTR)) diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 7ddc4ba2bf..7c4b982bf3 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -261,7 +261,8 @@ static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto) return spi_nor_get_protocol_data_nbits(proto); } -#define SPI_NOR_MAX_CMD_SIZE 8 +#define SPI_NOR_MAX_CMD_SIZE 8 +#define MAX_COMMAND_SEQUENCE_DTR 4 enum spi_nor_ops { SPI_NOR_OPS_READ = 0, SPI_NOR_OPS_WRITE, @@ -375,6 +376,10 @@ struct spi_nor_pp_command { enum spi_nor_protocol proto; }; +struct csoddr { + u8 cmd_byte[8]; +}; + enum spi_nor_read_command_index { SNOR_CMD_READ, SNOR_CMD_READ_FAST, @@ -563,6 +568,7 @@ struct spi_nor { void *priv; /* Compatibility for spi_flash, remove once sf layer is merged with mtd */ const char *name; + struct csoddr csoddr_cmd[MAX_COMMAND_SEQUENCE_DTR]; u32 size; u32 sector_size; u32 erase_size; -- 2.17.1