diff --git a/kernel.spec b/kernel.spec index 5725c98..1878e57 100644 --- a/kernel.spec +++ b/kernel.spec @@ -32,7 +32,7 @@ Name: kernel Version: 4.19.90 -Release: %{hulkrelease}.0241 +Release: %{hulkrelease}.0243 Summary: Linux Kernel License: GPLv2 URL: http://www.kernel.org/ @@ -849,8 +849,48 @@ fi %endif %changelog +* TUE Nov 14 2023 YunYi Yang - 4.19.90-2311.2.0.0243 +- config: arm64: Build HiSilicon SPI/SFC driver as module +- spi: hisi-sfc-v3xx: drop unnecessary ACPI_PTR and related ifendif protection +- spi: hisi-sfc-v3xx: fix potential irq race condition +- spi: hisi-sfc-v3xx: add address mode check +- spi: hisi-sfc-v3xx: extend version checking compatibility +- spi: hisi-sfc-v3xx: add support for IRQ mode +- spi: hisi-sfc-v3xx: factor out the bit definition of interrupt register +- spi: hisi-sfc-v3xx: factor out bus config and transfer functions +- spi: hisi-sfc-v3xx: factor out IO modes configuration +- spi: Remove CONFIG_ prefix from Kconfig select +- spi: hisi-sfc-v3xx: add error check after per operation +- spi: HiSilicon v3xx: Use DMI quirk to set controller buswidth override bits +- spi: HiSilicon v3xx: Properly set CMD_CONFIG for Dual/Quad modes +- spi: Allow SPI controller override device buswidth +- spi: Add HiSilicon v3xx SPI NOR flash controller driver +- spi/acpi: avoid spurious matches during slave enumeration +- spi/acpi: fix incorrect ACPI parent check +- spi/acpi: enumerate all SPI slaves in the namespace +- driver core: platform: return -ENXIO for missing GpioInt +- driver: platform: Support parsing GpioInt 0 in platform_get_irq() +- spi: spi-mem: Fix build error without CONFIG_SPI_MEM +- spi: spi-mem: Add SPI_MEM_NO_DATA to the spi_mem_data_dir enum +- sh: Replace CONFIG_MTD_M25P80 with CONFIG_MTD_SPI_NOR in sh7757lcr_defconfig +- powerpc: Drop CONFIG_MTD_M25P80 in 85xx-hw.config +- m68k: Drop CONFIG_MTD_M25P80 in stmark2_defconfig +- mips: Drop CONFIG_MTD_M25P80 in various defconfig files +- ARM: shmobile: defconfig: Refresh config CONFIG_MTD_M25P80 for v5.4-rc1 +- mtd: spi-nor: core: Fix an issue of releasing resources during read/write +- mtd: spi-nor: fix kernel-doc for spi_nor::spimem +- mtd: spi-nor: Pointer parameter for CR in spi_nor_read_cr() +- mtd: spi-nor: Pointer parameter for FSR in spi_nor_read_fsr() +- mtd: spi-nor: Pointer parameter for SR in spi_nor_read_sr() +- mtd: spi-nor: Stop compare with negative in Reg Ops methods +- mtd: spi-nor: Prepend spi_nor_ to all Reg Ops methods +- mtd: spi-nor: Fix direction of the write_sr() transfer +- mtd: spi-nor: Move m25p80 code in spi-nor.c +- mtd: spi-nor: always use bounce buffer for register read/writes +- mtd: spi-nor: Add support for mx25u12835f +- spi: add support for octal mode I/O data transfer -* TUE Nov 14 2023 Li Xiaodong - 4.19.90-2311.2.0.0241 +* TUE Nov 14 2023 Li Xiaodong - 4.19.90-2311.2.0.0242 - !2793 handle uninitialized numa nodes gracefully. - !2789 linux-4.19.y inclusion - arch/x86/mm/numa: Do not initialize nodes twice @@ -915,7 +955,7 @@ fi - i2c: add phytium i2c driver DT binding docs - Net: ethernet: Support 3snic 3s9xx network card -* Mon Nov 13 2023 mingqian218472 - 4.19.90-2311.1.0.0240 +* Mon Nov 13 2023 mingqian218472 - 4.19.90-2311.1.0.0241 - Add feature for nfs client support multipath * Mon Nov 13 2023 Yu Liao - 4.19.90-2311.1.0.0240 diff --git a/patches/0323-spi-add-support-for-octal-mode-I-O-data-transfer.patch b/patches/0323-spi-add-support-for-octal-mode-I-O-data-transfer.patch new file mode 100644 index 0000000..f3200f3 --- /dev/null +++ b/patches/0323-spi-add-support-for-octal-mode-I-O-data-transfer.patch @@ -0,0 +1,94 @@ +From dedb0ed826070a5a8c6b3a02688f5bafccb9c263 Mon Sep 17 00:00:00 2001 +From: Yogesh Narayan Gaur +Date: Mon, 3 Dec 2018 08:39:06 +0000 +Subject: [PATCH 01/39] spi: add support for octal mode I/O data transfer + +mainline inclusion +from mainline-v5.0-rc1 +commit 6b03061f882de49b83ccf44beb3a12c920a2da1b +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6b03061f882de49b83ccf44beb3a12c920a2da1b + +---------------------------------------------------------------------------- + +Add flags for Octal mode I/O data transfer +Required for the SPI controller which can do the data transfer (TX/RX) +on 8 data lines e.g. NXP FlexSPI controller. + SPI_TX_OCTAL: transmit with 8 wires + SPI_RX_OCTAL: receive with 8 wires + +Signed-off-by: Yogesh Gaur +Reviewed-by: Boris Brezillon +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang + + Conflicts: + include/linux/spi/spi.h +--- + drivers/spi/spi.c | 12 ++++++++++-- + include/linux/spi/spi.h | 3 +++ + 2 files changed, 13 insertions(+), 2 deletions(-) + +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index d2334bfae076..08c7c8ee3271 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1593,6 +1593,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, + case 4: + spi->mode |= SPI_TX_QUAD; + break; ++ case 8: ++ spi->mode |= SPI_TX_OCTAL; ++ break; + default: + dev_warn(&ctlr->dev, + "spi-tx-bus-width %d not supported\n", +@@ -1611,6 +1614,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, + case 4: + spi->mode |= SPI_RX_QUAD; + break; ++ case 8: ++ spi->mode |= SPI_RX_OCTAL; ++ break; + default: + dev_warn(&ctlr->dev, + "spi-rx-bus-width %d not supported\n", +@@ -2857,14 +2863,16 @@ int spi_setup(struct spi_device *spi) + /* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden + */ + if ((spi->mode & SPI_3WIRE) && (spi->mode & +- (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD))) ++ (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL | ++ SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL))) + return -EINVAL; + /* help drivers fail *cleanly* when they need options + * that aren't supported with their current controller + */ + bad_bits = spi->mode & ~spi->controller->mode_bits; + ugly_bits = bad_bits & +- (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD); ++ (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL | ++ SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL); + if (ugly_bits) { + dev_warn(&spi->dev, + "setup: ignoring unsupported mode bits %x\n", +diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h +index 1c0a0fa34586..80d6bd6e46c5 100644 +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -163,6 +163,9 @@ struct spi_device { + #define SPI_TX_QUAD 0x200 /* transmit with 4 wires */ + #define SPI_RX_DUAL 0x400 /* receive with 2 wires */ + #define SPI_RX_QUAD 0x800 /* receive with 4 wires */ ++#define SPI_CS_WORD 0x1000 /* toggle cs after each word */ ++#define SPI_TX_OCTAL 0x2000 /* transmit with 8 wires */ ++#define SPI_RX_OCTAL 0x4000 /* receive with 8 wires */ + int irq; + void *controller_state; + void *controller_data; +-- +2.27.0 + diff --git a/patches/0324-mtd-spi-nor-Add-support-for-mx25u12835f.patch b/patches/0324-mtd-spi-nor-Add-support-for-mx25u12835f.patch new file mode 100644 index 0000000..27f5956 --- /dev/null +++ b/patches/0324-mtd-spi-nor-Add-support-for-mx25u12835f.patch @@ -0,0 +1,42 @@ +From 4f934b660a1bd32920a9953bbeec8e4f107aa47b Mon Sep 17 00:00:00 2001 +From: Alexander Sverdlin +Date: Fri, 13 Jul 2018 15:06:46 +0200 +Subject: [PATCH 02/39] mtd: spi-nor: Add support for mx25u12835f + +mainline inclusion +from mainline-v5.0-rc1 +commit 81554171373018b83f3554b9e725d2b5bf1844a5 +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=81554171373018b83f3554b9e725d2b5bf1844a5 + +---------------------------------------------------------------------------- + +This chip supports dual and quad read and uniform 4K-byte erase. + +Signed-off-by: Alexander Sverdlin +Reviewed-by: Tudor Ambarus +Signed-off-by: Boris Brezillon +Signed-off-by: YunYi Yang +--- + drivers/mtd/spi-nor/spi-nor.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 2e183425facd..5440e33b360c 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1088,6 +1088,8 @@ static const struct flash_info spi_nor_ids[] = { + { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, + { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, ++ { "mx25u12835f", INFO(0xc22538, 0, 64 * 1024, 256, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) }, + { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, +-- +2.27.0 + diff --git a/patches/0325-mtd-spi-nor-always-use-bounce-buffer-for-register-re.patch b/patches/0325-mtd-spi-nor-always-use-bounce-buffer-for-register-re.patch new file mode 100644 index 0000000..3f1f0c0 --- /dev/null +++ b/patches/0325-mtd-spi-nor-always-use-bounce-buffer-for-register-re.patch @@ -0,0 +1,326 @@ +From 32b2c87f0bc38bca34563d24d35086b5442413d9 Mon Sep 17 00:00:00 2001 +From: Vignesh Raghavendra +Date: Tue, 6 Aug 2019 10:40:39 +0530 +Subject: [PATCH 03/39] mtd: spi-nor: always use bounce buffer for register + read/writes + +mainline inclusion +from mainline-v5.4-rc1 +commit f173f26a4d543fa57e6ed9505bbbbf9bec61f544 +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f173f26a4d543fa57e6ed9505bbbbf9bec61f544 + +---------------------------------------------------------------------------- + +spi-mem layer expects all buffers passed to it to be DMA'able. But +spi-nor layer mostly allocates buffers on stack for reading/writing to +registers and therefore are not DMA'able. Introduce bounce buffer to be +used to read/write to registers. This ensures that buffer passed to +spi-mem layer during register read/writes is DMA'able. With this change +nor->cmd-buf is no longer used, so drop it. + +Signed-off-by: Vignesh Raghavendra +Reviewed-by: Boris Brezillon +Signed-off-by: Tudor Ambarus +Signed-off-by: YunYi Yang + + Conflicts: + drivers/mtd/spi-nor/spi-nor.c + include/linux/mtd/spi-nor.h +--- + drivers/mtd/spi-nor/spi-nor.c | 78 +++++++++++++++++++---------------- + include/linux/mtd/spi-nor.h | 7 +++- + 2 files changed, 48 insertions(+), 37 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 5440e33b360c..7771d4519245 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -105,15 +105,14 @@ static const struct flash_info *spi_nor_match_id(const char *name); + static int read_sr(struct spi_nor *nor) + { + int ret; +- u8 val; + +- ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1); ++ ret = nor->read_reg(nor, SPINOR_OP_RDSR, nor->bouncebuf, 1); + if (ret < 0) { + pr_err("error %d reading SR\n", (int) ret); + return ret; + } + +- return val; ++ return nor->bouncebuf[0]; + } + + /* +@@ -124,15 +123,14 @@ static int read_sr(struct spi_nor *nor) + static int read_fsr(struct spi_nor *nor) + { + int ret; +- u8 val; + +- ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1); ++ ret = nor->read_reg(nor, SPINOR_OP_RDFSR, nor->bouncebuf, 1); + if (ret < 0) { + pr_err("error %d reading FSR\n", ret); + return ret; + } + +- return val; ++ return nor->bouncebuf[0]; + } + + /* +@@ -143,15 +141,14 @@ static int read_fsr(struct spi_nor *nor) + static int read_cr(struct spi_nor *nor) + { + int ret; +- u8 val; + +- ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1); ++ ret = nor->read_reg(nor, SPINOR_OP_RDCR, nor->bouncebuf, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading CR\n", ret); + return ret; + } + +- return val; ++ return nor->bouncebuf[0]; + } + + /* +@@ -160,8 +157,8 @@ static int read_cr(struct spi_nor *nor) + */ + static inline int write_sr(struct spi_nor *nor, u8 val) + { +- nor->cmd_buf[0] = val; +- return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1); ++ nor->bouncebuf[0] = val; ++ return nor->write_reg(nor, SPINOR_OP_WRSR, nor->bouncebuf, 1); + } + + /* +@@ -293,31 +290,31 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, + * We must clear the register to enable normal behavior. + */ + write_enable(nor); +- nor->cmd_buf[0] = 0; +- nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1); ++ nor->bouncebuf[0] = 0; ++ nor->write_reg(nor, SPINOR_OP_WREAR, ++ nor->bouncebuf, 1); + write_disable(nor); + } + + return status; + default: + /* Spansion style */ +- nor->cmd_buf[0] = enable << 7; +- return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1); ++ nor->bouncebuf[0] = enable << 7; ++ return nor->write_reg(nor, SPINOR_OP_BRWR, nor->bouncebuf, 1); + } + } + + static int s3an_sr_ready(struct spi_nor *nor) + { + int ret; +- u8 val; + +- ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1); ++ ret = nor->read_reg(nor, SPINOR_OP_XRDSR, nor->bouncebuf, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); + return ret; + } + +- return !!(val & XSR_RDY); ++ return !!(nor->bouncebuf[0] & XSR_RDY); + } + + static inline int spi_nor_sr_ready(struct spi_nor *nor) +@@ -476,7 +473,6 @@ static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr) + */ + static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) + { +- u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; + int i; + + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) +@@ -490,11 +486,12 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) + * control + */ + for (i = nor->addr_width - 1; i >= 0; i--) { +- buf[i] = addr & 0xff; ++ nor->bouncebuf[i] = addr & 0xff; + addr >>= 8; + } + +- return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width); ++ return nor->write_reg(nor, nor->erase_opcode, nor->bouncebuf, ++ nor->addr_width); + } + + /* +@@ -1271,7 +1268,7 @@ static const struct flash_info spi_nor_ids[] = { + static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) + { + int tmp; +- u8 id[SPI_NOR_MAX_ID_LEN]; ++ u8 *id = nor->bouncebuf; + const struct flash_info *info; + + tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); +@@ -1571,9 +1568,11 @@ static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr) + */ + static int spansion_quad_enable(struct spi_nor *nor) + { +- u8 sr_cr[2] = {0, CR_QUAD_EN_SPAN}; ++ u8 *sr_cr = nor->bouncebuf; + int ret; + ++ sr_cr[0] = 0; ++ sr_cr[1] = CR_QUAD_EN_SPAN; + ret = write_sr_cr(nor, sr_cr); + if (ret) + return ret; +@@ -1603,7 +1602,7 @@ static int spansion_quad_enable(struct spi_nor *nor) + */ + static int spansion_no_read_cr_quad_enable(struct spi_nor *nor) + { +- u8 sr_cr[2]; ++ u8 *sr_cr = nor->bouncebuf; + int ret; + + /* Keep the current value of the Status Register. */ +@@ -1634,7 +1633,7 @@ static int spansion_no_read_cr_quad_enable(struct spi_nor *nor) + static int spansion_read_cr_quad_enable(struct spi_nor *nor) + { + struct device *dev = nor->dev; +- u8 sr_cr[2]; ++ u8 *sr_cr = nor->bouncebuf; + int ret; + + /* Check current Quad Enable bit value. */ +@@ -1685,22 +1684,22 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor) + */ + static int sr2_bit7_quad_enable(struct spi_nor *nor) + { +- u8 sr2; ++ u8 *sr2 = nor->bouncebuf; + int ret; + + /* Check current Quad Enable bit value. */ +- ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1); ++ ret = nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1); + if (ret) + return ret; +- if (sr2 & SR2_QUAD_EN_BIT7) ++ if (*sr2 & SR2_QUAD_EN_BIT7) + return 0; + + /* Update the Quad Enable bit. */ +- sr2 |= SR2_QUAD_EN_BIT7; ++ *sr2 |= SR2_QUAD_EN_BIT7; + + write_enable(nor); + +- ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1); ++ ret = nor->write_reg(nor, SPINOR_OP_WRSR2, sr2, 1); + if (ret < 0) { + dev_err(nor->dev, "error while writing status register 2\n"); + return -EINVAL; +@@ -1713,8 +1712,8 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor) + } + + /* Read back and check it. */ +- ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1); +- if (!(ret > 0 && (sr2 & SR2_QUAD_EN_BIT7))) { ++ ret = nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1); ++ if (!(ret > 0 && (*sr2 & SR2_QUAD_EN_BIT7))) { + dev_err(nor->dev, "SR2 Quad bit not set\n"); + return -EINVAL; + } +@@ -1736,9 +1735,8 @@ static int spi_nor_check(struct spi_nor *nor) + static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) + { + int ret; +- u8 val; + +- ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1); ++ ret = nor->read_reg(nor, SPINOR_OP_XRDSR, nor->bouncebuf, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); + return ret; +@@ -1760,7 +1758,7 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) + * The current addressing mode can be read from the XRDSR register + * and should not be changed, because is a destructive operation. + */ +- if (val & XSR_PAGESIZE) { ++ if (nor->bouncebuf[0] & XSR_PAGESIZE) { + /* Flash in Power of 2 mode */ + nor->page_size = (nor->page_size == 264) ? 256 : 512; + nor->mtd.writebufsize = nor->page_size; +@@ -2819,6 +2817,16 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, + nor->read_proto = SNOR_PROTO_1_1_1; + nor->write_proto = SNOR_PROTO_1_1_1; + ++ /* ++ * We need the bounce buffer early to read/write registers when going ++ * through the spi-mem layer (buffers have to be DMA-able). ++ */ ++ nor->bouncebuf_size = PAGE_SIZE; ++ nor->bouncebuf = devm_kmalloc(dev, nor->bouncebuf_size, ++ GFP_KERNEL); ++ if (!nor->bouncebuf) ++ return -ENOMEM; ++ + if (name) + info = spi_nor_match_id(name); + /* Try to auto-detect if chip name wasn't specified or not found */ +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index c922e97f205a..66431fd2a48b 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -249,6 +249,9 @@ struct flash_info; + * @mtd: point to a mtd_info structure + * @lock: the lock for the read/write/erase/lock/unlock operations + * @dev: point to a spi device, or a spi nor controller device. ++ * @bouncebuf: bounce buffer used when the buffer passed by the MTD ++ * layer is not DMA-able ++ * @bouncebuf_size: size of the bounce buffer + * @info: spi-nor part JDEC MFR id and other info + * @page_size: the page size of the SPI NOR + * @addr_width: number of address bytes +@@ -261,7 +264,6 @@ struct flash_info; + * @read_proto: the SPI protocol for read operations + * @write_proto: the SPI protocol for write operations + * @reg_proto the SPI protocol for read_reg/write_reg/erase operations +- * @cmd_buf: used by the write_reg + * @prepare: [OPTIONAL] do some preparations for the + * read/write/erase/lock/unlock operations + * @unprepare: [OPTIONAL] do some post work after the +@@ -284,6 +286,8 @@ struct spi_nor { + struct mtd_info mtd; + struct mutex lock; + struct device *dev; ++ u8 *bouncebuf; ++ size_t bouncebuf_size; + const struct flash_info *info; + u32 page_size; + u8 addr_width; +@@ -296,7 +300,6 @@ struct spi_nor { + enum spi_nor_protocol reg_proto; + bool sst_write_second; + u32 flags; +- u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; + + int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); + void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); +-- +2.27.0 + diff --git a/patches/0326-mtd-spi-nor-Move-m25p80-code-in-spi-nor.c.patch b/patches/0326-mtd-spi-nor-Move-m25p80-code-in-spi-nor.c.patch new file mode 100644 index 0000000..2f31234 --- /dev/null +++ b/patches/0326-mtd-spi-nor-Move-m25p80-code-in-spi-nor.c.patch @@ -0,0 +1,1362 @@ +From 5509962a6895d57c21da5c6708ebbf114c3c7a54 Mon Sep 17 00:00:00 2001 +From: Boris Brezillon +Date: Tue, 6 Aug 2019 10:40:40 +0530 +Subject: [PATCH 04/39] mtd: spi-nor: Move m25p80 code in spi-nor.c + +mainline inclusion +from mainline-v5.4-rc1 +commit b35b9a10362d203451d920d01ab8d6cd55cbaf2a +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b35b9a10362d203451d920d01ab8d6cd55cbaf2a + +---------------------------------------------------------------------------- + +The m25p80 driver is actually a generic wrapper around the spi-mem +layer. Not only the driver name is misleading, but we'd expect such a +common logic to be directly available in the core. Another reason for +moving this code is that SPI NOR controller drivers should +progressively be replaced by SPI controller drivers implementing the +spi_mem_ops interface, and when the conversion is done, we should have +a single spi-nor driver directly interfacing with the spi-mem layer. + +While moving the code we also fix a longstanding issue when +non-DMA-able buffers are passed by the MTD layer. + +Signed-off-by: Boris Brezillon +Signed-off-by: Vignesh Raghavendra +Signed-off-by: Tudor Ambarus +Signed-off-by: YunYi Yang + + Conflicts: + drivers/mtd/devices/m25p80.c + drivers/mtd/spi-nor/spi-nor.c +--- + drivers/mtd/devices/Kconfig | 18 - + drivers/mtd/devices/Makefile | 1 - + drivers/mtd/devices/m25p80.c | 344 ------------------- + drivers/mtd/spi-nor/Kconfig | 2 + + drivers/mtd/spi-nor/spi-nor.c | 630 ++++++++++++++++++++++++++++++++-- + include/linux/mtd/spi-nor.h | 3 + + 6 files changed, 607 insertions(+), 391 deletions(-) + delete mode 100644 drivers/mtd/devices/m25p80.c + +diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig +index aa983422aa97..951e5c10fdd8 100644 +--- a/drivers/mtd/devices/Kconfig ++++ b/drivers/mtd/devices/Kconfig +@@ -78,24 +78,6 @@ config MTD_DATAFLASH_OTP + other key product data. The second half is programmed with a + unique-to-each-chip bit pattern at the factory. + +-config MTD_M25P80 +- tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" +- depends on SPI_MASTER && MTD_SPI_NOR +- select SPI_MEM +- help +- This enables access to most modern SPI flash chips, used for +- program and data storage. Series supported include Atmel AT26DF, +- Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips +- are supported as well. See the driver source for the current list, +- or to add other chips. +- +- Note that the original DataFlash chips (AT45 series, not AT26DF), +- need an entirely different driver. +- +- Set up your spi devices with the right board-specific platform data, +- if you want to specify device partitioning or to use a device which +- doesn't support the JEDEC ID instruction. +- + config MTD_MCHP23K256 + tristate "Microchip 23K256 SRAM" + depends on SPI_MASTER +diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile +index 94895eab3066..991c8d12c016 100644 +--- a/drivers/mtd/devices/Makefile ++++ b/drivers/mtd/devices/Makefile +@@ -12,7 +12,6 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o + obj-$(CONFIG_MTD_LART) += lart.o + obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o + obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o +-obj-$(CONFIG_MTD_M25P80) += m25p80.o + obj-$(CONFIG_MTD_MCHP23K256) += mchp23k256.o + obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o + obj-$(CONFIG_MTD_SST25L) += sst25l.o +diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c +deleted file mode 100644 +index c4a1d04b8c80..000000000000 +--- a/drivers/mtd/devices/m25p80.c ++++ /dev/null +@@ -1,344 +0,0 @@ +-/* +- * MTD SPI driver for ST M25Pxx (and similar) serial flash chips +- * +- * Author: Mike Lavender, mike@steroidmicros.com +- * +- * Copyright (c) 2005, Intec Automation Inc. +- * +- * Some parts are based on lart.c by Abraham Van Der Merwe +- * +- * Cleaned up and generalized based on mtd_dataflash.c +- * +- * This code is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- * +- */ +- +-#include +-#include +-#include +-#include +- +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-struct m25p { +- struct spi_mem *spimem; +- struct spi_nor spi_nor; +-}; +- +-static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) +-{ +- struct m25p *flash = nor->priv; +- struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1), +- SPI_MEM_OP_NO_ADDR, +- SPI_MEM_OP_NO_DUMMY, +- SPI_MEM_OP_DATA_IN(len, NULL, 1)); +- void *scratchbuf; +- int ret; +- +- scratchbuf = kmalloc(len, GFP_KERNEL); +- if (!scratchbuf) +- return -ENOMEM; +- +- op.data.buf.in = scratchbuf; +- ret = spi_mem_exec_op(flash->spimem, &op); +- if (ret < 0) +- dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret, +- code); +- else +- memcpy(val, scratchbuf, len); +- +- kfree(scratchbuf); +- +- return ret; +-} +- +-static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +-{ +- struct m25p *flash = nor->priv; +- struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1), +- SPI_MEM_OP_NO_ADDR, +- SPI_MEM_OP_NO_DUMMY, +- SPI_MEM_OP_DATA_OUT(len, NULL, 1)); +- void *scratchbuf; +- int ret; +- +- scratchbuf = kmemdup(buf, len, GFP_KERNEL); +- if (!scratchbuf) +- return -ENOMEM; +- +- op.data.buf.out = scratchbuf; +- ret = spi_mem_exec_op(flash->spimem, &op); +- kfree(scratchbuf); +- +- return ret; +-} +- +-static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len, +- const u_char *buf) +-{ +- struct m25p *flash = nor->priv; +- struct spi_mem_op op = +- SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1), +- SPI_MEM_OP_ADDR(nor->addr_width, to, 1), +- SPI_MEM_OP_NO_DUMMY, +- SPI_MEM_OP_DATA_OUT(len, buf, 1)); +- int ret; +- +- /* get transfer protocols. */ +- op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); +- op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); +- op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); +- +- if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) +- op.addr.nbytes = 0; +- +- ret = spi_mem_adjust_op_size(flash->spimem, &op); +- if (ret) +- return ret; +- op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes; +- +- ret = spi_mem_exec_op(flash->spimem, &op); +- if (ret) +- return ret; +- +- return op.data.nbytes; +-} +- +-/* +- * Read an address range from the nor chip. The address range +- * may be any size provided it is within the physical boundaries. +- */ +-static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len, +- u_char *buf) +-{ +- struct m25p *flash = nor->priv; +- struct spi_mem_op op = +- SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1), +- SPI_MEM_OP_ADDR(nor->addr_width, from, 1), +- SPI_MEM_OP_DUMMY(nor->read_dummy, 1), +- SPI_MEM_OP_DATA_IN(len, buf, 1)); +- size_t remaining = len; +- int ret; +- +- /* get transfer protocols. */ +- op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); +- op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); +- op.dummy.buswidth = op.addr.buswidth; +- op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); +- +- /* convert the dummy cycles to the number of bytes */ +- op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; +- +- while (remaining) { +- op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX; +- ret = spi_mem_adjust_op_size(flash->spimem, &op); +- if (ret) +- return ret; +- +- ret = spi_mem_exec_op(flash->spimem, &op); +- if (ret) +- return ret; +- +- op.addr.val += op.data.nbytes; +- remaining -= op.data.nbytes; +- op.data.buf.in += op.data.nbytes; +- } +- +- return len; +-} +- +-/* +- * board specific setup should have ensured the SPI clock used here +- * matches what the READ command supports, at least until this driver +- * understands FAST_READ (for clocks over 25 MHz). +- */ +-static int m25p_probe(struct spi_mem *spimem) +-{ +- struct spi_device *spi = spimem->spi; +- struct flash_platform_data *data; +- struct m25p *flash; +- struct spi_nor *nor; +- struct spi_nor_hwcaps hwcaps = { +- .mask = SNOR_HWCAPS_READ | +- SNOR_HWCAPS_READ_FAST | +- SNOR_HWCAPS_PP, +- }; +- char *flash_name; +- int ret; +- +- data = dev_get_platdata(&spimem->spi->dev); +- +- flash = devm_kzalloc(&spimem->spi->dev, sizeof(*flash), GFP_KERNEL); +- if (!flash) +- return -ENOMEM; +- +- nor = &flash->spi_nor; +- +- /* install the hooks */ +- nor->read = m25p80_read; +- nor->write = m25p80_write; +- nor->write_reg = m25p80_write_reg; +- nor->read_reg = m25p80_read_reg; +- +- nor->dev = &spimem->spi->dev; +- spi_nor_set_flash_node(nor, spi->dev.of_node); +- nor->priv = flash; +- +- spi_mem_set_drvdata(spimem, flash); +- flash->spimem = spimem; +- +- if (spi->mode & SPI_RX_QUAD) { +- hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; +- +- if (spi->mode & SPI_TX_QUAD) +- hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 | +- SNOR_HWCAPS_PP_1_1_4 | +- SNOR_HWCAPS_PP_1_4_4); +- } else if (spi->mode & SPI_RX_DUAL) { +- hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; +- +- if (spi->mode & SPI_TX_DUAL) +- hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2; +- } +- +- if (data && data->name) +- nor->mtd.name = data->name; +- +- if (!nor->mtd.name) +- nor->mtd.name = spi_mem_get_name(spimem); +- +- /* For some (historical?) reason many platforms provide two different +- * names in flash_platform_data: "name" and "type". Quite often name is +- * set to "m25p80" and then "type" provides a real chip name. +- * If that's the case, respect "type" and ignore a "name". +- */ +- if (data && data->type) +- flash_name = data->type; +- else if (!strcmp(spi->modalias, "spi-nor")) +- flash_name = NULL; /* auto-detect */ +- else +- flash_name = spi->modalias; +- +- ret = spi_nor_scan(nor, flash_name, &hwcaps); +- if (ret) +- return ret; +- +- return mtd_device_register(&nor->mtd, data ? data->parts : NULL, +- data ? data->nr_parts : 0); +-} +- +- +-static int m25p_remove(struct spi_mem *spimem) +-{ +- struct m25p *flash = spi_mem_get_drvdata(spimem); +- +- spi_nor_restore(&flash->spi_nor); +- +- /* Clean up MTD stuff. */ +- return mtd_device_unregister(&flash->spi_nor.mtd); +-} +- +-static void m25p_shutdown(struct spi_mem *spimem) +-{ +- struct m25p *flash = spi_mem_get_drvdata(spimem); +- +- spi_nor_restore(&flash->spi_nor); +-} +-/* +- * Do NOT add to this array without reading the following: +- * +- * Historically, many flash devices are bound to this driver by their name. But +- * since most of these flash are compatible to some extent, and their +- * differences can often be differentiated by the JEDEC read-ID command, we +- * encourage new users to add support to the spi-nor library, and simply bind +- * against a generic string here (e.g., "jedec,spi-nor"). +- * +- * Many flash names are kept here in this list (as well as in spi-nor.c) to +- * keep them available as module aliases for existing platforms. +- */ +-static const struct spi_device_id m25p_ids[] = { +- /* +- * Allow non-DT platform devices to bind to the "spi-nor" modalias, and +- * hack around the fact that the SPI core does not provide uevent +- * matching for .of_match_table +- */ +- {"spi-nor"}, +- +- /* +- * Entries not used in DTs that should be safe to drop after replacing +- * them with "spi-nor" in platform data. +- */ +- {"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"}, +- +- /* +- * Entries that were used in DTs without "jedec,spi-nor" fallback and +- * should be kept for backward compatibility. +- */ +- {"at25df321a"}, {"at25df641"}, {"at26df081a"}, +- {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"}, +- {"mx25l25635e"},{"mx66l51235l"}, +- {"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"}, +- {"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"}, +- {"s25fl064k"}, +- {"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"}, +- {"m25p40"}, {"m25p80"}, {"m25p16"}, {"m25p32"}, +- {"m25p64"}, {"m25p128"}, +- {"w25x80"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"}, +- {"w25q80bl"}, {"w25q128"}, {"w25q256"}, +- +- /* Flashes that can't be detected using JEDEC */ +- {"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"}, +- {"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"}, +- {"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"}, +- +- /* Everspin MRAMs (non-JEDEC) */ +- { "mr25h128" }, /* 128 Kib, 40 MHz */ +- { "mr25h256" }, /* 256 Kib, 40 MHz */ +- { "mr25h10" }, /* 1 Mib, 40 MHz */ +- { "mr25h40" }, /* 4 Mib, 40 MHz */ +- +- { }, +-}; +-MODULE_DEVICE_TABLE(spi, m25p_ids); +- +-static const struct of_device_id m25p_of_table[] = { +- /* +- * Generic compatibility for SPI NOR that can be identified by the +- * JEDEC READ ID opcode (0x9F). Use this, if possible. +- */ +- { .compatible = "jedec,spi-nor" }, +- {} +-}; +-MODULE_DEVICE_TABLE(of, m25p_of_table); +- +-static struct spi_mem_driver m25p80_driver = { +- .spidrv = { +- .driver = { +- .name = "m25p80", +- .of_match_table = m25p_of_table, +- }, +- .id_table = m25p_ids, +- }, +- .probe = m25p_probe, +- .remove = m25p_remove, +- .shutdown = m25p_shutdown, +- +- /* REVISIT: many of these chips have deep power-down modes, which +- * should clearly be entered on suspend() to minimize power use. +- * And also when they're otherwise idle... +- */ +-}; +- +-module_spi_mem_driver(m25p80_driver); +- +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Mike Lavender"); +-MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips"); +diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig +index 37775fc09e09..55e38e38e822 100644 +--- a/drivers/mtd/spi-nor/Kconfig ++++ b/drivers/mtd/spi-nor/Kconfig +@@ -1,6 +1,8 @@ + menuconfig MTD_SPI_NOR + tristate "SPI-NOR device support" + depends on MTD ++ depends on MTD && SPI_MASTER ++ select SPI_MEM + help + This is the framework for the SPI NOR which can be used by the SPI + device drivers and the SPI-NOR device driver. +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 7771d4519245..0161662768e7 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -21,6 +21,7 @@ + + #include + #include ++#include + #include + #include + +@@ -97,6 +98,154 @@ struct flash_info { + + static const struct flash_info *spi_nor_match_id(const char *name); + ++/** ++ * spi_nor_spimem_xfer_data() - helper function to read/write data to ++ * flash's memory region ++ * @nor: pointer to 'struct spi_nor' ++ * @op: pointer to 'struct spi_mem_op' template for transfer ++ * ++ * Return: number of bytes transferred on success, -errno otherwise ++ */ ++static ssize_t spi_nor_spimem_xfer_data(struct spi_nor *nor, ++ struct spi_mem_op *op) ++{ ++ bool usebouncebuf = false; ++ void *rdbuf = NULL; ++ const void *buf; ++ int ret; ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) ++ buf = op->data.buf.in; ++ else ++ buf = op->data.buf.out; ++ ++ if (object_is_on_stack(buf) || !virt_addr_valid(buf)) ++ usebouncebuf = true; ++ ++ if (usebouncebuf) { ++ if (op->data.nbytes > nor->bouncebuf_size) ++ op->data.nbytes = nor->bouncebuf_size; ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) { ++ rdbuf = op->data.buf.in; ++ op->data.buf.in = nor->bouncebuf; ++ } else { ++ op->data.buf.out = nor->bouncebuf; ++ memcpy(nor->bouncebuf, buf, ++ op->data.nbytes); ++ } ++ } ++ ++ ret = spi_mem_adjust_op_size(nor->spimem, op); ++ if (ret) ++ return ret; ++ ++ ret = spi_mem_exec_op(nor->spimem, op); ++ if (ret) ++ return ret; ++ ++ if (usebouncebuf && op->data.dir == SPI_MEM_DATA_IN) ++ memcpy(rdbuf, nor->bouncebuf, op->data.nbytes); ++ ++ return op->data.nbytes; ++} ++ ++/** ++ * spi_nor_spimem_read_data() - read data from flash's memory region via ++ * spi-mem ++ * @nor: pointer to 'struct spi_nor' ++ * @from: offset to read from ++ * @len: number of bytes to read ++ * @buf: pointer to dst buffer ++ * ++ * Return: number of bytes read successfully, -errno otherwise ++ */ ++static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, ++ size_t len, u8 *buf) ++{ ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1), ++ SPI_MEM_OP_ADDR(nor->addr_width, from, 1), ++ SPI_MEM_OP_DUMMY(nor->read_dummy, 1), ++ SPI_MEM_OP_DATA_IN(len, buf, 1)); ++ ++ /* get transfer protocols. */ ++ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); ++ op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); ++ op.dummy.buswidth = op.addr.buswidth; ++ op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); ++ ++ /* convert the dummy cycles to the number of bytes */ ++ op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; ++ ++ return spi_nor_spimem_xfer_data(nor, &op); ++} ++ ++/** ++ * spi_nor_read_data() - read data from flash memory ++ * @nor: pointer to 'struct spi_nor' ++ * @from: offset to read from ++ * @len: number of bytes to read ++ * @buf: pointer to dst buffer ++ * ++ * Return: number of bytes read successfully, -errno otherwise ++ */ ++static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, ++ u8 *buf) ++{ ++ if (nor->spimem) ++ return spi_nor_spimem_read_data(nor, from, len, buf); ++ ++ return nor->read(nor, from, len, buf); ++} ++ ++/** ++ * spi_nor_spimem_write_data() - write data to flash memory via ++ * spi-mem ++ * @nor: pointer to 'struct spi_nor' ++ * @to: offset to write to ++ * @len: number of bytes to write ++ * @buf: pointer to src buffer ++ * ++ * Return: number of bytes written successfully, -errno otherwise ++ */ ++static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, ++ size_t len, const u8 *buf) ++{ ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1), ++ SPI_MEM_OP_ADDR(nor->addr_width, to, 1), ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_OUT(len, buf, 1)); ++ ++ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); ++ op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); ++ op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); ++ ++ if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) ++ op.addr.nbytes = 0; ++ ++ return spi_nor_spimem_xfer_data(nor, &op); ++} ++ ++/** ++ * spi_nor_write_data() - write data to flash memory ++ * @nor: pointer to 'struct spi_nor' ++ * @to: offset to write to ++ * @len: number of bytes to write ++ * @buf: pointer to src buffer ++ * ++ * Return: number of bytes written successfully, -errno otherwise ++ */ ++static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, ++ const u8 *buf) ++{ ++ if (nor->spimem) ++ return spi_nor_spimem_write_data(nor, to, len, buf); ++ ++ return nor->write(nor, to, len, buf); ++} ++ + /* + * Read the status register, returning its value in the location + * Return the status register value. +@@ -106,7 +255,18 @@ static int read_sr(struct spi_nor *nor) + { + int ret; + +- ret = nor->read_reg(nor, SPINOR_OP_RDSR, nor->bouncebuf, 1); ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); ++ ++ ret = spi_mem_exec_op(nor->spimem, &op); ++ } else { ++ ret = nor->read_reg(nor, SPINOR_OP_RDSR, nor->bouncebuf, 1); ++ } ++ + if (ret < 0) { + pr_err("error %d reading SR\n", (int) ret); + return ret; +@@ -124,7 +284,18 @@ static int read_fsr(struct spi_nor *nor) + { + int ret; + +- ret = nor->read_reg(nor, SPINOR_OP_RDFSR, nor->bouncebuf, 1); ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); ++ ++ ret = spi_mem_exec_op(nor->spimem, &op); ++ } else { ++ ret = nor->read_reg(nor, SPINOR_OP_RDFSR, nor->bouncebuf, 1); ++ } ++ + if (ret < 0) { + pr_err("error %d reading FSR\n", ret); + return ret; +@@ -142,7 +313,18 @@ static int read_cr(struct spi_nor *nor) + { + int ret; + +- ret = nor->read_reg(nor, SPINOR_OP_RDCR, nor->bouncebuf, 1); ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); ++ ++ ret = spi_mem_exec_op(nor->spimem, &op); ++ } else { ++ ret = nor->read_reg(nor, SPINOR_OP_RDCR, nor->bouncebuf, 1); ++ } ++ + if (ret < 0) { + dev_err(nor->dev, "error %d reading CR\n", ret); + return ret; +@@ -158,6 +340,16 @@ static int read_cr(struct spi_nor *nor) + static inline int write_sr(struct spi_nor *nor, u8 val) + { + nor->bouncebuf[0] = val; ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ + return nor->write_reg(nor, SPINOR_OP_WRSR, nor->bouncebuf, 1); + } + +@@ -167,6 +359,16 @@ static inline int write_sr(struct spi_nor *nor, u8 val) + */ + static inline int write_enable(struct spi_nor *nor) + { ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_NO_DATA); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ + return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); + } + +@@ -175,6 +377,16 @@ static inline int write_enable(struct spi_nor *nor) + */ + static inline int write_disable(struct spi_nor *nor) + { ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_NO_DATA); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ + return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0); + } + +@@ -259,13 +471,65 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor, + nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode); + } + ++static int macronix_set_4byte(struct spi_nor *nor, bool enable) ++{ ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? ++ SPINOR_OP_EN4B : ++ SPINOR_OP_EX4B, ++ 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_NO_DATA); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ ++ return nor->write_reg(nor, enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B, ++ NULL, 0); ++} ++ ++static int spansion_set_4byte(struct spi_nor *nor, bool enable) ++{ ++ nor->bouncebuf[0] = enable << 7; ++ ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ ++ return nor->write_reg(nor, SPINOR_OP_BRWR, nor->bouncebuf, 1); ++} ++ ++static int spi_nor_write_ear(struct spi_nor *nor, u8 ear) ++{ ++ nor->bouncebuf[0] = ear; ++ ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ ++ return nor->write_reg(nor, SPINOR_OP_WREAR, nor->bouncebuf, 1); ++} ++ + /* Enable/disable 4-byte addressing mode. */ + static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, + int enable) + { + int status; + bool need_wren = false; +- u8 cmd; + + switch (JEDEC_MFR(info)) { + case SNOR_MFR_MICRON: +@@ -276,8 +540,7 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, + if (need_wren) + write_enable(nor); + +- cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; +- status = nor->write_reg(nor, cmd, NULL, 0); ++ status = macronix_set_4byte(nor, enable); + if (need_wren) + write_disable(nor); + +@@ -290,25 +553,37 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, + * We must clear the register to enable normal behavior. + */ + write_enable(nor); +- nor->bouncebuf[0] = 0; +- nor->write_reg(nor, SPINOR_OP_WREAR, +- nor->bouncebuf, 1); ++ spi_nor_write_ear(nor, 0); + write_disable(nor); + } + + return status; + default: + /* Spansion style */ +- nor->bouncebuf[0] = enable << 7; +- return nor->write_reg(nor, SPINOR_OP_BRWR, nor->bouncebuf, 1); ++ return spansion_set_4byte(nor, enable); + } + } + ++static int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr) ++{ ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_IN(1, sr, 1)); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ ++ return nor->read_reg(nor, SPINOR_OP_XRDSR, sr, 1); ++} ++ + static int s3an_sr_ready(struct spi_nor *nor) + { + int ret; + +- ret = nor->read_reg(nor, SPINOR_OP_XRDSR, nor->bouncebuf, 1); ++ ret = spi_nor_xread_sr(nor, nor->bouncebuf); + if (ret < 0) { + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); + return ret; +@@ -317,6 +592,21 @@ static int s3an_sr_ready(struct spi_nor *nor) + return !!(nor->bouncebuf[0] & XSR_RDY); + } + ++static int spi_nor_clear_sr(struct spi_nor *nor) ++{ ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_NO_DATA); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ ++ return nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0); ++} ++ + static inline int spi_nor_sr_ready(struct spi_nor *nor) + { + int sr = read_sr(nor); +@@ -329,13 +619,28 @@ static inline int spi_nor_sr_ready(struct spi_nor *nor) + else + dev_err(nor->dev, "Programming Error occurred\n"); + +- nor->write_reg(nor, SPINOR_OP_CLSR, NULL, 0); ++ spi_nor_clear_sr(nor); + return -EIO; + } + + return !(sr & SR_WIP); + } + ++static int spi_nor_clear_fsr(struct spi_nor *nor) ++{ ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_NO_DATA); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ ++ return nor->write_reg(nor, SPINOR_OP_CLFSR, NULL, 0); ++} ++ + static inline int spi_nor_fsr_ready(struct spi_nor *nor) + { + int fsr = read_fsr(nor); +@@ -352,7 +657,7 @@ static inline int spi_nor_fsr_ready(struct spi_nor *nor) + dev_err(nor->dev, + "Attempted to modify a protected sector.\n"); + +- nor->write_reg(nor, SPINOR_OP_CLFSR, NULL, 0); ++ spi_nor_clear_fsr(nor); + return -EIO; + } + +@@ -420,6 +725,16 @@ static int erase_chip(struct spi_nor *nor) + { + dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10)); + ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_NO_DATA); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ + return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0); + } + +@@ -481,6 +796,16 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) + if (nor->erase) + return nor->erase(nor, addr); + ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 1), ++ SPI_MEM_OP_ADDR(nor->addr_width, addr, 1), ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_NO_DATA); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ + /* + * Default implementation, if driver doesn't have a specialized HW + * control +@@ -1265,13 +1590,55 @@ static const struct flash_info spi_nor_ids[] = { + { }, + }; + ++static int spi_nor_write_sr2(struct spi_nor *nor, u8 *sr2) ++{ ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_OUT(1, sr2, 1)); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ ++ return nor->write_reg(nor, SPINOR_OP_WRSR2, sr2, 1); ++} ++ ++static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) ++{ ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_IN(1, sr2, 1)); ++ ++ return spi_mem_exec_op(nor->spimem, &op); ++ } ++ ++ return nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1); ++} ++ ++ + static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) + { + int tmp; + u8 *id = nor->bouncebuf; + const struct flash_info *info; + +- tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_IN(SPI_NOR_MAX_ID_LEN, ++ id, 1)); ++ tmp = spi_mem_exec_op(nor->spimem, &op); ++ } else { ++ tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, ++ SPI_NOR_MAX_ID_LEN); ++ } + if (tmp < 0) { + dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); +@@ -1307,7 +1674,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) + addr = spi_nor_s3an_addr_convert(nor, addr); + +- ret = nor->read(nor, addr, len, buf); ++ ret = spi_nor_read_data(nor, addr, len, buf); + if (ret == 0) { + /* We shouldn't see 0-length reads */ + ret = -EIO; +@@ -1352,7 +1719,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + nor->program_opcode = SPINOR_OP_BP; + + /* write one byte. */ +- ret = nor->write(nor, to, 1, buf); ++ ret = spi_nor_write_data(nor, to, 1, buf); + if (ret < 0) + goto sst_write_err; + WARN(ret != 1, "While writing 1 byte written %i bytes\n", +@@ -1368,7 +1735,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + nor->program_opcode = SPINOR_OP_AAI_WP; + + /* write two bytes. */ +- ret = nor->write(nor, to, 2, buf + actual); ++ ret = spi_nor_write_data(nor, to, 2, buf + actual); + if (ret < 0) + goto sst_write_err; + WARN(ret != 2, "While writing 2 bytes written %i bytes\n", +@@ -1391,7 +1758,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + write_enable(nor); + + nor->program_opcode = SPINOR_OP_BP; +- ret = nor->write(nor, to, 1, buf + actual); ++ ret = spi_nor_write_data(nor, to, 1, buf + actual); + if (ret < 0) + goto sst_write_err; + WARN(ret != 1, "While writing 1 byte written %i bytes\n", +@@ -1453,7 +1820,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, + addr = spi_nor_s3an_addr_convert(nor, addr); + + write_enable(nor); +- ret = nor->write(nor, addr, page_remain, buf + i); ++ ret = spi_nor_write_data(nor, addr, page_remain, buf + i); + if (ret < 0) + goto write_err; + written = ret; +@@ -1526,7 +1893,19 @@ static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr) + + write_enable(nor); + +- ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2); ++ if (nor->spimem) { ++ struct spi_mem_op op = ++ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), ++ SPI_MEM_OP_NO_ADDR, ++ SPI_MEM_OP_NO_DUMMY, ++ SPI_MEM_OP_DATA_OUT(2, sr_cr, 1)); ++ ++ ret = spi_mem_exec_op(nor->spimem, &op); ++ } else { ++ ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2); ++ } ++ ++ + if (ret < 0) { + dev_err(nor->dev, + "error while writing configuration register\n"); +@@ -1688,7 +2067,7 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor) + int ret; + + /* Check current Quad Enable bit value. */ +- ret = nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1); ++ ret = spi_nor_read_sr2(nor, sr2); + if (ret) + return ret; + if (*sr2 & SR2_QUAD_EN_BIT7) +@@ -1699,7 +2078,7 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor) + + write_enable(nor); + +- ret = nor->write_reg(nor, SPINOR_OP_WRSR2, sr2, 1); ++ ret = spi_nor_write_sr2(nor, sr2); + if (ret < 0) { + dev_err(nor->dev, "error while writing status register 2\n"); + return -EINVAL; +@@ -1712,7 +2091,7 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor) + } + + /* Read back and check it. */ +- ret = nor->read_reg(nor, SPINOR_OP_RDSR2, sr2, 1); ++ ret = spi_nor_read_sr2(nor, sr2); + if (!(ret > 0 && (*sr2 & SR2_QUAD_EN_BIT7))) { + dev_err(nor->dev, "SR2 Quad bit not set\n"); + return -EINVAL; +@@ -1723,8 +2102,10 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor) + + static int spi_nor_check(struct spi_nor *nor) + { +- if (!nor->dev || !nor->read || !nor->write || +- !nor->read_reg || !nor->write_reg) { ++ if (!nor->dev || ++ (!nor->spimem && ++ (!nor->read || !nor->write || !nor->read_reg || ++ !nor->write_reg))) { + pr_err("spi-nor: please fill all the necessary fields!\n"); + return -EINVAL; + } +@@ -1736,7 +2117,7 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) + { + int ret; + +- ret = nor->read_reg(nor, SPINOR_OP_XRDSR, nor->bouncebuf, 1); ++ ret = spi_nor_xread_sr(nor, nor->bouncebuf); + if (ret < 0) { + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); + return ret; +@@ -1891,7 +2272,7 @@ static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr, + nor->read_dummy = 8; + + while (len) { +- ret = nor->read(nor, addr, len, (u8 *)buf); ++ ret = spi_nor_read_data(nor, addr, len, buf); + if (!ret || ret > len) { + ret = -EIO; + goto read_err; +@@ -2820,6 +3201,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, + /* + * We need the bounce buffer early to read/write registers when going + * through the spi-mem layer (buffers have to be DMA-able). ++ * For spi-mem drivers, we'll reallocate a new buffer if ++ * nor->page_size turns out to be greater than PAGE_SIZE (which ++ * shouldn't happen before long since NOR pages are usually less ++ * than 1KB) after spi_nor_scan() returns. + */ + nor->bouncebuf_size = PAGE_SIZE; + nor->bouncebuf = devm_kmalloc(dev, nor->bouncebuf_size, +@@ -3017,6 +3402,195 @@ static const struct flash_info *spi_nor_match_id(const char *name) + return NULL; + } + ++static int spi_nor_probe(struct spi_mem *spimem) ++{ ++ struct spi_device *spi = spimem->spi; ++ struct flash_platform_data *data = dev_get_platdata(&spi->dev); ++ struct spi_nor *nor; ++ struct spi_nor_hwcaps hwcaps = { ++ .mask = SNOR_HWCAPS_READ | ++ SNOR_HWCAPS_READ_FAST | ++ SNOR_HWCAPS_PP, ++ }; ++ char *flash_name; ++ int ret; ++ ++ nor = devm_kzalloc(&spi->dev, sizeof(*nor), GFP_KERNEL); ++ if (!nor) ++ return -ENOMEM; ++ ++ nor->spimem = spimem; ++ nor->dev = &spi->dev; ++ spi_nor_set_flash_node(nor, spi->dev.of_node); ++ ++ spi_mem_set_drvdata(spimem, nor); ++ ++ if (spi->mode & SPI_RX_OCTAL) { ++ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8; ++ ++ if (spi->mode & SPI_TX_OCTAL) ++ hwcaps.mask |= (SNOR_HWCAPS_READ_1_8_8 | ++ SNOR_HWCAPS_PP_1_1_8 | ++ SNOR_HWCAPS_PP_1_8_8); ++ } else if (spi->mode & SPI_RX_QUAD) { ++ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; ++ ++ if (spi->mode & SPI_TX_QUAD) ++ hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 | ++ SNOR_HWCAPS_PP_1_1_4 | ++ SNOR_HWCAPS_PP_1_4_4); ++ } else if (spi->mode & SPI_RX_DUAL) { ++ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; ++ ++ if (spi->mode & SPI_TX_DUAL) ++ hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2; ++ } ++ ++ if (data && data->name) ++ nor->mtd.name = data->name; ++ ++ if (!nor->mtd.name) ++ nor->mtd.name = spi_mem_get_name(spimem); ++ ++ /* ++ * For some (historical?) reason many platforms provide two different ++ * names in flash_platform_data: "name" and "type". Quite often name is ++ * set to "m25p80" and then "type" provides a real chip name. ++ * If that's the case, respect "type" and ignore a "name". ++ */ ++ if (data && data->type) ++ flash_name = data->type; ++ else if (!strcmp(spi->modalias, "spi-nor")) ++ flash_name = NULL; /* auto-detect */ ++ else ++ flash_name = spi->modalias; ++ ++ ret = spi_nor_scan(nor, flash_name, &hwcaps); ++ if (ret) ++ return ret; ++ ++ /* ++ * None of the existing parts have > 512B pages, but let's play safe ++ * and add this logic so that if anyone ever adds support for such ++ * a NOR we don't end up with buffer overflows. ++ */ ++ if (nor->page_size > PAGE_SIZE) { ++ nor->bouncebuf_size = nor->page_size; ++ devm_kfree(nor->dev, nor->bouncebuf); ++ nor->bouncebuf = devm_kmalloc(nor->dev, ++ nor->bouncebuf_size, ++ GFP_KERNEL); ++ if (!nor->bouncebuf) ++ return -ENOMEM; ++ } ++ ++ return mtd_device_register(&nor->mtd, data ? data->parts : NULL, ++ data ? data->nr_parts : 0); ++} ++ ++static int spi_nor_remove(struct spi_mem *spimem) ++{ ++ struct spi_nor *nor = spi_mem_get_drvdata(spimem); ++ ++ spi_nor_restore(nor); ++ ++ /* Clean up MTD stuff. */ ++ return mtd_device_unregister(&nor->mtd); ++} ++ ++static void spi_nor_shutdown(struct spi_mem *spimem) ++{ ++ struct spi_nor *nor = spi_mem_get_drvdata(spimem); ++ ++ spi_nor_restore(nor); ++} ++ ++/* ++ * Do NOT add to this array without reading the following: ++ * ++ * Historically, many flash devices are bound to this driver by their name. But ++ * since most of these flash are compatible to some extent, and their ++ * differences can often be differentiated by the JEDEC read-ID command, we ++ * encourage new users to add support to the spi-nor library, and simply bind ++ * against a generic string here (e.g., "jedec,spi-nor"). ++ * ++ * Many flash names are kept here in this list (as well as in spi-nor.c) to ++ * keep them available as module aliases for existing platforms. ++ */ ++static const struct spi_device_id spi_nor_dev_ids[] = { ++ /* ++ * Allow non-DT platform devices to bind to the "spi-nor" modalias, and ++ * hack around the fact that the SPI core does not provide uevent ++ * matching for .of_match_table ++ */ ++ {"spi-nor"}, ++ ++ /* ++ * Entries not used in DTs that should be safe to drop after replacing ++ * them with "spi-nor" in platform data. ++ */ ++ {"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"}, ++ ++ /* ++ * Entries that were used in DTs without "jedec,spi-nor" fallback and ++ * should be kept for backward compatibility. ++ */ ++ {"at25df321a"}, {"at25df641"}, {"at26df081a"}, ++ {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"}, ++ {"mx25l25635e"}, {"mx66l51235l"}, ++ {"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"}, ++ {"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"}, ++ {"s25fl064k"}, ++ {"sst25vf040b"}, {"sst25vf016b"}, {"sst25vf032b"}, {"sst25wf040"}, ++ {"m25p40"}, {"m25p80"}, {"m25p16"}, {"m25p32"}, ++ {"m25p64"}, {"m25p128"}, ++ {"w25x80"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"}, ++ {"w25q80bl"}, {"w25q128"}, {"w25q256"}, ++ ++ /* Flashes that can't be detected using JEDEC */ ++ {"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"}, ++ {"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"}, ++ {"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"}, ++ ++ /* Everspin MRAMs (non-JEDEC) */ ++ { "mr25h128" }, /* 128 Kib, 40 MHz */ ++ { "mr25h256" }, /* 256 Kib, 40 MHz */ ++ { "mr25h10" }, /* 1 Mib, 40 MHz */ ++ { "mr25h40" }, /* 4 Mib, 40 MHz */ ++ ++ { }, ++}; ++MODULE_DEVICE_TABLE(spi, spi_nor_dev_ids); ++ ++static const struct of_device_id spi_nor_of_table[] = { ++ /* ++ * Generic compatibility for SPI NOR that can be identified by the ++ * JEDEC READ ID opcode (0x9F). Use this, if possible. ++ */ ++ { .compatible = "jedec,spi-nor" }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, spi_nor_of_table); ++ ++/* ++ * REVISIT: many of these chips have deep power-down modes, which ++ * should clearly be entered on suspend() to minimize power use. ++ * And also when they're otherwise idle... ++ */ ++static struct spi_mem_driver spi_nor_driver = { ++ .spidrv = { ++ .driver = { ++ .name = "spi-nor", ++ .of_match_table = spi_nor_of_table, ++ }, ++ .id_table = spi_nor_dev_ids, ++ }, ++ .probe = spi_nor_probe, ++ .remove = spi_nor_remove, ++ .shutdown = spi_nor_shutdown, ++}; ++module_spi_mem_driver(spi_nor_driver); ++ + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Huang Shijie "); + MODULE_AUTHOR("Mike Lavender"); +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index 66431fd2a48b..ac5e2656e9b3 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + /* + * Manufacturer IDs +@@ -249,6 +250,7 @@ struct flash_info; + * @mtd: point to a mtd_info structure + * @lock: the lock for the read/write/erase/lock/unlock operations + * @dev: point to a spi device, or a spi nor controller device. ++ * @spimem: point to the spi mem device + * @bouncebuf: bounce buffer used when the buffer passed by the MTD + * layer is not DMA-able + * @bouncebuf_size: size of the bounce buffer +@@ -286,6 +288,7 @@ struct spi_nor { + struct mtd_info mtd; + struct mutex lock; + struct device *dev; ++ struct spi_mem *spimem; + u8 *bouncebuf; + size_t bouncebuf_size; + const struct flash_info *info; +-- +2.27.0 + diff --git a/patches/0327-mtd-spi-nor-Fix-direction-of-the-write_sr-transfer.patch b/patches/0327-mtd-spi-nor-Fix-direction-of-the-write_sr-transfer.patch new file mode 100644 index 0000000..beb9306 --- /dev/null +++ b/patches/0327-mtd-spi-nor-Fix-direction-of-the-write_sr-transfer.patch @@ -0,0 +1,45 @@ +From 1389e9a38b24a3e0dcf0b5dd62c99a57756619fb Mon Sep 17 00:00:00 2001 +From: Tudor Ambarus +Date: Fri, 4 Oct 2019 10:47:55 +0000 +Subject: [PATCH 05/39] mtd: spi-nor: Fix direction of the write_sr() transfer + +mainline inclusion +from mainline-v5.4-rc3 +commit 41e086e1550646344dd47d3462ca2d19caabb943 +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=41e086e1550646344dd47d3462ca2d19caabb943 + +---------------------------------------------------------------------------- + +write_sr() sends data to the SPI memory, fix the direction. + +Fixes: b35b9a10362d ("mtd: spi-nor: Move m25p80 code in spi-nor.c") +Reported-by: John Garry +Signed-off-by: Tudor Ambarus +Tested-by: John Garry +Acked-by: Vignesh Raghavendra +Signed-off-by: Miquel Raynal +Signed-off-by: YunYi Yang +--- + drivers/mtd/spi-nor/spi-nor.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 0161662768e7..33dace0b4c8b 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -345,7 +345,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, +- SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); ++ SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); + + return spi_mem_exec_op(nor->spimem, &op); + } +-- +2.27.0 + diff --git a/patches/0328-mtd-spi-nor-Prepend-spi_nor_-to-all-Reg-Ops-methods.patch b/patches/0328-mtd-spi-nor-Prepend-spi_nor_-to-all-Reg-Ops-methods.patch new file mode 100644 index 0000000..7e3a86f --- /dev/null +++ b/patches/0328-mtd-spi-nor-Prepend-spi_nor_-to-all-Reg-Ops-methods.patch @@ -0,0 +1,412 @@ +From 2a3bbff9e6e53d35b55a9439b14ba3a81662bb11 Mon Sep 17 00:00:00 2001 +From: Tudor Ambarus +Date: Tue, 29 Oct 2019 11:16:49 +0000 +Subject: [PATCH 06/39] mtd: spi-nor: Prepend spi_nor_ to all Reg Ops methods + +mainline inclusion +from mainline-v5.5-rc1 +commit 567c2983efb9a4b3d26a221b477346d927092b8a +category: cleanup +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=567c2983efb9a4b3d26a221b477346d927092b8a + +---------------------------------------------------------------------------- + +All the core functions should begin with "spi_nor_". + +Signed-off-by: Tudor Ambarus +Reviewed-by: Boris Brezillon +Signed-off-by: YunYi Yang + + Conflicts: + drivers/mtd/spi-nor/spi-nor.c +--- + drivers/mtd/spi-nor/spi-nor.c | 97 ++++++++++++++++++----------------- + 1 file changed, 49 insertions(+), 48 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 33dace0b4c8b..0809018daf4c 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -251,7 +251,7 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, + * Return the status register value. + * Returns negative if error occurred. + */ +-static int read_sr(struct spi_nor *nor) ++static int spi_nor_read_sr(struct spi_nor *nor) + { + int ret; + +@@ -280,7 +280,7 @@ static int read_sr(struct spi_nor *nor) + * Return the status register value. + * Returns negative if error occurred. + */ +-static int read_fsr(struct spi_nor *nor) ++static int spi_nor_read_fsr(struct spi_nor *nor) + { + int ret; + +@@ -309,7 +309,7 @@ static int read_fsr(struct spi_nor *nor) + * location. Return the configuration register value. + * Returns negative if error occurred. + */ +-static int read_cr(struct spi_nor *nor) ++static int spi_nor_read_cr(struct spi_nor *nor) + { + int ret; + +@@ -337,7 +337,7 @@ static int read_cr(struct spi_nor *nor) + * Write status register 1 byte + * Returns negative if error occurred. + */ +-static inline int write_sr(struct spi_nor *nor, u8 val) ++static int spi_nor_write_sr(struct spi_nor *nor, u8 val) + { + nor->bouncebuf[0] = val; + if (nor->spimem) { +@@ -357,7 +357,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val) + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +-static inline int write_enable(struct spi_nor *nor) ++static int spi_nor_write_enable(struct spi_nor *nor) + { + if (nor->spimem) { + struct spi_mem_op op = +@@ -375,7 +375,7 @@ static inline int write_enable(struct spi_nor *nor) + /* + * Send write disable instruction to the chip. + */ +-static inline int write_disable(struct spi_nor *nor) ++static int spi_nor_write_disable(struct spi_nor *nor) + { + if (nor->spimem) { + struct spi_mem_op op = +@@ -538,11 +538,11 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, + case SNOR_MFR_MACRONIX: + case SNOR_MFR_WINBOND: + if (need_wren) +- write_enable(nor); ++ spi_nor_write_enable(nor); + + status = macronix_set_4byte(nor, enable); + if (need_wren) +- write_disable(nor); ++ spi_nor_write_disable(nor); + + if (!status && !enable && + JEDEC_MFR(info) == SNOR_MFR_WINBOND) { +@@ -552,9 +552,9 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, + * 3-byte-address reads come from the second 16M. + * We must clear the register to enable normal behavior. + */ +- write_enable(nor); ++ spi_nor_write_enable(nor); + spi_nor_write_ear(nor, 0); +- write_disable(nor); ++ spi_nor_write_disable(nor); + } + + return status; +@@ -609,7 +609,7 @@ static int spi_nor_clear_sr(struct spi_nor *nor) + + static inline int spi_nor_sr_ready(struct spi_nor *nor) + { +- int sr = read_sr(nor); ++ int sr = spi_nor_read_sr(nor); + if (sr < 0) + return sr; + +@@ -643,7 +643,7 @@ static int spi_nor_clear_fsr(struct spi_nor *nor) + + static inline int spi_nor_fsr_ready(struct spi_nor *nor) + { +- int fsr = read_fsr(nor); ++ int fsr = spi_nor_read_fsr(nor); + if (fsr < 0) + return fsr; + +@@ -721,7 +721,7 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor) + * + * Returns 0 if successful, non-zero otherwise. + */ +-static int erase_chip(struct spi_nor *nor) ++static int spi_nor_erase_chip(struct spi_nor *nor) + { + dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10)); + +@@ -848,9 +848,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) + if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { + unsigned long timeout; + +- write_enable(nor); ++ spi_nor_write_enable(nor); + +- if (erase_chip(nor)) { ++ if (spi_nor_erase_chip(nor)) { + ret = -EIO; + goto erase_err; + } +@@ -876,7 +876,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) + /* "sector"-at-a-time erase */ + } else { + while (len) { +- write_enable(nor); ++ spi_nor_write_enable(nor); + + ret = spi_nor_erase_sector(nor, addr); + if (ret) +@@ -891,7 +891,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) + } + } + +- write_disable(nor); ++ spi_nor_write_disable(nor); + + erase_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); +@@ -900,12 +900,13 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) + } + + /* Write status register and ensure bits in mask match written values */ +-static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask) ++static int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 status_new, ++ u8 mask) + { + int ret; + +- write_enable(nor); +- ret = write_sr(nor, status_new); ++ spi_nor_write_enable(nor); ++ ret = spi_nor_write_sr(nor, status_new); + if (ret) + return ret; + +@@ -913,7 +914,7 @@ static int write_sr_and_check(struct spi_nor *nor, u8 status_new, u8 mask) + if (ret) + return ret; + +- ret = read_sr(nor); ++ ret = spi_nor_read_sr(nor); + if (ret < 0) + return ret; + +@@ -1019,7 +1020,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) + bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; + bool use_top; + +- status_old = read_sr(nor); ++ status_old = spi_nor_read_sr(nor); + if (status_old < 0) + return status_old; + +@@ -1081,7 +1082,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) + if ((status_new & mask) < (status_old & mask)) + return -EINVAL; + +- return write_sr_and_check(nor, status_new, mask); ++ return spi_nor_write_sr_and_check(nor, status_new, mask); + } + + /* +@@ -1099,7 +1100,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) + bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; + bool use_top; + +- status_old = read_sr(nor); ++ status_old = spi_nor_read_sr(nor); + if (status_old < 0) + return status_old; + +@@ -1164,7 +1165,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) + if ((status_new & mask) > (status_old & mask)) + return -EINVAL; + +- return write_sr_and_check(nor, status_new, mask); ++ return spi_nor_write_sr_and_check(nor, status_new, mask); + } + + /* +@@ -1178,7 +1179,7 @@ static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) + { + int status; + +- status = read_sr(nor); ++ status = spi_nor_read_sr(nor); + if (status < 0) + return status; + +@@ -1709,7 +1710,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + if (ret) + return ret; + +- write_enable(nor); ++ spi_nor_write_enable(nor); + + nor->sst_write_second = false; + +@@ -1748,14 +1749,14 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + } + nor->sst_write_second = false; + +- write_disable(nor); ++ spi_nor_write_disable(nor); + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto sst_write_err; + + /* Write out trailing byte if it exists. */ + if (actual != len) { +- write_enable(nor); ++ spi_nor_write_enable(nor); + + nor->program_opcode = SPINOR_OP_BP; + ret = spi_nor_write_data(nor, to, 1, buf + actual); +@@ -1766,7 +1767,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto sst_write_err; +- write_disable(nor); ++ spi_nor_write_disable(nor); + actual += 1; + } + sst_write_err: +@@ -1819,7 +1820,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) + addr = spi_nor_s3an_addr_convert(nor, addr); + +- write_enable(nor); ++ spi_nor_write_enable(nor); + ret = spi_nor_write_data(nor, addr, page_remain, buf + i); + if (ret < 0) + goto write_err; +@@ -1858,21 +1859,21 @@ static int macronix_quad_enable(struct spi_nor *nor) + { + int ret, val; + +- val = read_sr(nor); ++ val = spi_nor_read_sr(nor); + if (val < 0) + return val; + if (val & SR_QUAD_EN_MX) + return 0; + +- write_enable(nor); ++ spi_nor_write_enable(nor); + +- write_sr(nor, val | SR_QUAD_EN_MX); ++ spi_nor_write_sr(nor, val | SR_QUAD_EN_MX); + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + +- ret = read_sr(nor); ++ ret = spi_nor_read_sr(nor); + if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { + dev_err(nor->dev, "Macronix Quad bit not set\n"); + return -EINVAL; +@@ -1887,11 +1888,11 @@ static int macronix_quad_enable(struct spi_nor *nor) + * second byte will be written to the configuration register. + * Return negative if error occurred. + */ +-static int write_sr_cr(struct spi_nor *nor, u8 *sr_cr) ++static int spi_nor_write_sr_cr(struct spi_nor *nor, u8 *sr_cr) + { + int ret; + +- write_enable(nor); ++ spi_nor_write_enable(nor); + + if (nor->spimem) { + struct spi_mem_op op = +@@ -1952,12 +1953,12 @@ static int spansion_quad_enable(struct spi_nor *nor) + + sr_cr[0] = 0; + sr_cr[1] = CR_QUAD_EN_SPAN; +- ret = write_sr_cr(nor, sr_cr); ++ ret = spi_nor_write_sr_cr(nor, sr_cr); + if (ret) + return ret; + + /* read back and check it */ +- ret = read_cr(nor); ++ ret = spi_nor_read_cr(nor); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + dev_err(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; +@@ -1985,7 +1986,7 @@ static int spansion_no_read_cr_quad_enable(struct spi_nor *nor) + int ret; + + /* Keep the current value of the Status Register. */ +- ret = read_sr(nor); ++ ret = spi_nor_read_sr(nor); + if (ret < 0) { + dev_err(nor->dev, "error while reading status register\n"); + return -EINVAL; +@@ -1993,7 +1994,7 @@ static int spansion_no_read_cr_quad_enable(struct spi_nor *nor) + sr_cr[0] = ret; + sr_cr[1] = CR_QUAD_EN_SPAN; + +- return write_sr_cr(nor, sr_cr); ++ return spi_nor_write_sr_cr(nor, sr_cr); + } + + /** +@@ -2016,7 +2017,7 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor) + int ret; + + /* Check current Quad Enable bit value. */ +- ret = read_cr(nor); ++ ret = spi_nor_read_cr(nor); + if (ret < 0) { + dev_err(dev, "error while reading configuration register\n"); + return -EINVAL; +@@ -2028,19 +2029,19 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor) + sr_cr[1] = ret | CR_QUAD_EN_SPAN; + + /* Keep the current value of the Status Register. */ +- ret = read_sr(nor); ++ ret = spi_nor_read_sr(nor); + if (ret < 0) { + dev_err(dev, "error while reading status register\n"); + return -EINVAL; + } + sr_cr[0] = ret; + +- ret = write_sr_cr(nor, sr_cr); ++ ret = spi_nor_write_sr_cr(nor, sr_cr); + if (ret) + return ret; + + /* Read back and check it. */ +- ret = read_cr(nor); ++ ret = spi_nor_read_cr(nor); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + dev_err(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; +@@ -2076,7 +2077,7 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor) + /* Update the Quad Enable bit. */ + *sr2 |= SR2_QUAD_EN_BIT7; + +- write_enable(nor); ++ spi_nor_write_enable(nor); + + ret = spi_nor_write_sr2(nor, sr2); + if (ret < 0) { +@@ -3123,8 +3124,8 @@ static int spi_nor_init(struct spi_nor *nor) + JEDEC_MFR(nor->info) == SNOR_MFR_INTEL || + JEDEC_MFR(nor->info) == SNOR_MFR_SST || + nor->info->flags & SPI_NOR_HAS_LOCK) { +- write_enable(nor); +- write_sr(nor, 0); ++ spi_nor_write_enable(nor); ++ spi_nor_write_sr(nor, 0); + spi_nor_wait_till_ready(nor); + } + +-- +2.27.0 + diff --git a/patches/0329-mtd-spi-nor-Stop-compare-with-negative-in-Reg-Ops-me.patch b/patches/0329-mtd-spi-nor-Stop-compare-with-negative-in-Reg-Ops-me.patch new file mode 100644 index 0000000..155c70b --- /dev/null +++ b/patches/0329-mtd-spi-nor-Stop-compare-with-negative-in-Reg-Ops-me.patch @@ -0,0 +1,124 @@ +From 3462ae48d7dd80fffd0f61a8cd885a7a9016931a Mon Sep 17 00:00:00 2001 +From: Tudor Ambarus +Date: Tue, 29 Oct 2019 11:16:54 +0000 +Subject: [PATCH 07/39] mtd: spi-nor: Stop compare with negative in Reg Ops + methods + +mainline inclusion +from mainline-v5.5-rc1 +commit ebe04bfe26dd816839d3d24fdeb5f6bed430a3df +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ebe04bfe26dd816839d3d24fdeb5f6bed430a3df + +---------------------------------------------------------------------------- + +spi_mem_exec_op() +nor->controller_ops->write_reg() +nor->controller_ops->read_reg() +spi_nor_wait_till_ready() +Return 0 on success, -errno otherwise. + +Stop compare with negative and compare with zero in all the register +operations methods. + +Signed-off-by: Tudor Ambarus +Reviewed-by: Boris Brezillon +Signed-off-by: YunYi Yang + + Conflicts: + drivers/mtd/spi-nor/spi-nor.c +--- + drivers/mtd/spi-nor/spi-nor.c | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 0809018daf4c..0c9f58d8819c 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -267,7 +267,7 @@ static int spi_nor_read_sr(struct spi_nor *nor) + ret = nor->read_reg(nor, SPINOR_OP_RDSR, nor->bouncebuf, 1); + } + +- if (ret < 0) { ++ if (ret) { + pr_err("error %d reading SR\n", (int) ret); + return ret; + } +@@ -296,7 +296,7 @@ static int spi_nor_read_fsr(struct spi_nor *nor) + ret = nor->read_reg(nor, SPINOR_OP_RDFSR, nor->bouncebuf, 1); + } + +- if (ret < 0) { ++ if (ret) { + pr_err("error %d reading FSR\n", ret); + return ret; + } +@@ -325,7 +325,7 @@ static int spi_nor_read_cr(struct spi_nor *nor) + ret = nor->read_reg(nor, SPINOR_OP_RDCR, nor->bouncebuf, 1); + } + +- if (ret < 0) { ++ if (ret) { + dev_err(nor->dev, "error %d reading CR\n", ret); + return ret; + } +@@ -584,7 +584,7 @@ static int s3an_sr_ready(struct spi_nor *nor) + int ret; + + ret = spi_nor_xread_sr(nor, nor->bouncebuf); +- if (ret < 0) { ++ if (ret) { + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); + return ret; + } +@@ -1640,7 +1640,7 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) + tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, + SPI_NOR_MAX_ID_LEN); + } +- if (tmp < 0) { ++ if (tmp) { + dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } +@@ -2080,20 +2080,23 @@ static int sr2_bit7_quad_enable(struct spi_nor *nor) + spi_nor_write_enable(nor); + + ret = spi_nor_write_sr2(nor, sr2); +- if (ret < 0) { ++ if (ret) { + dev_err(nor->dev, "error while writing status register 2\n"); + return -EINVAL; + } + + ret = spi_nor_wait_till_ready(nor); +- if (ret < 0) { ++ if (ret) { + dev_err(nor->dev, "timeout while writing status register 2\n"); + return ret; + } + + /* Read back and check it. */ + ret = spi_nor_read_sr2(nor, sr2); +- if (!(ret > 0 && (*sr2 & SR2_QUAD_EN_BIT7))) { ++ if (ret) ++ return ret; ++ ++ if (!(*sr2 & SR2_QUAD_EN_BIT7)) { + dev_err(nor->dev, "SR2 Quad bit not set\n"); + return -EINVAL; + } +@@ -2119,7 +2122,7 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) + int ret; + + ret = spi_nor_xread_sr(nor, nor->bouncebuf); +- if (ret < 0) { ++ if (ret) { + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); + return ret; + } +-- +2.27.0 + diff --git a/patches/0330-mtd-spi-nor-Pointer-parameter-for-SR-in-spi_nor_read.patch b/patches/0330-mtd-spi-nor-Pointer-parameter-for-SR-in-spi_nor_read.patch new file mode 100644 index 0000000..bb85605 --- /dev/null +++ b/patches/0330-mtd-spi-nor-Pointer-parameter-for-SR-in-spi_nor_read.patch @@ -0,0 +1,258 @@ +From b5eb1d02d31155f46aab15b435f9bff0a2e83097 Mon Sep 17 00:00:00 2001 +From: Tudor Ambarus +Date: Thu, 24 Oct 2019 15:55:34 +0300 +Subject: [PATCH 08/39] mtd: spi-nor: Pointer parameter for SR in + spi_nor_read_sr() + +mainline inclusion +from mainline-v5.5-rc1 +commit cd1718f5c49d53539c99f45a485ca0e0ac7f0a99 +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=cd1718f5c49d53539c99f45a485ca0e0ac7f0a99 + +---------------------------------------------------------------------------- + +Let the callers pass the pointer to the DMA-able buffer where +the value of the Status Register will be written. This way we +avoid the casts between int and u8, which can be confusing. + +Callers stop compare the return value of spi_nor_read_sr() with negative, +spi_nor_read_sr() returns 0 on success and -errno otherwise. + +Signed-off-by: Tudor Ambarus +Reviewed-by: Boris Brezillon +Signed-off-by: YunYi Yang + + Conflicts: + drivers/mtd/spi-nor/spi-nor.c +--- + drivers/mtd/spi-nor/spi-nor.c | 101 ++++++++++++++++++---------------- + 1 file changed, 55 insertions(+), 46 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 0c9f58d8819c..3a10b5939cd2 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -246,12 +246,15 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, + return nor->write(nor, to, len, buf); + } + +-/* +- * Read the status register, returning its value in the location +- * Return the status register value. +- * Returns negative if error occurred. ++/** ++ * spi_nor_read_sr() - Read the Status Register. ++ * @nor: pointer to 'struct spi_nor'. ++ * @sr: pointer to a DMA-able buffer where the value of the ++ * Status Register will be written. ++ * ++ * Return: 0 on success, -errno otherwise. + */ +-static int spi_nor_read_sr(struct spi_nor *nor) ++static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) + { + int ret; + +@@ -260,19 +263,17 @@ static int spi_nor_read_sr(struct spi_nor *nor) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, +- SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); ++ SPI_MEM_OP_DATA_IN(1, sr, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { +- ret = nor->read_reg(nor, SPINOR_OP_RDSR, nor->bouncebuf, 1); ++ ret = nor->read_reg(nor, SPINOR_OP_RDSR, sr, 1); + } + +- if (ret) { ++ if (ret) + pr_err("error %d reading SR\n", (int) ret); +- return ret; +- } + +- return nor->bouncebuf[0]; ++ return ret; + } + + /* +@@ -609,12 +610,14 @@ static int spi_nor_clear_sr(struct spi_nor *nor) + + static inline int spi_nor_sr_ready(struct spi_nor *nor) + { +- int sr = spi_nor_read_sr(nor); +- if (sr < 0) +- return sr; ++ int ret = spi_nor_read_sr(nor, nor->bouncebuf); + +- if (nor->flags & SNOR_F_USE_CLSR && sr & (SR_E_ERR | SR_P_ERR)) { +- if (sr & SR_E_ERR) ++ if (ret) ++ return ret; ++ ++ if (nor->flags & SNOR_F_USE_CLSR && ++ nor->bouncebuf[0] & (SR_E_ERR | SR_P_ERR)) { ++ if (nor->bouncebuf[0] & SR_E_ERR) + dev_err(nor->dev, "Erase Error occurred\n"); + else + dev_err(nor->dev, "Programming Error occurred\n"); +@@ -623,7 +626,7 @@ static inline int spi_nor_sr_ready(struct spi_nor *nor) + return -EIO; + } + +- return !(sr & SR_WIP); ++ return !(nor->bouncebuf[0] & SR_WIP); + } + + static int spi_nor_clear_fsr(struct spi_nor *nor) +@@ -914,8 +917,8 @@ static int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 status_new, + if (ret) + return ret; + +- ret = spi_nor_read_sr(nor); +- if (ret < 0) ++ ret = spi_nor_read_sr(nor, nor->bouncebuf); ++ if (ret) + return ret; + + return ((ret & mask) != (status_new & mask)) ? -EIO : 0; +@@ -1013,16 +1016,18 @@ static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, + static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) + { + struct mtd_info *mtd = &nor->mtd; +- int status_old, status_new; ++ int ret, status_old, status_new; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 shift = ffs(mask) - 1, pow, val; + loff_t lock_len; + bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; + bool use_top; + +- status_old = spi_nor_read_sr(nor); +- if (status_old < 0) +- return status_old; ++ ret = spi_nor_read_sr(nor, nor->bouncebuf); ++ if (ret) ++ return ret; ++ ++ status_old = nor->bouncebuf[0]; + + /* If nothing in our range is unlocked, we don't need to do anything */ + if (stm_is_locked_sr(nor, ofs, len, status_old)) +@@ -1093,16 +1098,18 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) + static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) + { + struct mtd_info *mtd = &nor->mtd; +- int status_old, status_new; ++ int ret, status_old, status_new; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 shift = ffs(mask) - 1, pow, val; + loff_t lock_len; + bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; + bool use_top; + +- status_old = spi_nor_read_sr(nor); +- if (status_old < 0) +- return status_old; ++ ret = spi_nor_read_sr(nor, nor->bouncebuf); ++ if (ret) ++ return ret; ++ ++ status_old = nor->bouncebuf[0]; + + /* If nothing in our range is locked, we don't need to do anything */ + if (stm_is_unlocked_sr(nor, ofs, len, status_old)) +@@ -1177,13 +1184,13 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) + */ + static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) + { +- int status; ++ int ret; + +- status = spi_nor_read_sr(nor); +- if (status < 0) +- return status; ++ ret = spi_nor_read_sr(nor, nor->bouncebuf); ++ if (ret) ++ return ret; + +- return stm_is_locked_sr(nor, ofs, len, status); ++ return stm_is_locked_sr(nor, ofs, len, nor->bouncebuf[0]); + } + + static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +@@ -1857,24 +1864,28 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, + */ + static int macronix_quad_enable(struct spi_nor *nor) + { +- int ret, val; ++ int ret; + +- val = spi_nor_read_sr(nor); +- if (val < 0) +- return val; +- if (val & SR_QUAD_EN_MX) ++ ret = spi_nor_read_sr(nor, nor->bouncebuf); ++ if (ret) ++ return ret; ++ ++ if (nor->bouncebuf[0] & SR_QUAD_EN_MX) + return 0; + + spi_nor_write_enable(nor); + +- spi_nor_write_sr(nor, val | SR_QUAD_EN_MX); ++ spi_nor_write_sr(nor, nor->bouncebuf[0] | SR_QUAD_EN_MX); + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + +- ret = spi_nor_read_sr(nor); +- if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { ++ ret = spi_nor_read_sr(nor, nor->bouncebuf); ++ if (ret) ++ return ret; ++ ++ if (!(nor->bouncebuf[0] & SR_QUAD_EN_MX)) { + dev_err(nor->dev, "Macronix Quad bit not set\n"); + return -EINVAL; + } +@@ -1986,12 +1997,11 @@ static int spansion_no_read_cr_quad_enable(struct spi_nor *nor) + int ret; + + /* Keep the current value of the Status Register. */ +- ret = spi_nor_read_sr(nor); +- if (ret < 0) { ++ ret = spi_nor_read_sr(nor, sr_cr); ++ if (ret) { + dev_err(nor->dev, "error while reading status register\n"); + return -EINVAL; + } +- sr_cr[0] = ret; + sr_cr[1] = CR_QUAD_EN_SPAN; + + return spi_nor_write_sr_cr(nor, sr_cr); +@@ -2029,12 +2039,11 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor) + sr_cr[1] = ret | CR_QUAD_EN_SPAN; + + /* Keep the current value of the Status Register. */ +- ret = spi_nor_read_sr(nor); +- if (ret < 0) { ++ ret = spi_nor_read_sr(nor, sr_cr); ++ if (ret) { + dev_err(dev, "error while reading status register\n"); + return -EINVAL; + } +- sr_cr[0] = ret; + + ret = spi_nor_write_sr_cr(nor, sr_cr); + if (ret) +-- +2.27.0 + diff --git a/patches/0331-mtd-spi-nor-Pointer-parameter-for-FSR-in-spi_nor_rea.patch b/patches/0331-mtd-spi-nor-Pointer-parameter-for-FSR-in-spi_nor_rea.patch new file mode 100644 index 0000000..ab444e7 --- /dev/null +++ b/patches/0331-mtd-spi-nor-Pointer-parameter-for-FSR-in-spi_nor_rea.patch @@ -0,0 +1,120 @@ +From 7ac30bd8a32bd1b992f5c252b5608c9c26e4be26 Mon Sep 17 00:00:00 2001 +From: Tudor Ambarus +Date: Thu, 24 Oct 2019 16:40:13 +0300 +Subject: [PATCH 09/39] mtd: spi-nor: Pointer parameter for FSR in + spi_nor_read_fsr() + +mainline inclusion +from mainline-v5.5-rc1 +commit 5ce1b49ccb52fc3dd5679d8c523a3e8b5c812fb0 +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5ce1b49ccb52fc3dd5679d8c523a3e8b5c812fb0 + +---------------------------------------------------------------------------- + +Let the callers pass the pointer to the DMA-able buffer where +the value of the Flag Status Register will be written. This way we +avoid the casts between int and u8, which can be confusing. + +Caller stops compare the return value of spi_nor_read_fsr() with negative, +spi_nor_read_fsr() returns 0 on success and -errno otherwise. + +Signed-off-by: Tudor Ambarus +Reviewed-by: Boris Brezillon +Signed-off-by: YunYi Yang + + Conflicts: + drivers/mtd/spi-nor/spi-nor.c +--- + drivers/mtd/spi-nor/spi-nor.c | 38 ++++++++++++++++++----------------- + 1 file changed, 20 insertions(+), 18 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 3a10b5939cd2..766ad458197e 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -276,12 +276,15 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) + return ret; + } + +-/* +- * Read the flag status register, returning its value in the location +- * Return the status register value. +- * Returns negative if error occurred. ++/** ++ * spi_nor_read_fsr() - Read the Flag Status Register. ++ * @nor: pointer to 'struct spi_nor' ++ * @fsr: pointer to a DMA-able buffer where the value of the ++ * Flag Status Register will be written. ++ * ++ * Return: 0 on success, -errno otherwise. + */ +-static int spi_nor_read_fsr(struct spi_nor *nor) ++static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr) + { + int ret; + +@@ -290,19 +293,17 @@ static int spi_nor_read_fsr(struct spi_nor *nor) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, +- SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); ++ SPI_MEM_OP_DATA_IN(1, fsr, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { +- ret = nor->read_reg(nor, SPINOR_OP_RDFSR, nor->bouncebuf, 1); ++ ret = nor->read_reg(nor, SPINOR_OP_RDFSR, fsr, 1); + } + +- if (ret) { ++ if (ret) + pr_err("error %d reading FSR\n", ret); +- return ret; +- } + +- return nor->bouncebuf[0]; ++ return ret; + } + + /* +@@ -646,17 +647,18 @@ static int spi_nor_clear_fsr(struct spi_nor *nor) + + static inline int spi_nor_fsr_ready(struct spi_nor *nor) + { +- int fsr = spi_nor_read_fsr(nor); +- if (fsr < 0) +- return fsr; ++ int ret = spi_nor_read_fsr(nor, nor->bouncebuf); ++ ++ if (ret) ++ return ret; + +- if (fsr & (FSR_E_ERR | FSR_P_ERR)) { +- if (fsr & FSR_E_ERR) ++ if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) { ++ if (nor->bouncebuf[0] & FSR_E_ERR) + dev_err(nor->dev, "Erase operation failed.\n"); + else + dev_err(nor->dev, "Program operation failed.\n"); + +- if (fsr & FSR_PT_ERR) ++ if (nor->bouncebuf[0] & FSR_PT_ERR) + dev_err(nor->dev, + "Attempted to modify a protected sector.\n"); + +@@ -664,7 +666,7 @@ static inline int spi_nor_fsr_ready(struct spi_nor *nor) + return -EIO; + } + +- return fsr & FSR_READY; ++ return nor->bouncebuf[0] & FSR_READY; + } + + static int spi_nor_ready(struct spi_nor *nor) +-- +2.27.0 + diff --git a/patches/0332-mtd-spi-nor-Pointer-parameter-for-CR-in-spi_nor_read.patch b/patches/0332-mtd-spi-nor-Pointer-parameter-for-CR-in-spi_nor_read.patch new file mode 100644 index 0000000..6c24e25 --- /dev/null +++ b/patches/0332-mtd-spi-nor-Pointer-parameter-for-CR-in-spi_nor_read.patch @@ -0,0 +1,136 @@ +From 09f73e2e9bba97398fa7a66affaa0e820ac16b5c Mon Sep 17 00:00:00 2001 +From: Tudor Ambarus +Date: Thu, 24 Oct 2019 16:59:55 +0300 +Subject: [PATCH 10/39] mtd: spi-nor: Pointer parameter for CR in + spi_nor_read_cr() + +mainline inclusion +from mainline-v5.5-rc1 +commit b662d398ccf114a80c92140287a6507efb3e2dfc +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b662d398ccf114a80c92140287a6507efb3e2dfc + +---------------------------------------------------------------------------- + +Let the callers pass the pointer to the DMA-able buffer where +the value of the Configuration Register will be written. This way we +avoid the casts between int and u8, which can be confusing. + +Callers stop compare the return value of spi_nor_read_cr() with negative, +spi_nor_read_cr() returns 0 on success and -errno otherwise. + +Signed-off-by: Tudor Ambarus +Reviewed-by: Boris Brezillon +Signed-off-by: YunYi Yang + + Conflicts: + drivers/mtd/spi-nor/spi-nor.c +--- + drivers/mtd/spi-nor/spi-nor.c | 46 ++++++++++++++++++++--------------- + 1 file changed, 27 insertions(+), 19 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 766ad458197e..f1e92f63cab6 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -306,12 +306,16 @@ static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr) + return ret; + } + +-/* +- * Read configuration register, returning its value in the +- * location. Return the configuration register value. +- * Returns negative if error occurred. ++/** ++ * spi_nor_read_cr() - Read the Configuration Register using the ++ * SPINOR_OP_RDCR (35h) command. ++ * @nor: pointer to 'struct spi_nor' ++ * @cr: pointer to a DMA-able buffer where the value of the ++ * Configuration Register will be written. ++ * ++ * Return: 0 on success, -errno otherwise. + */ +-static int spi_nor_read_cr(struct spi_nor *nor) ++static int spi_nor_read_cr(struct spi_nor *nor, u8 *cr) + { + int ret; + +@@ -320,19 +324,17 @@ static int spi_nor_read_cr(struct spi_nor *nor) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, +- SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); ++ SPI_MEM_OP_DATA_IN(1, cr, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { +- ret = nor->read_reg(nor, SPINOR_OP_RDCR, nor->bouncebuf, 1); ++ ret = nor->read_reg(nor, SPINOR_OP_RDCR, cr, 1); + } + +- if (ret) { ++ if (ret) + dev_err(nor->dev, "error %d reading CR\n", ret); +- return ret; +- } + +- return nor->bouncebuf[0]; ++ return ret; + } + + /* +@@ -1971,8 +1973,11 @@ static int spansion_quad_enable(struct spi_nor *nor) + return ret; + + /* read back and check it */ +- ret = spi_nor_read_cr(nor); +- if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { ++ ret = spi_nor_read_cr(nor, nor->bouncebuf); ++ if (ret) ++ return ret; ++ ++ if (!(nor->bouncebuf[0] & CR_QUAD_EN_SPAN)) { + dev_err(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; + } +@@ -2029,16 +2034,16 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor) + int ret; + + /* Check current Quad Enable bit value. */ +- ret = spi_nor_read_cr(nor); +- if (ret < 0) { ++ ret = spi_nor_read_cr(nor, &sr_cr[1]); ++ if (ret) { + dev_err(dev, "error while reading configuration register\n"); + return -EINVAL; + } + +- if (ret & CR_QUAD_EN_SPAN) ++ if (sr_cr[1] & CR_QUAD_EN_SPAN) + return 0; + +- sr_cr[1] = ret | CR_QUAD_EN_SPAN; ++ sr_cr[1] |= CR_QUAD_EN_SPAN; + + /* Keep the current value of the Status Register. */ + ret = spi_nor_read_sr(nor, sr_cr); +@@ -2052,8 +2057,11 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor) + return ret; + + /* Read back and check it. */ +- ret = spi_nor_read_cr(nor); +- if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { ++ ret = spi_nor_read_cr(nor, &sr_cr[1]); ++ if (ret) ++ return ret; ++ ++ if (!(sr_cr[1] & CR_QUAD_EN_SPAN)) { + dev_err(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; + } +-- +2.27.0 + diff --git a/patches/0333-mtd-spi-nor-fix-kernel-doc-for-spi_nor-spimem.patch b/patches/0333-mtd-spi-nor-fix-kernel-doc-for-spi_nor-spimem.patch new file mode 100644 index 0000000..5905433 --- /dev/null +++ b/patches/0333-mtd-spi-nor-fix-kernel-doc-for-spi_nor-spimem.patch @@ -0,0 +1,49 @@ +From 9eaef9e96377baccef091f6247b064e09b91394a Mon Sep 17 00:00:00 2001 +From: Sergei Shtylyov +Date: Fri, 3 Apr 2020 23:50:57 +0300 +Subject: [PATCH 11/39] mtd: spi-nor: fix kernel-doc for spi_nor::spimem + +mainline inclusion +from mainline-v5.8-rc1 +commit 1f241ad2a093b889122bd6bfdce57551d21bba5b +category: cleanup +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1f241ad2a093b889122bd6bfdce57551d21bba5b + +---------------------------------------------------------------------------- + +When adding the 'spimem' field to 'struct spi_nor', a grammar mistake +("point" instead of "pointer") was made -- fix it and convert the SPI +acronym to uppercase and fully spell out "memory", while at it... + +Fixes: b35b9a10362 ("mtd: spi-nor: Move m25p80 code in spi-nor.c") +Signed-off-by: Sergei Shtylyov +Signed-off-by: Tudor Ambarus +Signed-off-by: YunYi Yang + + Conflicts: + include/linux/mtd/spi-nor.h +--- + include/linux/mtd/spi-nor.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index ac5e2656e9b3..98d7794ceedb 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -249,8 +249,8 @@ struct flash_info; + * struct spi_nor - Structure for defining a the SPI NOR layer + * @mtd: point to a mtd_info structure + * @lock: the lock for the read/write/erase/lock/unlock operations +- * @dev: point to a spi device, or a spi nor controller device. +- * @spimem: point to the spi mem device ++ * @dev: pointer to an SPI device or an SPI NOR controller device ++ * @spimem: pointer to the SPI memory device + * @bouncebuf: bounce buffer used when the buffer passed by the MTD + * layer is not DMA-able + * @bouncebuf_size: size of the bounce buffer +-- +2.27.0 + diff --git a/patches/0334-mtd-spi-nor-core-Fix-an-issue-of-releasing-resources.patch b/patches/0334-mtd-spi-nor-core-Fix-an-issue-of-releasing-resources.patch new file mode 100644 index 0000000..47e029e --- /dev/null +++ b/patches/0334-mtd-spi-nor-core-Fix-an-issue-of-releasing-resources.patch @@ -0,0 +1,105 @@ +From a30685e70936de07484b543dd506ba78e2dda7e9 Mon Sep 17 00:00:00 2001 +From: Xiang Chen +Date: Sat, 22 May 2021 11:53:14 +0000 +Subject: [PATCH 12/39] mtd: spi-nor: core: Fix an issue of releasing resources + during read/write + +mainline inclusion +from mainline-v5.13-rc1 +commit be94215be1ab19e5d38f50962f611c88d4bfc83a +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=be94215be1ab19e5d38f50962f611c88d4bfc83a + +---------------------------------------------------------------------------- + +commit be94215be1ab19e5d38f50962f611c88d4bfc83a upstream. + +If rmmod the driver during read or write, the driver will release the +resources which are used during read or write, so it is possible to +refer to NULL pointer. + +Use the testcase "mtd_debug read /dev/mtd0 0xc00000 0x400000 dest_file & +sleep 0.5;rmmod spi_hisi_sfc_v3xx.ko", the issue can be reproduced in +hisi_sfc_v3xx driver. + +To avoid the issue, fill the interface _get_device and _put_device of +mtd_info to grab the reference to the spi controller driver module, so +the request of rmmod the driver is rejected before read/write is finished. + +Fixes: b199489d37b2 ("mtd: spi-nor: add the framework for SPI NOR") +Signed-off-by: Xiang Chen +Signed-off-by: Yicong Yang +Signed-off-by: Tudor Ambarus +Tested-by: Michael Walle +Tested-by: Tudor Ambarus +Reviewed-by: Michael Walle +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/r/1617262486-4223-1-git-send-email-yangyicong@hisilicon.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Chen Jun +Acked-by: Weilong Chen +Signed-off-by: Zheng Zengkai +Signed-off-by: YunYi Yang + + Conflicts: + drivers/mtd/spi-nor/core.c +--- + drivers/mtd/spi-nor/spi-nor.c | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index f1e92f63cab6..9792b776cc4c 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -3190,6 +3190,36 @@ static void spi_nor_resume(struct mtd_info *mtd) + dev_err(dev, "resume() failed\n"); + } + ++static int spi_nor_get_device(struct mtd_info *mtd) ++{ ++ struct spi_nor *nor = mtd_to_spi_nor(mtd); ++ struct device *dev; ++ ++ if (nor->spimem) ++ dev = nor->spimem->spi->controller->dev.parent; ++ else ++ dev = nor->dev; ++ ++ if (!try_module_get(dev->driver->owner)) ++ return -ENODEV; ++ ++ return 0; ++} ++ ++static void spi_nor_put_device(struct mtd_info *mtd) ++{ ++ struct spi_nor *nor = mtd_to_spi_nor(mtd); ++ struct device *dev; ++ ++ if (nor->spimem) ++ dev = nor->spimem->spi->controller->dev.parent; ++ else ++ dev = nor->dev; ++ ++ module_put(dev->driver->owner); ++} ++ ++ + void spi_nor_restore(struct spi_nor *nor) + { + /* restore the addressing mode */ +@@ -3292,6 +3322,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, + mtd->_erase = spi_nor_erase; + mtd->_read = spi_nor_read; + mtd->_resume = spi_nor_resume; ++ mtd->_get_device = spi_nor_get_device; ++ mtd->_put_device = spi_nor_put_device; + + /* NOR protection support for STmicro/Micron chips and similar */ + if (JEDEC_MFR(info) == SNOR_MFR_MICRON || +-- +2.27.0 + diff --git a/patches/0335-ARM-shmobile-defconfig-Refresh-config-CONFIG_MTD_M25.patch b/patches/0335-ARM-shmobile-defconfig-Refresh-config-CONFIG_MTD_M25.patch new file mode 100644 index 0000000..5df97cc --- /dev/null +++ b/patches/0335-ARM-shmobile-defconfig-Refresh-config-CONFIG_MTD_M25.patch @@ -0,0 +1,46 @@ +From 6dc80f062988a0251723db031b4e89cbc899550f Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Fri, 25 Oct 2019 15:53:25 +0200 +Subject: [PATCH 13/39] ARM: shmobile: defconfig: Refresh config + CONFIG_MTD_M25P80 for v5.4-rc1 + +mainline inclusion +from mainline-v5.5-rc1 +commit 51e0f6a1917831373e153eca2fb27902f0f8bc7b +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=51e0f6a1917831373e153eca2fb27902f0f8bc7b + +---------------------------------------------------------------------------- + +Update the defconfig for Renesas ARM boards: + - Drop CONFIG_MTD_M25P80=y (removed in commit b35b9a10362d2034 ("mtd: + spi-nor: Move m25p80 code in spi-nor.c")), + +Signed-off-by: Geert Uytterhoeven +Link: https://lore.kernel.org/r/20191025135325.32242-1-geert+renesas@glider.be +Signed-off-by: YunYi Yang + + Conflicts: + arch/arm/configs/shmobile_defconfig +--- + arch/arm/configs/shmobile_defconfig | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/arch/arm/configs/shmobile_defconfig b/arch/arm/configs/shmobile_defconfig +index f8faf3729464..f52302ae49a6 100644 +--- a/arch/arm/configs/shmobile_defconfig ++++ b/arch/arm/configs/shmobile_defconfig +@@ -65,7 +65,6 @@ CONFIG_CMA_SIZE_MBYTES=64 + CONFIG_SIMPLE_PM_BUS=y + CONFIG_MTD=y + CONFIG_MTD_BLOCK=y +-CONFIG_MTD_M25P80=y + CONFIG_MTD_SPI_NOR=y + CONFIG_EEPROM_AT24=y + CONFIG_BLK_DEV_SD=y +-- +2.27.0 + diff --git a/patches/0336-mips-Drop-CONFIG_MTD_M25P80-in-various-defconfig-fil.patch b/patches/0336-mips-Drop-CONFIG_MTD_M25P80-in-various-defconfig-fil.patch new file mode 100644 index 0000000..d218307 --- /dev/null +++ b/patches/0336-mips-Drop-CONFIG_MTD_M25P80-in-various-defconfig-fil.patch @@ -0,0 +1,96 @@ +From e8d9db794570db82ac042b0c661456e0f7c3af21 Mon Sep 17 00:00:00 2001 +From: Bin Meng +Date: Fri, 1 May 2020 21:25:49 -0700 +Subject: [PATCH 14/39] mips: Drop CONFIG_MTD_M25P80 in various defconfig files + +mainline inclusion +from mainline-v5.8-rc1 +commit 9d139131e973dac0ef6207a161b228a7fcad8180 +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9d139131e973dac0ef6207a161b228a7fcad8180 + +---------------------------------------------------------------------------- + +Drop CONFIG_MTD_M25P80 that was removed in +commit b35b9a10362d ("mtd: spi-nor: Move m25p80 code in spi-nor.c") + +Signed-off-by: Bin Meng +Signed-off-by: Thomas Bogendoerfer +Signed-off-by: YunYi Yang + + Conflicts: + arch/mips/configs/generic/board-ocelot.config +--- + arch/mips/configs/ath79_defconfig | 1 - + arch/mips/configs/db1xxx_defconfig | 1 - + arch/mips/configs/generic/board-ocelot.config | 1 - + arch/mips/configs/pistachio_defconfig | 1 - + arch/mips/configs/rt305x_defconfig | 1 - + 5 files changed, 5 deletions(-) + +diff --git a/arch/mips/configs/ath79_defconfig b/arch/mips/configs/ath79_defconfig +index 4c47b3fd958b..7195338da194 100644 +--- a/arch/mips/configs/ath79_defconfig ++++ b/arch/mips/configs/ath79_defconfig +@@ -49,7 +49,6 @@ CONFIG_MTD_JEDECPROBE=y + CONFIG_MTD_CFI_AMDSTD=y + CONFIG_MTD_COMPLEX_MAPPINGS=y + CONFIG_MTD_PHYSMAP=y +-CONFIG_MTD_M25P80=y + CONFIG_MTD_SPI_NOR=y + CONFIG_NETDEVICES=y + # CONFIG_NET_PACKET_ENGINE is not set +diff --git a/arch/mips/configs/db1xxx_defconfig b/arch/mips/configs/db1xxx_defconfig +index 0108bb9f1e37..b517d469b1b2 100644 +--- a/arch/mips/configs/db1xxx_defconfig ++++ b/arch/mips/configs/db1xxx_defconfig +@@ -105,7 +105,6 @@ CONFIG_MTD_CFI=y + CONFIG_MTD_CFI_ADV_OPTIONS=y + CONFIG_MTD_CFI_AMDSTD=y + CONFIG_MTD_PHYSMAP=y +-CONFIG_MTD_M25P80=y + CONFIG_MTD_SST25L=y + CONFIG_MTD_NAND=y + CONFIG_MTD_NAND_ECC_BCH=y +diff --git a/arch/mips/configs/generic/board-ocelot.config b/arch/mips/configs/generic/board-ocelot.config +index aa815761d85e..7e7695e1542a 100644 +--- a/arch/mips/configs/generic/board-ocelot.config ++++ b/arch/mips/configs/generic/board-ocelot.config +@@ -5,7 +5,6 @@ CONFIG_LEGACY_BOARD_OCELOT=y + CONFIG_MTD=y + CONFIG_MTD_CMDLINE_PARTS=y + CONFIG_MTD_BLOCK=y +-CONFIG_MTD_M25P80=y + CONFIG_MTD_NAND=y + CONFIG_MTD_NAND_PLATFORM=y + CONFIG_MTD_SPI_NOR=y +diff --git a/arch/mips/configs/pistachio_defconfig b/arch/mips/configs/pistachio_defconfig +index b22a3cf149b6..e524c5ced381 100644 +--- a/arch/mips/configs/pistachio_defconfig ++++ b/arch/mips/configs/pistachio_defconfig +@@ -131,7 +131,6 @@ CONFIG_DEBUG_DEVRES=y + CONFIG_CONNECTOR=y + CONFIG_MTD=y + CONFIG_MTD_BLOCK=y +-CONFIG_MTD_M25P80=y + CONFIG_MTD_SPI_NOR=y + CONFIG_MTD_UBI=y + CONFIG_MTD_UBI_BLOCK=y +diff --git a/arch/mips/configs/rt305x_defconfig b/arch/mips/configs/rt305x_defconfig +index dbe6a4639d05..6c9f3c892f5a 100644 +--- a/arch/mips/configs/rt305x_defconfig ++++ b/arch/mips/configs/rt305x_defconfig +@@ -84,7 +84,6 @@ CONFIG_MTD_CFI_AMDSTD=y + CONFIG_MTD_COMPLEX_MAPPINGS=y + CONFIG_MTD_PHYSMAP=y + CONFIG_MTD_PHYSMAP_OF=y +-CONFIG_MTD_M25P80=y + CONFIG_MTD_SPI_NOR=y + CONFIG_EEPROM_93CX6=m + CONFIG_SCSI=y +-- +2.27.0 + diff --git a/patches/0337-m68k-Drop-CONFIG_MTD_M25P80-in-stmark2_defconfig.patch b/patches/0337-m68k-Drop-CONFIG_MTD_M25P80-in-stmark2_defconfig.patch new file mode 100644 index 0000000..238219e --- /dev/null +++ b/patches/0337-m68k-Drop-CONFIG_MTD_M25P80-in-stmark2_defconfig.patch @@ -0,0 +1,41 @@ +From 679821ee5b370e36451dd353809ce29f4a217da0 Mon Sep 17 00:00:00 2001 +From: Bin Meng +Date: Fri, 1 May 2020 21:30:21 -0700 +Subject: [PATCH 15/39] m68k: Drop CONFIG_MTD_M25P80 in stmark2_defconfig + +mainline inclusion +from mainline-v5.8-rc1 +commit e00091071615378e66291c61ea02d02d15a2cc7e +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e00091071615378e66291c61ea02d02d15a2cc7e + +---------------------------------------------------------------------------- + +Drop CONFIG_MTD_M25P80 that was removed in +commit b35b9a10362d ("mtd: spi-nor: Move m25p80 code in spi-nor.c") + +Signed-off-by: Bin Meng +Signed-off-by: Greg Ungerer +Signed-off-by: YunYi Yang +--- + arch/m68k/configs/stmark2_defconfig | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/arch/m68k/configs/stmark2_defconfig b/arch/m68k/configs/stmark2_defconfig +index 3d07b1de7eb0..f2a0937f1d22 100644 +--- a/arch/m68k/configs/stmark2_defconfig ++++ b/arch/m68k/configs/stmark2_defconfig +@@ -49,7 +49,6 @@ CONFIG_MTD_CFI_STAA=y + CONFIG_MTD_ROM=y + CONFIG_MTD_COMPLEX_MAPPINGS=y + CONFIG_MTD_PLATRAM=y +-CONFIG_MTD_M25P80=y + CONFIG_MTD_SPI_NOR=y + # CONFIG_INPUT_KEYBOARD is not set + # CONFIG_INPUT_MOUSE is not set +-- +2.27.0 + diff --git a/patches/0338-powerpc-Drop-CONFIG_MTD_M25P80-in-85xx-hw.config.patch b/patches/0338-powerpc-Drop-CONFIG_MTD_M25P80-in-85xx-hw.config.patch new file mode 100644 index 0000000..0e32226 --- /dev/null +++ b/patches/0338-powerpc-Drop-CONFIG_MTD_M25P80-in-85xx-hw.config.patch @@ -0,0 +1,42 @@ +From ff14e23b9018738156c8943d0edf556901d9fe6e Mon Sep 17 00:00:00 2001 +From: Bin Meng +Date: Fri, 1 May 2020 21:44:54 -0700 +Subject: [PATCH 16/39] powerpc: Drop CONFIG_MTD_M25P80 in 85xx-hw.config + +mainline inclusion +from mainline-v5.9-rc1 +commit 76f09371bc05d6eb8d5a01823c9eaab768d6e934 +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=76f09371bc05d6eb8d5a01823c9eaab768d6e934 + +---------------------------------------------------------------------------- + +Drop CONFIG_MTD_M25P80 that was removed in +commit b35b9a10362d ("mtd: spi-nor: Move m25p80 code in spi-nor.c") + +Signed-off-by: Bin Meng +Signed-off-by: Michael Ellerman +Link: https://lore.kernel.org/r/1588394694-517-1-git-send-email-bmeng.cn@gmail.com +Signed-off-by: YunYi Yang +--- + arch/powerpc/configs/85xx-hw.config | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/arch/powerpc/configs/85xx-hw.config b/arch/powerpc/configs/85xx-hw.config +index c03d0fb16665..2bce8e571651 100644 +--- a/arch/powerpc/configs/85xx-hw.config ++++ b/arch/powerpc/configs/85xx-hw.config +@@ -68,7 +68,6 @@ CONFIG_MTD_CFI_AMDSTD=y + CONFIG_MTD_CFI_INTELEXT=y + CONFIG_MTD_CFI=y + CONFIG_MTD_CMDLINE_PARTS=y +-CONFIG_MTD_M25P80=y + CONFIG_MTD_NAND_FSL_ELBC=y + CONFIG_MTD_NAND_FSL_IFC=y + CONFIG_MTD_NAND=y +-- +2.27.0 + diff --git a/patches/0339-sh-Replace-CONFIG_MTD_M25P80-with-CONFIG_MTD_SPI_NOR.patch b/patches/0339-sh-Replace-CONFIG_MTD_M25P80-with-CONFIG_MTD_SPI_NOR.patch new file mode 100644 index 0000000..c408da7 --- /dev/null +++ b/patches/0339-sh-Replace-CONFIG_MTD_M25P80-with-CONFIG_MTD_SPI_NOR.patch @@ -0,0 +1,43 @@ +From b76e9c7fbcd02e7062b13ab29330860d77181cc7 Mon Sep 17 00:00:00 2001 +From: Bin Meng +Date: Sat, 2 May 2020 04:04:43 -0700 +Subject: [PATCH 17/39] sh: Replace CONFIG_MTD_M25P80 with CONFIG_MTD_SPI_NOR + in sh7757lcr_defconfig + +mainline inclusion +from mainline-v5.9-rc1 +commit bd158322ba5f6190403e6aeb53c1e7b659f9ade8 +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=bd158322ba5f6190403e6aeb53c1e7b659f9ade8 + +---------------------------------------------------------------------------- + +CONFIG_MTD_M25P80 was removed and replaced by CONFIG_MTD_SPI_NOR in +commit b35b9a10362d ("mtd: spi-nor: Move m25p80 code in spi-nor.c") + +Signed-off-by: Bin Meng +Signed-off-by: Rich Felker +Signed-off-by: YunYi Yang +--- + arch/sh/configs/sh7757lcr_defconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/sh/configs/sh7757lcr_defconfig b/arch/sh/configs/sh7757lcr_defconfig +index b0c4bc830fb8..225b82d49d6d 100644 +--- a/arch/sh/configs/sh7757lcr_defconfig ++++ b/arch/sh/configs/sh7757lcr_defconfig +@@ -37,7 +37,7 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" + # CONFIG_FW_LOADER is not set + CONFIG_MTD=y + CONFIG_MTD_BLOCK=y +-CONFIG_MTD_M25P80=y ++CONFIG_MTD_SPI_NOR=y + CONFIG_BLK_DEV_RAM=y + CONFIG_SCSI=y + CONFIG_BLK_DEV_SD=y +-- +2.27.0 + diff --git a/patches/0340-spi-spi-mem-Add-SPI_MEM_NO_DATA-to-the-spi_mem_data_.patch b/patches/0340-spi-spi-mem-Add-SPI_MEM_NO_DATA-to-the-spi_mem_data_.patch new file mode 100644 index 0000000..08d2e60 --- /dev/null +++ b/patches/0340-spi-spi-mem-Add-SPI_MEM_NO_DATA-to-the-spi_mem_data_.patch @@ -0,0 +1,67 @@ +From c95fab40e779f7e66d3643daf9a9760aeb964929 Mon Sep 17 00:00:00 2001 +From: Boris Brezillon +Date: Tue, 6 Nov 2018 17:05:31 +0100 +Subject: [PATCH 18/39] spi: spi-mem: Add SPI_MEM_NO_DATA to the + spi_mem_data_dir enum + +mainline inclusion +from mainline-v5.0-rc1 +commit 0ebb261a0b2d090de618a383d2378d4a00834958 +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0ebb261a0b2d090de618a383d2378d4a00834958 + +---------------------------------------------------------------------------- + +When defining spi_mem_op templates we don't necessarily know the size +that will be passed when the template is actually used, and basing the +supports_op() check on op->data.nbytes to know whether there will be +data transferred for a specific operation is this not possible. + +Add SPI_MEM_NO_DATA to the spi_mem_data_dir enum so that we can base +our checks on op->data.dir instead of op->data.nbytes. + +Signed-off-by: Boris Brezillon +Reviewed-by: Miquel Raynal +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/spi-mem.c | 2 +- + include/linux/spi/spi-mem.h | 2 ++ + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c +index eb72dba71d83..526b1ed5c306 100644 +--- a/drivers/spi/spi-mem.c ++++ b/drivers/spi/spi-mem.c +@@ -140,7 +140,7 @@ static bool spi_mem_default_supports_op(struct spi_mem *mem, + spi_check_buswidth_req(mem, op->dummy.buswidth, true)) + return false; + +- if (op->data.nbytes && ++ if (op->data.dir != SPI_MEM_NO_DATA && + spi_check_buswidth_req(mem, op->data.buswidth, + op->data.dir == SPI_MEM_DATA_OUT)) + return false; +diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h +index 69ee30456864..80db2de83402 100644 +--- a/include/linux/spi/spi-mem.h ++++ b/include/linux/spi/spi-mem.h +@@ -57,10 +57,12 @@ + /** + * enum spi_mem_data_dir - describes the direction of a SPI memory data + * transfer from the controller perspective ++ * @SPI_MEM_NO_DATA: no data transferred + * @SPI_MEM_DATA_IN: data coming from the SPI memory + * @SPI_MEM_DATA_OUT: data sent the SPI memory + */ + enum spi_mem_data_dir { ++ SPI_MEM_NO_DATA, + SPI_MEM_DATA_IN, + SPI_MEM_DATA_OUT, + }; +-- +2.27.0 + diff --git a/patches/0341-spi-spi-mem-Fix-build-error-without-CONFIG_SPI_MEM.patch b/patches/0341-spi-spi-mem-Fix-build-error-without-CONFIG_SPI_MEM.patch new file mode 100644 index 0000000..6318816 --- /dev/null +++ b/patches/0341-spi-spi-mem-Fix-build-error-without-CONFIG_SPI_MEM.patch @@ -0,0 +1,80 @@ +From 9fc413c03fc3aa5566c7ce9da96b6ee7e417184a Mon Sep 17 00:00:00 2001 +From: YueHaibing +Date: Mon, 8 Apr 2019 22:39:49 +0800 +Subject: [PATCH 19/39] spi: spi-mem: Fix build error without CONFIG_SPI_MEM + +mainline inclusion +from mainline-v5.2-rc1 +commit 72e6841608b9ce7e04515ed43693b2878936c93a +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=72e6841608b9ce7e04515ed43693b2878936c93a + +---------------------------------------------------------------------------- + +When building with CONFIG_SPI_MEM is not set +gc warns this: + +drivers/spi/spi-zynq-qspi.o: In function `zynq_qspi_supports_op': +spi-zynq-qspi.c:(.text+0x1da): undefined reference to `spi_mem_default_supports_op' + +Fixes: 67dca5e580f1 ("spi: spi-mem: Add support for Zynq QSPI controller") +Signed-off-by: YueHaibing +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang + + Conflicts: + include/linux/spi/spi-mem.h +--- + drivers/spi/spi-mem.c | 2 +- + include/linux/spi/spi-mem.h | 12 ++++++++++++ + 2 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c +index 526b1ed5c306..788824ee00c7 100644 +--- a/drivers/spi/spi-mem.c ++++ b/drivers/spi/spi-mem.c +@@ -126,7 +126,7 @@ static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx) + return -ENOTSUPP; + } + +-static bool spi_mem_default_supports_op(struct spi_mem *mem, ++bool spi_mem_default_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) + { + if (spi_check_buswidth_req(mem, op->cmd.buswidth, true)) +diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h +index 80db2de83402..991a394efe9c 100644 +--- a/include/linux/spi/spi-mem.h ++++ b/include/linux/spi/spi-mem.h +@@ -224,6 +224,10 @@ int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, + void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg); ++ ++bool spi_mem_default_supports_op(struct spi_mem *mem, ++ const struct spi_mem_op *op); ++ + #else + static inline int + spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, +@@ -239,6 +243,14 @@ spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr, + struct sg_table *sg) + { + } ++ ++static inline ++bool spi_mem_default_supports_op(struct spi_mem *mem, ++ const struct spi_mem_op *op) ++{ ++ return false; ++} ++ + #endif /* CONFIG_SPI_MEM */ + + int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op); +-- +2.27.0 + diff --git a/patches/0342-driver-platform-Support-parsing-GpioInt-0-in-platfor.patch b/patches/0342-driver-platform-Support-parsing-GpioInt-0-in-platfor.patch new file mode 100644 index 0000000..0b3d714 --- /dev/null +++ b/patches/0342-driver-platform-Support-parsing-GpioInt-0-in-platfor.patch @@ -0,0 +1,77 @@ +From 65d39d8a4568e1f70da4ec8313e2cff534a2c70b Mon Sep 17 00:00:00 2001 +From: Enrico Granata +Date: Mon, 11 Feb 2019 11:01:12 -0800 +Subject: [PATCH 20/39] driver: platform: Support parsing GpioInt 0 in + platform_get_irq() + +mainline inclusion +from mainline-v5.1-rc1 +commit daaef255dc96834aaaad627d3271504cba3ac2dc +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=daaef255dc96834aaaad627d3271504cba3ac2dc + +---------------------------------------------------------------------------- + +ACPI 5 added support for GpioInt resources as a way to provide +information about interrupts mediated via a GPIO controller. + +Several device buses (e.g. SPI, I2C) have support for retrieving +an IRQ specified via this type of resource, and providing it +directly to the driver as an IRQ number. + +This is not currently done for the platform drivers, as platform_get_irq() +does not try to parse GpioInt() resources. This requires drivers to +either have to support only one possible IRQ resource, or to have code +in place to try both as a failsafe. + +While there is a possibility of ambiguity for devices that exposes +multiple IRQs, it is easy and feasible to support the common case +of devices that only expose one IRQ which would be of either type +depending on the underlying system's architecture. + +This commit adds support for parsing a GpioInt resource in order +to fulfill a request for the index 0 IRQ for a platform device. + +Signed-off-by: Enrico Granata +Reviewed-by: Dmitry Torokhov +Acked-by: Hans de Goede +Reviewed-by: Mika Westerberg +Reviewed-by: Rafael J. Wysocki +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: YunYi Yang +--- + drivers/base/platform.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/drivers/base/platform.c b/drivers/base/platform.c +index be8c82cc4445..77412f3509de 100644 +--- a/drivers/base/platform.c ++++ b/drivers/base/platform.c +@@ -128,7 +128,20 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) + irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS); + } + +- return r ? r->start : -ENXIO; ++ if (r) ++ return r->start; ++ ++ /* ++ * For the index 0 interrupt, allow falling back to GpioInt ++ * resources. While a device could have both Interrupt and GpioInt ++ * resources, making this fallback ambiguous, in many common cases ++ * the device will only expose one IRQ, and this fallback ++ * allows a common code path across either kind of resource. ++ */ ++ if (num == 0 && has_acpi_companion(&dev->dev)) ++ return acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num); ++ ++ return -ENXIO; + #endif + } + EXPORT_SYMBOL_GPL(platform_get_irq); +-- +2.27.0 + diff --git a/patches/0343-driver-core-platform-return-ENXIO-for-missing-GpioIn.patch b/patches/0343-driver-core-platform-return-ENXIO-for-missing-GpioIn.patch new file mode 100644 index 0000000..16ab0eb --- /dev/null +++ b/patches/0343-driver-core-platform-return-ENXIO-for-missing-GpioIn.patch @@ -0,0 +1,83 @@ +From 897f087ebc8218977d66b43ab0df30bd26ad65c3 Mon Sep 17 00:00:00 2001 +From: Brian Norris +Date: Mon, 29 Jul 2019 13:49:54 -0700 +Subject: [PATCH 21/39] driver core: platform: return -ENXIO for missing + GpioInt + +mainline inclusion +from mainline-v5.3-rc4 +commit 46c42d844211ef5902e32aa507beac0817c585e9 +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=46c42d844211ef5902e32aa507beac0817c585e9 + +---------------------------------------------------------------------------- + +Commit daaef255dc96 ("driver: platform: Support parsing GpioInt 0 in +platform_get_irq()") broke the Embedded Controller driver on most LPC +Chromebooks (i.e., most x86 Chromebooks), because cros_ec_lpc expects +platform_get_irq() to return -ENXIO for non-existent IRQs. +Unfortunately, acpi_dev_gpio_irq_get() doesn't follow this convention +and returns -ENOENT instead. So we get this error from cros_ec_lpc: + + couldn't retrieve IRQ number (-2) + +I see a variety of drivers that treat -ENXIO specially, so rather than +fix all of them, let's fix up the API to restore its previous behavior. + +I reported this on v2 of this patch: + +https://lore.kernel.org/lkml/20190220180538.GA42642@google.com/ + +but apparently the patch had already been merged before v3 got sent out: + +https://lore.kernel.org/lkml/20190221193429.161300-1-egranata@chromium.org/ + +and the result is that the bug landed and remains unfixed. + +I differ from the v3 patch by: + * allowing for ret==0, even though acpi_dev_gpio_irq_get() specifically + documents (and enforces) that 0 is not a valid return value (noted on + the v3 review) + * adding a small comment + +Reported-by: Brian Norris +Reported-by: Salvatore Bellizzi +Cc: Enrico Granata +Cc: +Fixes: daaef255dc96 ("driver: platform: Support parsing GpioInt 0 in platform_get_irq()") +Signed-off-by: Brian Norris +Reviewed-by: Andy Shevchenko +Acked-by: Enrico Granata +Link: https://lore.kernel.org/r/20190729204954.25510-1-briannorris@chromium.org +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: YunYi Yang +--- + drivers/base/platform.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/drivers/base/platform.c b/drivers/base/platform.c +index 77412f3509de..d04ac9f54b57 100644 +--- a/drivers/base/platform.c ++++ b/drivers/base/platform.c +@@ -138,8 +138,13 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) + * the device will only expose one IRQ, and this fallback + * allows a common code path across either kind of resource. + */ +- if (num == 0 && has_acpi_companion(&dev->dev)) +- return acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num); ++ if (num == 0 && has_acpi_companion(&dev->dev)) { ++ int ret = acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num); ++ ++ /* Our callers expect -ENXIO for missing IRQs. */ ++ if (ret >= 0 || ret == -EPROBE_DEFER) ++ return ret; ++ } + + return -ENXIO; + #endif +-- +2.27.0 + diff --git a/patches/0344-spi-acpi-enumerate-all-SPI-slaves-in-the-namespace.patch b/patches/0344-spi-acpi-enumerate-all-SPI-slaves-in-the-namespace.patch new file mode 100644 index 0000000..d1d23d1 --- /dev/null +++ b/patches/0344-spi-acpi-enumerate-all-SPI-slaves-in-the-namespace.patch @@ -0,0 +1,258 @@ +From 3fb5bcdbcd9ceaf3d8d69f8ba5a20c692c3ba105 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Thu, 30 May 2019 13:16:34 +0200 +Subject: [PATCH 22/39] spi/acpi: enumerate all SPI slaves in the namespace + +mainline inclusion +from mainline-v5.3-rc1 +commit 4c3c59544f33e97cf8557f27e05a9904ead16363 +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4c3c59544f33e97cf8557f27e05a9904ead16363 + +---------------------------------------------------------------------------- + +Currently, the ACPI enumeration that takes place when registering a +SPI master only considers immediate child devices in the ACPI namespace, +rather than checking the ResourceSource field in the SpiSerialBus() +resource descriptor. + +This is incorrect: SPI slaves could reside anywhere in the ACPI +namespace, and so we should enumerate the entire namespace and look for +any device that refers to the newly registered SPI master in its +resource descriptor. + +So refactor the existing code and use a lookup structure so that +allocating the SPI device structure is deferred until we have identified +the device as an actual child of the controller. This approach is +loosely based on the way the I2C subsystem handles ACPI enumeration. + +Note that Apple x86 hardware does not rely on SpiSerialBus() resources +in _CRS but uses nested devices below the controller's device node in +the ACPI namespace, with a special set of device properties. This means +we have to take care to only parse those properties for device nodes +that are direct children of the controller node. + +Cc: Mika Westerberg +Cc: linux-spi@vger.kernel.org +Cc: broonie@kernel.org +Cc: andy.shevchenko@gmail.com +Cc: masahisa.kojima@linaro.org +Cc: "Rafael J. Wysocki" +Cc: Jarkko Nikula +Cc: linux-acpi@vger.kernel.org +Cc: Lukas Wunner + +Signed-off-by: Ard Biesheuvel +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/spi.c | 104 ++++++++++++++++++++++++++++++++-------------- + 1 file changed, 73 insertions(+), 31 deletions(-) + +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index 08c7c8ee3271..c6e272edd806 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1732,9 +1732,18 @@ static void of_register_spi_devices(struct spi_controller *ctlr) { } + #endif + + #ifdef CONFIG_ACPI +-static void acpi_spi_parse_apple_properties(struct spi_device *spi) ++struct acpi_spi_lookup { ++ struct spi_controller *ctlr; ++ u32 max_speed_hz; ++ u32 mode; ++ int irq; ++ u8 bits_per_word; ++ u8 chip_select; ++}; ++ ++static void acpi_spi_parse_apple_properties(struct acpi_device *dev, ++ struct acpi_spi_lookup *lookup) + { +- struct acpi_device *dev = ACPI_COMPANION(&spi->dev); + const union acpi_object *obj; + + if (!x86_apple_machine) +@@ -1742,35 +1751,47 @@ static void acpi_spi_parse_apple_properties(struct spi_device *spi) + + if (!acpi_dev_get_property(dev, "spiSclkPeriod", ACPI_TYPE_BUFFER, &obj) + && obj->buffer.length >= 4) +- spi->max_speed_hz = NSEC_PER_SEC / *(u32 *)obj->buffer.pointer; ++ lookup->max_speed_hz = NSEC_PER_SEC / ++ *(u32 *)obj->buffer.pointer; + + if (!acpi_dev_get_property(dev, "spiWordSize", ACPI_TYPE_BUFFER, &obj) + && obj->buffer.length == 8) +- spi->bits_per_word = *(u64 *)obj->buffer.pointer; ++ lookup->bits_per_word = *(u64 *)obj->buffer.pointer; + + if (!acpi_dev_get_property(dev, "spiBitOrder", ACPI_TYPE_BUFFER, &obj) + && obj->buffer.length == 8 && !*(u64 *)obj->buffer.pointer) +- spi->mode |= SPI_LSB_FIRST; ++ lookup->mode |= SPI_LSB_FIRST; + + if (!acpi_dev_get_property(dev, "spiSPO", ACPI_TYPE_BUFFER, &obj) + && obj->buffer.length == 8 && *(u64 *)obj->buffer.pointer) +- spi->mode |= SPI_CPOL; ++ lookup->mode |= SPI_CPOL; + + if (!acpi_dev_get_property(dev, "spiSPH", ACPI_TYPE_BUFFER, &obj) + && obj->buffer.length == 8 && *(u64 *)obj->buffer.pointer) +- spi->mode |= SPI_CPHA; ++ lookup->mode |= SPI_CPHA; + } + + static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) + { +- struct spi_device *spi = data; +- struct spi_controller *ctlr = spi->controller; ++ struct acpi_spi_lookup *lookup = data; ++ struct spi_controller *ctlr = lookup->ctlr; + + if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { + struct acpi_resource_spi_serialbus *sb; ++ acpi_handle parent_handle; ++ acpi_status status; + + sb = &ares->data.spi_serial_bus; + if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_SPI) { ++ ++ status = acpi_get_handle(NULL, ++ sb->resource_source.string_ptr, ++ &parent_handle); ++ ++ if (!status || ++ ACPI_HANDLE(ctlr->dev.parent) != parent_handle) ++ return -ENODEV; ++ + /* + * ACPI DeviceSelection numbering is handled by the + * host controller driver in Windows and can vary +@@ -1783,25 +1804,25 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) + sb->device_selection); + if (cs < 0) + return cs; +- spi->chip_select = cs; ++ lookup->chip_select = cs; + } else { +- spi->chip_select = sb->device_selection; ++ lookup->chip_select = sb->device_selection; + } + +- spi->max_speed_hz = sb->connection_speed; ++ lookup->max_speed_hz = sb->connection_speed; + + if (sb->clock_phase == ACPI_SPI_SECOND_PHASE) +- spi->mode |= SPI_CPHA; ++ lookup->mode |= SPI_CPHA; + if (sb->clock_polarity == ACPI_SPI_START_HIGH) +- spi->mode |= SPI_CPOL; ++ lookup->mode |= SPI_CPOL; + if (sb->device_polarity == ACPI_SPI_ACTIVE_HIGH) +- spi->mode |= SPI_CS_HIGH; ++ lookup->mode |= SPI_CS_HIGH; + } +- } else if (spi->irq < 0) { ++ } else if (lookup->irq < 0) { + struct resource r; + + if (acpi_dev_resource_interrupt(ares, 0, &r)) +- spi->irq = r.start; ++ lookup->irq = r.start; + } + + /* Always tell the ACPI core to skip this resource */ +@@ -1811,7 +1832,9 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) + static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, + struct acpi_device *adev) + { ++ acpi_handle parent_handle = NULL; + struct list_head resource_list; ++ struct acpi_spi_lookup lookup; + struct spi_device *spi; + int ret; + +@@ -1819,28 +1842,44 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, + acpi_device_enumerated(adev)) + return AE_OK; + +- spi = spi_alloc_device(ctlr); +- if (!spi) { +- dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n", +- dev_name(&adev->dev)); +- return AE_NO_MEMORY; +- } +- +- ACPI_COMPANION_SET(&spi->dev, adev); +- spi->irq = -1; ++ lookup.ctlr = ctlr; ++ lookup.mode = 0; ++ lookup.bits_per_word = 0; ++ lookup.irq = -1; + + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, +- acpi_spi_add_resource, spi); ++ acpi_spi_add_resource, &lookup); + acpi_dev_free_resource_list(&resource_list); + +- acpi_spi_parse_apple_properties(spi); ++ if (ret < 0) ++ /* found SPI in _CRS but it points to another controller */ ++ return AE_OK; + +- if (ret < 0 || !spi->max_speed_hz) { +- spi_dev_put(spi); ++ if (!lookup.max_speed_hz && ++ !ACPI_FAILURE(acpi_get_parent(adev->handle, &parent_handle)) && ++ ACPI_HANDLE(ctlr->dev.parent) == parent_handle) { ++ /* Apple does not use _CRS but nested devices for SPI slaves */ ++ acpi_spi_parse_apple_properties(adev, &lookup); ++ } ++ ++ if (!lookup.max_speed_hz) + return AE_OK; ++ ++ spi = spi_alloc_device(ctlr); ++ if (!spi) { ++ dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n", ++ dev_name(&adev->dev)); ++ return AE_NO_MEMORY; + } + ++ ACPI_COMPANION_SET(&spi->dev, adev); ++ spi->max_speed_hz = lookup.max_speed_hz; ++ spi->mode = lookup.mode; ++ spi->irq = lookup.irq; ++ spi->bits_per_word = lookup.bits_per_word; ++ spi->chip_select = lookup.chip_select; ++ + acpi_set_modalias(adev, acpi_device_hid(adev), spi->modalias, + sizeof(spi->modalias)); + +@@ -1872,6 +1911,8 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level, + return acpi_register_spi_device(ctlr, adev); + } + ++#define SPI_ACPI_ENUMERATE_MAX_DEPTH 32 ++ + static void acpi_register_spi_devices(struct spi_controller *ctlr) + { + acpi_status status; +@@ -1881,7 +1922,8 @@ static void acpi_register_spi_devices(struct spi_controller *ctlr) + if (!handle) + return; + +- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, ++ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ++ SPI_ACPI_ENUMERATE_MAX_DEPTH, + acpi_spi_add_device, NULL, ctlr, NULL); + if (ACPI_FAILURE(status)) + dev_warn(&ctlr->dev, "failed to enumerate SPI slaves\n"); +-- +2.27.0 + diff --git a/patches/0345-spi-acpi-fix-incorrect-ACPI-parent-check.patch b/patches/0345-spi-acpi-fix-incorrect-ACPI-parent-check.patch new file mode 100644 index 0000000..3d218f2 --- /dev/null +++ b/patches/0345-spi-acpi-fix-incorrect-ACPI-parent-check.patch @@ -0,0 +1,66 @@ +From d6e11ded3fafb57845205b7392da6ea3257ac444 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Wed, 19 Jun 2019 11:52:54 +0200 +Subject: [PATCH 23/39] spi/acpi: fix incorrect ACPI parent check + +mainline inclusion +from mainline-v5.3-rc1 +commit b5e3cf410b486a2415ff09b12f3ef18aba9f53ff +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b5e3cf410b486a2415ff09b12f3ef18aba9f53ff + +---------------------------------------------------------------------------- + +The ACPI device object parsing code for SPI slaves enumerates the +entire ACPI namespace to look for devices that refer to the master +in question via the 'resource_source' field in the 'SPISerialBus' +resource. If that field does not refer to a valid ACPI device or +if it refers to the wrong SPI master, we should disregard the +device. + +Current, the valid device check is wrong, since it gets the +polarity of 'status' wrong. This could cause issues if the +'resource_source' field is bogus but parent_handle happens to +refer to the correct master (which is not entirely imaginary +since this code runs in a loop) + +So test for ACPI_FAILURE() instead, to make the code more +self explanatory. + +Fixes: 4c3c59544f33 ("spi/acpi: enumerate all SPI slaves in the namespace") +Reported-by: kbuild test robot +Reported-by: Dan Carpenter +Cc: Mika Westerberg +Cc: andy.shevchenko@gmail.com +Cc: masahisa.kojima@linaro.org +Cc: "Rafael J. Wysocki" +Cc: Jarkko Nikula +Cc: linux-acpi@vger.kernel.org +Cc: Lukas Wunner +Signed-off-by: Ard Biesheuvel +Acked-by: Mika Westerberg +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/spi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index c6e272edd806..59465736b574 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1788,7 +1788,7 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) + sb->resource_source.string_ptr, + &parent_handle); + +- if (!status || ++ if (ACPI_FAILURE(status) || + ACPI_HANDLE(ctlr->dev.parent) != parent_handle) + return -ENODEV; + +-- +2.27.0 + diff --git a/patches/0346-spi-acpi-avoid-spurious-matches-during-slave-enumera.patch b/patches/0346-spi-acpi-avoid-spurious-matches-during-slave-enumera.patch new file mode 100644 index 0000000..c954c7a --- /dev/null +++ b/patches/0346-spi-acpi-avoid-spurious-matches-during-slave-enumera.patch @@ -0,0 +1,68 @@ +From 0c9807ae12a27c3e2f1c4332b942a5b10106d0d9 Mon Sep 17 00:00:00 2001 +From: Ard Biesheuvel +Date: Thu, 20 Jun 2019 14:36:49 +0200 +Subject: [PATCH 24/39] spi/acpi: avoid spurious matches during slave + enumeration + +mainline inclusion +from mainline-v5.3-rc1 +commit b28944c6f6d3951f0c8f23f90c83ef741d30bfca +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b28944c6f6d3951f0c8f23f90c83ef741d30bfca + +---------------------------------------------------------------------------- + +In the new SPI ACPI slave enumeration code, we use the value of +lookup.max_speed_khz as a flag to decide whether a match occurred. +However, doing so only makes sense if we initialize its value to +zero beforehand, or otherwise, random junk from the stack will +cause spurious matches. + +So zero initialize the lookup struct fully, and only set the non-zero +members explicitly. + +Fixes: 4c3c59544f33 ("spi/acpi: enumerate all SPI slaves in the namespace") +Cc: Mika Westerberg +Cc: andy.shevchenko@gmail.com +Cc: masahisa.kojima@linaro.org +Cc: "Rafael J. Wysocki" +Cc: Jarkko Nikula +Cc: linux-acpi@vger.kernel.org +Cc: Lukas Wunner +Signed-off-by: Ard Biesheuvel +Tested-by: Jarkko Nikula +Acked-by: Mika Westerberg +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/spi.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index 59465736b574..69d569744ea1 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1834,7 +1834,7 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, + { + acpi_handle parent_handle = NULL; + struct list_head resource_list; +- struct acpi_spi_lookup lookup; ++ struct acpi_spi_lookup lookup = {}; + struct spi_device *spi; + int ret; + +@@ -1843,8 +1843,6 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, + return AE_OK; + + lookup.ctlr = ctlr; +- lookup.mode = 0; +- lookup.bits_per_word = 0; + lookup.irq = -1; + + INIT_LIST_HEAD(&resource_list); +-- +2.27.0 + diff --git a/patches/0347-spi-Add-HiSilicon-v3xx-SPI-NOR-flash-controller-driv.patch b/patches/0347-spi-Add-HiSilicon-v3xx-SPI-NOR-flash-controller-driv.patch new file mode 100644 index 0000000..6463720 --- /dev/null +++ b/patches/0347-spi-Add-HiSilicon-v3xx-SPI-NOR-flash-controller-driv.patch @@ -0,0 +1,373 @@ +From 6304949808ee3a17a542a989c435f39c24afa697 Mon Sep 17 00:00:00 2001 +From: John Garry +Date: Mon, 9 Dec 2019 22:08:09 +0800 +Subject: [PATCH 25/39] spi: Add HiSilicon v3xx SPI NOR flash controller driver + +mainline inclusion +from mainline-v5.6-rc1 +commit a2ca53b52e007de81752bbb443d828f5950d6d04 +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a2ca53b52e007de81752bbb443d828f5950d6d04 + +---------------------------------------------------------------------------- + +Add the driver for the HiSilicon v3xx SPI NOR flash controller, commonly +found in hi16xx chipsets. + +This is a different controller than that in drivers/mtd/spi-nor/hisi-sfc.c; +indeed, the naming for that driver is poor, since it is really known as +FMC, and can support other memory technologies. + +The driver module name is "hisi-sfc-v3xx", as recommended by HW designer, +being an attempt to provide a distinct name - v3xx being the unique +controller versioning. + +Only ACPI firmware is supported. + +DMA is not supported, and we just use polling mode for operation +completion notification. + +The driver uses the SPI MEM OPs. + +Signed-off-by: John Garry +Link: https://lore.kernel.org/r/1575900490-74467-3-git-send-email-john.garry@huawei.com +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang + + Conflicts: + drivers/spi/Kconfig +--- + drivers/spi/Kconfig | 9 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-hisi-sfc-v3xx.c | 284 ++++++++++++++++++++++++++++++++ + 3 files changed, 294 insertions(+) + create mode 100644 drivers/spi/spi-hisi-sfc-v3xx.c + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 7bfdf8d2cbf9..a6f1a5c2ea82 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -261,6 +261,15 @@ config SPI_HISI_KUNPENG + This driver can also be built as a module. If so, the module + will be called hisi-kunpeng-spi. + ++config SPI_HISI_SFC_V3XX ++ tristate "HiSilicon SPI-NOR Flash Controller for Hi16XX chipsets" ++ depends on (ARM64 && ACPI) || COMPILE_TEST ++ depends on HAS_IOMEM ++ select CONFIG_MTD_SPI_NOR ++ help ++ This enables support for HiSilicon v3xx SPI-NOR flash controller ++ found in hi16xx chipsets. ++ + config SPI_GPIO + tristate "GPIO-based bitbanging SPI Master" + depends on GPIOLIB || COMPILE_TEST +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index 49c499d5e1a2..f1f733772e09 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -46,6 +46,7 @@ obj-$(CONFIG_SPI_FSL_LPSPI) += spi-fsl-lpspi.o + obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o + obj-$(CONFIG_SPI_GPIO) += spi-gpio.o + obj-$(CONFIG_SPI_HISI_KUNPENG) += spi-hisi-kunpeng.o ++obj-$(CONFIG_SPI_HISI_SFC_V3XX) += spi-hisi-sfc-v3xx.o + obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o + obj-$(CONFIG_SPI_IMX) += spi-imx.o + obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +new file mode 100644 +index 000000000000..4cf8fc80a7b7 +--- /dev/null ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -0,0 +1,284 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++// ++// HiSilicon SPI NOR V3XX Flash Controller Driver for hi16xx chipsets ++// ++// Copyright (c) 2019 HiSilicon Technologies Co., Ltd. ++// Author: John Garry ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define HISI_SFC_V3XX_VERSION (0x1f8) ++ ++#define HISI_SFC_V3XX_CMD_CFG (0x300) ++#define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9 ++#define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8) ++#define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7) ++#define HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF 4 ++#define HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK BIT(3) ++#define HISI_SFC_V3XX_CMD_CFG_CS_SEL_OFF 1 ++#define HISI_SFC_V3XX_CMD_CFG_START_MSK BIT(0) ++#define HISI_SFC_V3XX_CMD_INS (0x308) ++#define HISI_SFC_V3XX_CMD_ADDR (0x30c) ++#define HISI_SFC_V3XX_CMD_DATABUF0 (0x400) ++ ++struct hisi_sfc_v3xx_host { ++ struct device *dev; ++ void __iomem *regbase; ++ int max_cmd_dword; ++}; ++ ++#define HISI_SFC_V3XX_WAIT_TIMEOUT_US 1000000 ++#define HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US 10 ++ ++static int hisi_sfc_v3xx_wait_cmd_idle(struct hisi_sfc_v3xx_host *host) ++{ ++ u32 reg; ++ ++ return readl_poll_timeout(host->regbase + HISI_SFC_V3XX_CMD_CFG, reg, ++ !(reg & HISI_SFC_V3XX_CMD_CFG_START_MSK), ++ HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US, ++ HISI_SFC_V3XX_WAIT_TIMEOUT_US); ++} ++ ++static int hisi_sfc_v3xx_adjust_op_size(struct spi_mem *mem, ++ struct spi_mem_op *op) ++{ ++ struct spi_device *spi = mem->spi; ++ struct hisi_sfc_v3xx_host *host; ++ uintptr_t addr = (uintptr_t)op->data.buf.in; ++ int max_byte_count; ++ ++ host = spi_controller_get_devdata(spi->master); ++ ++ max_byte_count = host->max_cmd_dword * 4; ++ ++ if (!IS_ALIGNED(addr, 4) && op->data.nbytes >= 4) ++ op->data.nbytes = 4 - (addr % 4); ++ else if (op->data.nbytes > max_byte_count) ++ op->data.nbytes = max_byte_count; ++ ++ return 0; ++} ++ ++/* ++ * memcpy_{to,from}io doesn't gurantee 32b accesses - which we require for the ++ * DATABUF registers -so use __io{read,write}32_copy when possible. For ++ * trailing bytes, copy them byte-by-byte from the DATABUF register, as we ++ * can't clobber outside the source/dest buffer. ++ * ++ * For efficient data read/write, we try to put any start 32b unaligned data ++ * into a separate transaction in hisi_sfc_v3xx_adjust_op_size(). ++ */ ++static void hisi_sfc_v3xx_read_databuf(struct hisi_sfc_v3xx_host *host, ++ u8 *to, unsigned int len) ++{ ++ void __iomem *from; ++ int i; ++ ++ from = host->regbase + HISI_SFC_V3XX_CMD_DATABUF0; ++ ++ if (IS_ALIGNED((uintptr_t)to, 4)) { ++ int words = len / 4; ++ ++ __ioread32_copy(to, from, words); ++ ++ len -= words * 4; ++ if (len) { ++ u32 val; ++ ++ to += words * 4; ++ from += words * 4; ++ ++ val = __raw_readl(from); ++ ++ for (i = 0; i < len; i++, val >>= 8, to++) ++ *to = (u8)val; ++ } ++ } else { ++ for (i = 0; i < DIV_ROUND_UP(len, 4); i++, from += 4) { ++ u32 val = __raw_readl(from); ++ int j; ++ ++ for (j = 0; j < 4 && (j + (i * 4) < len); ++ to++, val >>= 8, j++) ++ *to = (u8)val; ++ } ++ } ++} ++ ++static void hisi_sfc_v3xx_write_databuf(struct hisi_sfc_v3xx_host *host, ++ const u8 *from, unsigned int len) ++{ ++ void __iomem *to; ++ int i; ++ ++ to = host->regbase + HISI_SFC_V3XX_CMD_DATABUF0; ++ ++ if (IS_ALIGNED((uintptr_t)from, 4)) { ++ int words = len / 4; ++ ++ __iowrite32_copy(to, from, words); ++ ++ len -= words * 4; ++ if (len) { ++ u32 val = 0; ++ ++ to += words * 4; ++ from += words * 4; ++ ++ for (i = 0; i < len; i++, from++) ++ val |= *from << i * 8; ++ __raw_writel(val, to); ++ } ++ ++ } else { ++ for (i = 0; i < DIV_ROUND_UP(len, 4); i++, to += 4) { ++ u32 val = 0; ++ int j; ++ ++ for (j = 0; j < 4 && (j + (i * 4) < len); ++ from++, j++) ++ val |= *from << j * 8; ++ __raw_writel(val, to); ++ } ++ } ++} ++ ++static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, ++ const struct spi_mem_op *op, ++ u8 chip_select) ++{ ++ int ret, len = op->data.nbytes; ++ u32 config = 0; ++ ++ if (op->addr.nbytes) ++ config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK; ++ ++ if (op->data.dir != SPI_MEM_NO_DATA) { ++ config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF; ++ config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK; ++ } ++ ++ if (op->data.dir == SPI_MEM_DATA_OUT) ++ hisi_sfc_v3xx_write_databuf(host, op->data.buf.out, len); ++ else if (op->data.dir == SPI_MEM_DATA_IN) ++ config |= HISI_SFC_V3XX_CMD_CFG_RW_MSK; ++ ++ config |= op->dummy.nbytes << HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF | ++ chip_select << HISI_SFC_V3XX_CMD_CFG_CS_SEL_OFF | ++ HISI_SFC_V3XX_CMD_CFG_START_MSK; ++ ++ writel(op->addr.val, host->regbase + HISI_SFC_V3XX_CMD_ADDR); ++ writel(op->cmd.opcode, host->regbase + HISI_SFC_V3XX_CMD_INS); ++ ++ writel(config, host->regbase + HISI_SFC_V3XX_CMD_CFG); ++ ++ ret = hisi_sfc_v3xx_wait_cmd_idle(host); ++ if (ret) ++ return ret; ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) ++ hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len); ++ ++ return 0; ++} ++ ++static int hisi_sfc_v3xx_exec_op(struct spi_mem *mem, ++ const struct spi_mem_op *op) ++{ ++ struct hisi_sfc_v3xx_host *host; ++ struct spi_device *spi = mem->spi; ++ u8 chip_select = spi->chip_select; ++ ++ host = spi_controller_get_devdata(spi->master); ++ ++ return hisi_sfc_v3xx_generic_exec_op(host, op, chip_select); ++} ++ ++static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = { ++ .adjust_op_size = hisi_sfc_v3xx_adjust_op_size, ++ .exec_op = hisi_sfc_v3xx_exec_op, ++}; ++ ++static int hisi_sfc_v3xx_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct hisi_sfc_v3xx_host *host; ++ struct spi_controller *ctlr; ++ u32 version; ++ int ret; ++ ++ ctlr = spi_alloc_master(&pdev->dev, sizeof(*host)); ++ if (!ctlr) ++ return -ENOMEM; ++ ++ ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | ++ SPI_TX_DUAL | SPI_TX_QUAD; ++ ++ host = spi_controller_get_devdata(ctlr); ++ host->dev = dev; ++ ++ platform_set_drvdata(pdev, host); ++ ++ host->regbase = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(host->regbase)) { ++ ret = PTR_ERR(host->regbase); ++ goto err_put_master; ++ } ++ ++ ctlr->bus_num = -1; ++ ctlr->num_chipselect = 1; ++ ctlr->mem_ops = &hisi_sfc_v3xx_mem_ops; ++ ++ version = readl(host->regbase + HISI_SFC_V3XX_VERSION); ++ ++ switch (version) { ++ case 0x351: ++ host->max_cmd_dword = 64; ++ break; ++ default: ++ host->max_cmd_dword = 16; ++ break; ++ } ++ ++ ret = devm_spi_register_controller(dev, ctlr); ++ if (ret) ++ goto err_put_master; ++ ++ dev_info(&pdev->dev, "hw version 0x%x\n", version); ++ ++ return 0; ++ ++err_put_master: ++ spi_master_put(ctlr); ++ return ret; ++} ++ ++#if IS_ENABLED(CONFIG_ACPI) ++static const struct acpi_device_id hisi_sfc_v3xx_acpi_ids[] = { ++ {"HISI0341", 0}, ++ {} ++}; ++MODULE_DEVICE_TABLE(acpi, hisi_sfc_v3xx_acpi_ids); ++#endif ++ ++static struct platform_driver hisi_sfc_v3xx_spi_driver = { ++ .driver = { ++ .name = "hisi-sfc-v3xx", ++ .acpi_match_table = ACPI_PTR(hisi_sfc_v3xx_acpi_ids), ++ }, ++ .probe = hisi_sfc_v3xx_probe, ++}; ++ ++module_platform_driver(hisi_sfc_v3xx_spi_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("John Garry "); ++MODULE_DESCRIPTION("HiSilicon SPI NOR V3XX Flash Controller Driver for hi16xx chipsets"); +-- +2.27.0 + diff --git a/patches/0348-spi-Allow-SPI-controller-override-device-buswidth.patch b/patches/0348-spi-Allow-SPI-controller-override-device-buswidth.patch new file mode 100644 index 0000000..8732a57 --- /dev/null +++ b/patches/0348-spi-Allow-SPI-controller-override-device-buswidth.patch @@ -0,0 +1,79 @@ +From 783917502bc47a28b0ee6b877bf56342bf6e4f9b Mon Sep 17 00:00:00 2001 +From: John Garry +Date: Fri, 28 Feb 2020 23:18:49 +0800 +Subject: [PATCH 26/39] spi: Allow SPI controller override device buswidth + +mainline inclusion +from mainline-v5.7-rc1 +commit ea23578611dce2eeaf31dcfe12cd7130cf3d1411 +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ea23578611dce2eeaf31dcfe12cd7130cf3d1411 + +---------------------------------------------------------------------------- + +Currently ACPI firmware description for a SPI device does not have any +method to describe the data buswidth on the board. + +So even through the controller and device may support higher modes than +standard SPI, it cannot be assumed that the board does - as such, that +device is limited to standard SPI in such a circumstance. + +As a workaround, allow the controller driver supply buswidth override bits, +which are used inform the core code that the controller driver knows the +buswidth supported on that board for that device. + +A host controller driver might know this info from DMI tables, for example. + +Signed-off-by: John Garry +Link: https://lore.kernel.org/r/1582903131-160033-2-git-send-email-john.garry@huawei.com +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/spi.c | 4 +++- + include/linux/spi/spi.h | 3 +++ + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index 69d569744ea1..a4f366a77072 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -474,6 +474,7 @@ struct spi_device *spi_alloc_device(struct spi_controller *ctlr) + spi->dev.bus = &spi_bus_type; + spi->dev.release = spidev_release; + spi->cs_gpio = -ENOENT; ++ spi->mode = ctlr->buswidth_override_bits; + + spin_lock_init(&spi->statistics.lock); + +@@ -1871,9 +1872,10 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, + return AE_NO_MEMORY; + } + ++ + ACPI_COMPANION_SET(&spi->dev, adev); + spi->max_speed_hz = lookup.max_speed_hz; +- spi->mode = lookup.mode; ++ spi->mode |= lookup.mode; + spi->irq = lookup.irq; + spi->bits_per_word = lookup.bits_per_word; + spi->chip_select = lookup.chip_select; +diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h +index 80d6bd6e46c5..843f187e046b 100644 +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -434,6 +434,9 @@ struct spi_controller { + /* spi_device.mode flags understood by this controller driver */ + u16 mode_bits; + ++ /* spi_device.mode flags override flags for this controller */ ++ u32 buswidth_override_bits; ++ + /* bitmask of supported bits_per_word for transfers */ + u32 bits_per_word_mask; + #define SPI_BPW_MASK(bits) BIT((bits) - 1) +-- +2.27.0 + diff --git a/patches/0349-spi-HiSilicon-v3xx-Properly-set-CMD_CONFIG-for-Dual-.patch b/patches/0349-spi-HiSilicon-v3xx-Properly-set-CMD_CONFIG-for-Dual-.patch new file mode 100644 index 0000000..aacb3b0 --- /dev/null +++ b/patches/0349-spi-HiSilicon-v3xx-Properly-set-CMD_CONFIG-for-Dual-.patch @@ -0,0 +1,91 @@ +From 6c6f965aa6f867a5f3d8103df3734bcae9d18bd3 Mon Sep 17 00:00:00 2001 +From: John Garry +Date: Fri, 28 Feb 2020 23:18:50 +0800 +Subject: [PATCH 27/39] spi: HiSilicon v3xx: Properly set CMD_CONFIG for + Dual/Quad modes + +mainline inclusion +from mainline-v5.7-rc1 +commit 8fe21d6b347247227c349c9b2f7c462fae362af4 +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8fe21d6b347247227c349c9b2f7c462fae362af4 + +---------------------------------------------------------------------------- + +The CMD_CONFIG register memory interface type field is not set configured +for Dual and Quad modes, so set appropriately. + +This was not detected previously as we only ever operated in standard SPI +mode. + +Signed-off-by: John Garry +Link: https://lore.kernel.org/r/1582903131-160033-3-git-send-email-john.garry@huawei.com +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/spi-hisi-sfc-v3xx.c | 39 +++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +index 4cf8fc80a7b7..8c2eea439d5b 100644 +--- a/drivers/spi/spi-hisi-sfc-v3xx.c ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -17,6 +17,12 @@ + #define HISI_SFC_V3XX_VERSION (0x1f8) + + #define HISI_SFC_V3XX_CMD_CFG (0x300) ++#define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17) ++#define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 17) ++#define HISI_SFC_V3XX_CMD_CFG_FULL_DIO (3 << 17) ++#define HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT (5 << 17) ++#define HISI_SFC_V3XX_CMD_CFG_QUAD_IO (6 << 17) ++#define HISI_SFC_V3XX_CMD_CFG_FULL_QIO (7 << 17) + #define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9 + #define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8) + #define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7) +@@ -161,6 +167,39 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + if (op->addr.nbytes) + config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK; + ++ switch (op->data.buswidth) { ++ case 0 ... 1: ++ break; ++ case 2: ++ if (op->addr.buswidth <= 1) { ++ config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT; ++ } else if (op->addr.buswidth == 2) { ++ if (op->cmd.buswidth <= 1) ++ config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IO; ++ else if (op->cmd.buswidth == 2) ++ config |= HISI_SFC_V3XX_CMD_CFG_FULL_DIO; ++ else ++ return -EIO; ++ } else ++ return -EIO; ++ break; ++ case 4: ++ if (op->addr.buswidth <= 1) { ++ config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT; ++ } else if (op->addr.buswidth == 4) { ++ if (op->cmd.buswidth <= 1) ++ config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IO; ++ else if (op->cmd.buswidth == 4) ++ config |= HISI_SFC_V3XX_CMD_CFG_FULL_QIO; ++ else ++ return -EIO; ++ } else ++ return -EIO; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ + if (op->data.dir != SPI_MEM_NO_DATA) { + config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF; + config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK; +-- +2.27.0 + diff --git a/patches/0350-spi-HiSilicon-v3xx-Use-DMI-quirk-to-set-controller-b.patch b/patches/0350-spi-HiSilicon-v3xx-Use-DMI-quirk-to-set-controller-b.patch new file mode 100644 index 0000000..bf0771c --- /dev/null +++ b/patches/0350-spi-HiSilicon-v3xx-Use-DMI-quirk-to-set-controller-b.patch @@ -0,0 +1,122 @@ +From 9336c101918a51d00f6a5508cd5290be5c1596ee Mon Sep 17 00:00:00 2001 +From: John Garry +Date: Fri, 28 Feb 2020 23:18:51 +0800 +Subject: [PATCH 28/39] spi: HiSilicon v3xx: Use DMI quirk to set controller + buswidth override bits + +mainline inclusion +from mainline-v5.7-rc1 +commit 34e608b023e96f51b31274435b49c8ae61e2389f +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=34e608b023e96f51b31274435b49c8ae61e2389f + +---------------------------------------------------------------------------- + +The Huawei D06 board (and variants) can support Quad mode of operation. + +Since we have no current method in ACPI SPI bus device resource description +to describe this information, use DMI to detect the board, and set the +controller buswidth override bits. + +Signed-off-by: John Garry +Link: https://lore.kernel.org/r/1582903131-160033-4-git-send-email-john.garry@huawei.com +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/spi-hisi-sfc-v3xx.c | 56 ++++++++++++++++++++++++++++++++- + 1 file changed, 55 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +index 8c2eea439d5b..c46812f004b7 100644 +--- a/drivers/spi/spi-hisi-sfc-v3xx.c ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -7,6 +7,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -246,6 +247,44 @@ static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = { + .exec_op = hisi_sfc_v3xx_exec_op, + }; + ++static int hisi_sfc_v3xx_buswidth_override_bits; ++ ++/* ++ * ACPI FW does not allow us to currently set the device buswidth, so quirk it ++ * depending on the board. ++ */ ++static int __init hisi_sfc_v3xx_dmi_quirk(const struct dmi_system_id *d) ++{ ++ hisi_sfc_v3xx_buswidth_override_bits = SPI_RX_QUAD | SPI_TX_QUAD; ++ ++ return 0; ++} ++ ++static const struct dmi_system_id hisi_sfc_v3xx_dmi_quirk_table[] = { ++ { ++ .callback = hisi_sfc_v3xx_dmi_quirk, ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Huawei"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "D06"), ++ }, ++ }, ++ { ++ .callback = hisi_sfc_v3xx_dmi_quirk, ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Huawei"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 2280 V2"), ++ }, ++ }, ++ { ++ .callback = hisi_sfc_v3xx_dmi_quirk, ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Huawei"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 200 (Model 2280)"), ++ }, ++ }, ++ {} ++}; ++ + static int hisi_sfc_v3xx_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -261,6 +300,8 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) + ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | + SPI_TX_DUAL | SPI_TX_QUAD; + ++ ctlr->buswidth_override_bits = hisi_sfc_v3xx_buswidth_override_bits; ++ + host = spi_controller_get_devdata(ctlr); + host->dev = dev; + +@@ -316,7 +357,20 @@ static struct platform_driver hisi_sfc_v3xx_spi_driver = { + .probe = hisi_sfc_v3xx_probe, + }; + +-module_platform_driver(hisi_sfc_v3xx_spi_driver); ++static int __init hisi_sfc_v3xx_spi_init(void) ++{ ++ dmi_check_system(hisi_sfc_v3xx_dmi_quirk_table); ++ ++ return platform_driver_register(&hisi_sfc_v3xx_spi_driver); ++} ++ ++static void __exit hisi_sfc_v3xx_spi_exit(void) ++{ ++ platform_driver_unregister(&hisi_sfc_v3xx_spi_driver); ++} ++ ++module_init(hisi_sfc_v3xx_spi_init); ++module_exit(hisi_sfc_v3xx_spi_exit); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("John Garry "); +-- +2.27.0 + diff --git a/patches/0351-spi-hisi-sfc-v3xx-add-error-check-after-per-operatio.patch b/patches/0351-spi-hisi-sfc-v3xx-add-error-check-after-per-operatio.patch new file mode 100644 index 0000000..a6abe14 --- /dev/null +++ b/patches/0351-spi-hisi-sfc-v3xx-add-error-check-after-per-operatio.patch @@ -0,0 +1,88 @@ +From 2863ac8e4dd2da305e6b3bbf4e653e57ea36af6b Mon Sep 17 00:00:00 2001 +From: Yicong Yang +Date: Fri, 17 Apr 2020 15:48:27 +0800 +Subject: [PATCH 29/39] spi: hisi-sfc-v3xx: add error check after per operation + +mainline inclusion +from mainline-v5.8-rc1 +commit 59fc9ad5cb108bce18043281c7cf67f2b425d55d +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=59fc9ad5cb108bce18043281c7cf67f2b425d55d + +---------------------------------------------------------------------------- + +The controller may receive instructions of accessing protected address, +or may perform failed page program. These operations will not succeed +and the controller will receive interrupts when such failure occur. +Previously we don't check the interrupts and return 0 even if such +operation fails. + +Check the interrupts after per command and inform the user +if there is an error. + +Signed-off-by: Yicong Yang +Acked-by: John Garry +Link: https://lore.kernel.org/r/1587109707-23597-1-git-send-email-yangyicong@hisilicon.com +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/spi-hisi-sfc-v3xx.c | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +index c46812f004b7..7b90ba2184ac 100644 +--- a/drivers/spi/spi-hisi-sfc-v3xx.c ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -17,6 +17,11 @@ + + #define HISI_SFC_V3XX_VERSION (0x1f8) + ++#define HISI_SFC_V3XX_INT_STAT (0x120) ++#define HISI_SFC_V3XX_INT_STAT_PP_ERR BIT(2) ++#define HISI_SFC_V3XX_INT_STAT_ADDR_IACCES BIT(5) ++#define HISI_SFC_V3XX_INT_CLR (0x12c) ++#define HISI_SFC_V3XX_INT_CLR_CLEAR (0xff) + #define HISI_SFC_V3XX_CMD_CFG (0x300) + #define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17) + #define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 17) +@@ -163,7 +168,7 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + u8 chip_select) + { + int ret, len = op->data.nbytes; +- u32 config = 0; ++ u32 int_stat, config = 0; + + if (op->addr.nbytes) + config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK; +@@ -224,6 +229,25 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + if (ret) + return ret; + ++ /* ++ * The interrupt status register indicates whether an error occurs ++ * after per operation. Check it, and clear the interrupts for ++ * next time judgement. ++ */ ++ int_stat = readl(host->regbase + HISI_SFC_V3XX_INT_STAT); ++ writel(HISI_SFC_V3XX_INT_CLR_CLEAR, ++ host->regbase + HISI_SFC_V3XX_INT_CLR); ++ ++ if (int_stat & HISI_SFC_V3XX_INT_STAT_ADDR_IACCES) { ++ dev_err(host->dev, "fail to access protected address\n"); ++ return -EIO; ++ } ++ ++ if (int_stat & HISI_SFC_V3XX_INT_STAT_PP_ERR) { ++ dev_err(host->dev, "page program operation failed\n"); ++ return -EIO; ++ } ++ + if (op->data.dir == SPI_MEM_DATA_IN) + hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len); + +-- +2.27.0 + diff --git a/patches/0352-spi-Remove-CONFIG_-prefix-from-Kconfig-select.patch b/patches/0352-spi-Remove-CONFIG_-prefix-from-Kconfig-select.patch new file mode 100644 index 0000000..5006e05 --- /dev/null +++ b/patches/0352-spi-Remove-CONFIG_-prefix-from-Kconfig-select.patch @@ -0,0 +1,46 @@ +From 4d2691677028cbb0e588744dc738502c5d3f210a Mon Sep 17 00:00:00 2001 +From: Joe Perches +Date: Thu, 5 Mar 2020 07:15:53 -0800 +Subject: [PATCH 30/39] spi: Remove CONFIG_ prefix from Kconfig select + +mainline inclusion +from mainline-v5.7-rc1 +commit e14572c52546c16e159c4c1814984843a119e823 +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e14572c52546c16e159c4c1814984843a119e823 + +---------------------------------------------------------------------------- + +commit a2ca53b52e00 ("spi: Add HiSilicon v3xx SPI NOR flash +controller driver") likely inadvertently used a select statement +with a CONFIG_ prefix, remove the prefix. + +Reported-by: Randy Dunlap +Signed-off-by: Joe Perches +Acked-by: John Garry +Link: https://lore.kernel.org/r/f8ac6b32a29b9a05b58a7e58ffe8b780642abbf1.camel@perches.com +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index a6f1a5c2ea82..4b4b8e4882bb 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -265,7 +265,7 @@ config SPI_HISI_SFC_V3XX + tristate "HiSilicon SPI-NOR Flash Controller for Hi16XX chipsets" + depends on (ARM64 && ACPI) || COMPILE_TEST + depends on HAS_IOMEM +- select CONFIG_MTD_SPI_NOR ++ select MTD_SPI_NOR + help + This enables support for HiSilicon v3xx SPI-NOR flash controller + found in hi16xx chipsets. +-- +2.27.0 + diff --git a/patches/0353-spi-hisi-sfc-v3xx-factor-out-IO-modes-configuration.patch b/patches/0353-spi-hisi-sfc-v3xx-factor-out-IO-modes-configuration.patch new file mode 100644 index 0000000..c48b9ab --- /dev/null +++ b/patches/0353-spi-hisi-sfc-v3xx-factor-out-IO-modes-configuration.patch @@ -0,0 +1,178 @@ +From 3a41ac28583128600bce37a94ba7716f55c51d23 Mon Sep 17 00:00:00 2001 +From: Yicong Yang +Date: Thu, 24 Sep 2020 20:24:27 +0800 +Subject: [PATCH 31/39] spi: hisi-sfc-v3xx: factor out IO modes configuration + +mainline inclusion +from mainline-v5.10-rc1 +commit 2c8af6a59744b242a193118c799a45621476f8ed +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2c8af6a59744b242a193118c799a45621476f8ed + +---------------------------------------------------------------------------- + +Factor IO modes configuration out of hisi_sfc_v3xx_generic_exec_op() +using an IO modes lookup table. This will make the process a bit clearer +and reduce the cyclomatic complexity. Simplify the IO mode definition +macros a little bit as well. + +Also add the .supports_op() method for the controller mem ops, in order +to avoid OOB access. + +Acked-by: John Garry +Signed-off-by: Yicong Yang +Link: https://lore.kernel.org/r/1600950270-52536-2-git-send-email-yangyicong@hisilicon.com +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang + + Conflicts: + drivers/spi/spi-hisi-sfc-v3xx.c +--- + drivers/spi/spi-hisi-sfc-v3xx.c | 94 ++++++++++++++++++++------------- + 1 file changed, 56 insertions(+), 38 deletions(-) + +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +index 7b90ba2184ac..9203a7c06aaa 100644 +--- a/drivers/spi/spi-hisi-sfc-v3xx.c ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -23,12 +23,6 @@ + #define HISI_SFC_V3XX_INT_CLR (0x12c) + #define HISI_SFC_V3XX_INT_CLR_CLEAR (0xff) + #define HISI_SFC_V3XX_CMD_CFG (0x300) +-#define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17) +-#define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 17) +-#define HISI_SFC_V3XX_CMD_CFG_FULL_DIO (3 << 17) +-#define HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT (5 << 17) +-#define HISI_SFC_V3XX_CMD_CFG_QUAD_IO (6 << 17) +-#define HISI_SFC_V3XX_CMD_CFG_FULL_QIO (7 << 17) + #define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9 + #define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8) + #define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7) +@@ -40,6 +34,33 @@ + #define HISI_SFC_V3XX_CMD_ADDR (0x30c) + #define HISI_SFC_V3XX_CMD_DATABUF0 (0x400) + ++/* IO Mode definition in HISI_SFC_V3XX_CMD_CFG */ ++#define HISI_SFC_V3XX_STD (0 << 17) ++#define HISI_SFC_V3XX_DIDO (1 << 17) ++#define HISI_SFC_V3XX_DIO (2 << 17) ++#define HISI_SFC_V3XX_FULL_DIO (3 << 17) ++#define HISI_SFC_V3XX_QIQO (5 << 17) ++#define HISI_SFC_V3XX_QIO (6 << 17) ++#define HISI_SFC_V3XX_FULL_QIO (7 << 17) ++ ++/* ++ * The IO modes lookup table. hisi_sfc_v3xx_io_modes[(z - 1) / 2][y / 2][x / 2] ++ * stands for x-y-z mode, as described in SFDP terminology. -EIO indicates ++ * an invalid mode. ++ */ ++static const int hisi_sfc_v3xx_io_modes[2][3][3] = { ++ { ++ { HISI_SFC_V3XX_DIDO, HISI_SFC_V3XX_DIDO, HISI_SFC_V3XX_DIDO }, ++ { HISI_SFC_V3XX_DIO, HISI_SFC_V3XX_FULL_DIO, -EIO }, ++ { -EIO, -EIO, -EIO }, ++ }, ++ { ++ { HISI_SFC_V3XX_QIQO, HISI_SFC_V3XX_QIQO, HISI_SFC_V3XX_QIQO }, ++ { -EIO, -EIO, -EIO }, ++ { HISI_SFC_V3XX_QIO, -EIO, HISI_SFC_V3XX_FULL_QIO }, ++ }, ++}; ++ + struct hisi_sfc_v3xx_host { + struct device *dev; + void __iomem *regbase; +@@ -79,6 +100,20 @@ static int hisi_sfc_v3xx_adjust_op_size(struct spi_mem *mem, + return 0; + } + ++/* ++ * The controller only supports Standard SPI mode, Duall mode and ++ * Quad mode. Double sanitize the ops here to avoid OOB access. ++ */ ++static bool hisi_sfc_v3xx_supports_op(struct spi_mem *mem, ++ const struct spi_mem_op *op) ++{ ++ if (op->data.buswidth > 4 || op->dummy.buswidth > 4 || ++ op->addr.buswidth > 4 || op->cmd.buswidth > 4) ++ return false; ++ ++ return spi_mem_default_supports_op(mem, op); ++} ++ + /* + * memcpy_{to,from}io doesn't gurantee 32b accesses - which we require for the + * DATABUF registers -so use __io{read,write}32_copy when possible. For +@@ -167,44 +202,26 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + const struct spi_mem_op *op, + u8 chip_select) + { +- int ret, len = op->data.nbytes; ++ int ret = 0, len = op->data.nbytes, buswidth_mode; + u32 int_stat, config = 0; + + if (op->addr.nbytes) + config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK; + +- switch (op->data.buswidth) { +- case 0 ... 1: +- break; +- case 2: +- if (op->addr.buswidth <= 1) { +- config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT; +- } else if (op->addr.buswidth == 2) { +- if (op->cmd.buswidth <= 1) +- config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IO; +- else if (op->cmd.buswidth == 2) +- config |= HISI_SFC_V3XX_CMD_CFG_FULL_DIO; +- else +- return -EIO; +- } else +- return -EIO; +- break; +- case 4: +- if (op->addr.buswidth <= 1) { +- config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT; +- } else if (op->addr.buswidth == 4) { +- if (op->cmd.buswidth <= 1) +- config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IO; +- else if (op->cmd.buswidth == 4) +- config |= HISI_SFC_V3XX_CMD_CFG_FULL_QIO; +- else +- return -EIO; +- } else +- return -EIO; +- break; +- default: +- return -EOPNOTSUPP; ++ if (op->data.buswidth == 0 || op->data.buswidth == 1) { ++ buswidth_mode = HISI_SFC_V3XX_STD; ++ } else { ++ int data_idx, addr_idx, cmd_idx; ++ ++ data_idx = (op->data.buswidth - 1) / 2; ++ addr_idx = op->addr.buswidth / 2; ++ cmd_idx = op->cmd.buswidth / 2; ++ buswidth_mode = ++ hisi_sfc_v3xx_io_modes[data_idx][addr_idx][cmd_idx]; + } ++ if (buswidth_mode < 0) ++ return buswidth_mode; ++ config |= buswidth_mode; + + if (op->data.dir != SPI_MEM_NO_DATA) { + config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF; +@@ -268,6 +285,7 @@ static int hisi_sfc_v3xx_exec_op(struct spi_mem *mem, + + static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = { + .adjust_op_size = hisi_sfc_v3xx_adjust_op_size, ++ .supports_op = hisi_sfc_v3xx_supports_op, + .exec_op = hisi_sfc_v3xx_exec_op, + }; + +-- +2.27.0 + diff --git a/patches/0354-spi-hisi-sfc-v3xx-factor-out-bus-config-and-transfer.patch b/patches/0354-spi-hisi-sfc-v3xx-factor-out-bus-config-and-transfer.patch new file mode 100644 index 0000000..fecacab --- /dev/null +++ b/patches/0354-spi-hisi-sfc-v3xx-factor-out-bus-config-and-transfer.patch @@ -0,0 +1,104 @@ +From 1092dc3f08140ec688913cc84267e4f7ef466efe Mon Sep 17 00:00:00 2001 +From: Yicong Yang +Date: Thu, 24 Sep 2020 20:24:28 +0800 +Subject: [PATCH 32/39] spi: hisi-sfc-v3xx: factor out bus config and transfer + functions + +mainline inclusion +from mainline-v5.10-rc1 +commit f6d2737720d6f6e5f4825b7203ad8b5cfcf9906c +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f6d2737720d6f6e5f4825b7203ad8b5cfcf9906c + +---------------------------------------------------------------------------- + +In hisi_sfc_v3xx_generic_exec_op(), we will write the data to the buffer, +configure and start the transfer, read the data to the buffer and check +whether occurs an error. Factor out the config and transfer start codes +as individual functions, to make the process a bit clearer. + +Acked-by: John Garry +Signed-off-by: Yicong Yang +Link: https://lore.kernel.org/r/1600950270-52536-3-git-send-email-yangyicong@hisilicon.com +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/spi-hisi-sfc-v3xx.c | 37 +++++++++++++++++++++++++-------- + 1 file changed, 28 insertions(+), 9 deletions(-) + +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +index 9203a7c06aaa..02b18fdd1a50 100644 +--- a/drivers/spi/spi-hisi-sfc-v3xx.c ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -198,12 +198,12 @@ static void hisi_sfc_v3xx_write_databuf(struct hisi_sfc_v3xx_host *host, + } + } + +-static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, +- const struct spi_mem_op *op, +- u8 chip_select) ++static int hisi_sfc_v3xx_start_bus(struct hisi_sfc_v3xx_host *host, ++ const struct spi_mem_op *op, ++ u8 chip_select) + { +- int ret = 0, len = op->data.nbytes, buswidth_mode; +- u32 int_stat, config = 0; ++ int len = op->data.nbytes, buswidth_mode; ++ u32 config = 0; + + if (op->addr.nbytes) + config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK; +@@ -228,9 +228,7 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK; + } + +- if (op->data.dir == SPI_MEM_DATA_OUT) +- hisi_sfc_v3xx_write_databuf(host, op->data.buf.out, len); +- else if (op->data.dir == SPI_MEM_DATA_IN) ++ if (op->data.dir == SPI_MEM_DATA_IN) + config |= HISI_SFC_V3XX_CMD_CFG_RW_MSK; + + config |= op->dummy.nbytes << HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF | +@@ -242,6 +240,25 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + + writel(config, host->regbase + HISI_SFC_V3XX_CMD_CFG); + ++ return 0; ++} ++ ++static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, ++ const struct spi_mem_op *op, ++ u8 chip_select) ++{ ++ u32 int_stat; ++ int ret; ++ ++ if (op->data.dir == SPI_MEM_DATA_OUT) ++ hisi_sfc_v3xx_write_databuf(host, ++ op->data.buf.out, ++ op->data.nbytes); ++ ++ ret = hisi_sfc_v3xx_start_bus(host, op, chip_select); ++ if (ret) ++ return ret; ++ + ret = hisi_sfc_v3xx_wait_cmd_idle(host); + if (ret) + return ret; +@@ -266,7 +283,9 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + } + + if (op->data.dir == SPI_MEM_DATA_IN) +- hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len); ++ hisi_sfc_v3xx_read_databuf(host, ++ op->data.buf.in, ++ op->data.nbytes); + + return 0; + } +-- +2.27.0 + diff --git a/patches/0355-spi-hisi-sfc-v3xx-factor-out-the-bit-definition-of-i.patch b/patches/0355-spi-hisi-sfc-v3xx-factor-out-the-bit-definition-of-i.patch new file mode 100644 index 0000000..cc2b1d7 --- /dev/null +++ b/patches/0355-spi-hisi-sfc-v3xx-factor-out-the-bit-definition-of-i.patch @@ -0,0 +1,80 @@ +From 91bf6de1a7690174b9dad2c8188c7a1fd78f5d6d Mon Sep 17 00:00:00 2001 +From: Yicong Yang +Date: Thu, 24 Sep 2020 20:24:29 +0800 +Subject: [PATCH 33/39] spi: hisi-sfc-v3xx: factor out the bit definition of + interrupt register + +mainline inclusion +from mainline-v5.10-rc1 +commit aac6edff843871d7d732a6aa6f495b9eb1dea83a +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=aac6edff843871d7d732a6aa6f495b9eb1dea83a + +---------------------------------------------------------------------------- + +The definition of the register field in the interrupt corresponding +registers are the same. So factor them out to public place. + +Acked-by: John Garry +Signed-off-by: Yicong Yang +Link: https://lore.kernel.org/r/1600950270-52536-4-git-send-email-yangyicong@hisilicon.com +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/spi-hisi-sfc-v3xx.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +index 02b18fdd1a50..fbb9b837655c 100644 +--- a/drivers/spi/spi-hisi-sfc-v3xx.c ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -18,10 +18,7 @@ + #define HISI_SFC_V3XX_VERSION (0x1f8) + + #define HISI_SFC_V3XX_INT_STAT (0x120) +-#define HISI_SFC_V3XX_INT_STAT_PP_ERR BIT(2) +-#define HISI_SFC_V3XX_INT_STAT_ADDR_IACCES BIT(5) + #define HISI_SFC_V3XX_INT_CLR (0x12c) +-#define HISI_SFC_V3XX_INT_CLR_CLEAR (0xff) + #define HISI_SFC_V3XX_CMD_CFG (0x300) + #define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9 + #define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8) +@@ -34,6 +31,13 @@ + #define HISI_SFC_V3XX_CMD_ADDR (0x30c) + #define HISI_SFC_V3XX_CMD_DATABUF0 (0x400) + ++/* Common definition of interrupt bit masks */ ++#define HISI_SFC_V3XX_INT_MASK_ALL (0x1ff) /* all the masks */ ++#define HISI_SFC_V3XX_INT_MASK_PP_ERR BIT(2) /* page progrom error */ ++#define HISI_SFC_V3XX_INT_MASK_IACCES BIT(5) /* error visiting inaccessible/ ++ * protected address ++ */ ++ + /* IO Mode definition in HISI_SFC_V3XX_CMD_CFG */ + #define HISI_SFC_V3XX_STD (0 << 17) + #define HISI_SFC_V3XX_DIDO (1 << 17) +@@ -269,15 +273,15 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + * next time judgement. + */ + int_stat = readl(host->regbase + HISI_SFC_V3XX_INT_STAT); +- writel(HISI_SFC_V3XX_INT_CLR_CLEAR, ++ writel(HISI_SFC_V3XX_INT_MASK_ALL, + host->regbase + HISI_SFC_V3XX_INT_CLR); + +- if (int_stat & HISI_SFC_V3XX_INT_STAT_ADDR_IACCES) { ++ if (int_stat & HISI_SFC_V3XX_INT_MASK_IACCES) { + dev_err(host->dev, "fail to access protected address\n"); + return -EIO; + } + +- if (int_stat & HISI_SFC_V3XX_INT_STAT_PP_ERR) { ++ if (int_stat & HISI_SFC_V3XX_INT_MASK_PP_ERR) { + dev_err(host->dev, "page program operation failed\n"); + return -EIO; + } +-- +2.27.0 + diff --git a/patches/0356-spi-hisi-sfc-v3xx-add-support-for-IRQ-mode.patch b/patches/0356-spi-hisi-sfc-v3xx-add-support-for-IRQ-mode.patch new file mode 100644 index 0000000..1aa0fff --- /dev/null +++ b/patches/0356-spi-hisi-sfc-v3xx-add-support-for-IRQ-mode.patch @@ -0,0 +1,244 @@ +From 34f366184657baff168c7b6e8f219585a7cd5e14 Mon Sep 17 00:00:00 2001 +From: Yicong Yang +Date: Thu, 24 Sep 2020 20:24:30 +0800 +Subject: [PATCH 34/39] spi: hisi-sfc-v3xx: add support for IRQ mode + +mainline inclusion +from mainline-v5.10-rc1 +commit b1dd565124bea0f3ecde87336b48c5d0e98cd5bc +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b1dd565124bea0f3ecde87336b48c5d0e98cd5bc + +---------------------------------------------------------------------------- + +The controller can work with interrupts, so add support for it. +Then we can work under IRQ mode or Poll mode now, if firmware +doesn't declare the IRQ support, it will fall back to Poll mode. + +Acked-by: John Garry +Signed-off-by: Yicong Yang +Link: https://lore.kernel.org/r/1600950270-52536-5-git-send-email-yangyicong@hisilicon.com +Signed-off-by: Mark Brown +Signed-off-by: YunYi Yang +--- + drivers/spi/spi-hisi-sfc-v3xx.c | 137 +++++++++++++++++++++++++++----- + 1 file changed, 115 insertions(+), 22 deletions(-) + +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +index fbb9b837655c..3e695784abf4 100644 +--- a/drivers/spi/spi-hisi-sfc-v3xx.c ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -7,7 +7,9 @@ + + #include + #include ++#include + #include ++#include + #include + #include + #include +@@ -17,7 +19,9 @@ + + #define HISI_SFC_V3XX_VERSION (0x1f8) + +-#define HISI_SFC_V3XX_INT_STAT (0x120) ++#define HISI_SFC_V3XX_RAW_INT_STAT (0x120) ++#define HISI_SFC_V3XX_INT_STAT (0x124) ++#define HISI_SFC_V3XX_INT_MASK (0x128) + #define HISI_SFC_V3XX_INT_CLR (0x12c) + #define HISI_SFC_V3XX_CMD_CFG (0x300) + #define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9 +@@ -33,6 +37,7 @@ + + /* Common definition of interrupt bit masks */ + #define HISI_SFC_V3XX_INT_MASK_ALL (0x1ff) /* all the masks */ ++#define HISI_SFC_V3XX_INT_MASK_CPLT BIT(0) /* command execution complete */ + #define HISI_SFC_V3XX_INT_MASK_PP_ERR BIT(2) /* page progrom error */ + #define HISI_SFC_V3XX_INT_MASK_IACCES BIT(5) /* error visiting inaccessible/ + * protected address +@@ -69,8 +74,64 @@ struct hisi_sfc_v3xx_host { + struct device *dev; + void __iomem *regbase; + int max_cmd_dword; ++ struct completion *completion; ++ int irq; + }; + ++static void hisi_sfc_v3xx_disable_int(struct hisi_sfc_v3xx_host *host) ++{ ++ writel(0, host->regbase + HISI_SFC_V3XX_INT_MASK); ++} ++ ++static void hisi_sfc_v3xx_enable_int(struct hisi_sfc_v3xx_host *host) ++{ ++ writel(HISI_SFC_V3XX_INT_MASK_ALL, ++ host->regbase + HISI_SFC_V3XX_INT_MASK); ++} ++ ++static void hisi_sfc_v3xx_clear_int(struct hisi_sfc_v3xx_host *host) ++{ ++ writel(HISI_SFC_V3XX_INT_MASK_ALL, ++ host->regbase + HISI_SFC_V3XX_INT_CLR); ++} ++ ++/* ++ * The interrupt status register indicates whether an error occurs ++ * after per operation. Check it, and clear the interrupts for ++ * next time judgement. ++ */ ++static int hisi_sfc_v3xx_handle_completion(struct hisi_sfc_v3xx_host *host) ++{ ++ u32 reg; ++ ++ reg = readl(host->regbase + HISI_SFC_V3XX_RAW_INT_STAT); ++ hisi_sfc_v3xx_clear_int(host); ++ ++ if (reg & HISI_SFC_V3XX_INT_MASK_IACCES) { ++ dev_err(host->dev, "fail to access protected address\n"); ++ return -EIO; ++ } ++ ++ if (reg & HISI_SFC_V3XX_INT_MASK_PP_ERR) { ++ dev_err(host->dev, "page program operation failed\n"); ++ return -EIO; ++ } ++ ++ /* ++ * The other bits of the interrupt registers is not currently ++ * used and probably not be triggered in this driver. When it ++ * happens, we regard it as an unsupported error here. ++ */ ++ if (!(reg & HISI_SFC_V3XX_INT_MASK_CPLT)) { ++ dev_err(host->dev, ++ "unsupported error occurred, status=0x%x\n", ++ reg); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ + #define HISI_SFC_V3XX_WAIT_TIMEOUT_US 1000000 + #define HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US 10 + +@@ -251,9 +312,14 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + const struct spi_mem_op *op, + u8 chip_select) + { +- u32 int_stat; ++ DECLARE_COMPLETION_ONSTACK(done); + int ret; + ++ if (host->irq) { ++ host->completion = &done; ++ hisi_sfc_v3xx_enable_int(host); ++ } ++ + if (op->data.dir == SPI_MEM_DATA_OUT) + hisi_sfc_v3xx_write_databuf(host, + op->data.buf.out, +@@ -263,28 +329,21 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + if (ret) + return ret; + +- ret = hisi_sfc_v3xx_wait_cmd_idle(host); +- if (ret) +- return ret; +- +- /* +- * The interrupt status register indicates whether an error occurs +- * after per operation. Check it, and clear the interrupts for +- * next time judgement. +- */ +- int_stat = readl(host->regbase + HISI_SFC_V3XX_INT_STAT); +- writel(HISI_SFC_V3XX_INT_MASK_ALL, +- host->regbase + HISI_SFC_V3XX_INT_CLR); ++ if (host->irq) { ++ ret = wait_for_completion_timeout(host->completion, ++ usecs_to_jiffies(HISI_SFC_V3XX_WAIT_TIMEOUT_US)); ++ if (!ret) ++ ret = -ETIMEDOUT; ++ else ++ ret = 0; + +- if (int_stat & HISI_SFC_V3XX_INT_MASK_IACCES) { +- dev_err(host->dev, "fail to access protected address\n"); +- return -EIO; ++ hisi_sfc_v3xx_disable_int(host); ++ host->completion = NULL; ++ } else { ++ ret = hisi_sfc_v3xx_wait_cmd_idle(host); + } +- +- if (int_stat & HISI_SFC_V3XX_INT_MASK_PP_ERR) { +- dev_err(host->dev, "page program operation failed\n"); ++ if (hisi_sfc_v3xx_handle_completion(host) || ret) + return -EIO; +- } + + if (op->data.dir == SPI_MEM_DATA_IN) + hisi_sfc_v3xx_read_databuf(host, +@@ -312,6 +371,17 @@ static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = { + .exec_op = hisi_sfc_v3xx_exec_op, + }; + ++static irqreturn_t hisi_sfc_v3xx_isr(int irq, void *data) ++{ ++ struct hisi_sfc_v3xx_host *host = data; ++ ++ hisi_sfc_v3xx_disable_int(host); ++ ++ complete(host->completion); ++ ++ return IRQ_HANDLED; ++} ++ + static int hisi_sfc_v3xx_buswidth_override_bits; + + /* +@@ -378,6 +448,28 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) + goto err_put_master; + } + ++ host->irq = platform_get_irq(pdev, 0); ++ if (host->irq == -EPROBE_DEFER) { ++ ret = -EPROBE_DEFER; ++ goto err_put_master; ++ } ++ ++ hisi_sfc_v3xx_disable_int(host); ++ ++ if (host->irq > 0) { ++ ret = devm_request_irq(dev, host->irq, hisi_sfc_v3xx_isr, 0, ++ "hisi-sfc-v3xx", host); ++ ++ if (ret) { ++ dev_err(dev, ++ "failed to request irq%d, ret = %d\n", ++ host->irq, ret); ++ host->irq = 0; ++ } ++ } else { ++ host->irq = 0; ++ } ++ + ctlr->bus_num = -1; + ctlr->num_chipselect = 1; + ctlr->mem_ops = &hisi_sfc_v3xx_mem_ops; +@@ -397,7 +489,8 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) + if (ret) + goto err_put_master; + +- dev_info(&pdev->dev, "hw version 0x%x\n", version); ++ dev_info(&pdev->dev, "hw version 0x%x, %s mode.\n", ++ version, host->irq ? "irq" : "polling"); + + return 0; + +-- +2.27.0 + diff --git a/patches/0357-spi-hisi-sfc-v3xx-extend-version-checking-compatibil.patch b/patches/0357-spi-hisi-sfc-v3xx-extend-version-checking-compatibil.patch new file mode 100644 index 0000000..e0e852f --- /dev/null +++ b/patches/0357-spi-hisi-sfc-v3xx-extend-version-checking-compatibil.patch @@ -0,0 +1,62 @@ +From c09b5df3383e9a19a8a5b011c1bd7b902795b561 Mon Sep 17 00:00:00 2001 +From: Yicong Yang +Date: Thu, 8 Dec 2022 21:45:24 +0800 +Subject: [PATCH 35/39] spi: hisi-sfc-v3xx: extend version checking + compatibility + +mainline inclusion +from mainline-v5.12-rc1 +commit 566c6120f095be74862bed35f557f797478abade +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=566c6120f095be74862bed35f557f797478abade + +-------------------------------------------------------------------------- + +Currently we use concrete version to determine the max_cmd_dword. +New entries should be added for compatible hardwares of new version +or on new platform, otherwise the device will use 16 dwords instead +of 64 even if it supports, which will degrade the performance. +This will decrease the compatibility and the maintainability. + +Drop the switch-case statement of the version checking. Only version +less than 0x351 supports maximum 16 command dwords. + +Signed-off-by: Yicong Yang +Acked-by: John Garry +Link: https://lore.kernel.org/r/1610526716-14882-1-git-send-email-yangyicong@hisilicon.com +Signed-off-by: Mark Brown +Signed-off-by: Wangming Shao +Reviewed-by: Yicong Yang +Signed-off-by: Zheng Zengkai +Signed-off-by: YunYi Yang +--- + drivers/spi/spi-hisi-sfc-v3xx.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +index 3e695784abf4..e679321c7e23 100644 +--- a/drivers/spi/spi-hisi-sfc-v3xx.c ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -476,14 +476,10 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) + + version = readl(host->regbase + HISI_SFC_V3XX_VERSION); + +- switch (version) { +- case 0x351: ++ if (version >= 0x351) + host->max_cmd_dword = 64; +- break; +- default: ++ else + host->max_cmd_dword = 16; +- break; +- } + + ret = devm_spi_register_controller(dev, ctlr); + if (ret) +-- +2.27.0 + diff --git a/patches/0358-spi-hisi-sfc-v3xx-add-address-mode-check.patch b/patches/0358-spi-hisi-sfc-v3xx-add-address-mode-check.patch new file mode 100644 index 0000000..5b4db6c --- /dev/null +++ b/patches/0358-spi-hisi-sfc-v3xx-add-address-mode-check.patch @@ -0,0 +1,104 @@ +From a009f74ef5b6cf1953abd034da40e4ba27e7e1a1 Mon Sep 17 00:00:00 2001 +From: Yicong Yang +Date: Thu, 8 Dec 2022 21:45:25 +0800 +Subject: [PATCH 36/39] spi: hisi-sfc-v3xx: add address mode check + +mainline inclusion +from mainline-v5.12-rc1 +commit 6d2386e36440165da782dbc5c0de40f31665e108 +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6d2386e36440165da782dbc5c0de40f31665e108 + +-------------------------------------------------------------------------- + +The address mode is either 3 or 4 for the controller, which is configured +by the firmware and cannot be modified in the OS driver. Get the +firmware configuration and add address mode check in the .supports_op() +to block invalid operations. + +Signed-off-by: Yicong Yang +Acked-by: John Garry +Link: https://lore.kernel.org/r/1611740450-47975-3-git-send-email-yangyicong@hisilicon.com +Signed-off-by: Mark Brown +Signed-off-by: Wangming Shao +Reviewed-by: Yicong Yang +Signed-off-by: Zheng Zengkai +Signed-off-by: YunYi Yang +--- + drivers/spi/spi-hisi-sfc-v3xx.c | 25 ++++++++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +index e679321c7e23..3cb6735aaf34 100644 +--- a/drivers/spi/spi-hisi-sfc-v3xx.c ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -19,6 +19,8 @@ + + #define HISI_SFC_V3XX_VERSION (0x1f8) + ++#define HISI_SFC_V3XX_GLB_CFG (0x100) ++#define HISI_SFC_V3XX_GLB_CFG_CS0_ADDR_MODE BIT(2) + #define HISI_SFC_V3XX_RAW_INT_STAT (0x120) + #define HISI_SFC_V3XX_INT_STAT (0x124) + #define HISI_SFC_V3XX_INT_MASK (0x128) +@@ -75,6 +77,7 @@ struct hisi_sfc_v3xx_host { + void __iomem *regbase; + int max_cmd_dword; + struct completion *completion; ++ u8 address_mode; + int irq; + }; + +@@ -172,10 +175,18 @@ static int hisi_sfc_v3xx_adjust_op_size(struct spi_mem *mem, + static bool hisi_sfc_v3xx_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) + { ++ struct spi_device *spi = mem->spi; ++ struct hisi_sfc_v3xx_host *host; ++ ++ host = spi_controller_get_devdata(spi->master); ++ + if (op->data.buswidth > 4 || op->dummy.buswidth > 4 || + op->addr.buswidth > 4 || op->cmd.buswidth > 4) + return false; + ++ if (op->addr.nbytes != host->address_mode && op->addr.nbytes) ++ return false; ++ + return spi_mem_default_supports_op(mem, op); + } + +@@ -425,7 +436,7 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) + struct device *dev = &pdev->dev; + struct hisi_sfc_v3xx_host *host; + struct spi_controller *ctlr; +- u32 version; ++ u32 version, glb_config; + int ret; + + ctlr = spi_alloc_master(&pdev->dev, sizeof(*host)); +@@ -474,6 +485,18 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) + ctlr->num_chipselect = 1; + ctlr->mem_ops = &hisi_sfc_v3xx_mem_ops; + ++ /* ++ * The address mode of the controller is either 3 or 4, ++ * which is indicated by the address mode bit in ++ * the global config register. The register is read only ++ * for the OS driver. ++ */ ++ glb_config = readl(host->regbase + HISI_SFC_V3XX_GLB_CFG); ++ if (glb_config & HISI_SFC_V3XX_GLB_CFG_CS0_ADDR_MODE) ++ host->address_mode = 4; ++ else ++ host->address_mode = 3; ++ + version = readl(host->regbase + HISI_SFC_V3XX_VERSION); + + if (version >= 0x351) +-- +2.27.0 + diff --git a/patches/0359-spi-hisi-sfc-v3xx-fix-potential-irq-race-condition.patch b/patches/0359-spi-hisi-sfc-v3xx-fix-potential-irq-race-condition.patch new file mode 100644 index 0000000..57357b1 --- /dev/null +++ b/patches/0359-spi-hisi-sfc-v3xx-fix-potential-irq-race-condition.patch @@ -0,0 +1,47 @@ +From 6aca11b49d76afb6bed69e0d8714744ee52b203b Mon Sep 17 00:00:00 2001 +From: Yicong Yang +Date: Thu, 8 Dec 2022 21:45:26 +0800 +Subject: [PATCH 37/39] spi: hisi-sfc-v3xx: fix potential irq race condition + +mainline inclusion +from mainline-v5.13-rc1 +commit 4c84e42d29afa3dce201a4db747db2a5ba404604 +category: bugfix +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4c84e42d29afa3dce201a4db747db2a5ba404604 + +-------------------------------------------------------------------------- + +We mask the irq when the command completion is timeout. This won't +stop the already running irq handler. Use sychronize_irq() after +we mask the irq, to make sure there is no running handler. + +Acked-by: John Garry +Signed-off-by: Yicong Yang +Link: https://lore.kernel.org/r/1618228708-37949-2-git-send-email-yangyicong@hisilicon.com +Signed-off-by: Mark Brown +Signed-off-by: Wangming Shao +Reviewed-by: Yicong Yang +Signed-off-by: Zheng Zengkai +Signed-off-by: YunYi Yang +--- + drivers/spi/spi-hisi-sfc-v3xx.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +index 3cb6735aaf34..2636bf6be06f 100644 +--- a/drivers/spi/spi-hisi-sfc-v3xx.c ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -349,6 +349,7 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + ret = 0; + + hisi_sfc_v3xx_disable_int(host); ++ synchronize_irq(host->irq); + host->completion = NULL; + } else { + ret = hisi_sfc_v3xx_wait_cmd_idle(host); +-- +2.27.0 + diff --git a/patches/0360-spi-hisi-sfc-v3xx-drop-unnecessary-ACPI_PTR-and-rela.patch b/patches/0360-spi-hisi-sfc-v3xx-drop-unnecessary-ACPI_PTR-and-rela.patch new file mode 100644 index 0000000..64ba105 --- /dev/null +++ b/patches/0360-spi-hisi-sfc-v3xx-drop-unnecessary-ACPI_PTR-and-rela.patch @@ -0,0 +1,80 @@ +From 06d413fe5831dcc0a9095c08eb5c0463b2343556 Mon Sep 17 00:00:00 2001 +From: Yicong Yang +Date: Thu, 8 Dec 2022 21:45:27 +0800 +Subject: [PATCH 38/39] spi: hisi-sfc-v3xx: drop unnecessary ACPI_PTR and + related ifendif protection + +mainline inclusion +from mainline-v5.13-rc1 +commit 4a46f88681ca514f9cb33b39312d0ec4e2ec84da +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP +CVE: NA + +Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4a46f88681ca514f9cb33b39312d0ec4e2ec84da + +-------------------------------------------------------------------------- + +We use ACPI_PTR() and related ifendif protection for the id table. +This is unnecessary as the struct acpi_device_id is defined in +mod_devicetable.h and doesn't rely on ACPI. The driver doesn't +use any ACPI apis, so it can be compiled in the ACPI=n case +with no warnings. + +So remove the ACPI_PTR and related ifendif protection, also +replace the header acpi.h with mod_devicetable.h. + +Acked-by: John Garry +Signed-off-by: Yicong Yang +Link: https://lore.kernel.org/r/1618228708-37949-3-git-send-email-yangyicong@hisilicon.com +Signed-off-by: Mark Brown +Signed-off-by: Wangming Shao +Reviewed-by: Yicong Yang +Signed-off-by: Zheng Zengkai +Signed-off-by: YunYi Yang +--- + drivers/spi/spi-hisi-sfc-v3xx.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c +index 2636bf6be06f..2946604c5efd 100644 +--- a/drivers/spi/spi-hisi-sfc-v3xx.c ++++ b/drivers/spi/spi-hisi-sfc-v3xx.c +@@ -5,13 +5,13 @@ + // Copyright (c) 2019 HiSilicon Technologies Co., Ltd. + // Author: John Garry + +-#include + #include + #include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -519,18 +519,16 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) + return ret; + } + +-#if IS_ENABLED(CONFIG_ACPI) + static const struct acpi_device_id hisi_sfc_v3xx_acpi_ids[] = { + {"HISI0341", 0}, + {} + }; + MODULE_DEVICE_TABLE(acpi, hisi_sfc_v3xx_acpi_ids); +-#endif + + static struct platform_driver hisi_sfc_v3xx_spi_driver = { + .driver = { + .name = "hisi-sfc-v3xx", +- .acpi_match_table = ACPI_PTR(hisi_sfc_v3xx_acpi_ids), ++ .acpi_match_table = hisi_sfc_v3xx_acpi_ids, + }, + .probe = hisi_sfc_v3xx_probe, + }; +-- +2.27.0 + diff --git a/patches/0361-config-arm64-Build-HiSilicon-SPI-SFC-driver-as-modul.patch b/patches/0361-config-arm64-Build-HiSilicon-SPI-SFC-driver-as-modul.patch new file mode 100644 index 0000000..4d78b76 --- /dev/null +++ b/patches/0361-config-arm64-Build-HiSilicon-SPI-SFC-driver-as-modul.patch @@ -0,0 +1,40 @@ +From 573abc0cb48c35eb8d931e0cf4a76346a5b2a3d4 Mon Sep 17 00:00:00 2001 +From: Yicong Yang +Date: Fri, 26 Nov 2021 16:26:59 +0800 +Subject: [PATCH 39/39] config: arm64: Build HiSilicon SPI/SFC driver as module + +driver inclusion +category: feature +bugzilla: https://gitee.com/openeuler/kernel/issues/I8CSBP + +------------------------------------------------------------------------ + +Build HiSilicon SPI/SFC driver as module. + +Signed-off-by: Yicong Yang +Reviewed-by: Jay Fang +Signed-off-by: Zheng Zengkai +Signed-off-by: YunYi Yang + + Conflicts: + arch/arm64/configs/openeuler_defconfig +--- + arch/arm64/configs/openeuler_defconfig | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig +index b89c386c1bc7..2c81e9dd3cab 100644 +--- a/arch/arm64/configs/openeuler_defconfig ++++ b/arch/arm64/configs/openeuler_defconfig +@@ -3199,6 +3199,8 @@ CONFIG_SPI_DESIGNWARE=y + CONFIG_SPI_DW_PCI=m + # CONFIG_SPI_DW_MID_DMA is not set + CONFIG_SPI_DW_MMIO=m ++CONFIG_SPI_HISI_KUNPENG=m ++CONFIG_SPI_HISI_SFC_V3XX=m + # CONFIG_SPI_GPIO is not set + # CONFIG_SPI_FSL_SPI is not set + # CONFIG_SPI_OC_TINY is not set +-- +2.27.0 + diff --git a/series.conf b/series.conf index dac9f39..d78c29d 100644 --- a/series.conf +++ b/series.conf @@ -323,3 +323,42 @@ patches/0319-ACPICA-Add-support-for-Arm-s-MPAM-ACPI-table-version.patch patches/0320-ACPICA-ACPI-6.4-PPTT-add-new-version-of-subtable-typ.patch patches/0321-ACPI-PPTT-Find-PPTT-processor-node-by-cache-id.patch patches/0322-ACPI-MPAM-Adapt-to-Arm-s-MPAM-ACPI-table-version-2.patch +patches/0323-spi-add-support-for-octal-mode-I-O-data-transfer.patch +patches/0324-mtd-spi-nor-Add-support-for-mx25u12835f.patch +patches/0325-mtd-spi-nor-always-use-bounce-buffer-for-register-re.patch +patches/0326-mtd-spi-nor-Move-m25p80-code-in-spi-nor.c.patch +patches/0327-mtd-spi-nor-Fix-direction-of-the-write_sr-transfer.patch +patches/0328-mtd-spi-nor-Prepend-spi_nor_-to-all-Reg-Ops-methods.patch +patches/0329-mtd-spi-nor-Stop-compare-with-negative-in-Reg-Ops-me.patch +patches/0330-mtd-spi-nor-Pointer-parameter-for-SR-in-spi_nor_read.patch +patches/0331-mtd-spi-nor-Pointer-parameter-for-FSR-in-spi_nor_rea.patch +patches/0332-mtd-spi-nor-Pointer-parameter-for-CR-in-spi_nor_read.patch +patches/0333-mtd-spi-nor-fix-kernel-doc-for-spi_nor-spimem.patch +patches/0334-mtd-spi-nor-core-Fix-an-issue-of-releasing-resources.patch +patches/0335-ARM-shmobile-defconfig-Refresh-config-CONFIG_MTD_M25.patch +patches/0336-mips-Drop-CONFIG_MTD_M25P80-in-various-defconfig-fil.patch +patches/0337-m68k-Drop-CONFIG_MTD_M25P80-in-stmark2_defconfig.patch +patches/0338-powerpc-Drop-CONFIG_MTD_M25P80-in-85xx-hw.config.patch +patches/0339-sh-Replace-CONFIG_MTD_M25P80-with-CONFIG_MTD_SPI_NOR.patch +patches/0340-spi-spi-mem-Add-SPI_MEM_NO_DATA-to-the-spi_mem_data_.patch +patches/0341-spi-spi-mem-Fix-build-error-without-CONFIG_SPI_MEM.patch +patches/0342-driver-platform-Support-parsing-GpioInt-0-in-platfor.patch +patches/0343-driver-core-platform-return-ENXIO-for-missing-GpioIn.patch +patches/0344-spi-acpi-enumerate-all-SPI-slaves-in-the-namespace.patch +patches/0345-spi-acpi-fix-incorrect-ACPI-parent-check.patch +patches/0346-spi-acpi-avoid-spurious-matches-during-slave-enumera.patch +patches/0347-spi-Add-HiSilicon-v3xx-SPI-NOR-flash-controller-driv.patch +patches/0348-spi-Allow-SPI-controller-override-device-buswidth.patch +patches/0349-spi-HiSilicon-v3xx-Properly-set-CMD_CONFIG-for-Dual-.patch +patches/0350-spi-HiSilicon-v3xx-Use-DMI-quirk-to-set-controller-b.patch +patches/0351-spi-hisi-sfc-v3xx-add-error-check-after-per-operatio.patch +patches/0352-spi-Remove-CONFIG_-prefix-from-Kconfig-select.patch +patches/0353-spi-hisi-sfc-v3xx-factor-out-IO-modes-configuration.patch +patches/0354-spi-hisi-sfc-v3xx-factor-out-bus-config-and-transfer.patch +patches/0355-spi-hisi-sfc-v3xx-factor-out-the-bit-definition-of-i.patch +patches/0356-spi-hisi-sfc-v3xx-add-support-for-IRQ-mode.patch +patches/0357-spi-hisi-sfc-v3xx-extend-version-checking-compatibil.patch +patches/0358-spi-hisi-sfc-v3xx-add-address-mode-check.patch +patches/0359-spi-hisi-sfc-v3xx-fix-potential-irq-race-condition.patch +patches/0360-spi-hisi-sfc-v3xx-drop-unnecessary-ACPI_PTR-and-rela.patch +patches/0361-config-arm64-Build-HiSilicon-SPI-SFC-driver-as-modul.patch