Message ID | 53d37eead9bdd09744af555b93381af477643a46.1660291281.git.Takahiro.Kuwano@infineon.com |
---|---|
State | Not Applicable |
Delegated to: | Ambarus Tudor |
Headers | show |
Series | Discover current address mode by CRC | expand |
Hi, Takahiro, On 8/12/22 11:06, tkuw584924@gmail.com wrote: > +#define CRC32C_POLY 0x1edc6f41 > +static u32 cypress_nor_calc_crc32c(u32 len, u8 *buf) > +{ > + u32 crc = 0; > + int i; > + > + while (len--) { > + crc ^= *buf++ << 24; > + for (i = 0; i < 8; i++) > + crc = (crc << 1) ^ ((crc & 0x80000000) ? CRC32C_POLY : > + 0); > + } > + > + return crc; > +} I haven't gone through the details of the patch set, but I like the idea. I stumbled on your crc implementation. Can't we use #include <linux/crc32.h> instead?
On 10/5/2022 4:10 PM, Tudor.Ambarus@microchip.com wrote: > Hi, Takahiro, > > On 8/12/22 11:06, tkuw584924@gmail.com wrote: >> +#define CRC32C_POLY 0x1edc6f41 >> +static u32 cypress_nor_calc_crc32c(u32 len, u8 *buf) >> +{ >> + u32 crc = 0; >> + int i; >> + >> + while (len--) { >> + crc ^= *buf++ << 24; >> + for (i = 0; i < 8; i++) >> + crc = (crc << 1) ^ ((crc & 0x80000000) ? CRC32C_POLY : >> + 0); >> + } >> + >> + return crc; >> +} > > I haven't gone through the details of the patch set, but I like the idea. > I stumbled on your crc implementation. Can't we use #include <linux/crc32.h> > instead? > CRC32C BE is a missing piece in lib/crc32 so I added this local helper. I have created following patch that adds CRC32C BE into lib/crc32. Do you agree to submit this? We also need to add test case to crc32test.c. diff --git a/include/linux/crc32.h b/include/linux/crc32.h index 9e8a032c1788..adbfd886ccf8 100644 --- a/include/linux/crc32.h +++ b/include/linux/crc32.h @@ -37,6 +37,7 @@ static inline u32 crc32_le_combine(u32 crc1, u32 crc2, size_t len2) } u32 __pure __crc32c_le(u32 crc, unsigned char const *p, size_t len); +u32 __pure __crc32c_be(u32 crc, unsigned char const *p, size_t len); /** * __crc32c_le_combine - Combine two crc32c check values into one. For two diff --git a/include/linux/crc32poly.h b/include/linux/crc32poly.h index 62c4b7790a28..ed5d81f61d4e 100644 --- a/include/linux/crc32poly.h +++ b/include/linux/crc32poly.h @@ -16,5 +16,6 @@ * x^8+x^6+x^0 */ #define CRC32C_POLY_LE 0x82F63B78 +#define CRC32C_POLY_BE 0x1EDC6F41 #endif /* _LINUX_CRC32_POLY_H */ diff --git a/lib/crc32.c b/lib/crc32.c index 5649847d0a8d..15dc6f99ff46 100644 --- a/lib/crc32.c +++ b/lib/crc32.c @@ -335,10 +335,19 @@ u32 __pure __weak crc32_be(u32 crc, unsigned char const *p, size_t len) { return crc32_be_generic(crc, p, len, NULL, CRC32_POLY_BE); } +u32 __pure __weak __crc32c_be(u32 crc, unsigned char const *p, size_t len) +{ + return crc32_be_generic(crc, p, len, NULL, CRC32C_POLY_BE); +} #else u32 __pure __weak crc32_be(u32 crc, unsigned char const *p, size_t len) { return crc32_be_generic(crc, p, len, crc32table_be, CRC32_POLY_BE); } +u32 __pure __weak __crc32c_be(u32 crc, unsigned char const *p, size_t len) +{ + return crc32_be_generic(crc, p, len, crc32ctable_be, CRC32C_POLY_BE); +} #endif EXPORT_SYMBOL(crc32_be); +EXPORT_SYMBOL(__crc32c_be); diff --git a/lib/gen_crc32table.c b/lib/gen_crc32table.c index f755b997b967..fcb90c496e48 100644 --- a/lib/gen_crc32table.c +++ b/lib/gen_crc32table.c @@ -26,6 +26,7 @@ static uint32_t crc32table_le[LE_TABLE_ROWS][256]; static uint32_t crc32table_be[BE_TABLE_ROWS][256]; static uint32_t crc32ctable_le[LE_TABLE_ROWS][256]; +static uint32_t crc32ctable_be[BE_TABLE_ROWS][256]; /** * crc32init_le() - allocate and initialize LE table data @@ -67,29 +68,40 @@ static void crc32cinit_le(void) } /** - * crc32init_be() - allocate and initialize BE table data + * crc32init_be_generic() - allocate and initialize BE table data */ -static void crc32init_be(void) +static void crc32init_be_generic(const uint32_t polynomial, + uint32_t (*tab)[256]) { unsigned i, j; uint32_t crc = 0x80000000; - crc32table_be[0][0] = 0; + tab[0][0] = 0; for (i = 1; i < BE_TABLE_SIZE; i <<= 1) { - crc = (crc << 1) ^ ((crc & 0x80000000) ? CRC32_POLY_BE : 0); + crc = (crc << 1) ^ ((crc & 0x80000000) ? polynomial : 0); for (j = 0; j < i; j++) - crc32table_be[0][i + j] = crc ^ crc32table_be[0][j]; + tab[0][i + j] = crc ^ tab[0][j]; } for (i = 0; i < BE_TABLE_SIZE; i++) { - crc = crc32table_be[0][i]; + crc = tab[0][i]; for (j = 1; j < BE_TABLE_ROWS; j++) { - crc = crc32table_be[0][(crc >> 24) & 0xff] ^ (crc << 8); - crc32table_be[j][i] = crc; + crc = tab[0][(crc >> 24) & 0xff] ^ (crc << 8); + tab[j][i] = crc; } } } +static void crc32init_be(void) +{ + crc32init_be_generic(CRC32_POLY_BE, crc32table_be); +} + +static void crc32cinit_be(void) +{ + crc32init_be_generic(CRC32C_POLY_BE, crc32ctable_be); +} + static void output_table(uint32_t (*table)[256], int rows, int len, char *trans) { int i, j; @@ -137,6 +149,15 @@ int main(int argc, char** argv) LE_TABLE_SIZE, "tole"); printf("};\n"); } + if (CRC_BE_BITS > 1) { + crc32cinit_be(); + printf("static const u32 ____cacheline_aligned " + "crc32ctable_be[%d][%d] = {", + BE_TABLE_ROWS, BE_TABLE_SIZE); + output_table(crc32ctable_be, BE_TABLE_ROWS, + BE_TABLE_SIZE, "tobe"); + printf("};\n"); + } return 0; }
diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h index bbf80d2990ab..d7e1620bd870 100644 --- a/drivers/mtd/spi-nor/sfdp.h +++ b/drivers/mtd/spi-nor/sfdp.h @@ -90,6 +90,7 @@ struct sfdp_bfpt { #define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) #define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ +#define BFPT_DWORD16_EX4B_PWRCYC BIT(21) #define BFPT_DWORD16_SWRST_EN_RST BIT(12) #define BFPT_DWORD18_CMD_EXT_MASK GENMASK(30, 29) diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 676ffd6d12ec..8ed4b2d53403 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -23,7 +23,9 @@ #define SPINOR_REG_CYPRESS_CFR5V 0x00800006 #define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN 0x3 #define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS 0 +#define SPINOR_REG_CYPRESS_DCRV 0x00800095 #define SPINOR_OP_CYPRESS_RD_FAST 0xee +#define SPINOR_OP_CYPRESS_DI_CRC 0x5b /* Cypress SPI NOR flash operations. */ #define CYPRESS_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf) \ @@ -213,11 +215,139 @@ static int cypress_nor_set_page_size(struct spi_nor *nor) return 0; } +#define CRC32C_POLY 0x1edc6f41 +static u32 cypress_nor_calc_crc32c(u32 len, u8 *buf) +{ + u32 crc = 0; + int i; + + while (len--) { + crc ^= *buf++ << 24; + for (i = 0; i < 8; i++) + crc = (crc << 1) ^ ((crc & 0x80000000) ? CRC32C_POLY : + 0); + } + + return crc; +} + +static int cypress_nor_data_integrity_crc(struct spi_nor *nor, u32 addr, + u32 len) +{ + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CYPRESS_DI_CRC, 0), + SPI_MEM_OP_ADDR(4, addr, 0), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(4, nor->bouncebuf, 0)); + u32 end_addr = addr + len - 1; + int ret; + + nor->bouncebuf[0] = (end_addr >> 24) & 0xff; + nor->bouncebuf[1] = (end_addr >> 16) & 0xff; + nor->bouncebuf[2] = (end_addr >> 8) & 0xff; + nor->bouncebuf[3] = end_addr & 0xff; + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + return spi_nor_wait_till_ready(nor); +} + +/** + * cypress_nor_verify_crc() - Compare CRC and data integrity CRC registers. + * @nor: pointer to 'struct spi_nor'. + * @addr_nbytes: number of address bytes used in Read Any Reg op + * @crc: CRC value to compare with registers + * + * Return: 1 if match, 0 if not match, -errno on errors. + */ +static int cypress_nor_verify_crc(struct spi_nor *nor, u8 addr_nbytes, u32 crc) +{ + struct spi_mem_op op = + CYPRESS_NOR_RD_ANY_REG_OP(addr_nbytes, + SPINOR_REG_CYPRESS_DCRV, + nor->bouncebuf); + int i, ret; + + for (i = 0; i < 4; i++) { + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + if ((crc & 0xff) != nor->bouncebuf[0]) + return 0; + + crc >>= 8; + op.addr.val++; + } + + return 1; +} + +static int cypress_nor_discover_addr_mode(struct spi_nor *nor) +{ + u32 addr = 0; + u32 len = 4; + u32 crc; + u8 addr_nbytes; + int ret; + + /* Read flash memory array */ + ret = spi_nor_alt_read(nor, addr, len, nor->bouncebuf, + SPINOR_OP_READ_4B, 4, 0); + if (ret) + return ret; + + /* Calculate CRC32C of read data */ + crc = cypress_nor_calc_crc32c(len, nor->bouncebuf); + + /* Perform on-die CRC calculation */ + ret = cypress_nor_data_integrity_crc(nor, addr, len); + if (ret) + return ret; + + /* Read and verify CRC registers with current addr_mode_nbytes */ + ret = cypress_nor_verify_crc(nor, nor->params->addr_mode_nbytes, crc); + if (ret < 0) + return ret; + if (ret) + return 0; + + /* Read and verify CRC registers with another number of address bytes */ + addr_nbytes = nor->params->addr_mode_nbytes == 3 ? 4 : 3; + ret = cypress_nor_verify_crc(nor, addr_nbytes, crc); + if (ret < 0) + return ret; + if (ret) + nor->params->addr_mode_nbytes = addr_nbytes; + else + dev_warn(nor->dev, "Failed to discover current address mode. Assuming default %d-byte.\n", + nor->params->addr_mode_nbytes); + + return 0; +} + static int s25hx_t_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { + int ret; + + /* + * Discover flash's current address mode. Determine factory default + * address mode in advance by checking if power cycle exits 4-byte + * address mode, meaning factory default is 3-byte. + */ + nor->params->addr_mode_nbytes = + bfpt->dwords[BFPT_DWORD(16)] & BFPT_DWORD16_EX4B_PWRCYC ? 3 : 4; + ret = cypress_nor_discover_addr_mode(nor); + if (ret) + return ret; + /* Replace Quad Enable with volatile version */ nor->params->quad_enable = cypress_nor_quad_enable_volatile;