mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support
Subsystems (3)
v3 cover mtd: spinand: Add Octal DTR SPI (8D-8D-8D) mode support
Hi,
This series proposes patches for adding the following functionality in SPI NAND core:
- Octal DTR SPI (8D-8D-8D) mode support
- ctrl_ops support
- Winbond W35N01JW SPI NAND chip support
This series has been tested on TI J721e EVM with the Winbond W35N01JW flash with following test utilities:
- nandtest
Test log: https://pastebin.com/raw/PusiLYVQ
- mtd_stresstest
Test log: https://pastebin.com/raw/gBtV2CAf
- UBIFS LTP stress test (NAND_XL_STRESS_DD_RW_UBIFS). Test log: https://pastebin.com/raw/J8WyLsz1
Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf
---
Changes in v3:
- Added support for ctrl_ops, data_ops and ctrl_ops_variants.
- Made the implementation generic in terms of spinand protocol and SPI mode changes.
- Added option to disable Octal DTR mode.
- Removed spinand_patch_op(), adjust_op() manufacturer_op.
- Removed Power-on-Reset functionality and its call from mtd_suspend(), now just disable Octal DTR at suspend.
Changes in v2:
- Removed *_ALL_ARGS() macros from spi-mem.h, and redefined DTR macros.
- Renamed spinand_setup_op() to spinand_patch_op(). Reduced one conditional check from this function. Had to keep tweaking in hot-path to avoid complicated implementation "hacks".
- Changes in commit messages and added comments.
- Dropped "Reject 8D-8D-8D op_templates if octal_dtr_enale() is missing in manufacturer_op" patch.
- Reduced PoR reset delay.
- Splitted "mtd: spinand: Add support for Winbond W35N01JW SPI NAND flash" into 3 independent patches.
Apurva Nandan (17):
spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase
mtd: spinand: Define macros for Octal DTR ops
mtd: spinand: Add enum spinand_protocol to indicate current SPI IO
mode
mtd: spinand: Rename 'op_templates' to 'data_ops'
mtd: spinand: Define ctrl_ops for non-page read/write op templates
mtd: spinand: Define default ctrl_ops in the core
mtd: spinand: Switch from op macros usage to 'ctrl_ops' in the core
mtd: spinand: Add support for manufacturer-based ctrl_ops variations
mtd: spinand: Add change_mode() in manufacturer_ops
mtd: spinand: Add pointer to probed flash's spinand_info
mtd: spinand: Allow enabling/disabling Octal DTR mode in the core
mtd: spinand: Add mtd_suspend() to disable Octal DTR mode at suspend
mtd: spinand: winbond: Add support for write volatile configuration
register op
mtd: spinand: winbond: Add octal_dtr_enable/disable() in
manufacturer_ops
mtd: spianand: winbond: Add change_mode() manufacturer_ops
mtd: spinand: winbond: Rename cache op_variants struct variable
mtd: spinand: winbond: Add support for Winbond W35N01JW SPI NAND flash
drivers/mtd/nand/spi/core.c | 213 +++++++++++++++++++++++++---- drivers/mtd/nand/spi/winbond.c | 242 +++++++++++++++++++++++++++++++-- include/linux/mtd/spinand.h | 154 +++++++++++++++++++-- include/linux/spi/spi-mem.h | 41 ++++++ 4 files changed, 599 insertions(+), 51 deletions(-)
Signature · 1 line
2.25.1
PATCH v3 1/17 spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase
Setting dtr field of spi_mem_op is useful when creating templates for DTR ops in spinand.h. Also, 2 bytes cmd phases are required when operating in Octal DTR SPI mode.
Create new templates for dtr mode cmd, address, dummy and data phase in spi_mem_op, which set the dtr field to 1 and also allow passing the nbytes for the cmd phase.
--- include/linux/spi/spi-mem.h | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+)
include/linux/spi/spi-mem.h +41 -0
@@ -20,6 +20,14 @@ .nbytes = 1, \ } +#define SPI_MEM_OP_CMD_DTR (__nbytes, __opcode, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .opcode = __opcode, \ + .buswidth = __buswidth, \ + .dtr = 1, \ + } +#define SPI_MEM_OP_ADDR (__nbytes, __val, __buswidth) \ { \ .nbytes = __nbytes, \
@@ -27,6 +35,14 @@ .buswidth = __buswidth, \ } +#define SPI_MEM_OP_ADDR_DTR (__nbytes, __val, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .val = __val, \ + .buswidth = __buswidth, \ + .dtr = 1, \ + } +#define SPI_MEM_OP_NO_ADDR { }#define SPI_MEM_OP_DUMMY (__nbytes, __buswidth) \
@@ -35,6 +51,13 @@ .buswidth = __buswidth, \ } +#define SPI_MEM_OP_DUMMY_DTR (__nbytes, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .buswidth = __buswidth, \ + .dtr = 1, \ + } +#define SPI_MEM_OP_NO_DUMMY { }#define SPI_MEM_OP_DATA_IN (__nbytes, __buf, __buswidth) \
@@ -45,6 +68,15 @@ .buswidth = __buswidth, \ } +#define SPI_MEM_OP_DATA_IN_DTR (__nbytes, __buf, __buswidth) \ + { \ + .dir = SPI_MEM_DATA_IN, \ + .nbytes = __nbytes, \ + .buf.in = __buf, \ + .buswidth = __buswidth, \ + .dtr = 1, \ + } +#define SPI_MEM_OP_DATA_OUT (__nbytes, __buf, __buswidth) \ { \ .dir = SPI_MEM_DATA_OUT, \
@@ -53,6 +85,15 @@ .buswidth = __buswidth, \ } +#define SPI_MEM_OP_DATA_OUT_DTR (__nbytes, __buf, __buswidth) \ + { \ + .dir = SPI_MEM_DATA_OUT, \ + .nbytes = __nbytes, \ + .buf.out = __buf, \ + .buswidth = __buswidth, \ + .dtr = 1, \ + } +#define SPI_MEM_OP_NO_DATA { }/**
Signature · 1 line
2.25.1
Mark Brown
maintainerRe: [PATCH v3 01/17] spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase
Quoted 3 lines · Apurva Nandan
Setting dtr field of
spi_mem_opis useful when creating templates for DTR ops inspinand.h. Also, 2 bytes cmd phases are required when operating in Octal DTR SPI mode.
Are people OK with this? It looks fine to me, if nobody objects I can send it in the next merge window so it's there for the MTD bits.
Re: [PATCH v3 01/17] spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase
In reply to Apurva NandanQuoted 83 lines · On Sat, 1 Jan 2022 13:12:34 +0530 Apurva Nandan <[email protected]> wrote:
Setting dtr field of
spi_mem_opis useful when creating templates for DTR ops inspinand.h. Also, 2 bytes cmd phases are required when operating in Octal DTR SPI mode.Create new templates for dtr mode cmd, address, dummy and data phase in
spi_mem_op, which set the dtr field to 1 and also allow passing the nbytes for the cmd phase.Signed-off-by Apurva Nandan <[email protected]>--- include/linux/spi/spi-mem.h | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+)include/linux/spi/spi-mem.h +34
-0@@ -20,6 +20,14 @@ .nbytes = 1, \ } +#define SPI_MEM_OP_CMD_DTR (__nbytes, __opcode, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .opcode = __opcode, \ + .buswidth = __buswidth, \ + .dtr = 1, \ + } +#define SPI_MEM_OP_ADDR (__nbytes, __val, __buswidth) \ { \ .nbytes = __nbytes, \@@ -27,6 +35,14 @@ .buswidth = __buswidth, \ } +#define SPI_MEM_OP_ADDR_DTR (__nbytes, __val, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .val = __val, \ + .buswidth = __buswidth, \ + .dtr = 1, \ + } +#define SPI_MEM_OP_NO_ADDR { }#define SPI_MEM_OP_DUMMY (__nbytes, __buswidth) \@@ -35,6 +51,13 @@ .buswidth = __buswidth, \ } +#define SPI_MEM_OP_DUMMY_DTR (__nbytes, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .buswidth = __buswidth, \ + .dtr = 1, \ + } +#define SPI_MEM_OP_NO_DUMMY { }#define SPI_MEM_OP_DATA_IN (__nbytes, __buf, __buswidth) \@@ -45,6 +68,15 @@ .buswidth = __buswidth, \ } +#define SPI_MEM_OP_DATA_IN_DTR (__nbytes, __buf, __buswidth) \ + { \ + .dir = SPI_MEM_DATA_IN, \ + .nbytes = __nbytes, \ + .buf.in = __buf, \ + .buswidth = __buswidth, \ + .dtr = 1, \ + } +#define SPI_MEM_OP_DATA_OUT (__nbytes, __buf, __buswidth) \ { \ .dir = SPI_MEM_DATA_OUT, \@@ -53,6 +85,15 @@ .buswidth = __buswidth, \ } +#define SPI_MEM_OP_DATA_OUT_DTR (__nbytes, __buf, __buswidth) \ + { \
include/linux/spi/spi-mem.h line 99
Quoted 10 lines
include/linux/spi/spi-mem.h +7
-0+ .dir = SPI_MEM_DATA_OUT, \ + .nbytes = __nbytes, \ + .buf.out = __buf, \ + .buswidth = __buswidth, \ + .dtr = 1, \ + } +#define SPI_MEM_OP_NO_DATA { }/**
How about:
include/linux/spi/spi-mem.h +20 -5
@@ -13,44 +13,59 @@#include <linux/spi/spi.h> -#define SPI_MEM_OP_CMD (__opcode, __buswidth) \ +#define SPI_MEM_OP_DTR .dtr = 1 + +#define SPI_MEM_OP_CMD (__opcode, __buswidth, ...) \ { \ .buswidth = __buswidth, \ .opcode = __opcode, \ .nbytes = 1, \ + __VA_ARGS__ \ } -#define SPI_MEM_OP_ADDR (__nbytes, __val, __buswidth) \ +#define SPI_MEM_OP_EXT_CMD (__nbytes, __opcode, __buswidth, ...) \ + { \ + .buswidth = __buswidth, \ + .opcode = __opcode, \ + .nbytes = __nbytes, \ + __VA_ARGS__ \ + } + +#define SPI_MEM_OP_ADDR (__nbytes, __val, __buswidth, ...) \ { \ .nbytes = __nbytes, \ .val = __val, \ .buswidth = __buswidth, \ + __VA_ARGS__ \ }#define SPI_MEM_OP_NO_ADDR { } -#define SPI_MEM_OP_DUMMY (__nbytes, __buswidth) \ +#define SPI_MEM_OP_DUMMY (__nbytes, __buswidth, ...) \ { \ .nbytes = __nbytes, \ .buswidth = __buswidth, \ + __VA_ARGS__ \ }#define SPI_MEM_OP_NO_DUMMY { } -#define SPI_MEM_OP_DATA_IN (__nbytes, __buf, __buswidth) \ +#define SPI_MEM_OP_DATA_IN (__nbytes, __buf, __buswidth, ...) \ { \ .dir = SPI_MEM_DATA_IN, \ .nbytes = __nbytes, \ .buf.in = __buf, \ .buswidth = __buswidth, \ + __VA_ARGS__ \ } -#define SPI_MEM_OP_DATA_OUT (__nbytes, __buf, __buswidth) \ +#define SPI_MEM_OP_DATA_OUT (__nbytes, __buf, __buswidth, ...) \ { \ .dir = SPI_MEM_DATA_OUT, \ .nbytes = __nbytes, \ .buf.out = __buf, \ .buswidth = __buswidth, \ + __VA_ARGS__ \ }#define SPI_MEM_OP_NO_DATA { }
and you get to define a DTR op like that:
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_EXT_CMD(2, 0x1234, 8, SPI_MEM_OP_DTR), SPI_MEM_OP_ADDR(4, 0xdeadbeef, 8, SPI_MEM_OP_DTR), SPI_MEM_OP_DATA_OUT(128, buf, 8, SPI_MEM_OP_DTR));
This also means we can extend the struct without having to define new macros.
Re: [PATCH v3 01/17] spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase
Quoted 8 lines · Boris Brezillon
and you get to define a DTR op like that:
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_EXT_CMD(2, 0x1234, 8, SPI_MEM_OP_DTR), SPI_MEM_OP_ADDR(4, 0xdeadbeef, 8, SPI_MEM_OP_DTR), SPI_MEM_OP_DATA_OUT(128, buf, 8, SPI_MEM_OP_DTR));This also means we can extend the struct without having to define new macros.
I like this. It would also let us easily mix-and-match the ecc parameter that Miquel is adding.
Signature · 3 lines
Regards, Pratyush Yadav Texas Instruments Inc.
Re: [PATCH v3 01/17] spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase
Quoted 8 lines · On Wed, 5 Jan 2022 11:20:55 +0530 Pratyush Yadav <[email protected]> wrote:
Quoted 7 lines · Boris Brezillon
and you get to define a DTR op like that:
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_EXT_CMD(2, 0x1234, 8, SPI_MEM_OP_DTR), SPI_MEM_OP_ADDR(4, 0xdeadbeef, 8, SPI_MEM_OP_DTR), SPI_MEM_OP_DATA_OUT(128, buf, 8, SPI_MEM_OP_DTR));
Quoted 5 lines
Quoted 1 line
This also means we can extend the struct without having to define new macros.
I like this. It would also let us easily mix-and-match the ecc parameter that Miquel is adding.
In practice, I doubt you'll ever set the ecc bit when declaring the op, it's more a modification you do afterwards if ECC needs to be enabled, but who knows...
Re: [PATCH v3 01/17] spi: spi-mem: Add DTR templates for cmd, address, dummy and data phase
In reply to Boris BrezillonQuoted 8 lines · Boris Brezillon
include/linux/spi/spi-mem.h +2
-1-#define SPI_MEM_OP_DATA_IN (__nbytes, __buf, __buswidth) \ +#define SPI_MEM_OP_DATA_IN (__nbytes, __buf, __buswidth, ...) \ { \ .dir = SPI_MEM_DATA_IN, \ .nbytes = __nbytes, \ .buf.in = __buf, \ .buswidth = __buswidth, \ + __VA_ARGS__ \
include/linux/spi/spi-mem.h
Quoted 10 lines
include/linux/spi/spi-mem.h +2
-1} -#define SPI_MEM_OP_DATA_OUT (__nbytes, __buf, __buswidth) \ +#define SPI_MEM_OP_DATA_OUT (__nbytes, __buf, __buswidth, ...) \ { \ .dir = SPI_MEM_DATA_OUT, \ .nbytes = __nbytes, \ .buf.out = __buf, \ .buswidth = __buswidth, \ + __VA_ARGS__
I like it too. This also comes in handy when we'll have to differentiate between register and memory accesses.
Cheers,
ta
PATCH v3 2/17 mtd: spinand: Define macros for Octal DTR ops
In reply to Apurva NandanDefine new op templates for reset, write enable, set_feature, get_feature, block_erase, read/write page operations for Octal DTR SPI mode. These templates will be used in data_ops and ctrl_ops for performing all flash operations.
Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf
--- include/linux/mtd/spinand.h | 54 +++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)
include/linux/mtd/spinand.h +54 -0
@@ -26,12 +26,24 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) +#define SPINAND_RESET_OP_OCTAL_DTR \ + SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, 0xffff, 8), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) +#define SPINAND_WR_EN_DIS_OP (enable) \ SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1), \ SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) +#define SPINAND_WR_EN_DIS_OP_OCTAL_DTR (enable) \ + SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, (enable) ? 0x0606 : 0x0404, 8),\ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) +#define SPINAND_READID_OP (naddr, ndummy, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \ SPI_MEM_OP_ADDR(naddr, 0, 1), \
@@ -44,24 +56,48 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(1, valptr, 1)) +#define SPINAND_SET_FEATURE_OP_OCTAL_DTR (reg, valptr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, 0x1f1f, 8), \ + SPI_MEM_OP_ADDR_DTR(2, reg, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT_DTR(2, valptr, 8)) +#define SPINAND_GET_FEATURE_OP (reg, valptr) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0x0f, 1), \ SPI_MEM_OP_ADDR(1, reg, 1), \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_IN(1, valptr, 1)) +#define SPINAND_GET_FEATURE_OP_OCTAL_DTR (reg, valptr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, 0x0f0f, 8), \ + SPI_MEM_OP_ADDR_DTR(2, reg, 8), \ + SPI_MEM_OP_DUMMY_DTR(14, 8), \ + SPI_MEM_OP_DATA_IN_DTR(2, valptr, 8)) +#define SPINAND_BLK_ERASE_OP (addr) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0xd8, 1), \ SPI_MEM_OP_ADDR(3, addr, 1), \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) +#define SPINAND_BLK_ERASE_OP_OCTAL_DTR (addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, 0xd8d8, 8), \ + SPI_MEM_OP_ADDR_DTR(2, addr, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) +#define SPINAND_PAGE_READ_OP (addr) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0x13, 1), \ SPI_MEM_OP_ADDR(3, addr, 1), \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) +#define SPINAND_PAGE_READ_OP_OCTAL_DTR (addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, 0x1313, 8), \ + SPI_MEM_OP_ADDR_DTR(2, addr, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) +#define SPINAND_PAGE_READ_FROM_CACHE_OP (fast, addr, ndummy, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \ SPI_MEM_OP_ADDR(2, addr, 1), \
@@ -122,12 +158,24 @@ SPI_MEM_OP_DUMMY(ndummy, 4), \ SPI_MEM_OP_DATA_IN(len, buf, 4)) +#define SPINAND_PAGE_READ_FROM_CACHE_OCTALIO_DTR_OP (addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, 0x9d9d, 8), \ + SPI_MEM_OP_ADDR_DTR(2, addr, 8), \ + SPI_MEM_OP_DUMMY_DTR(ndummy, 8), \ + SPI_MEM_OP_DATA_IN_DTR(len, buf, 8)) +#define SPINAND_PROG_EXEC_OP (addr) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0x10, 1), \ SPI_MEM_OP_ADDR(3, addr, 1), \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) +#define SPINAND_PROG_EXEC_OP_OCTAL_DTR (addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, 0x1010, 8), \ + SPI_MEM_OP_ADDR_DTR(2, addr, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) +#define SPINAND_PROG_LOAD (reset, addr, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x02 : 0x84, 1), \ SPI_MEM_OP_ADDR(2, addr, 1), \
@@ -140,6 +188,12 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(len, buf, 4)) +#define SPINAND_PROG_LOAD_OCTALIO_DTR (reset, addr, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD_DTR(2, reset ? 0x0202 : 0x8484, 8), \ + SPI_MEM_OP_ADDR_DTR(2, addr, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT_DTR(len, buf, 8)) +/** * Standard SPI NAND flash commands */
Signature · 1 line
2.25.1
PATCH v3 3/17 mtd: spinand: Add enum spinand_protocol to indicate current SPI IO mode
In reply to Apurva NandanUnlike Dual and Quad SPI modes flashes, Octal DTR SPI NAND flashes require all instructions to be made in 8D-8D-8D protocol when the flash is in Octal DTR mode. Hence, storing the current SPI IO mode becomes necessary for operating the flash and switching between modes.
Store the current SPI IO mode in the spinand struct using a spinand_protocol enum. This would act as a flag, denoting that the core should use the given SPI protocol all types of flash operations.
--- drivers/mtd/nand/spi/core.c | 2 ++ include/linux/mtd/spinand.h | 16 ++++++++++++++++ 2 files changed, 18 insertions(+)
drivers/mtd/nand/spi/core.c +2 -0
@@ -1155,6 +1155,7 @@ static void spinand_mtd_resume(struct mtd_info *mtd)struct spinand_device *spinand =mtd_to_spinand (mtd);int ret; + spinand->protocol =SPINAND_1S_1S_1S ; ret =spinand_reset_op (spinand);if (ret)return ;
@@ -1181,6 +1182,7 @@ static int spinand_init(struct spinand_device *spinand)if (!spinand->scratchbuf)return -ENOMEM ; + spinand->protocol =SPINAND_1S_1S_1S ; ret =spinand_detect (spinand);if (ret) goto err_free_bufs;
include/linux/mtd/spinand.h +16 -0
@@ -194,6 +194,18 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT_DTR(len, buf, 8)) +/** +* enum spinand_protocol - List of SPI protocols to denote the op protocol and +* SPI NAND flash IO modes. +*/ +enum spinand_protocol { +SPINAND_1S_1S_1S , +SPINAND_2S_2S_2S , +SPINAND_4S_4S_4S , +SPINAND_8S_8S_8S , +SPINAND_8D_8D_8D , +}; +/** * Standard SPI NAND flash commands */
@@ -461,6 +473,8 @@ struct spinand_dirmap {* this die. Only required if your chip exposes several dies * @cur_target: currently selected target/die * @eccinfo: on-die ECC information +* @protocol: SPI IO protocol in operation. Update on successful transition into +* a different SPI IO protocol. * @cfg_cache: config register cache. One entry per die * @databuf: bounce buffer for data * @oobbuf: bounce buffer for OOB data
@@ -492,6 +506,8 @@ struct spinand_device {struct spinand_ecc_info eccinfo; +enum spinand_protocol protocol; +u8 *cfg_cache;u8 *databuf;u8 *oobbuf;
Signature · 1 line
2.25.1
Re: [PATCH v3 03/17] mtd: spinand: Add enum spinand_protocol to indicate current SPI IO mode
Quoted 8 lines · On Sat, 1 Jan 2022 13:12:36 +0530 Apurva Nandan <[email protected]> wrote:
include/linux/mtd/spinand.h +0
-0@@ -194,6 +194,18 @@SPI_MEM_OP_NO_DUMMY , \ SPI_MEM_OP_DATA_OUT_DTR(len, buf, 8))
include/linux/mtd/spinand.h line 206
Quoted 10 lines
include/linux/mtd/spinand.h +10
-0+/** +* enum spinand_protocol - List of SPI protocols to denote the op protocol and +* SPI NAND flash IO modes. +*/ +enum spinand_protocol { +SPINAND_1S_1S_1S , +SPINAND_2S_2S_2S , +SPINAND_4S_4S_4S , +SPINAND_8S_8S_8S , +SPINAND_8D_8D_8D ,
I'd just name those SPINAND_PROTO_<width><S or D> since what really matters is the command cycle.
PATCH v3 4/17 mtd: spinand: Rename 'op_templates' to 'data_ops'
In reply to Apurva NandanManufacturers have been deviating from the standard SPI operations for NAND flashes. There have been variations in non-page read/write instructions too. Additionally, operations, including non-page r/w ops, vary when flash is in different SPI mode, eg. Octal DTR.
To avoid live-patching in hot-paths or vendor-specific adjustment, it is better to have a set of operation templates and variants for non-page read/write operations as well. These would get initialized at the probe time or when flash changes modes. These would be called 'ctrl_ops'.
To make code better understandable, create two types of op templates which are: data_ops and ctrl_ops. Reason for having two different type of templates is the difference in their use cases i.e. it is possible to have ops of different protocol for read/write/update simulatneously in the data_ops, but all the ops in the ctrl_ops follow same protocol.
Rename op_templates to data_ops, and the ctrl_ops would be introduced in later commits.
--- drivers/mtd/nand/spi/core.c | 32 ++++++++++++++++---------------- include/linux/mtd/spinand.h | 26 +++++++++++++------------- 2 files changed, 29 insertions(+), 29 deletions(-)
drivers/mtd/nand/spi/core.c +16 -16
@@ -184,9 +184,9 @@ static int spinand_init_quad_enable(struct spinand_device *spinand)if (!(spinand->flags &SPINAND_HAS_QE_BIT ))return 0 ; -if (spinand->op_templates.read_cache->data.buswidth ==4 || - spinand->op_templates.write_cache->data.buswidth ==4 || - spinand->op_templates.update_cache->data.buswidth ==4 ) +if (spinand->data_ops.read_cache->data.buswidth ==4 || + spinand->data_ops.write_cache->data.buswidth ==4 || + spinand->data_ops.update_cache->data.buswidth ==4 ) enable = true; return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE,
@@ -849,7 +849,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,/* The plane number is passed in MSB just above the column address */ info.offset = plane <<fls (nand->memorg.pagesize); - info.op_tmpl = *spinand->op_templates.update_cache; + info.op_tmpl = *spinand->data_ops.update_cache; desc =devm_spi_mem_dirmap_create (&spinand->spimem->spi->dev, spinand->spimem, &info);if (IS_ERR (desc))
@@ -857,7 +857,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand, spinand->dirmaps[plane].wdesc = desc; - info.op_tmpl = *spinand->op_templates.read_cache; + info.op_tmpl = *spinand->data_ops.read_cache; desc =devm_spi_mem_dirmap_create (&spinand->spimem->spi->dev, spinand->spimem, &info);if (IS_ERR (desc))
@@ -976,8 +976,8 @@ static void spinand_manufacturer_cleanup(struct spinand_device *spinand) }static const struct spi_mem_op * -spinand_select_op_variant (struct spinand_device *spinand, -const struct spinand_op_variants *variants) +spinand_select_data_op_variant (struct spinand_device *spinand, +const struct spinand_op_variants *variants) {struct nand_device *nand =spinand_to_nand (spinand);unsigned int i;
@@ -1050,23 +1050,23 @@ int spinand_match_and_init(struct spinand_device *spinand, spinand->id.len =1 + table[i].devid.len; spinand->select_target = table[i].select_target; - op =spinand_select_op_variant (spinand, - info->op_variants.read_cache); + op =spinand_select_data_op_variant (spinand, + info->data_ops_variants.read_cache);if (!op)return -ENOTSUPP ; - spinand->op_templates.read_cache = op; + spinand->data_ops.read_cache = op; - op =spinand_select_op_variant (spinand, - info->op_variants.write_cache); + op =spinand_select_data_op_variant (spinand, + info->data_ops_variants.write_cache);if (!op)return -ENOTSUPP ; - spinand->op_templates.write_cache = op; + spinand->data_ops.write_cache = op; - op =spinand_select_op_variant (spinand, - info->op_variants.update_cache); - spinand->op_templates.update_cache = op; + op =spinand_select_data_op_variant (spinand, + info->data_ops_variants.update_cache); + spinand->data_ops.update_cache = op;return 0 ; }
include/linux/mtd/spinand.h +13 -13
@@ -391,10 +391,10 @@ struct spinand_ondie_ecc_conf {* @memorg: memory organization * @eccreq: ECC requirements * @eccinfo: on-die ECC info -* @op_variants: operations variants -* @op_variants.read_cache: variants of the read-cache operation -* @op_variants.write_cache: variants of the write-cache operation -* @op_variants.update_cache: variants of the update-cache operation +* @data_ops_variants: operations variants for page read/writes +* @data_ops_variants.read_cache: variants of the read-cache operation +* @data_ops_variants.write_cache: variants of the write-cache operation +* @data_ops_variants.update_cache: variants of the update-cache operation * @select_target: function used to select a target/die. Required only for * multi-die chips *
@@ -412,7 +412,7 @@ struct spinand_info {const struct spinand_op_variants *read_cache;const struct spinand_op_variants *write_cache;const struct spinand_op_variants *update_cache; - } op_variants; + } data_ops_variants;int (*select_target)(struct spinand_device *spinand,unsigned int target); };
@@ -440,14 +440,14 @@ struct spinand_info {#define SPINAND_SELECT_TARGET (__func) \ .select_target = __func, -#define SPINAND_INFO (__model, __id, __memorg, __eccreq, __op_variants, \ - __flags, ...) \ +#define SPINAND_INFO (__model, __id, __memorg, __eccreq, \ + __data_ops_variants, __flags, ...) \ { \ .model = __model, \ .devid = __id, \ .memorg = __memorg, \ .eccreq = __eccreq, \ - .op_variants = __op_variants, \ + .data_ops_variants = __data_ops_variants, \ .flags = __flags, \ __VA_ARGS__ \ }
@@ -464,10 +464,10 @@ struct spinand_dirmap {* @lock: lock used to serialize accesses to the NAND * @id: NAND ID as returned by READ_ID * @flags: NAND flags -* @op_templates: various SPI mem op templates -* @op_templates.read_cache: read cache op template -* @op_templates.write_cache: write cache op template -* @op_templates.update_cache: update cache op template +* @data_ops: various SPI mem op templates for reading and writing on pages +* @data_ops.read_cache: read cache op template +* @data_ops.write_cache: write cache op template +* @data_ops.update_cache: update cache op template * @select_target: select a specific target/die. Usually called before sending * a command addressing a page or an eraseblock embedded in * this die. Only required if your chip exposes several dies
@@ -496,7 +496,7 @@ struct spinand_device {const struct spi_mem_op *read_cache;const struct spi_mem_op *write_cache;const struct spi_mem_op *update_cache; - } op_templates; + } data_ops;struct spinand_dirmap *dirmaps;
Signature · 1 line
2.25.1
Re: [PATCH v3 04/17] mtd: spinand: Rename 'op_templates' to 'data_ops'
Quoted 11 lines · On Sat, 1 Jan 2022 13:12:37 +0530 Apurva Nandan <[email protected]> wrote:
Manufacturers have been deviating from the standard SPI operations for NAND flashes. There have been variations in non-page read/write instructions too. Additionally, operations, including non-page r/w ops, vary when flash is in different SPI mode, eg. Octal DTR.
To avoid live-patching in hot-paths or vendor-specific adjustment, it is better to have a set of operation templates and variants for non-page read/write operations as well. These would get initialized at the probe time or when flash changes modes. These would be called '
ctrl_ops'.
Quoted 8 lines
To make code better understandable, create two types of op templates which are:
data_opsandctrl_ops. Reason for having two different type of templates is the difference in their use cases i.e. it is possible to have ops of different protocol for read/write/update simulatneously in thedata_ops, but all the ops in thectrl_opsfollow same protocol.Rename
op_templatestodata_ops, and thectrl_opswould be introduced in later commits.
Didn't read till the end, but I don't see any overlap between the control and data operations in the first half of this series, so I'm wondering if the split is actually necessary.
PATCH v3 5/17 mtd: spinand: Define ctrl_ops for non-page read/write op templates
In reply to Apurva Nandan'ctrl_ops' are op templates for non-page read/write operations, which are: reset, get_feature, set_feature, write_enable, block_erase, page_read and program_execute ops. The 'ctrl_ops' struct contains in it op templates for each of this op, as well as enum spinand_protocol denoting protocol of all these ops.
We require these new op templates because of deviation in standard SPINAND ops by manufacturers and also due to changes when there is a change in SPI protocol/mode. This prevents the core from live-patching and vendor-specific adjustments in ops.
Define 'ctrl_ops', add macros to initialize it and add it in spinand_device.
--- include/linux/mtd/spinand.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
include/linux/mtd/spinand.h +33 -0
@@ -356,6 +356,35 @@ struct spinand_op_variants { sizeof(struct spi_mem_op), \ } +struct spinand_ctrl_ops { +const struct { +struct spi_mem_op reset; +struct spi_mem_op get_feature; +struct spi_mem_op set_feature; +struct spi_mem_op write_enable; +struct spi_mem_op block_erase; +struct spi_mem_op page_read; +struct spi_mem_op program_execute; + } ops; +const enum spinand_protocol protocol; +}; + +#define SPINAND_CTRL_OPS (__protocol, __reset, __get_feature, __set_feature, \ + __write_enable, __block_erase, __page_read, \ + __program_execute) \ + { \ + .ops = { \ + .reset = __reset, \ + .get_feature = __get_feature, \ + .set_feature = __set_feature, \ + .write_enable = __write_enable, \ + .block_erase = __block_erase, \ + .page_read = __page_read, \ + .program_execute = __program_execute, \ + }, \ + .protocol = __protocol, \ + } +/** * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND * chip
@@ -468,6 +497,8 @@ struct spinand_dirmap {* @data_ops.read_cache: read cache op template * @data_ops.write_cache: write cache op template * @data_ops.update_cache: update cache op template +* @ctrl_ops: various SPI mem op templates for handling the flash device, i.e. +* non page-read/write ops. * @select_target: select a specific target/die. Usually called before sending * a command addressing a page or an eraseblock embedded in * this die. Only required if your chip exposes several dies
@@ -498,6 +529,8 @@ struct spinand_device {const struct spi_mem_op *update_cache; } data_ops; +const struct spinand_ctrl_ops *ctrl_ops; +struct spinand_dirmap *dirmaps; int (*select_target)(struct spinand_device *spinand,
Signature · 1 line
2.25.1
Re: [PATCH v3 05/17] mtd: spinand: Define ctrl_ops for non-page read/write op templates
Quoted 28 lines · On Sat, 1 Jan 2022 13:12:38 +0530 Apurva Nandan <[email protected]> wrote:
'
ctrl_ops' are op templates for non-page read/write operations, which are: reset,get_feature,set_feature,write_enable,block_erase,page_readandprogram_executeops. The 'ctrl_ops' struct contains in it op templates for each of this op, as well as enumspinand_protocoldenoting protocol of all these ops.We require these new op templates because of deviation in standard SPINAND ops by manufacturers and also due to changes when there is a change in SPI protocol/mode. This prevents the core from live-patching and vendor-specific adjustments in ops.
Define '
ctrl_ops', add macros to initialize it and add it inspinand_device.Signed-off-by Apurva Nandan <[email protected]>--- include/linux/mtd/spinand.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)include/linux/mtd/spinand.h +1
-0@@ -356,6 +356,35 @@ struct spinand_op_variants { sizeof(struct spi_mem_op), \ } +struct spinand_ctrl_ops {
include/linux/mtd/spinand.h struct spinand_op_variants { · line 369
Quoted 10 lines
include/linux/mtd/spinand.h +10
-0+const struct { +struct spi_mem_op reset; +struct spi_mem_op get_feature; +struct spi_mem_op set_feature; +struct spi_mem_op write_enable; +struct spi_mem_op block_erase; +struct spi_mem_op page_read; +struct spi_mem_op program_execute; + } ops; +const enum spinand_protocol protocol;
Do you really need that protocol field?
Quoted 31 lines
include/linux/mtd/spinand.h +20
-0+}; + +#define SPINAND_CTRL_OPS (__protocol, __reset, __get_feature, __set_feature, \ + __write_enable, __block_erase, __page_read, \ + __program_execute) \ + { \ + .ops = { \ + .reset = __reset, \ + .get_feature = __get_feature, \ + .set_feature = __set_feature, \ + .write_enable = __write_enable, \ + .block_erase = __block_erase, \ + .page_read = __page_read, \ + .program_execute = __program_execute, \ + }, \ + .protocol = __protocol, \ + } +/** * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND * chip @@ -468,6 +497,8 @@ struct spinand_dirmap {* @data_ops.read_cache: read cache op template * @data_ops.write_cache: write cache op template * @data_ops.update_cache: update cache op template +* @ctrl_ops: various SPI mem op templates for handling the flash device, i.e. +* non page-read/write ops. * @select_target: select a specific target/die. Usually called before sending * a command addressing a page or an eraseblock embedded in * this die. Only required if your chip exposes several dies
include/linux/mtd/spinand.h struct spinand_device { · line 533
Quoted 5 lines
include/linux/mtd/spinand.h +2
-0@@ -498,6 +529,8 @@ struct spinand_device {const struct spi_mem_op *update_cache; } data_ops; +const struct spinand_ctrl_ops *ctrl_ops; +
Okay, I had something slightly different in mind. First, I'd put all templates in a struct:
struct spinand_op_templates {
const struct spi_mem_op *read_cache;
const struct spi_mem_op *write_cache;
const struct spi_mem_op *update_cache;
const struct spi_mem_op *reset;
const struct spi_mem_op *get_feature;
const struct spi_mem_op *set_feature;
const struct spi_mem_op *write_enable;
const struct spi_mem_op *block_erase;
const struct spi_mem_op *page_load;
const struct spi_mem_op *program_execute;
};Then, at the spinand level, I'd define an array of templates:
enum spinand_protocol {
SPINAND_1S_1S_1S,
SPINAND_2S_2S_2S,
SPINAND_4S_4S_4S,
SPINAND_8S_8S_8S,
SPINAND_8D_8D_8D,
SPINAND_NUM_PROTOCOLS,
};struct spinand_device {
...
enum spinand_protocol protocol;
const struct spinand_op_templates *op_templates[SPINAND_NUM_PROTOCOLS];
...
};This way, you can easily pick the right set of operations based on the protocol/mode you're in:
#define spinand_get_op_template(spinand, opname) \ ((spinand)->op_templates[(spinand)->protocol]->opname)
static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
{
struct spi_mem_op op = *spinand_get_op_template(spinand, get_feature);
int ret;
...
}Re: [PATCH v3 05/17] mtd: spinand: Define ctrl_ops for non-page read/write op templates
Quoted 4 lines · On Mon, 3 Jan 2022 11:01:07 +0100 Boris Brezillon…
struct spinand_device {
...
enum spinand_protocol protocol;
const struct spinand_op_templates *op_templates[SPINAND_NUM_PROTOCOLS];My bad, it should be:
struct spinand_op_templates op_templates[SPINAND_NUM_PROTOCOLS];
since those templates get populated dynamically at probe time based on what the flash and the controller support.
Quoted 3 lines
... };
Re: [PATCH v3 05/17] mtd: spinand: Define ctrl_ops for non-page read/write op templates
In reply to Boris BrezillonHi Boris,
Quoted 119 lines · Boris Brezillon
Quoted 28 lines · On Sat, 1 Jan 2022 13:12:38 +0530 Apurva Nandan <[email protected]> wrote:
'
ctrl_ops' are op templates for non-page read/write operations, which are: reset,get_feature,set_feature,write_enable,block_erase,page_readandprogram_executeops. The 'ctrl_ops' struct contains in it op templates for each of this op, as well as enumspinand_protocoldenoting protocol of all these ops.We require these new op templates because of deviation in standard SPINAND ops by manufacturers and also due to changes when there is a change in SPI protocol/mode. This prevents the core from live-patching and vendor-specific adjustments in ops.
Define '
ctrl_ops', add macros to initialize it and add it inspinand_device.Signed-off-by Apurva Nandan <[email protected]>--- include/linux/mtd/spinand.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)include/linux/mtd/spinand.h +1
-0@@ -356,6 +356,35 @@ struct spinand_op_variants { sizeof(struct spi_mem_op), \ } +struct spinand_ctrl_ops {include/linux/mtd/spinand.h struct spinand_op_variants { · line 369
Quoted 10 lines
include/linux/mtd/spinand.h +10
-0+const struct { +struct spi_mem_op reset; +struct spi_mem_op get_feature; +struct spi_mem_op set_feature; +struct spi_mem_op write_enable; +struct spi_mem_op block_erase; +struct spi_mem_op page_read; +struct spi_mem_op program_execute; + } ops; +const enum spinand_protocol protocol;Do you really need that protocol field?
Quoted 31 lines
include/linux/mtd/spinand.h +20
-0+}; + +#define SPINAND_CTRL_OPS (__protocol, __reset, __get_feature, __set_feature, \ + __write_enable, __block_erase, __page_read, \ + __program_execute) \ + { \ + .ops = { \ + .reset = __reset, \ + .get_feature = __get_feature, \ + .set_feature = __set_feature, \ + .write_enable = __write_enable, \ + .block_erase = __block_erase, \ + .page_read = __page_read, \ + .program_execute = __program_execute, \ + }, \ + .protocol = __protocol, \ + } +/** * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND * chip @@ -468,6 +497,8 @@ struct spinand_dirmap {* @data_ops.read_cache: read cache op template * @data_ops.write_cache: write cache op template * @data_ops.update_cache: update cache op template +* @ctrl_ops: various SPI mem op templates for handling the flash device, i.e. +* non page-read/write ops. * @select_target: select a specific target/die. Usually called before sending * a command addressing a page or an eraseblock embedded in * this die. Only required if your chip exposes several dies include/linux/mtd/spinand.h struct spinand_device { · line 533
Quoted 5 lines
include/linux/mtd/spinand.h +2
-0@@ -498,6 +529,8 @@ struct spinand_device {const struct spi_mem_op *update_cache; } data_ops; +const struct spinand_ctrl_ops *ctrl_ops; +Okay, I had something slightly different in mind. First, I'd put all templates in a struct:
struct spinand_op_templates { const struct spi_mem_op *read_cache; const struct spi_mem_op *write_cache; const struct spi_mem_op *update_cache; const struct spi_mem_op *reset; const struct spi_mem_op *get_feature; const struct spi_mem_op *set_feature; const struct spi_mem_op *write_enable; const struct spi_mem_op *block_erase; const struct spi_mem_op *page_load; const struct spi_mem_op *program_execute; };Then, at the spinand level, I'd define an array of templates:
enum spinand_protocol { SPINAND_1S_1S_1S, SPINAND_2S_2S_2S, SPINAND_4S_4S_4S, SPINAND_8S_8S_8S, SPINAND_8D_8D_8D, SPINAND_NUM_PROTOCOLS, };struct spinand_device { ... enum spinand_protocol protocol; const struct spinand_op_templates *op_templates[SPINAND_NUM_PROTOCOLS]; ... };This way, you can easily pick the right set of operations based on the protocol/mode you're in:
#define spinand_get_op_template(spinand, opname) \ ((spinand)->op_templates[(spinand)->protocol]->opname)
Quoted 7 lines
static int
spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val){ struct spi_mem_op op = *spinand_get_op_template(spinand, get_feature); int ret; ... }
I find a couple of issues with this method,
1. read_cache, write_cache, update_cache op templates don't fit well with the other non-data ops, as these data ops are used to create a dirmap, and that can be done only once at probe time. Hence, there is a different mechanism of selecting of data ops and non-data ops. Hence, this division in the op templates struct as data_ops and ctrl_ops is required. Currently, the core only supports using a single protocol for data ops, chosen at the time of probing.
2. If we use this single op_templates struct, I can't think of any good way to initialize these in the manufacturers driver (winbond.c), refer to 17th patch in this series. Could you please suggest a macro implementation also for winbond.c with the suggested op_templates struct.
Thanks,
Apurva Nandan
Re: [PATCH v3 05/17] mtd: spinand: Define ctrl_ops for non-page read/write op templates
Hi Apurva,
Quoted 113 lines · On Tue, 15 Feb 2022 21:03:52 +0530 Apurva Nandan <[email protected]> wrote:
Hi Boris,
Quoted 110 lines · Boris Brezillon
Quoted 28 lines · On Sat, 1 Jan 2022 13:12:38 +0530 Apurva Nandan <[email protected]> wrote:
'
ctrl_ops' are op templates for non-page read/write operations, which are: reset,get_feature,set_feature,write_enable,block_erase,page_readandprogram_executeops. The 'ctrl_ops' struct contains in it op templates for each of this op, as well as enumspinand_protocoldenoting protocol of all these ops.We require these new op templates because of deviation in standard SPINAND ops by manufacturers and also due to changes when there is a change in SPI protocol/mode. This prevents the core from live-patching and vendor-specific adjustments in ops.
Define '
ctrl_ops', add macros to initialize it and add it inspinand_device.Signed-off-by Apurva Nandan <[email protected]>--- include/linux/mtd/spinand.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)include/linux/mtd/spinand.h +1
-0@@ -356,6 +356,35 @@ struct spinand_op_variants { sizeof(struct spi_mem_op), \ } +struct spinand_ctrl_ops {include/linux/mtd/spinand.h struct spinand_op_variants { · line 369
Quoted 10 lines
include/linux/mtd/spinand.h +10
-0+const struct { +struct spi_mem_op reset; +struct spi_mem_op get_feature; +struct spi_mem_op set_feature; +struct spi_mem_op write_enable; +struct spi_mem_op block_erase; +struct spi_mem_op page_read; +struct spi_mem_op program_execute; + } ops; +const enum spinand_protocol protocol;Do you really need that protocol field?
Quoted 31 lines
include/linux/mtd/spinand.h +20
-0+}; + +#define SPINAND_CTRL_OPS (__protocol, __reset, __get_feature, __set_feature, \ + __write_enable, __block_erase, __page_read, \ + __program_execute) \ + { \ + .ops = { \ + .reset = __reset, \ + .get_feature = __get_feature, \ + .set_feature = __set_feature, \ + .write_enable = __write_enable, \ + .block_erase = __block_erase, \ + .page_read = __page_read, \ + .program_execute = __program_execute, \ + }, \ + .protocol = __protocol, \ + } +/** * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND * chip @@ -468,6 +497,8 @@ struct spinand_dirmap {* @data_ops.read_cache: read cache op template * @data_ops.write_cache: write cache op template * @data_ops.update_cache: update cache op template +* @ctrl_ops: various SPI mem op templates for handling the flash device, i.e. +* non page-read/write ops. * @select_target: select a specific target/die. Usually called before sending * a command addressing a page or an eraseblock embedded in * this die. Only required if your chip exposes several dies include/linux/mtd/spinand.h struct spinand_device { · line 533
Quoted 5 lines
include/linux/mtd/spinand.h +2
-0@@ -498,6 +529,8 @@ struct spinand_device {const struct spi_mem_op *update_cache; } data_ops; +const struct spinand_ctrl_ops *ctrl_ops; +Okay, I had something slightly different in mind. First, I'd put all templates in a struct:
struct spinand_op_templates { const struct spi_mem_op *read_cache; const struct spi_mem_op *write_cache; const struct spi_mem_op *update_cache; const struct spi_mem_op *reset; const struct spi_mem_op *get_feature; const struct spi_mem_op *set_feature; const struct spi_mem_op *write_enable; const struct spi_mem_op *block_erase; const struct spi_mem_op *page_load; const struct spi_mem_op *program_execute; };Then, at the spinand level, I'd define an array of templates:
enum spinand_protocol { SPINAND_1S_1S_1S, SPINAND_2S_2S_2S, SPINAND_4S_4S_4S, SPINAND_8S_8S_8S, SPINAND_8D_8D_8D, SPINAND_NUM_PROTOCOLS, };struct spinand_device { ... enum spinand_protocol protocol; const struct spinand_op_templates *op_templates[SPINAND_NUM_PROTOCOLS];
It should probably be
struct spinand_op_templates op_templates[SPINAND_NUM_PROTOCOLS];
with the spinand_op_templates struct defined as:
struct spinand_op_templates {
struct spi_mem_op read_cache;
struct spi_mem_op write_cache;
struct spi_mem_op update_cache;
struct spi_mem_op reset;
struct spi_mem_op get_feature;
struct spi_mem_op set_feature;
struct spi_mem_op write_enable;
struct spi_mem_op block_erase;
struct spi_mem_op page_load;
struct spi_mem_op program_execute;
};
so the NAND framework can populate these ops.
Or maybe even better, define an enum that contains all the ops:
enum spinand_op_id {
SPI_NAND_OP_READ_CACHE,
SPI_NAND_OP_WRITE_CACHE,
SPI_NAND_OP_UPDATE_CACHE,
SPI_NAND_OP_RESET,
...
SPI_NAND_NUM_OPS,
};
struct spinand_device {
...
enum spinand_protocol protocol;
struct spi_mem_op op_templates[SPINAND_NUM_PROTOCOLS][SPI_NAND_NUM_OPS];
...
};Quoted 15 lines
Quoted 7 lines
This way, you can easily pick the right set of operations based on the protocol/mode you're in:
#define spinand_get_op_template(spinand, opname) \ ((spinand)->op_templates[(spinand)->protocol]->opname)Quoted 7 lines
static int
spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val){ struct spi_mem_op op = *spinand_get_op_template(spinand, get_feature); int ret; ... }
Quoted 7 lines
I find a couple of issues with this method,
1.
read_cache,write_cache,update_cacheop templates don't fit well with the other non-data ops, as
these data ops are used to create a dirmap, and that can be done only once at probe time. Hence, there
is a different mechanism of selecting of data ops and non-data ops.
Not sure I see why this is a problem. You can populate data-ops for all modes, and pick the one that provides the best perfs when you create the dirmap (which should really be at the end of the probe, if it's not already).
Quoted 4 lines
Hence, this division in the op templates
struct asdata_opsandctrl_opsis required. Currently, the core only supports using a single protocol for
data ops, chosen at the time of probing.
Again, I don't see why you need to differentiate the control and data ops when populating this table. Those are just operations the NAND supports, and the data operations is just a subset.
Quoted 6 lines
2. If we use this single
op_templatesstruct, I can't think of any good way to initialize these in the
manufacturers driver (winbond.c), refer to 17th patch in this series. Could you please suggest a macro
implementation also forwinbond.cwith the suggestedop_templatesstruct.
First replace the op_variants field by something more generic:
struct spinand_info {
...
const struct spinand_op_variants **ops_variants;
...
};
#define SPINAND_OP_VARIANTS(_id, ...) \
[SPI_NAND_OP_ ## _id] = { __VA_ARGS__ }#define SPINAND_OPS_VARIANTS(name, ...)
const struct spinand_op_variants name[]{
__VA_ARGS__,
};#define SPINAND_INFO_OPS_VARIANTS(defs)
.ops_variants = defs
...
static SPINAND_OPS_VARIANTS(w35n01jw_ops_variants,
SPINAND_OP_VARIANTS(READ_CACHE, SPINAND_PAGE_READ_FROM_CACHE_OCTALIO_DTR_OP(0, 24, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ...)), SPINAND_OP_VARIANTS(WRITE_CACHE, SPINAND_PROG_LOAD_OCTALIO_DTR(true, 0, NULL, 0), SPINAND_PROG_LOAD(true, 0, NULL, 0)), ... SPINAND_OP_VARIANTS(RESET, SPINAND_RESET_OP_OCTAL_DTR, SPINAND_RESET_OP, ... );
...
SPINAND_INFO("W35N01JW",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdc),
NAND_MEMORG(1, 4096, 128, 64, 512, 20, 1, 1, 1),
NAND_ECCREQ(1, 512),
SPINAND_HAS_OCTAL_DTR_BIT | SPINAND_HAS_CR_FEAT_BIT,
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
SPINAND_INFO_OPS_VARIANTS(&w35n01jw_ops_variants)),You also need to adjust spinand_match_and_init() to account for this new layout and put each template op in the right subset based on op.cmd.width and op.cmd.dtr.
Regards,
Boris
Re: [PATCH v3 05/17] mtd: spinand: Define ctrl_ops for non-page read/write op templates
Hi Boris,
Quoted 230 lines · Boris Brezillon
Hi Apurva,
Quoted 113 lines · On Tue, 15 Feb 2022 21:03:52 +0530 Apurva Nandan<[email protected]> wrote:
Hi Boris,
Quoted 110 lines · Boris Brezillon
Quoted 28 lines · On Sat, 1 Jan 2022 13:12:38 +0530 Apurva Nandan<[email protected]> wrote:
'
ctrl_ops' are op templates for non-page read/write operations, which are: reset,get_feature,set_feature,write_enable,block_erase,page_readandprogram_executeops. The 'ctrl_ops' struct contains in it op templates for each of this op, as well as enumspinand_protocoldenoting protocol of all these ops.We require these new op templates because of deviation in standard SPINAND ops by manufacturers and also due to changes when there is a change in SPI protocol/mode. This prevents the core from live-patching and vendor-specific adjustments in ops.
Define '
ctrl_ops', add macros to initialize it and add it inspinand_device.Signed-off-by Apurva Nandan<[email protected]>--- include/linux/mtd/spinand.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)include/linux/mtd/spinand.h +1
-0@@ -356,6 +356,35 @@ struct spinand_op_variants { sizeof(struct spi_mem_op), \ } +struct spinand_ctrl_ops {include/linux/mtd/spinand.h struct spinand_op_variants { · line 369
Quoted 10 lines
include/linux/mtd/spinand.h +10
-0+const struct { +struct spi_mem_op reset; +struct spi_mem_op get_feature; +struct spi_mem_op set_feature; +struct spi_mem_op write_enable; +struct spi_mem_op block_erase; +struct spi_mem_op page_read; +struct spi_mem_op program_execute; + } ops; +const enum spinand_protocol protocol;Do you really need that protocol field?
Quoted 31 lines
include/linux/mtd/spinand.h +20
-0+}; + +#define SPINAND_CTRL_OPS (__protocol, __reset, __get_feature, __set_feature, \ + __write_enable, __block_erase, __page_read, \ + __program_execute) \ + { \ + .ops = { \ + .reset = __reset, \ + .get_feature = __get_feature, \ + .set_feature = __set_feature, \ + .write_enable = __write_enable, \ + .block_erase = __block_erase, \ + .page_read = __page_read, \ + .program_execute = __program_execute, \ + }, \ + .protocol = __protocol, \ + } +/** * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND * chip @@ -468,6 +497,8 @@ struct spinand_dirmap {* @data_ops.read_cache: read cache op template * @data_ops.write_cache: write cache op template * @data_ops.update_cache: update cache op template +* @ctrl_ops: various SPI mem op templates for handling the flash device, i.e. +* non page-read/write ops. * @select_target: select a specific target/die. Usually called before sending * a command addressing a page or an eraseblock embedded in * this die. Only required if your chip exposes several dies include/linux/mtd/spinand.h struct spinand_device { · line 533
Quoted 5 lines
include/linux/mtd/spinand.h +2
-0@@ -498,6 +529,8 @@ struct spinand_device {const struct spi_mem_op *update_cache; } data_ops; +const struct spinand_ctrl_ops *ctrl_ops; +Okay, I had something slightly different in mind. First, I'd put all templates in a struct:
struct spinand_op_templates { const struct spi_mem_op *read_cache; const struct spi_mem_op *write_cache; const struct spi_mem_op *update_cache; const struct spi_mem_op *reset; const struct spi_mem_op *get_feature; const struct spi_mem_op *set_feature; const struct spi_mem_op *write_enable; const struct spi_mem_op *block_erase; const struct spi_mem_op *page_load; const struct spi_mem_op *program_execute; };Then, at the spinand level, I'd define an array of templates:
enum spinand_protocol { SPINAND_1S_1S_1S, SPINAND_2S_2S_2S, SPINAND_4S_4S_4S, SPINAND_8S_8S_8S, SPINAND_8D_8D_8D, SPINAND_NUM_PROTOCOLS, };struct spinand_device { ... enum spinand_protocol protocol; const struct spinand_op_templates *op_templates[SPINAND_NUM_PROTOCOLS];It should probably be
struct spinand_op_templates op_templates[SPINAND_NUM_PROTOCOLS];with the
spinand_op_templatesstruct defined as:struct
spinand_op_templates{
structspi_mem_opread_cache;
structspi_mem_opwrite_cache;
structspi_mem_opupdate_cache;
structspi_mem_opreset;
structspi_mem_opget_feature;
structspi_mem_opset_feature;
structspi_mem_opwrite_enable;
structspi_mem_opblock_erase;
structspi_mem_oppage_load;
structspi_mem_opprogram_execute;
};so the NAND framework can populate these ops.
Or maybe even better, define an enum that contains all the ops:
enum
spinand_op_id{
SPI_NAND_OP_READ_CACHE,
SPI_NAND_OP_WRITE_CACHE,
SPI_NAND_OP_UPDATE_CACHE,
SPI_NAND_OP_RESET,
...
SPI_NAND_NUM_OPS,
};struct spinand_device { ... enum spinand_protocol protocol; struct spi_mem_op op_templates[SPINAND_NUM_PROTOCOLS][SPI_NAND_NUM_OPS]; ... };Quoted 13 lines
Quoted 6 lines
This way, you can easily pick the right set of operations based on the protocol/mode you're in:
#define spinand_get_op_template(spinand, opname) \ ((spinand)->op_templates[(spinand)->protocol]->opname)Quoted 7 lines
static int
spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val){ struct spi_mem_op op = *spinand_get_op_template(spinand, get_feature); int ret; ... }Quoted 7 lines
I find a couple of issues with this method,
1.
read_cache,write_cache,update_cacheop templates don't fit well with the other non-data ops, as
these data ops are used to create a dirmap, and that can be done only once at probe time. Hence, there
is a different mechanism of selecting of data ops and non-data ops.Not sure I see why this is a problem. You can populate data-ops for all modes, and pick the one that provides the best perfs when you create the dirmap (which should really be at the end of the probe, if it's not already).
Quoted 4 lines
Hence, this division in the op templates
struct asdata_opsandctrl_opsis required. Currently, the core only supports using a single protocol for
data ops, chosen at the time of probing.Again, I don't see why you need to differentiate the control and data ops when populating this table. Those are just operations the NAND supports, and the data operations is just a subset.
Quoted 5 lines
2. If we use this single
op_templatesstruct, I can't think of any good way to initialize these in the
manufacturers driver (winbond.c), refer to 17th patch in this series. Could you please suggest a macro
implementation also forwinbond.cwith the suggestedop_templatesstruct.First replace the
op_variantsfield by something more generic:struct
spinand_info{
...const struct spinand_op_variants **ops_variants;...
};#define SPINAND_OP_VARIANTS(_id, ...) \ [SPI_NAND_OP_ ## _id] = { __VA_ARGS__ }#define SPINAND_OPS_VARIANTS(name, ...) const struct spinand_op_variants name[]{ __VA_ARGS__, };#define
SPINAND_INFO_OPS_VARIANTS(defs)
.ops_variants = defs...
static SPINAND_OPS_VARIANTS(w35n01jw_ops_variants,
SPINAND_OP_VARIANTS(READ_CACHE, SPINAND_PAGE_READ_FROM_CACHE_OCTALIO_DTR_OP(0, 24, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ...)), SPINAND_OP_VARIANTS(WRITE_CACHE, SPINAND_PROG_LOAD_OCTALIO_DTR(true, 0, NULL, 0), SPINAND_PROG_LOAD(true, 0, NULL, 0)), ... SPINAND_OP_VARIANTS(RESET, SPINAND_RESET_OP_OCTAL_DTR, SPINAND_RESET_OP, ... );
Quoted 1 line
...
I find a issue with this implementation, please give corrective suggestions:
In type of op variant listing, there is no way to specify the protocol of the op in the variants struct itself.
- This will lead to filtering/sorting/searching of ops for finding the protocols in the spinand core while in spinand_match_and_init(), which I don't feel is a good way for protocol based op categorization.
- This would also lead to complexities in cases of mixed mode operations.
- In addition, we can't simply choose the first supported protocol in each op id, as some ops have intendependency of protocol with other ops. This is because non-data ops (like reset, block erase..) cannot be in different protocols at same time, so it would make sense to have some form of protocol based arrangement while listing them.
Quoted 15 lines
SPINAND_INFO("W35N01JW", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdc), NAND_MEMORG(1, 4096, 128, 64, 512, 20, 1, 1, 1), NAND_ECCREQ(1, 512), SPINAND_HAS_OCTAL_DTR_BIT | SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), SPINAND_INFO_OPS_VARIANTS(&w35n01jw_ops_variants)),You also need to adjust
spinand_match_and_init()to account for this new layout and put each template op in the right subset based on op.cmd.width and op.cmd.dtr.Regards,
Boris
Re: [PATCH v3 05/17] mtd: spinand: Define ctrl_ops for non-page read/write op templates
Quoted 60 lines · On Wed, 2 Mar 2022 21:00:55 +0530 Apurva Nandan <[email protected]> wrote:
Quoted 58 lines
Quoted 5 lines
1.
read_cache,write_cache,update_cacheop templates don't fit well with the other non-data ops, as
these data ops are used to create a dirmap, and that can be done only once at probe time. Hence, there
is a different mechanism of selecting of data ops and non-data ops.Not sure I see why this is a problem. You can populate data-ops for all modes, and pick the one that provides the best perfs when you create the dirmap (which should really be at the end of the probe, if it's not already).
Quoted 4 lines
Hence, this division in the op templates
struct asdata_opsandctrl_opsis required. Currently, the core only supports using a single protocol for
data ops, chosen at the time of probing.Again, I don't see why you need to differentiate the control and data ops when populating this table. Those are just operations the NAND supports, and the data operations is just a subset.
Quoted 5 lines
2. If we use this single
op_templatesstruct, I can't think of any good way to initialize these in the
manufacturers driver (winbond.c), refer to 17th patch in this series. Could you please suggest a macro
implementation also forwinbond.cwith the suggestedop_templatesstruct.First replace the
op_variantsfield by something more generic:struct
spinand_info{
...const struct spinand_op_variants **ops_variants;...
};#define SPINAND_OP_VARIANTS(_id, ...) \ [SPI_NAND_OP_ ## _id] = { __VA_ARGS__ }#define SPINAND_OPS_VARIANTS(name, ...) const struct spinand_op_variants name[]{ __VA_ARGS__, };#define
SPINAND_INFO_OPS_VARIANTS(defs)
.ops_variants = defs...
static SPINAND_OPS_VARIANTS(w35n01jw_ops_variants,
SPINAND_OP_VARIANTS(READ_CACHE, SPINAND_PAGE_READ_FROM_CACHE_OCTALIO_DTR_OP(0, 24, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ...)), SPINAND_OP_VARIANTS(WRITE_CACHE, SPINAND_PROG_LOAD_OCTALIO_DTR(true, 0, NULL, 0), SPINAND_PROG_LOAD(true, 0, NULL, 0)), ... SPINAND_OP_VARIANTS(RESET, SPINAND_RESET_OP_OCTAL_DTR, SPINAND_RESET_OP, ... );Quoted 1 line
...
Quoted 8 lines
I find a issue with this implementation, please give corrective suggestions:
In type of op variant listing, there is no way to specify the protocol of the op in the variants struct itself.
- This will lead to filtering/sorting/searching of ops for finding
the protocols in the spinand core
while inspinand_match_and_init(), which I don't feel is a good way
for protocol based op categorization.
You'll have to go over all those operations to check which ones are supported by the controller anyway. And it's not like the classification is complicated since the cmd bus-width+DTR seems to be the discriminant, and it's stored directly in the operation template.
Quoted 2 lines
- This would also lead to complexities in cases of mixed mode
operations.
Not sure what you mean by mixed mode. Are you referring to something like 1S-8D-8D? IIUC, all we care about is the mode used for the cmd cycle. I don't think there are commands to switch between stateless (1S-x[S,D]-x[S,D]) modes (assuming 1S-xD-xD is a thing).
Quoted 7 lines
- In addition, we can't simply choose the first supported protocol
in each op id, as some ops have
intendependency of protocol with other ops. This is because
non-data ops (like reset, block erase..)
cannot be in different protocols at same time, so it would make
sense to have some form of protocol
based arrangement while listing them.
I'm not suggesting to choose the first supported operation and use it unconditionally, but instead choose one operation per stateful mode (1S, 2S, 4S, ..., 8D) and use the appropriate template depending on the mode the flash is currently in.
Re: [PATCH v3 05/17] mtd: spinand: Define ctrl_ops for non-page read/write op templates
In reply to Boris BrezillonHi Boris,
Quoted 206 lines · Boris Brezillon
Hi Apurva,
Quoted 113 lines · On Tue, 15 Feb 2022 21:03:52 +0530 Apurva Nandan <[email protected]> wrote:
Hi Boris,
Quoted 110 lines · Boris Brezillon
Quoted 28 lines · On Sat, 1 Jan 2022 13:12:38 +0530 Apurva Nandan <[email protected]> wrote:
'
ctrl_ops' are op templates for non-page read/write operations, which are: reset,get_feature,set_feature,write_enable,block_erase,page_readandprogram_executeops. The 'ctrl_ops' struct contains in it op templates for each of this op, as well as enumspinand_protocoldenoting protocol of all these ops.We require these new op templates because of deviation in standard SPINAND ops by manufacturers and also due to changes when there is a change in SPI protocol/mode. This prevents the core from live-patching and vendor-specific adjustments in ops.
Define '
ctrl_ops', add macros to initialize it and add it inspinand_device.Signed-off-by Apurva Nandan <[email protected]>--- include/linux/mtd/spinand.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)include/linux/mtd/spinand.h +1
-0@@ -356,6 +356,35 @@ struct spinand_op_variants { sizeof(struct spi_mem_op), \ } +struct spinand_ctrl_ops {include/linux/mtd/spinand.h struct spinand_op_variants { · line 369
Quoted 10 lines
include/linux/mtd/spinand.h +10
-0+const struct { +struct spi_mem_op reset; +struct spi_mem_op get_feature; +struct spi_mem_op set_feature; +struct spi_mem_op write_enable; +struct spi_mem_op block_erase; +struct spi_mem_op page_read; +struct spi_mem_op program_execute; + } ops; +const enum spinand_protocol protocol;Do you really need that protocol field?
Quoted 31 lines
include/linux/mtd/spinand.h +20
-0+}; + +#define SPINAND_CTRL_OPS (__protocol, __reset, __get_feature, __set_feature, \ + __write_enable, __block_erase, __page_read, \ + __program_execute) \ + { \ + .ops = { \ + .reset = __reset, \ + .get_feature = __get_feature, \ + .set_feature = __set_feature, \ + .write_enable = __write_enable, \ + .block_erase = __block_erase, \ + .page_read = __page_read, \ + .program_execute = __program_execute, \ + }, \ + .protocol = __protocol, \ + } +/** * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND * chip @@ -468,6 +497,8 @@ struct spinand_dirmap {* @data_ops.read_cache: read cache op template * @data_ops.write_cache: write cache op template * @data_ops.update_cache: update cache op template +* @ctrl_ops: various SPI mem op templates for handling the flash device, i.e. +* non page-read/write ops. * @select_target: select a specific target/die. Usually called before sending * a command addressing a page or an eraseblock embedded in * this die. Only required if your chip exposes several dies include/linux/mtd/spinand.h struct spinand_device { · line 533
Quoted 5 lines
include/linux/mtd/spinand.h +2
-0@@ -498,6 +529,8 @@ struct spinand_device {const struct spi_mem_op *update_cache; } data_ops; +const struct spinand_ctrl_ops *ctrl_ops; +Okay, I had something slightly different in mind. First, I'd put all templates in a struct:
struct spinand_op_templates { const struct spi_mem_op *read_cache; const struct spi_mem_op *write_cache; const struct spi_mem_op *update_cache; const struct spi_mem_op *reset; const struct spi_mem_op *get_feature; const struct spi_mem_op *set_feature; const struct spi_mem_op *write_enable; const struct spi_mem_op *block_erase; const struct spi_mem_op *page_load; const struct spi_mem_op *program_execute; };Then, at the spinand level, I'd define an array of templates:
enum spinand_protocol { SPINAND_1S_1S_1S, SPINAND_2S_2S_2S, SPINAND_4S_4S_4S, SPINAND_8S_8S_8S, SPINAND_8D_8D_8D, SPINAND_NUM_PROTOCOLS, };struct spinand_device { ... enum spinand_protocol protocol; const struct spinand_op_templates *op_templates[SPINAND_NUM_PROTOCOLS];It should probably be
struct spinand_op_templates op_templates[SPINAND_NUM_PROTOCOLS];with the
spinand_op_templatesstruct defined as:struct
spinand_op_templates{
structspi_mem_opread_cache;
structspi_mem_opwrite_cache;
structspi_mem_opupdate_cache;
structspi_mem_opreset;
structspi_mem_opget_feature;
structspi_mem_opset_feature;
structspi_mem_opwrite_enable;
structspi_mem_opblock_erase;
structspi_mem_oppage_load;
structspi_mem_opprogram_execute;
};so the NAND framework can populate these ops.
Or maybe even better, define an enum that contains all the ops:
enum
spinand_op_id{
SPI_NAND_OP_READ_CACHE,
SPI_NAND_OP_WRITE_CACHE,
SPI_NAND_OP_UPDATE_CACHE,
SPI_NAND_OP_RESET,
...
SPI_NAND_NUM_OPS,
};struct spinand_device { ... enum spinand_protocol protocol; struct spi_mem_op op_templates[SPINAND_NUM_PROTOCOLS][SPI_NAND_NUM_OPS]; ... };Quoted 13 lines
Quoted 6 lines
This way, you can easily pick the right set of operations based on the protocol/mode you're in:
#define spinand_get_op_template(spinand, opname) \ ((spinand)->op_templates[(spinand)->protocol]->opname)Quoted 7 lines
static int
spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val){ struct spi_mem_op op = *spinand_get_op_template(spinand, get_feature); int ret; ... }Quoted 7 lines
I find a couple of issues with this method,
1.
read_cache,write_cache,update_cacheop templates don't fit well with the other non-data ops, as
these data ops are used to create a dirmap, and that can be done only once at probe time. Hence, there
is a different mechanism of selecting of data ops and non-data ops.Not sure I see why this is a problem. You can populate data-ops for all modes, and pick the one that provides the best perfs when you create the dirmap (which should really be at the end of the probe, if it's not already).
Quoted 4 lines
Hence, this division in the op templates
struct asdata_opsandctrl_opsis required. Currently, the core only supports using a single protocol for
data ops, chosen at the time of probing.Again, I don't see why you need to differentiate the control and data ops when populating this table. Those are just operations the NAND supports, and the data operations is just a subset.
Quoted 5 lines
2. If we use this single
op_templatesstruct, I can't think of any good way to initialize these in the
manufacturers driver (winbond.c), refer to 17th patch in this series. Could you please suggest a macro
implementation also forwinbond.cwith the suggestedop_templatesstruct.First replace the
op_variantsfield by something more generic:struct
spinand_info{
...const struct spinand_op_variants **ops_variants;...
};#define SPINAND_OP_VARIANTS(_id, ...) \ [SPI_NAND_OP_ ## _id] = { __VA_ARGS__ }
Quoted 7 lines
#define SPINAND_OPS_VARIANTS(name, ...) const struct spinand_op_variants name[]{ __VA_ARGS__, };#define
SPINAND_INFO_OPS_VARIANTS(defs)
.ops_variants = defs
If we modify these macros, it would require other spinand vendor drivers to change (toshiba, micron, etc). The older macros suit them well, should we go about changing them to this new macro (will require re-testing all of them), or can we keep them unchanged and have new set of macros with different name (please give suggestion for it) for op variants.
Quoted 35 lines
...
static SPINAND_OPS_VARIANTS(w35n01jw_ops_variants,
SPINAND_OP_VARIANTS(READ_CACHE, SPINAND_PAGE_READ_FROM_CACHE_OCTALIO_DTR_OP(0, 24, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ...)), SPINAND_OP_VARIANTS(WRITE_CACHE, SPINAND_PROG_LOAD_OCTALIO_DTR(true, 0, NULL, 0), SPINAND_PROG_LOAD(true, 0, NULL, 0)), ... SPINAND_OP_VARIANTS(RESET, SPINAND_RESET_OP_OCTAL_DTR, SPINAND_RESET_OP, ... );...
SPINAND_INFO("W35N01JW", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdc), NAND_MEMORG(1, 4096, 128, 64, 512, 20, 1, 1, 1), NAND_ECCREQ(1, 512), SPINAND_HAS_OCTAL_DTR_BIT | SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), SPINAND_INFO_OPS_VARIANTS(&w35n01jw_ops_variants)),You also need to adjust
spinand_match_and_init()to account for this new layout and put each template op in the right subset based on op.cmd.width and op.cmd.dtr.Regards,
Boris
Re: [PATCH v3 05/17] mtd: spinand: Define ctrl_ops for non-page read/write op templates
Quoted 57 lines · On Thu, 10 Mar 2022 13:27:06 +0530 Apurva Nandan <[email protected]> wrote:
Quoted 49 lines
Quoted 13 lines
Quoted 6 lines
This way, you can easily pick the right set of operations based on the protocol/mode you're in:
#define spinand_get_op_template(spinand, opname) \ ((spinand)->op_templates[(spinand)->protocol]->opname)Quoted 7 lines
static int
spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val){ struct spi_mem_op op = *spinand_get_op_template(spinand, get_feature); int ret; ... }Quoted 7 lines
I find a couple of issues with this method,
1.
read_cache,write_cache,update_cacheop templates don't fit well with the other non-data ops, as
these data ops are used to create a dirmap, and that can be done only once at probe time. Hence, there
is a different mechanism of selecting of data ops and non-data ops.Not sure I see why this is a problem. You can populate data-ops for all modes, and pick the one that provides the best perfs when you create the dirmap (which should really be at the end of the probe, if it's not already).
Quoted 4 lines
Hence, this division in the op templates
struct asdata_opsandctrl_opsis required. Currently, the core only supports using a single protocol for
data ops, chosen at the time of probing.Again, I don't see why you need to differentiate the control and data ops when populating this table. Those are just operations the NAND supports, and the data operations is just a subset.
Quoted 5 lines
2. If we use this single
op_templatesstruct, I can't think of any good way to initialize these in the
manufacturers driver (winbond.c), refer to 17th patch in this series. Could you please suggest a macro
implementation also forwinbond.cwith the suggestedop_templatesstruct.First replace the
op_variantsfield by something more generic:struct
spinand_info{
...const struct spinand_op_variants **ops_variants;...
};#define SPINAND_OP_VARIANTS(_id, ...) \ [SPI_NAND_OP_ ## _id] = { __VA_ARGS__ }Quoted 7 lines
#define SPINAND_OPS_VARIANTS(name, ...) const struct spinand_op_variants name[]{ __VA_ARGS__, };#define
SPINAND_INFO_OPS_VARIANTS(defs)
.ops_variants = defs
Quoted 6 lines
If we modify these macros, it would require other spinand vendor drivers to change (toshiba, micron, etc).
The older macros suit them well, should we go about changing them to this new macro (will require re-testing all of them),
or can we keep them unchanged and have new set of macros with different name (please give suggestion for it) for op variants.
I'd rather have everything converted to the new approach (we don't want 2 ways of describing the same thing), and I'm not sure you can make the old macros map to the new solution, so I fear you'll have to patch all vendors. This being said, I'm fine providing simple wrappers if that helps, but I don't see how they'd make the description simpler/more compact to be honest.
Re: [PATCH v3 05/17] mtd: spinand: Define ctrl_ops for non-page read/write op templates
Quoted 65 lines · Boris Brezillon
Quoted 54 lines · On Thu, 10 Mar 2022 13:27:06 +0530 Apurva Nandan <[email protected]> wrote:
Quoted 54 lines
Quoted 13 lines
Quoted 6 lines
This way, you can easily pick the right set of operations based on the protocol/mode you're in:
#define spinand_get_op_template(spinand, opname) \ ((spinand)->op_templates[(spinand)->protocol]->opname)Quoted 7 lines
static int
spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val){ struct spi_mem_op op = *spinand_get_op_template(spinand, get_feature); int ret; ... }Quoted 7 lines
I find a couple of issues with this method,
1.
read_cache,write_cache,update_cacheop templates don't fit well with the other non-data ops, as
these data ops are used to create a dirmap, and that can be done only once at probe time. Hence, there
is a different mechanism of selecting of data ops and non-data ops.Not sure I see why this is a problem. You can populate data-ops for all modes, and pick the one that provides the best perfs when you create the dirmap (which should really be at the end of the probe, if it's not already).
Quoted 4 lines
Hence, this division in the op templates
struct asdata_opsandctrl_opsis required. Currently, the core only supports using a single protocol for
data ops, chosen at the time of probing.Again, I don't see why you need to differentiate the control and data ops when populating this table. Those are just operations the NAND supports, and the data operations is just a subset.
Quoted 5 lines
2. If we use this single
op_templatesstruct, I can't think of any good way to initialize these in the
manufacturers driver (winbond.c), refer to 17th patch in this series. Could you please suggest a macro
implementation also forwinbond.cwith the suggestedop_templatesstruct.First replace the
op_variantsfield by something more generic:struct
spinand_info{
...const struct spinand_op_variants **ops_variants;...
};#define SPINAND_OP_VARIANTS(_id, ...) \ [SPI_NAND_OP_ ## _id] = { __VA_ARGS__ }#define SPINAND_OPS_VARIANTS(name, ...) const struct spinand_op_variants name[]{ __VA_ARGS__, };Quoted 8 lines
Quoted 2 lines
#define
SPINAND_INFO_OPS_VARIANTS(defs)
.ops_variants = defsIf we modify these macros, it would require other spinand vendor drivers to change (toshiba, micron, etc).
The older macros suit them well, should we go about changing them to this new macro (will require re-testing all of them),
or can we keep them unchanged and have new set of macros with different name (please give suggestion for it) for op variants.
Quoted 6 lines
I'd rather have everything converted to the new approach (we don't want 2 ways of describing the same thing), and I'm not sure you can make the old macros map to the new solution, so I fear you'll have to patch all vendors. This being said, I'm fine providing simple wrappers if that helps, but I don't see how they'd make the description simpler/more compact to be honest.
Okay, I will convert all of the vendor drivers, but please note I don't have any way to test the changes on all the flashes.
PATCH v3 6/17 mtd: spinand: Define default ctrl_ops in the core
In reply to Apurva NandanAdd default ctrl_ops in the core, which can be used when the op templates are commonly used ones. Till now, the core had used only fixed ctrl operations, so the default 'ctrl_ops' is just these ops macros initialized with default arguments. The default protocol is 1S_1S_1S.
--- drivers/mtd/nand/spi/core.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
drivers/mtd/nand/spi/core.c +14 -0
@@ -904,6 +904,16 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { &winbond_spinand_manufacturer, }; +static const struct spinand_ctrl_ops spinand_default_ctrl_ops = +SPINAND_CTRL_OPS (SPINAND_1S_1S_1S , +SPINAND_RESET_OP , +SPINAND_GET_FEATURE_OP (0 ,NULL ), +SPINAND_SET_FEATURE_OP (0 ,NULL ), +SPINAND_WR_EN_DIS_OP (true), +SPINAND_BLK_ERASE_OP (0 ), +SPINAND_PAGE_READ_OP (0 ), +SPINAND_PROG_EXEC_OP (0 )); +static int spinand_manufacturer_match (struct spinand_device *spinand,enum spinand_readid_method rdid_method) {
@@ -1156,6 +1166,8 @@ static void spinand_mtd_resume(struct mtd_info *mtd)int ret; spinand->protocol =SPINAND_1S_1S_1S ; + spinand->ctrl_ops = &spinand_default_ctrl_ops; + ret =spinand_reset_op (spinand);if (ret)return ;
@@ -1183,6 +1195,8 @@ static int spinand_init(struct spinand_device *spinand)return -ENOMEM ; spinand->protocol =SPINAND_1S_1S_1S ; + spinand->ctrl_ops = &spinand_default_ctrl_ops; + ret =spinand_detect (spinand);if (ret) goto err_free_bufs;
Signature · 1 line
2.25.1
PATCH v3 7/17 mtd: spinand: Switch from op macros usage to 'ctrl_ops' in the core
In reply to Apurva NandanMake use of the ctrl_ops struct, to introduce the usage of templates in non-page read/write operations as well. These templates are initialized at the probe time or at SPI modes switches.
--- drivers/mtd/nand/spi/core.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-)
drivers/mtd/nand/spi/core.c +24 -12
@@ -22,9 +22,11 @@static int spinand_read_reg_op (struct spinand_device *spinand,u8 reg,u8 *val) { -struct spi_mem_op op =SPINAND_GET_FEATURE_OP (reg, - spinand->scratchbuf);int ret; +struct spi_mem_op op = spinand->ctrl_ops->ops.get_feature; + + op.data.buf.out = spinand->scratchbuf; +memset (&op.addr.val, reg, op.addr.nbytes); ret =spi_mem_exec_op (spinand->spimem, &op);if (ret)
@@ -36,10 +38,12 @@ static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)static int spinand_write_reg_op (struct spinand_device *spinand,u8 reg,u8 val) { -struct spi_mem_op op =SPINAND_SET_FEATURE_OP (reg, - spinand->scratchbuf); +struct spi_mem_op op = spinand->ctrl_ops->ops.set_feature; + + op.data.buf.out = spinand->scratchbuf; +memset (&op.addr.val, reg, op.addr.nbytes); +memset (spinand->scratchbuf, val, op.data.nbytes); - *spinand->scratchbuf = val;return spi_mem_exec_op (spinand->spimem, &op); }
@@ -341,7 +345,7 @@ static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status)static int spinand_write_enable_op (struct spinand_device *spinand) { -struct spi_mem_op op =SPINAND_WR_EN_DIS_OP (true); +struct spi_mem_op op = spinand->ctrl_ops->ops.write_enable;return spi_mem_exec_op (spinand->spimem, &op); }
@@ -351,7 +355,9 @@ static int spinand_load_page_op(struct spinand_device *spinand, {struct nand_device *nand =spinand_to_nand (spinand);unsigned int row =nanddev_pos_to_row (nand, &req->pos); -struct spi_mem_op op =SPINAND_PAGE_READ_OP (row); +struct spi_mem_op op = spinand->ctrl_ops->ops.page_read; + + op.addr.val = row;return spi_mem_exec_op (spinand->spimem, &op); }
@@ -475,7 +481,9 @@ static int spinand_program_op(struct spinand_device *spinand, {struct nand_device *nand =spinand_to_nand (spinand);unsigned int row =nanddev_pos_to_row (nand, &req->pos); -struct spi_mem_op op =SPINAND_PROG_EXEC_OP (row); +struct spi_mem_op op = spinand->ctrl_ops->ops.program_execute; + + op.addr.val = row;return spi_mem_exec_op (spinand->spimem, &op); }
@@ -485,7 +493,9 @@ static int spinand_erase_op(struct spinand_device *spinand, {struct nand_device *nand =spinand_to_nand (spinand);unsigned int row =nanddev_pos_to_row (nand, pos); -struct spi_mem_op op =SPINAND_BLK_ERASE_OP (row); +struct spi_mem_op op = spinand->ctrl_ops->ops.block_erase; + + op.addr.val = row;return spi_mem_exec_op (spinand->spimem, &op); }
@@ -495,11 +505,13 @@ static int spinand_wait(struct spinand_device *spinand,unsigned long poll_delay_us, u8 *s) { - struct spi_mem_op op = SPINAND_GET_FEATURE_OP(REG_STATUS, - spinand->scratchbuf); + struct spi_mem_op op = spinand->ctrl_ops->ops.get_feature;u8 status;int ret; + op.data.buf.out = spinand->scratchbuf; +memset (&op.addr.val,REG_STATUS , op.addr.nbytes); + ret = spi_mem_poll_status(spinand->spimem, &op, STATUS_BUSY, 0, initial_delay_us, poll_delay_us,
@@ -542,7 +554,7 @@ static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr,static int spinand_reset_op (struct spinand_device *spinand) { -struct spi_mem_op op =SPINAND_RESET_OP ; +struct spi_mem_op op = spinand->ctrl_ops->ops.reset;int ret; ret =spi_mem_exec_op (spinand->spimem, &op);
Signature · 1 line
2.25.1
PATCH v3 8/17 mtd: spinand: Add support for manufacturer-based ctrl_ops variations
In reply to Apurva NandanAdd ctrl_ops_variants, which can be used by the manufacturers' codes to define their SPI control operation variants. Add a macro to easily define ctrl_ops_varinats. This can be used to list out all the supported ctrl ops with their respective protocols by the vendors.
Add spinand_select_ctrl_ops_variant() helper function to search for a supported ctrl_ops variant with the required SPI protocol in a given list of variants.
--- drivers/mtd/nand/spi/core.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 17 +++++++++++++++++ 2 files changed, 53 insertions(+)
drivers/mtd/nand/spi/core.c +36 -0
@@ -1031,6 +1031,42 @@ spinand_select_data_op_variant(struct spinand_device *spinand,return NULL ; } +static const struct spinand_ctrl_ops * +spinand_select_ctrl_ops_variant (struct spinand_device *spinand, +const struct spinand_ctrl_ops_variants *variants, +const enum spinand_protocol protocol) +{ +unsigned int i; + +for (i =0 ; i < variants->nvariants; i++) { +const struct spinand_ctrl_ops *ctrl_ops = + &variants->ctrl_ops_list[i]; + +if (ctrl_ops->protocol != protocol) +continue ; + +if (!spi_mem_supports_op (spinand->spimem, + &ctrl_ops->ops.reset) || + !spi_mem_supports_op (spinand->spimem, + &ctrl_ops->ops.get_feature) || + !spi_mem_supports_op (spinand->spimem, + &ctrl_ops->ops.set_feature) || + !spi_mem_supports_op (spinand->spimem, + &ctrl_ops->ops.write_enable) || + !spi_mem_supports_op (spinand->spimem, + &ctrl_ops->ops.block_erase) || + !spi_mem_supports_op (spinand->spimem, + &ctrl_ops->ops.page_read) || + !spi_mem_supports_op (spinand->spimem, + &ctrl_ops->ops.program_execute)) +continue ; + +return ctrl_ops; + } + +return NULL ; +} +/** * spinand_match_and_init() - Try to find a match between a device ID and an * entry in a spinand_info table
include/linux/mtd/spinand.h +17 -0
@@ -385,6 +385,18 @@ struct spinand_ctrl_ops { .protocol = __protocol, \ } +struct spinand_ctrl_ops_variants { +const struct spinand_ctrl_ops *ctrl_ops_list; +unsigned int nvariants; +}; + +#define SPINAND_CTRL_OPS_VARIANTS (name, ...) \ + const struct spinand_ctrl_ops_variants name = { \ + .ctrl_ops_list = (struct spinand_ctrl_ops[]){ __VA_ARGS__ }, \ + .nvariants = sizeof((struct spinand_ctrl_ops[]){ __VA_ARGS__ })/\ + sizeof(struct spinand_ctrl_ops), \ + } +/** * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND * chip
@@ -442,6 +454,8 @@ struct spinand_info {const struct spinand_op_variants *write_cache;const struct spinand_op_variants *update_cache; } data_ops_variants; + +const struct spinand_ctrl_ops_variants *ctrl_ops_variants;int (*select_target)(struct spinand_device *spinand,unsigned int target); };
@@ -460,6 +474,9 @@ struct spinand_info { .update_cache = __update, \ } +#define SPINAND_INFO_CTRL_OPS_VARIANTS (__ctrl_ops_variants) \ + .ctrl_ops_variants = __ctrl_ops_variants +#define SPINAND_ECCINFO (__ooblayout, __get_status) \ .eccinfo = { \ .ooblayout = __ooblayout, \
Signature · 1 line
2.25.1
PATCH v3 9/17 mtd: spinand: Add change_mode() in manufacturer_ops
In reply to Apurva NandanIntroduce change_mode() manufacturer_op to let the vendor provide the implementation of switching of SPI IO modes.
The method to switch to different SPI IO mode may vary across manufacturers. For example, for Winbond, Octal DTR is enabled by writing values to the volatile configuration register. So, let the manufacturer's code have their own implementation for switching to any given SPI IO mode. Manufacturer's code need to take care, if the requested protocol change is allowed/needed and how to apply it.
--- include/linux/mtd/spinand.h | 3 +++ 1 file changed, 3 insertions(+)
include/linux/mtd/spinand.h +3 -0
@@ -298,6 +298,7 @@ struct spinand_devid {/** * struct manufacurer_ops - SPI NAND manufacturer specific operations * @init: initialize a SPI NAND device +* @change_mode: switch the SPI NAND flash to a specific SPI protocol * @cleanup: cleanup a SPI NAND device * * Each SPI NAND manufacturer driver should implement this interface so that
@@ -305,6 +306,8 @@ struct spinand_devid {*/ struct spinand_manufacturer_ops {int (*init)(struct spinand_device *spinand); +int (*change_mode)(struct spinand_device *spinand, +const enum spinand_protocol protocol);void (*cleanup)(struct spinand_device *spinand); };
Signature · 1 line
2.25.1
Re: [PATCH v3 09/17] mtd: spinand: Add change_mode() in manufacturer_ops
Quoted 29 lines · On Sat, 1 Jan 2022 13:12:42 +0530 Apurva Nandan <[email protected]> wrote:
Introduce
change_mode()manufacturer_opto let the vendor provide the implementation of switching of SPI IO modes.The method to switch to different SPI IO mode may vary across manufacturers. For example, for Winbond, Octal DTR is enabled by writing values to the volatile configuration register. So, let the manufacturer's code have their own implementation for switching to any given SPI IO mode. Manufacturer's code need to take care, if the requested protocol change is allowed/needed and how to apply it.
Signed-off-by Apurva Nandan <[email protected]>--- include/linux/mtd/spinand.h | 3 +++ 1 file changed, 3 insertions(+)include/linux/mtd/spinand.h +1
-0@@ -298,6 +298,7 @@ struct spinand_devid {/** * struct manufacurer_ops - SPI NAND manufacturer specific operations * @init: initialize a SPI NAND device +* @change_mode: switch the SPI NAND flash to a specific SPI protocol * @cleanup: cleanup a SPI NAND device * * Each SPI NAND manufacturer driver should implement this interface so that
include/linux/mtd/spinand.h struct spinand_devid { · line 310
Quoted 5 lines
include/linux/mtd/spinand.h +2
-0@@ -305,6 +306,8 @@ struct spinand_devid {*/ struct spinand_manufacturer_ops {int (*init)(struct spinand_device *spinand); +int (*change_mode)(struct spinand_device *spinand, +const enum spinand_protocol protocol);
Protocol or mode? Pick one and stick to it. Given you already use protocol elsewhere, maybe s/select_mode/select_protocol/.
Quoted 3 lines
include/linux/mtd/spinand.h +0
-0void (*cleanup)(struct spinand_device *spinand); };
PATCH v3 10/17 mtd: spinand: Add pointer to probed flash's spinand_info
In reply to Apurva NandanThe data_ops_variants and ctrl_ops_variants defined in manufacturer's code are required again when changing flash modes, because they hold the op templates for the new protocol. It would be useful to have a pointer to the device description entry i.e. probed flash's spinand_info table in the spinand_device struct itself.
--- drivers/mtd/nand/spi/core.c | 1 + include/linux/mtd/spinand.h | 3 +++ 2 files changed, 4 insertions(+)
drivers/mtd/nand/spi/core.c +1 -0
@@ -1107,6 +1107,7 @@ int spinand_match_and_init(struct spinand_device *spinand, spinand->flags = table[i].flags; spinand->id.len =1 + table[i].devid.len; spinand->select_target = table[i].select_target; + spinand->desc_entry = &table[i]; op =spinand_select_data_op_variant (spinand, info->data_ops_variants.read_cache);
include/linux/mtd/spinand.h +3 -0
@@ -534,6 +534,8 @@ struct spinand_dirmap {* passed in spi_mem_op be DMA-able, so we can't based the bufs on * the stack * @manufacturer: SPI NAND manufacturer information +* @desc_entry: pointer to device description entry in the manufacturer's +* spinand_info tables * @priv: manufacturer private data */ struct spinand_device {
@@ -566,6 +568,7 @@ struct spinand_device {u8 *oobbuf;u8 *scratchbuf;const struct spinand_manufacturer *manufacturer; +const struct spinand_info *desc_entry;void *priv; };
Signature · 1 line
2.25.1
PATCH v3 11/17 mtd: spinand: Allow enabling/disabling Octal DTR mode in the core
In reply to Apurva NandanEnable Octal DTR SPI mode, i.e. 8D-8D-8D mode, if the SPI NAND flash device supports it. Mixed OSPI (1S-1S-8S & 1S-8S-8S), mixed DTR modes (1S-1D-8D), etc. aren't supported yet.
The method to switch to Octal DTR SPI mode may vary across manufacturers. For example, for Winbond, it is enabled by writing values to the volatile configuration register. So, let the manufacturer's code have their own implementation for switching to Octal DTR SPI mode.
Check for the SPI NAND device's support for Octal DTR mode using spinand flags, and if the data_ops and ctrl_ops are 8D-8D-8D, call change_mode() manufacturer op. If the SPI controller doesn't supports these modes, the selected data_ops and ctrl_ops will prevent switching to the Octal DTR mode. And finally update the spinand protocol and ctrl_ops on success. Similarly, for disabling Octal DTR mode, call change_mode(), and update protocol and ctrl_ops.
--- drivers/mtd/nand/spi/core.c | 79 +++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 2 files changed, 80 insertions(+)
drivers/mtd/nand/spi/core.c +79 -0
@@ -1067,6 +1067,81 @@ spinand_select_ctrl_ops_variant(struct spinand_device *spinand,return NULL ; } +static bool spinand_op_is_octal_dtr (const struct spi_mem_op *op) +{ +return op->cmd.buswidth ==8 && op->cmd.dtr && + op->addr.buswidth ==8 && op->addr.dtr && + op->data.buswidth ==8 && op->data.dtr; +} + +static int spinand_init_octal_dtr_enable (struct spinand_device *spinand) +{ +struct device *dev = &spinand->spimem->spi->dev; +const struct spinand_ctrl_ops *octal_dtr_ctrl_ops; +int ret; + +if (!(spinand->flags &SPINAND_HAS_OCTAL_DTR_BIT )) +return 0 ; + +if (!(spinand_op_is_octal_dtr (spinand->data_ops.read_cache) && +spinand_op_is_octal_dtr (spinand->data_ops.write_cache) && +spinand_op_is_octal_dtr (spinand->data_ops.update_cache))) +return 0 ; + + octal_dtr_ctrl_ops =spinand_select_ctrl_ops_variant (spinand, + spinand->desc_entry->ctrl_ops_variants, +SPINAND_8D_8D_8D ); + +if (!octal_dtr_ctrl_ops) +return 0 ; + +if (!spinand->manufacturer->ops->change_mode) { +dev_dbg (dev, +"Missing ->change_mode(), unable to switch mode\n" ); +return -EINVAL ; + } + + ret = spinand->manufacturer->ops->change_mode (spinand, +SPINAND_8D_8D_8D ); +if (ret) { +dev_err (dev, +"Failed to enable Octal DTR SPI mode (err = %d)\n" , + ret); +return ret; + } + + spinand->protocol =SPINAND_8D_8D_8D ; + spinand->ctrl_ops = octal_dtr_ctrl_ops; + +dev_dbg (dev, +"%s SPI NAND switched to Octal DTR SPI (8D-8D-8D) mode\n" , + spinand->manufacturer->name); +return 0 ; +} + +static int spinand_init_octal_dtr_disable (struct spinand_device *spinand) +{ +struct device *dev = &spinand->spimem->spi->dev; +int ret; + +if (!spinand->manufacturer->ops->change_mode) +return -EINVAL ; + + ret = spinand->manufacturer->ops->change_mode (spinand, +SPINAND_1S_1S_1S ); + +if (ret) { +dev_err (dev, +"Failed to disable Octal DTR SPI mode (err = %d)\n" , + ret); +return ret; + } + + spinand->protocol =SPINAND_1S_1S_1S ; + spinand->ctrl_ops = &spinand_default_ctrl_ops; +return 0 ; +} +/** * spinand_match_and_init() - Try to find a match between a device ID and an * entry in a spinand_info table
@@ -1203,6 +1278,10 @@ static int spinand_init_flash(struct spinand_device *spinand)break ; } + ret =spinand_init_octal_dtr_enable (spinand); +if (ret) +return ret; +if (ret)spinand_manufacturer_cleanup (spinand);
include/linux/mtd/spinand.h +1 -0
@@ -417,6 +417,7 @@ struct spinand_ecc_info {#define SPINAND_HAS_QE_BIT BIT(0)#define SPINAND_HAS_CR_FEAT_BIT BIT(1) +#define SPINAND_HAS_OCTAL_DTR_BIT BIT(2)/** * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure
Signature · 1 line
2.25.1
Re: [PATCH v3 11/17] mtd: spinand: Allow enabling/disabling Octal DTR mode in the core
Quoted 54 lines · On Sat, 1 Jan 2022 13:12:44 +0530 Apurva Nandan <[email protected]> wrote:
Enable Octal DTR SPI mode, i.e. 8D-8D-8D mode, if the SPI NAND flash device supports it. Mixed OSPI (1S-1S-8S & 1S-8S-8S), mixed DTR modes (1S-1D-8D), etc. aren't supported yet.
The method to switch to Octal DTR SPI mode may vary across manufacturers. For example, for Winbond, it is enabled by writing values to the volatile configuration register. So, let the manufacturer's code have their own implementation for switching to Octal DTR SPI mode.
Check for the SPI NAND device's support for Octal DTR mode using spinand flags, and if the
data_opsandctrl_opsare 8D-8D-8D, callchange_mode()manufacturer op. If the SPI controller doesn't supports these modes, the selecteddata_opsandctrl_opswill prevent switching to the Octal DTR mode. And finally update the spinand protocol andctrl_opson success. Similarly, for disabling Octal DTR mode, callchange_mode(), and update protocol andctrl_ops.Signed-off-by Apurva Nandan <[email protected]>--- drivers/mtd/nand/spi/core.c | 79 +++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 2 files changed, 80 insertions(+)drivers/mtd/nand/spi/core.c +22
-0@@ -1067,6 +1067,81 @@ spinand_select_ctrl_ops_variant(struct spinand_device *spinand,return NULL ; } +static bool spinand_op_is_octal_dtr (const struct spi_mem_op *op) +{ +return op->cmd.buswidth ==8 && op->cmd.dtr && + op->addr.buswidth ==8 && op->addr.dtr && + op->data.buswidth ==8 && op->data.dtr; +} + +static int spinand_init_octal_dtr_enable (struct spinand_device *spinand) +{ +struct device *dev = &spinand->spimem->spi->dev; +const struct spinand_ctrl_ops *octal_dtr_ctrl_ops; +int ret; + +if (!(spinand->flags &SPINAND_HAS_OCTAL_DTR_BIT )) +return 0 ; + +if (!(spinand_op_is_octal_dtr (spinand->data_ops.read_cache) && +spinand_op_is_octal_dtr (spinand->data_ops.write_cache) && +spinand_op_is_octal_dtr (spinand->data_ops.update_cache))) +return 0 ; + + octal_dtr_ctrl_ops = spinand_select_ctrl_ops_variant(spinand,
drivers/mtd/nand/spi/core.c spinand_select_ctrl_ops_variant(struct spinand_device *spinand, · line 1101
Quoted 10 lines
drivers/mtd/nand/spi/core.c +10
-0+ spinand->desc_entry->ctrl_ops_variants, + SPINAND_8D_8D_8D); + +if (!octal_dtr_ctrl_ops) +return 0 ; + +if (!spinand->manufacturer->ops->change_mode) { +dev_dbg (dev, +"Missing ->change_mode(), unable to switch mode\n" ); +return -EINVAL ;
Looks like something that's worth printing at the info level, and I think returning EOPNOTSUPP would be more appropriate.
Quoted 11 lines
drivers/mtd/nand/spi/core.c +11
-0+ } + + ret = spinand->manufacturer->ops->change_mode (spinand, +SPINAND_8D_8D_8D ); +if (ret) { +dev_err (dev, +"Failed to enable Octal DTR SPI mode (err = %d)\n" , + ret); +return ret; + } +
drivers/mtd/nand/spi/core.c spinand_select_ctrl_ops_variant(struct spinand_device *spinand, · line 1122
Quoted 10 lines
drivers/mtd/nand/spi/core.c +10
-0+ spinand->protocol =SPINAND_8D_8D_8D ; + spinand->ctrl_ops = octal_dtr_ctrl_ops; + +dev_dbg (dev, +"%s SPI NAND switched to Octal DTR SPI (8D-8D-8D) mode\n" , + spinand->manufacturer->name); +return 0 ; +} + +static int spinand_init_octal_dtr_disable (struct spinand_device *spinand)
This function is never used. I guess it should be called in the suspend/shutdown path, at least.
Quoted 41 lines
drivers/mtd/nand/spi/core.c +26
-0+{ +struct device *dev = &spinand->spimem->spi->dev; +int ret; + +if (!spinand->manufacturer->ops->change_mode) +return -EINVAL ; + + ret = spinand->manufacturer->ops->change_mode (spinand, +SPINAND_1S_1S_1S ); + +if (ret) { +dev_err (dev, +"Failed to disable Octal DTR SPI mode (err = %d)\n" , + ret); +return ret; + } + + spinand->protocol =SPINAND_1S_1S_1S ; + spinand->ctrl_ops = &spinand_default_ctrl_ops; +return 0 ; +} +/** * spinand_match_and_init() - Try to find a match between a device ID and an * entry in a spinand_info table @@ -1203,6 +1278,10 @@ static int spinand_init_flash(struct spinand_device *spinand)break ; } + ret =spinand_init_octal_dtr_enable (spinand); +if (ret) +return ret; +if (ret)spinand_manufacturer_cleanup (spinand);include/linux/mtd/spinand.h +0
-0
include/linux/mtd/spinand.h struct spinand_ecc_info { · line 420
Quoted 4 lines
include/linux/mtd/spinand.h +1
-0@@ -417,6 +417,7 @@ struct spinand_ecc_info {#define SPINAND_HAS_QE_BIT BIT(0)#define SPINAND_HAS_CR_FEAT_BIT BIT(1) +#define SPINAND_HAS_OCTAL_DTR_BIT BIT(2)
Do we really need a new flag for this? Isn't the template op initialization enough to reflect whether the NAND and controller can do 8DTR or not?
Quoted 3 lines
include/linux/mtd/spinand.h +0
-0/** * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure
PATCH v3 12/17 mtd: spinand: Add mtd_suspend() to disable Octal DTR mode at suspend
In reply to Apurva NandanThe flash might or might not have gone a power-down during sleep. Hence, its SPI IO mode is unpredictable for the core at the time of resume. To ensure proper reinitialization during resume, disable the Octal DTR SPI IO mode and bring the flash to 1S-1S-1S mode when performing suspend using mtd_suspend().
--- drivers/mtd/nand/spi/core.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/mtd/nand/spi/core.c +11 -0
@@ -1307,6 +1307,16 @@ static void spinand_mtd_resume(struct mtd_info *mtd)spinand_ecc_enable (spinand, false); } +static int spinand_mtd_suspend (struct mtd_info *mtd) +{ +struct spinand_device *spinand =mtd_to_spinand (mtd); + +if (spinand->ctrl_ops->protocol ==SPINAND_8D_8D_8D ) +return spinand_init_octal_dtr_disable (spinand); + +return 0 ; +} +static int spinand_init (struct spinand_device *spinand) {struct device *dev = &spinand->spimem->spi->dev;
@@ -1381,6 +1391,7 @@ static int spinand_init(struct spinand_device *spinand) mtd->_erase = spinand_mtd_erase; mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks; mtd->_resume = spinand_mtd_resume; + mtd->_suspend = spinand_mtd_suspend;if (nand->ecc.engine) { ret =mtd_ooblayout_count_freebytes (mtd);
Signature · 1 line
2.25.1
Re: [PATCH v3 12/17] mtd: spinand: Add mtd_suspend() to disable Octal DTR mode at suspend
Quoted 16 lines · On Sat, 1 Jan 2022 13:12:45 +0530 Apurva Nandan <[email protected]> wrote:
The flash might or might not have gone a power-down during sleep. Hence, its SPI IO mode is unpredictable for the core at the time of resume. To ensure proper reinitialization during resume, disable the Octal DTR SPI IO mode and bring the flash to 1S-1S-1S mode when performing suspend using
mtd_suspend().Signed-off-by Apurva Nandan <[email protected]>--- drivers/mtd/nand/spi/core.c | 11 +++++++++++ 1 file changed, 11 insertions(+)drivers/mtd/nand/spi/core.c +0
-0
drivers/mtd/nand/spi/core.c static void spinand_mtd_resume(struct mtd_info *mtd) · line 1315
Quoted 9 lines
drivers/mtd/nand/spi/core.c +6
-0@@ -1307,6 +1307,16 @@ static void spinand_mtd_resume(struct mtd_info *mtd)spinand_ecc_enable (spinand, false); } +static int spinand_mtd_suspend (struct mtd_info *mtd) +{ +struct spinand_device *spinand =mtd_to_spinand (mtd); + +if (spinand->ctrl_ops->protocol ==SPINAND_8D_8D_8D ) +return spinand_init_octal_dtr_disable (spinand);
Ok, so you're calling it here. I'd re-order commmits so that DTR support is added after the suspend hook is defined, even if this hook does nothing at first. This way you don't have an unused warning in the previous commit, and most importantly, you don't enter 8DTR before you're sure things will be set back to 1S when entering suspend.
Quoted 15 lines
drivers/mtd/nand/spi/core.c +5
-0+ +return 0 ; +} +static int spinand_init (struct spinand_device *spinand) {struct device *dev = &spinand->spimem->spi->dev;@@ -1381,6 +1391,7 @@ static int spinand_init(struct spinand_device *spinand) mtd->_erase = spinand_mtd_erase; mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks; mtd->_resume = spinand_mtd_resume; + mtd->_suspend = spinand_mtd_suspend;if (nand->ecc.engine) { ret =mtd_ooblayout_count_freebytes (mtd);
PATCH v3 13/17 mtd: spinand: winbond: Add support for write volatile configuration register op
In reply to Apurva NandanVolatile configuration register are a different set of configuration registers, i.e. they differ from the status registers. A different SPI instruction is required to write to these registers. Any changes to the Volatile Configuration Register get transferred directly to the Internal Configuration Register and instantly reflect on the device operation.
In Winbond W35N01JW, these volatile configuration register must be configured in order to switch to Octal DTR SPI mode.
Add support for writing to volatile configuration registers using a new WRITE_VCR_OP template.
Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf
--- drivers/mtd/nand/spi/core.c | 2 +- drivers/mtd/nand/spi/winbond.c | 43 ++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 3 files changed, 45 insertions(+), 1 deletion(-)
drivers/mtd/nand/spi/core.c +1 -1
@@ -343,7 +343,7 @@ static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status) engine_conf->status = status; } -static int spinand_write_enable_op (struct spinand_device *spinand) +int spinand_write_enable_op (struct spinand_device *spinand) {struct spi_mem_op op = spinand->ctrl_ops->ops.write_enable;
drivers/mtd/nand/spi/winbond.c +43 -0
55 lines not on this page · raw message
include/linux/mtd/spinand.h +1 -0
@@ -638,5 +638,6 @@ int spinand_match_and_init(struct spinand_device *spinand,int spinand_upd_cfg (struct spinand_device *spinand,u8 mask,u8 val);int spinand_select_target (struct spinand_device *spinand,unsigned int target); +int spinand_write_enable_op (struct spinand_device *spinand);#endif /* __LINUX_MTD_SPINAND_H */
Signature · 1 line
2.25.1
PATCH v3 14/17 mtd: spinand: winbond: Add octal_dtr_enable/disable() in manufacturer_ops
In reply to Apurva NandanAdd implementation of octal_dtr_enable() and octal_dtr_disable() manufacturer_ops for Winbond. To switch to Ocatl DTR mode, setting programmable dummy cycles and SPI IO mode using the volatile configuration register is required. To function at max 120MHz SPI clock in Octal DTR mode, 12 programmable dummy clock cycle setting is required. (Default number of dummy cycle are 8 clocks)
Set the programmable dummy cycle to 12 clocks, and SPI IO mode to Octal DTR with Data Strobe in the VCR. Also, perform a READ ID operation in Octal DTR SPI mode to ensure the switch was successful. To disable Octal DTR mode, restore the VCR registers to their default values and verify it using READ ID operation.
Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf
--- drivers/mtd/nand/spi/winbond.c | 85 ++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+)
drivers/mtd/nand/spi/winbond.c +85 -0
@@ -16,6 +16,16 @@#define WINBOND_CFG_BUF_READ BIT(3) +/* Octal DTR SPI mode (8D-8D-8D) with Data Strobe output*/ +#define WINBOND_VCR_IO_MODE_OCTAL_DTR 0xE7 +#define WINBOND_VCR_IO_MODE_SINGLE_STR 0xFF +#define WINBOND_VCR_IO_MODE_ADDR 0x00 + +/* Use 12 dummy clk cycles for using Octal DTR SPI at max 120MHZ */ +#define WINBOND_VCR_DUMMY_CLK_COUNT 12 +#define WINBOND_VCR_DUMMY_CLK_DEFAULT 0xFF +#define WINBOND_VCR_DUMMY_CLK_ADDR 0x01 + static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
@@ -157,6 +167,81 @@ static int winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, u8 val)return 0 ; } +static int winbond_spinand_octal_dtr_enable (struct spinand_device *spinand) +{ +int ret; +struct spi_mem_op op; + + ret =winbond_write_vcr_op (spinand,WINBOND_VCR_DUMMY_CLK_ADDR , +WINBOND_VCR_DUMMY_CLK_COUNT ); +if (ret) +return ret; + + ret =winbond_write_vcr_op (spinand,WINBOND_VCR_IO_MODE_ADDR , +WINBOND_VCR_IO_MODE_OCTAL_DTR ); +if (ret) +return ret; + +/* Read flash ID to make sure the switch was successful. */ + op = (struct spi_mem_op ) +SPI_MEM_OP (SPI_MEM_OP_CMD_DTR (2 ,0x9f9f ,8 ), +SPI_MEM_OP_NO_ADDR , +SPI_MEM_OP_DUMMY_DTR (16 ,8 ), +SPI_MEM_OP_DATA_IN_DTR (SPINAND_MAX_ID_LEN , + spinand->scratchbuf,8 )); + + ret =spi_mem_exec_op (spinand->spimem, &op); +if (ret) +return ret; + +if (memcmp (spinand->scratchbuf, spinand->id.data,SPINAND_MAX_ID_LEN )) +return -EINVAL ; + +return 0 ; +} + +static int winbond_spinand_octal_dtr_disable (struct spinand_device *spinand) +{ +int ret; +struct spi_mem_op op = +SPI_MEM_OP (SPI_MEM_OP_CMD_DTR (2 ,0x8181 ,8 ), +SPI_MEM_OP_ADDR_DTR (4 ,WINBOND_VCR_IO_MODE_ADDR ,8 ), +SPI_MEM_OP_NO_DUMMY , +SPI_MEM_OP_DATA_OUT_DTR (2 , spinand->scratchbuf,8 )); + + *spinand->scratchbuf =WINBOND_VCR_IO_MODE_SINGLE_STR ; + + ret =spinand_write_enable_op (spinand); +if (ret) +return ret; + + ret =spi_mem_exec_op (spinand->spimem, &op); +if (ret) +return ret; + + ret =winbond_write_vcr_op (spinand,WINBOND_VCR_DUMMY_CLK_ADDR , +WINBOND_VCR_DUMMY_CLK_DEFAULT ); +if (ret) +return ret; + +/* Read flash ID to make sure the switch was successful. */ + op = (struct spi_mem_op ) +SPI_MEM_OP (SPI_MEM_OP_CMD (0x9f ,1 ), +SPI_MEM_OP_NO_ADDR , +SPI_MEM_OP_DUMMY (1 ,1 ), +SPI_MEM_OP_DATA_IN (SPINAND_MAX_ID_LEN , + spinand->scratchbuf,1 )); + + ret =spi_mem_exec_op (spinand->spimem, &op); +if (ret) +return ret; + +if (memcmp (spinand->scratchbuf, spinand->id.data,SPINAND_MAX_ID_LEN )) +return -EINVAL ; + +return 0 ; +} +static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { .init = winbond_spinand_init, };
Signature · 1 line
2.25.1
PATCH v3 15/17 mtd: spianand: winbond: Add change_mode() manufacturer_ops
In reply to Apurva NandanAdd implementation of change_mode() for Winbond's manufacturer_ops, that executes octal_dtr_enable() and octal_dtr_disable() according to requested protocol.
Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf
--- drivers/mtd/nand/spi/winbond.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
drivers/mtd/nand/spi/winbond.c +25 -0
@@ -242,8 +242,33 @@ static int winbond_spinand_octal_dtr_disable(struct spinand_device *spinand)return 0 ; } +static int winbond_change_spi_mode (struct spinand_device *spinand, +const enum spinand_protocol protocol) +{ +if (spinand->protocol == protocol) +return 0 ; + +switch (spinand->protocol) { +case SPINAND_1S_1S_1S : +if (protocol ==SPINAND_8D_8D_8D ) +return winbond_spinand_octal_dtr_enable (spinand); +break ; + +case SPINAND_8D_8D_8D : +if (protocol ==SPINAND_1S_1S_1S ) +return winbond_spinand_octal_dtr_disable (spinand); +break ; + +default : +return -EOPNOTSUPP ; + } + +return -EOPNOTSUPP ; +} +static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { .init = winbond_spinand_init, + .change_mode = winbond_change_spi_mode, };const struct spinand_manufacturer winbond_spinand_manufacturer = {
Signature · 1 line
2.25.1
Re: [PATCH v3 15/17] mtd: spianand: winbond: Add change_mode() manufacturer_ops
Quoted 31 lines · On Sat, 1 Jan 2022 13:12:48 +0530 Apurva Nandan <[email protected]> wrote:
Add implementation of
change_mode()for Winbond'smanufacturer_ops, that executesoctal_dtr_enable()andoctal_dtr_disable()according to requested protocol.Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf
Signed-off-by Apurva Nandan <[email protected]>--- drivers/mtd/nand/spi/winbond.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)drivers/mtd/nand/spi/winbond.c +12
-0@@ -242,8 +242,33 @@ static int winbond_spinand_octal_dtr_disable(struct spinand_device *spinand)return 0 ; } +static int winbond_change_spi_mode (struct spinand_device *spinand, +const enum spinand_protocol protocol) +{ +if (spinand->protocol == protocol) +return 0 ; + +switch (spinand->protocol) { +case SPINAND_1S_1S_1S : +if (protocol ==SPINAND_8D_8D_8D ) +return winbond_spinand_octal_dtr_enable (spinand); +break ; +
drivers/mtd/nand/spi/winbond.c static int winbond_spinand_octal_dtr_disable(struct spinand_device *spinand) · line 266
Quoted 10 lines
drivers/mtd/nand/spi/winbond.c +10
-0+case SPINAND_8D_8D_8D : +if (protocol ==SPINAND_1S_1S_1S ) +return winbond_spinand_octal_dtr_disable (spinand); +break ; + +default : +return -EOPNOTSUPP ; + } + +return -EOPNOTSUPP ;
This version is slightly more concise:
if (spinand->protocol == protocol) return 0; else if (spinand->protocol == SPINAND_1S && protocol == SPINAND_8D) return winbond_spinand_octal_dtr_enable(spinand); else if (spinand->protocol == SPINAND_8D && protocol == SPINAND_1S) return winbond_spinand_octal_dtr_disable(spinand); return -EOPNOTSUPP;
Quoted 8 lines
drivers/mtd/nand/spi/winbond.c +3
-0+} +static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { .init = winbond_spinand_init, + .change_mode = winbond_change_spi_mode, };const struct spinand_manufacturer winbond_spinand_manufacturer = {
PATCH v3 16/17 mtd: spinand: winbond: Rename cache op_variants struct variable
In reply to Apurva NandanTill now, supported Winbond SPI NAND flashes had same supported op_variants. W35N01JW introduces Octal DTR SPI IO mode, so now different op_variants struct variables are required for different Winbond flashes. Hence, rename and append the flash name in the op_variants struct variable.
Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf
--- drivers/mtd/nand/spi/winbond.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/mtd/nand/spi/winbond.c +9 -9
@@ -26,7 +26,7 @@#define WINBOND_VCR_DUMMY_CLK_DEFAULT 0xFF#define WINBOND_VCR_DUMMY_CLK_ADDR 0x01 -static SPINAND_OP_VARIANTS(read_cache_variants, +static SPINAND_OP_VARIANTS(read_cache_variants_w25xxgv, SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),SPINAND_PAGE_READ_FROM_CACHE_X4_OP (0 ,1 ,NULL ,0 ), SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
@@ -34,11 +34,11 @@ static SPINAND_OP_VARIANTS(read_cache_variants,SPINAND_PAGE_READ_FROM_CACHE_OP (true,0 ,1 ,NULL ,0 ), SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); -static SPINAND_OP_VARIANTS(write_cache_variants, +static SPINAND_OP_VARIANTS(write_cache_variants_w25xxgv, SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), SPINAND_PROG_LOAD(true, 0, NULL, 0)); -static SPINAND_OP_VARIANTS(update_cache_variants, +static SPINAND_OP_VARIANTS(update_cache_variants_w25xxgv, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0));
@@ -90,9 +90,9 @@ static const struct spinand_info winbond_spinand_table[] = {SPINAND_ID (SPINAND_READID_METHOD_OPCODE_DUMMY ,0xab ),NAND_MEMORG (1 ,2048 ,64 ,64 ,1024 ,20 ,1 ,1 ,2 ),NAND_ECCREQ (1 ,512 ), -SPINAND_INFO_OP_VARIANTS (&read_cache_variants, - &write_cache_variants, - &update_cache_variants), +SPINAND_INFO_OP_VARIANTS (&read_cache_variants_w25xxgv, + &write_cache_variants_w25xxgv, + &update_cache_variants_w25xxgv),0 ,SPINAND_ECCINFO (&w25m02gv_ooblayout,NULL ), SPINAND_SELECT_TARGET(w25m02gv_select_target)),
@@ -100,9 +100,9 @@ static const struct spinand_info winbond_spinand_table[] = {SPINAND_ID (SPINAND_READID_METHOD_OPCODE_DUMMY ,0xaa ),NAND_MEMORG (1 ,2048 ,64 ,64 ,1024 ,20 ,1 ,1 ,1 ),NAND_ECCREQ (1 ,512 ), -SPINAND_INFO_OP_VARIANTS (&read_cache_variants, - &write_cache_variants, - &update_cache_variants), +SPINAND_INFO_OP_VARIANTS (&read_cache_variants_w25xxgv, + &write_cache_variants_w25xxgv, + &update_cache_variants_w25xxgv),0 , SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), };
Signature · 1 line
2.25.1
PATCH v3 17/17 mtd: spinand: winbond: Add support for Winbond W35N01JW SPI NAND flash
In reply to Apurva NandanWinbond W35N01JW is a SPI NAND flash supporting Octal DTR SPI protocol. Add op_variants and ctrl_ops_variants for W35N01JW, thus adding all required Octal DTR ops. Add W35N01JW's OOB layout functions for the mtd_ooblayout_ops. Finally, add an entry for W35N01JW in spinand_info table.
Datasheet: https://www.winbond.com/export/sites/winbond/datasheet/W35N01JW_Datasheet_Brief.pdf
--- drivers/mtd/nand/spi/winbond.c | 71 ++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)
drivers/mtd/nand/spi/winbond.c +71 -0
@@ -42,6 +42,37 @@ static SPINAND_OP_VARIANTS(update_cache_variants_w25xxgv,SPINAND_PROG_LOAD_X4 (false,0 ,NULL ,0 ), SPINAND_PROG_LOAD(false, 0, NULL, 0)); +static SPINAND_OP_VARIANTS(read_cache_variants_w35n01jw, + SPINAND_PAGE_READ_FROM_CACHE_OCTALIO_DTR_OP(0, 24, NULL, 0), +SPINAND_PAGE_READ_FROM_CACHE_OP (true,0 ,1 ,NULL ,0 ), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants_w35n01jw, + SPINAND_PROG_LOAD_OCTALIO_DTR(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants_w35n01jw, + SPINAND_PROG_LOAD_OCTALIO_DTR(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static SPINAND_CTRL_OPS_VARIANTS(ctrl_ops_variants_w35n01jw, + SPINAND_CTRL_OPS(SPINAND_8D_8D_8D, + SPINAND_RESET_OP_OCTAL_DTR, + SPINAND_GET_FEATURE_OP_OCTAL_DTR(0, NULL), + SPINAND_SET_FEATURE_OP_OCTAL_DTR(0, NULL), + SPINAND_WR_EN_DIS_OP_OCTAL_DTR(true), + SPINAND_BLK_ERASE_OP_OCTAL_DTR(0), + SPINAND_PAGE_READ_OP_OCTAL_DTR(0), + SPINAND_PROG_EXEC_OP_OCTAL_DTR(0)), + SPINAND_CTRL_OPS(SPINAND_1S_1S_1S, + SPINAND_RESET_OP, + SPINAND_GET_FEATURE_OP(0, NULL), + SPINAND_SET_FEATURE_OP(0, NULL), + SPINAND_WR_EN_DIS_OP(true), + SPINAND_BLK_ERASE_OP(0), + SPINAND_PAGE_READ_OP(0), + SPINAND_PROG_EXEC_OP(0))); + static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) {
@@ -66,11 +97,40 @@ static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section,return 0 ; } +static int w35n01jw_ooblayout_ecc (struct mtd_info *mtd,int section, +struct mtd_oob_region *region) +{ +if (section >7 ) +return -ERANGE ; + + region->offset = (16 * section) +12 ; + region->length =4 ; + +return 0 ; +} + +static int w35n01jw_ooblayout_free (struct mtd_info *mtd,int section, +struct mtd_oob_region *region) +{ +if (section >7 ) +return -ERANGE ; + + region->offset = (16 * section) +2 ; + region->length =10 ; + +return 0 ; +} +static const struct mtd_ooblayout_ops w25m02gv_ooblayout = { .ecc = w25m02gv_ooblayout_ecc, .free = w25m02gv_ooblayout_free, }; +static const struct mtd_ooblayout_ops w35n01jw_ooblayout = { + .ecc = w35n01jw_ooblayout_ecc, + .free = w35n01jw_ooblayout_free, +}; +static int w25m02gv_select_target (struct spinand_device *spinand,unsigned int target) {
@@ -105,6 +165,17 @@ static const struct spinand_info winbond_spinand_table[] = { &update_cache_variants_w25xxgv), 0, SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), +SPINAND_INFO ("W35N01JW" , +SPINAND_ID (SPINAND_READID_METHOD_OPCODE_DUMMY ,0xdc ), +NAND_MEMORG (1 ,4096 ,128 ,64 ,512 ,20 ,1 ,1 ,1 ), +NAND_ECCREQ (1 ,512 ), +SPINAND_INFO_OP_VARIANTS (&read_cache_variants_w35n01jw, + &write_cache_variants_w35n01jw, + &update_cache_variants_w35n01jw), +SPINAND_HAS_OCTAL_DTR_BIT |SPINAND_HAS_CR_FEAT_BIT , +SPINAND_ECCINFO (&w35n01jw_ooblayout,NULL ), + SPINAND_INFO_CTRL_OPS_VARIANTS(&ctrl_ops_variants_w35n01jw)), + };static int winbond_spinand_init (struct spinand_device *spinand)
Signature · 1 line
2.25.1