diff options
-rw-r--r-- | drivers/mtd/spi-nor/controllers/Kconfig | 11 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/controllers/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/controllers/intel-spi-pci.c | 2 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/controllers/intel-spi.c | 13 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/core.c | 57 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/core.h | 10 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/macronix.c | 6 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/micron-st.c | 4 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/sfdp.c | 3 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/spansion.c | 4 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/winbond.c | 4 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 11 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/atmel-quadspi.c | 6 | ||||
-rw-r--r-- | drivers/spi/spi-cadence-quadspi.c (renamed from drivers/mtd/spi-nor/controllers/cadence-quadspi.c) | 541 | ||||
-rw-r--r-- | drivers/spi/spi-mem.c | 16 | ||||
-rw-r--r-- | drivers/spi/spi-mtk-nor.c | 10 | ||||
-rw-r--r-- | drivers/spi/spi-mxic.c | 3 | ||||
-rw-r--r-- | drivers/spi/spi-zynq-qspi.c | 11 | ||||
-rw-r--r-- | include/linux/spi/spi-mem.h | 14 |
20 files changed, 338 insertions, 390 deletions
diff --git a/drivers/mtd/spi-nor/controllers/Kconfig b/drivers/mtd/spi-nor/controllers/Kconfig index d89a5ea9446a..5c0e0ec2e6d1 100644 --- a/drivers/mtd/spi-nor/controllers/Kconfig +++ b/drivers/mtd/spi-nor/controllers/Kconfig @@ -9,17 +9,6 @@ config SPI_ASPEED_SMC and support for the SPI flash memory controller (SPI) for the host firmware. The implementation only supports SPI NOR. -config SPI_CADENCE_QUADSPI - tristate "Cadence Quad SPI controller" - depends on OF && (ARM || ARM64 || COMPILE_TEST) - help - Enable support for the Cadence Quad SPI Flash controller. - - Cadence QSPI is a specialized controller for connecting an SPI - Flash over 1/2/4-bit wide bus. Enable this option if you have a - device with a Cadence QSPI controller and want to access the - Flash as an MTD device. - config SPI_HISI_SFC tristate "Hisilicon FMC SPI NOR Flash Controller(SFC)" depends on ARCH_HISI || COMPILE_TEST diff --git a/drivers/mtd/spi-nor/controllers/Makefile b/drivers/mtd/spi-nor/controllers/Makefile index 46e6fbe586e3..e7abba491d98 100644 --- a/drivers/mtd/spi-nor/controllers/Makefile +++ b/drivers/mtd/spi-nor/controllers/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o -obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o diff --git a/drivers/mtd/spi-nor/controllers/intel-spi-pci.c b/drivers/mtd/spi-nor/controllers/intel-spi-pci.c index 81329f680bec..c72aa1ab71ad 100644 --- a/drivers/mtd/spi-nor/controllers/intel-spi-pci.c +++ b/drivers/mtd/spi-nor/controllers/intel-spi-pci.c @@ -68,7 +68,9 @@ static const struct pci_device_id intel_spi_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x06a4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x1bca), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info }, diff --git a/drivers/mtd/spi-nor/controllers/intel-spi.c b/drivers/mtd/spi-nor/controllers/intel-spi.c index 61d2a0ad2131..b54a56a68100 100644 --- a/drivers/mtd/spi-nor/controllers/intel-spi.c +++ b/drivers/mtd/spi-nor/controllers/intel-spi.c @@ -292,7 +292,7 @@ static int intel_spi_wait_hw_busy(struct intel_spi *ispi) u32 val; return readl_poll_timeout(ispi->base + HSFSTS_CTL, val, - !(val & HSFSTS_CTL_SCIP), 40, + !(val & HSFSTS_CTL_SCIP), 0, INTEL_SPI_TIMEOUT * 1000); } @@ -301,7 +301,7 @@ static int intel_spi_wait_sw_busy(struct intel_spi *ispi) u32 val; return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val, - !(val & SSFSTS_CTL_SCIP), 40, + !(val & SSFSTS_CTL_SCIP), 0, INTEL_SPI_TIMEOUT * 1000); } @@ -612,6 +612,15 @@ static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, return 0; } + /* + * We hope that HW sequencer will do the right thing automatically and + * with the SW sequencer we cannot use preopcode anyway, so just ignore + * the Write Disable operation and pretend it was completed + * successfully. + */ + if (opcode == SPINOR_OP_WRDI) + return 0; + writel(0, ispi->base + FADDR); /* Write the value beforehand */ diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 0369d98b2d12..65eff4ce6ab1 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1907,15 +1907,16 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) } /** - * spi_nor_sr1_bit6_quad_enable() - Set the Quad Enable BIT(6) in the Status - * Register 1. + * spi_nor_sr1_bit6_quad_enable() - Set/Unset the Quad Enable BIT(6) in the + * Status Register 1. * @nor: pointer to a 'struct spi_nor' + * @enable: true to enable Quad mode, false to disable Quad mode. * * Bit 6 of the Status Register 1 is the QE bit for Macronix like QSPI memories. * * Return: 0 on success, -errno otherwise. */ -int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor) +int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor, bool enable) { int ret; @@ -1923,45 +1924,56 @@ int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor) if (ret) return ret; - if (nor->bouncebuf[0] & SR1_QUAD_EN_BIT6) + if ((enable && (nor->bouncebuf[0] & SR1_QUAD_EN_BIT6)) || + (!enable && !(nor->bouncebuf[0] & SR1_QUAD_EN_BIT6))) return 0; - nor->bouncebuf[0] |= SR1_QUAD_EN_BIT6; + if (enable) + nor->bouncebuf[0] |= SR1_QUAD_EN_BIT6; + else + nor->bouncebuf[0] &= ~SR1_QUAD_EN_BIT6; return spi_nor_write_sr1_and_check(nor, nor->bouncebuf[0]); } /** - * spi_nor_sr2_bit1_quad_enable() - set the Quad Enable BIT(1) in the Status - * Register 2. + * spi_nor_sr2_bit1_quad_enable() - set/unset the Quad Enable BIT(1) in the + * Status Register 2. * @nor: pointer to a 'struct spi_nor'. + * @enable: true to enable Quad mode, false to disable Quad mode. * * Bit 1 of the Status Register 2 is the QE bit for Spansion like QSPI memories. * * Return: 0 on success, -errno otherwise. */ -int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor) +int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor, bool enable) { int ret; if (nor->flags & SNOR_F_NO_READ_CR) - return spi_nor_write_16bit_cr_and_check(nor, SR2_QUAD_EN_BIT1); + return spi_nor_write_16bit_cr_and_check(nor, + enable ? SR2_QUAD_EN_BIT1 : 0); ret = spi_nor_read_cr(nor, nor->bouncebuf); if (ret) return ret; - if (nor->bouncebuf[0] & SR2_QUAD_EN_BIT1) + if ((enable && (nor->bouncebuf[0] & SR2_QUAD_EN_BIT1)) || + (!enable && !(nor->bouncebuf[0] & SR2_QUAD_EN_BIT1))) return 0; - nor->bouncebuf[0] |= SR2_QUAD_EN_BIT1; + if (enable) + nor->bouncebuf[0] |= SR2_QUAD_EN_BIT1; + else + nor->bouncebuf[0] &= ~SR2_QUAD_EN_BIT1; return spi_nor_write_16bit_cr_and_check(nor, nor->bouncebuf[0]); } /** - * spi_nor_sr2_bit7_quad_enable() - set QE bit in Status Register 2. + * spi_nor_sr2_bit7_quad_enable() - set/unset QE bit in Status Register 2. * @nor: pointer to a 'struct spi_nor' + * @enable: true to enable Quad mode, false to disable Quad mode. * * Set the Quad Enable (QE) bit in the Status Register 2. * @@ -1971,7 +1983,7 @@ int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor) * * Return: 0 on success, -errno otherwise. */ -int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) +int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor, bool enable) { u8 *sr2 = nor->bouncebuf; int ret; @@ -1981,11 +1993,15 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) ret = spi_nor_read_sr2(nor, sr2); if (ret) return ret; - if (*sr2 & SR2_QUAD_EN_BIT7) + if ((enable && (*sr2 & SR2_QUAD_EN_BIT7)) || + (!enable && !(*sr2 & SR2_QUAD_EN_BIT7))) return 0; /* Update the Quad Enable bit. */ - *sr2 |= SR2_QUAD_EN_BIT7; + if (enable) + *sr2 |= SR2_QUAD_EN_BIT7; + else + *sr2 &= ~SR2_QUAD_EN_BIT7; ret = spi_nor_write_sr2(nor, sr2); if (ret) @@ -2898,12 +2914,13 @@ static int spi_nor_init_params(struct spi_nor *nor) } /** - * spi_nor_quad_enable() - enable Quad I/O if needed. + * spi_nor_quad_enable() - enable/disable Quad I/O if needed. * @nor: pointer to a 'struct spi_nor' + * @enable: true to enable Quad mode. false to disable Quad mode. * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_quad_enable(struct spi_nor *nor) +static int spi_nor_quad_enable(struct spi_nor *nor, bool enable) { if (!nor->params->quad_enable) return 0; @@ -2912,7 +2929,7 @@ static int spi_nor_quad_enable(struct spi_nor *nor) spi_nor_get_protocol_width(nor->write_proto) == 4)) return 0; - return nor->params->quad_enable(nor); + return nor->params->quad_enable(nor, enable); } /** @@ -2936,7 +2953,7 @@ static int spi_nor_init(struct spi_nor *nor) { int err; - err = spi_nor_quad_enable(nor); + err = spi_nor_quad_enable(nor, true); if (err) { dev_dbg(nor->dev, "quad mode not supported\n"); return err; @@ -2983,6 +3000,8 @@ void spi_nor_restore(struct spi_nor *nor) if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && nor->flags & SNOR_F_BROKEN_RESET) nor->params->set_4byte_addr_mode(nor, false); + + spi_nor_quad_enable(nor, false); } EXPORT_SYMBOL_GPL(spi_nor_restore); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 6f2f6b27173f..95aa32f3ceb1 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -198,7 +198,7 @@ struct spi_nor_locking_ops { * higher index in the array, the higher priority. * @erase_map: the erase map parsed from the SFDP Sector Map Parameter * Table. - * @quad_enable: enables SPI NOR quad mode. + * @quad_enable: enables/disables SPI NOR Quad mode. * @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode. * @convert_addr: converts an absolute address into something the flash * will understand. Particularly useful when pagesize is @@ -219,7 +219,7 @@ struct spi_nor_flash_parameter { struct spi_nor_erase_map erase_map; - int (*quad_enable)(struct spi_nor *nor); + int (*quad_enable)(struct spi_nor *nor, bool enable); int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable); u32 (*convert_addr)(struct spi_nor *nor, u32 addr); int (*setup)(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps); @@ -406,9 +406,9 @@ int spi_nor_write_ear(struct spi_nor *nor, u8 ear); int spi_nor_wait_till_ready(struct spi_nor *nor); int spi_nor_lock_and_prep(struct spi_nor *nor); void spi_nor_unlock_and_unprep(struct spi_nor *nor); -int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor); -int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor); -int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor); +int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor, bool enable); +int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor, bool enable); +int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor, bool enable); int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr); ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index 96735d83c77c..f97f3d127575 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -52,6 +52,9 @@ static const struct flash_info macronix_parts[] = { { "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) }, + { "mx25r1635f", INFO(0xc22815, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, { "mx25r3235f", INFO(0xc22816, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, @@ -84,6 +87,9 @@ static const struct flash_info macronix_parts[] = { SPI_NOR_QUAD_READ) }, { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, + { "mx66u2g45g", INFO(0xc2253c, 0, 64 * 1024, 4096, + SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, }; static void macronix_default_init(struct spi_nor *nor) diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 3dca5b9af3b6..ef3695080710 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -71,8 +71,8 @@ static const struct flash_info st_parts[] = { SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, { "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096, - SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | - NO_CHIP_ERASE) }, + SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 55c0c508464b..e2a43d39eb5f 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -598,7 +598,8 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, break; default: - return -EINVAL; + dev_dbg(nor->dev, "BFPT QER reserved value used\n"); + break; } /* Stop here if not JESD216 rev C or later. */ diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index e550cd5c9d3a..8429b4af999a 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -64,7 +64,6 @@ static const struct flash_info spansion_parts[] = { { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) .fixups = &s25fs_s_fixups, }, - { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, @@ -84,7 +83,8 @@ static const struct flash_info spansion_parts[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "s25fl116k", INFO(0x014015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) }, diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 5062af10f138..6dcde15fb1aa 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -64,10 +64,12 @@ static const struct flash_info winbond_parts[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, - { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "w25q64jvm", INFO(0xef7017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8f1f8fca79e3..6fbb7fe60bac 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -200,6 +200,17 @@ config SPI_CADENCE This selects the Cadence SPI controller master driver used by Xilinx Zynq and ZynqMP. +config SPI_CADENCE_QUADSPI + tristate "Cadence Quad SPI controller" + depends on OF && (ARM || ARM64 || COMPILE_TEST) + help + Enable support for the Cadence Quad SPI Flash controller. + + Cadence QSPI is a specialized controller for connecting an SPI + Flash over 1/2/4-bit wide bus. Enable this option if you have a + device with a Cadence QSPI controller and want to access the + Flash as an MTD device. + config SPI_CLPS711X tristate "CLPS711X host SPI controller" depends on ARCH_CLPS711X || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index d2e41d3d464a..81092ca58095 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_SPI_BCM_QSPI) += spi-iproc-qspi.o spi-brcmstb-qspi.o spi-bcm-qspi. obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o +obj-$(CONFIG_SPI_CADENCE_QUADSPI) += spi-cadence-quadspi.o obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index cb44d1e169aa..a898755fb41e 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -285,6 +285,12 @@ static bool atmel_qspi_supports_op(struct spi_mem *mem, op->dummy.nbytes == 0) return false; + /* DTR ops not supported. */ + if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) + return false; + if (op->cmd.nbytes != 1) + return false; + return true; } diff --git a/drivers/mtd/spi-nor/controllers/cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 494dcab4aaaa..1c1a9d17eec0 100644 --- a/drivers/mtd/spi-nor/controllers/cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1,9 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-only -/* - * Driver for Cadence QSPI Controller - * - * Copyright Altera Corporation (C) 2012-2014. All rights reserved. - */ +// +// Driver for Cadence QSPI Controller +// +// Copyright Altera Corporation (C) 2012-2014. All rights reserved. +// Copyright Intel Corporation (C) 2019-2020. All rights reserved. +// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com + #include <linux/clk.h> #include <linux/completion.h> #include <linux/delay.h> @@ -17,9 +19,6 @@ #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/partitions.h> -#include <linux/mtd/spi-nor.h> #include <linux/of_device.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -27,6 +26,7 @@ #include <linux/reset.h> #include <linux/sched.h> #include <linux/spi/spi.h> +#include <linux/spi/spi-mem.h> #include <linux/timer.h> #define CQSPI_NAME "cadence-qspi" @@ -34,17 +34,14 @@ /* Quirks */ #define CQSPI_NEEDS_WR_DELAY BIT(0) +#define CQSPI_DISABLE_DAC_MODE BIT(1) -/* Capabilities mask */ -#define CQSPI_BASE_HWCAPS_MASK \ - (SNOR_HWCAPS_READ | SNOR_HWCAPS_READ_FAST | \ - SNOR_HWCAPS_READ_1_1_2 | SNOR_HWCAPS_READ_1_1_4 | \ - SNOR_HWCAPS_PP) +/* Capabilities */ +#define CQSPI_SUPPORTS_OCTAL BIT(0) struct cqspi_st; struct cqspi_flash_pdata { - struct spi_nor nor; struct cqspi_st *cqspi; u32 clk_rate; u32 read_delay; @@ -56,8 +53,6 @@ struct cqspi_flash_pdata { u8 addr_width; u8 data_width; u8 cs; - bool registered; - bool use_direct_mode; }; struct cqspi_st { @@ -70,16 +65,12 @@ struct cqspi_st { void __iomem *ahb_base; resource_size_t ahb_size; struct completion transfer_complete; - struct mutex bus_mutex; struct dma_chan *rx_chan; struct completion rx_dma_complete; dma_addr_t mmap_phys_base; int current_cs; - int current_page_size; - int current_erase_size; - int current_addr_width; unsigned long master_ref_clk_hz; bool is_decoded_cs; u32 fifo_depth; @@ -87,6 +78,7 @@ struct cqspi_st { bool rclk_en; u32 trigger_address; u32 wr_delay; + bool use_direct_mode; struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIPSELECT]; }; @@ -285,9 +277,8 @@ static irqreturn_t cqspi_irq_handler(int this_irq, void *dev) return IRQ_HANDLED; } -static unsigned int cqspi_calc_rdreg(struct spi_nor *nor) +static unsigned int cqspi_calc_rdreg(struct cqspi_flash_pdata *f_pdata) { - struct cqspi_flash_pdata *f_pdata = nor->priv; u32 rdreg = 0; rdreg |= f_pdata->inst_width << CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB; @@ -354,19 +345,21 @@ static int cqspi_exec_flash_cmd(struct cqspi_st *cqspi, unsigned int reg) return cqspi_wait_idle(cqspi); } -static int cqspi_command_read(struct spi_nor *nor, u8 opcode, - u8 *rxbuf, size_t n_rx) +static int cqspi_command_read(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op) { - struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; + u8 *rxbuf = op->data.buf.in; + u8 opcode = op->cmd.opcode; + size_t n_rx = op->data.nbytes; unsigned int rdreg; unsigned int reg; size_t read_len; int status; if (!n_rx || n_rx > CQSPI_STIG_DATA_LEN_MAX || !rxbuf) { - dev_err(nor->dev, + dev_err(&cqspi->pdev->dev, "Invalid input argument, len %zu rxbuf 0x%p\n", n_rx, rxbuf); return -EINVAL; @@ -374,7 +367,7 @@ static int cqspi_command_read(struct spi_nor *nor, u8 opcode, reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; - rdreg = cqspi_calc_rdreg(nor); + rdreg = cqspi_calc_rdreg(f_pdata); writel(rdreg, reg_base + CQSPI_REG_RD_INSTR); reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB); @@ -403,25 +396,36 @@ static int cqspi_command_read(struct spi_nor *nor, u8 opcode, return 0; } -static int cqspi_command_write(struct spi_nor *nor, const u8 opcode, - const u8 *txbuf, size_t n_tx) +static int cqspi_command_write(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op) { - struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; + const u8 opcode = op->cmd.opcode; + const u8 *txbuf = op->data.buf.out; + size_t n_tx = op->data.nbytes; unsigned int reg; unsigned int data; size_t write_len; - int ret; if (n_tx > CQSPI_STIG_DATA_LEN_MAX || (n_tx && !txbuf)) { - dev_err(nor->dev, + dev_err(&cqspi->pdev->dev, "Invalid input argument, cmdlen %zu txbuf 0x%p\n", n_tx, txbuf); return -EINVAL; } reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; + + if (op->addr.nbytes) { + reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB); + reg |= ((op->addr.nbytes - 1) & + CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) + << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB; + + writel(op->addr.val, reg_base + CQSPI_REG_CMDADDRESS); + } + if (n_tx) { reg |= (0x1 << CQSPI_REG_CMDCTRL_WR_EN_LSB); reg |= ((n_tx - 1) & CQSPI_REG_CMDCTRL_WR_BYTES_MASK) @@ -439,73 +443,46 @@ static int cqspi_command_write(struct spi_nor *nor, const u8 opcode, writel(data, reg_base + CQSPI_REG_CMDWRITEDATAUPPER); } } - ret = cqspi_exec_flash_cmd(cqspi, reg); - return ret; -} - -static int cqspi_command_write_addr(struct spi_nor *nor, - const u8 opcode, const unsigned int addr) -{ - struct cqspi_flash_pdata *f_pdata = nor->priv; - struct cqspi_st *cqspi = f_pdata->cqspi; - void __iomem *reg_base = cqspi->iobase; - unsigned int reg; - - reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; - reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB); - reg |= ((nor->addr_width - 1) & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) - << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB; - - writel(addr, reg_base + CQSPI_REG_CMDADDRESS); return cqspi_exec_flash_cmd(cqspi, reg); } -static int cqspi_read_setup(struct spi_nor *nor) +static int cqspi_read_setup(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op) { - struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; unsigned int dummy_clk = 0; unsigned int reg; - reg = nor->read_opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB; - reg |= cqspi_calc_rdreg(nor); + reg = op->cmd.opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB; + reg |= cqspi_calc_rdreg(f_pdata); /* Setup dummy clock cycles */ - dummy_clk = nor->read_dummy; + dummy_clk = op->dummy.nbytes * 8; if (dummy_clk > CQSPI_DUMMY_CLKS_MAX) dummy_clk = CQSPI_DUMMY_CLKS_MAX; - if (dummy_clk / 8) { - reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB); - /* Set mode bits high to ensure chip doesn't enter XIP */ - writel(0xFF, reg_base + CQSPI_REG_MODE_BIT); - - /* Need to subtract the mode byte (8 clocks). */ - if (f_pdata->inst_width != CQSPI_INST_TYPE_QUAD) - dummy_clk -= 8; - - if (dummy_clk) - reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK) - << CQSPI_REG_RD_INSTR_DUMMY_LSB; - } + if (dummy_clk) + reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK) + << CQSPI_REG_RD_INSTR_DUMMY_LSB; writel(reg, reg_base + CQSPI_REG_RD_INSTR); /* Set address width */ reg = readl(reg_base + CQSPI_REG_SIZE); reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; - reg |= (nor->addr_width - 1); + reg |= (op->addr.nbytes - 1); writel(reg, reg_base + CQSPI_REG_SIZE); return 0; } -static int cqspi_indirect_read_execute(struct spi_nor *nor, u8 *rxbuf, - loff_t from_addr, const size_t n_rx) +static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata, + u8 *rxbuf, loff_t from_addr, + const size_t n_rx) { - struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; + struct device *dev = &cqspi->pdev->dev; void __iomem *reg_base = cqspi->iobase; void __iomem *ahb_base = cqspi->ahb_base; unsigned int remaining = n_rx; @@ -528,13 +505,13 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, u8 *rxbuf, while (remaining > 0) { if (!wait_for_completion_timeout(&cqspi->transfer_complete, - msecs_to_jiffies(CQSPI_READ_TIMEOUT_MS))) + msecs_to_jiffies(CQSPI_READ_TIMEOUT_MS))) ret = -ETIMEDOUT; bytes_to_read = cqspi_get_rd_sram_level(cqspi); if (ret && bytes_to_read == 0) { - dev_err(nor->dev, "Indirect read timeout, no bytes\n"); + dev_err(dev, "Indirect read timeout, no bytes\n"); goto failrd; } @@ -570,8 +547,7 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, u8 *rxbuf, ret = cqspi_wait_for_bit(reg_base + CQSPI_REG_INDIRECTRD, CQSPI_REG_INDIRECTRD_DONE_MASK, 0); if (ret) { - dev_err(nor->dev, - "Indirect read completion error (%i)\n", ret); + dev_err(dev, "Indirect read completion error (%i)\n", ret); goto failrd; } @@ -593,32 +569,32 @@ failrd: return ret; } -static int cqspi_write_setup(struct spi_nor *nor) +static int cqspi_write_setup(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op) { unsigned int reg; - struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; /* Set opcode. */ - reg = nor->program_opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB; + reg = op->cmd.opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB; writel(reg, reg_base + CQSPI_REG_WR_INSTR); - reg = cqspi_calc_rdreg(nor); + reg = cqspi_calc_rdreg(f_pdata); writel(reg, reg_base + CQSPI_REG_RD_INSTR); reg = readl(reg_base + CQSPI_REG_SIZE); reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; - reg |= (nor->addr_width - 1); + reg |= (op->addr.nbytes - 1); writel(reg, reg_base + CQSPI_REG_SIZE); return 0; } -static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr, - const u8 *txbuf, const size_t n_tx) +static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata, + loff_t to_addr, const u8 *txbuf, + const size_t n_tx) { - const unsigned int page_size = nor->page_size; - struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; + struct device *dev = &cqspi->pdev->dev; void __iomem *reg_base = cqspi->iobase; unsigned int remaining = n_tx; unsigned int write_bytes; @@ -648,7 +624,7 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr, while (remaining > 0) { size_t write_words, mod_bytes; - write_bytes = remaining > page_size ? page_size : remaining; + write_bytes = remaining; write_words = write_bytes / 4; mod_bytes = write_bytes % 4; /* Write 4 bytes at a time then single bytes. */ @@ -665,8 +641,8 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr, } if (!wait_for_completion_timeout(&cqspi->transfer_complete, - msecs_to_jiffies(CQSPI_TIMEOUT_MS))) { - dev_err(nor->dev, "Indirect write timeout\n"); + msecs_to_jiffies(CQSPI_TIMEOUT_MS))) { + dev_err(dev, "Indirect write timeout\n"); ret = -ETIMEDOUT; goto failwr; } @@ -681,8 +657,7 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr, ret = cqspi_wait_for_bit(reg_base + CQSPI_REG_INDIRECTWR, CQSPI_REG_INDIRECTWR_DONE_MASK, 0); if (ret) { - dev_err(nor->dev, - "Indirect write completion error (%i)\n", ret); + dev_err(dev, "Indirect write completion error (%i)\n", ret); goto failwr; } @@ -706,9 +681,8 @@ failwr: return ret; } -static void cqspi_chipselect(struct spi_nor *nor) +static void cqspi_chipselect(struct cqspi_flash_pdata *f_pdata) { - struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; unsigned int chip_select = f_pdata->cs; @@ -736,32 +710,6 @@ static void cqspi_chipselect(struct spi_nor *nor) writel(reg, reg_base + CQSPI_REG_CONFIG); } -static void cqspi_configure_cs_and_sizes(struct spi_nor *nor) -{ - struct cqspi_flash_pdata *f_pdata = nor->priv; - struct cqspi_st *cqspi = f_pdata->cqspi; - void __iomem *iobase = cqspi->iobase; - unsigned int reg; - - /* configure page size and block size. */ - reg = readl(iobase + CQSPI_REG_SIZE); - reg &= ~(CQSPI_REG_SIZE_PAGE_MASK << CQSPI_REG_SIZE_PAGE_LSB); - reg &= ~(CQSPI_REG_SIZE_BLOCK_MASK << CQSPI_REG_SIZE_BLOCK_LSB); - reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; - reg |= (nor->page_size << CQSPI_REG_SIZE_PAGE_LSB); - reg |= (ilog2(nor->mtd.erasesize) << CQSPI_REG_SIZE_BLOCK_LSB); - reg |= (nor->addr_width - 1); - writel(reg, iobase + CQSPI_REG_SIZE); - - /* configure the chip select */ - cqspi_chipselect(nor); - - /* Store the new configuration of the controller */ - cqspi->current_page_size = nor->page_size; - cqspi->current_erase_size = nor->mtd.erasesize; - cqspi->current_addr_width = nor->addr_width; -} - static unsigned int calculate_ticks_for_ns(const unsigned int ref_clk_hz, const unsigned int ns_val) { @@ -773,9 +721,8 @@ static unsigned int calculate_ticks_for_ns(const unsigned int ref_clk_hz, return ticks; } -static void cqspi_delay(struct spi_nor *nor) +static void cqspi_delay(struct cqspi_flash_pdata *f_pdata) { - struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *iobase = cqspi->iobase; const unsigned int ref_clk_hz = cqspi->master_ref_clk_hz; @@ -859,33 +806,27 @@ static void cqspi_controller_enable(struct cqspi_st *cqspi, bool enable) writel(reg, reg_base + CQSPI_REG_CONFIG); } -static void cqspi_configure(struct spi_nor *nor) +static void cqspi_configure(struct cqspi_flash_pdata *f_pdata, + unsigned long sclk) { - struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; - const unsigned int sclk = f_pdata->clk_rate; int switch_cs = (cqspi->current_cs != f_pdata->cs); int switch_ck = (cqspi->sclk != sclk); - if ((cqspi->current_page_size != nor->page_size) || - (cqspi->current_erase_size != nor->mtd.erasesize) || - (cqspi->current_addr_width != nor->addr_width)) - switch_cs = 1; - if (switch_cs || switch_ck) cqspi_controller_enable(cqspi, 0); /* Switch chip select. */ if (switch_cs) { cqspi->current_cs = f_pdata->cs; - cqspi_configure_cs_and_sizes(nor); + cqspi_chipselect(f_pdata); } /* Setup baudrate divisor and delays */ if (switch_ck) { cqspi->sclk = sclk; cqspi_config_baudrate_div(cqspi); - cqspi_delay(nor); + cqspi_delay(f_pdata); cqspi_readdata_capture(cqspi, !cqspi->rclk_en, f_pdata->read_delay); } @@ -894,26 +835,25 @@ static void cqspi_configure(struct spi_nor *nor) cqspi_controller_enable(cqspi, 1); } -static int cqspi_set_protocol(struct spi_nor *nor, const int read) +static int cqspi_set_protocol(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op) { - struct cqspi_flash_pdata *f_pdata = nor->priv; - f_pdata->inst_width = CQSPI_INST_TYPE_SINGLE; f_pdata->addr_width = CQSPI_INST_TYPE_SINGLE; f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; - if (read) { - switch (nor->read_proto) { - case SNOR_PROTO_1_1_1: + if (op->data.dir == SPI_MEM_DATA_IN) { + switch (op->data.buswidth) { + case 1: f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; break; - case SNOR_PROTO_1_1_2: + case 2: f_pdata->data_width = CQSPI_INST_TYPE_DUAL; break; - case SNOR_PROTO_1_1_4: + case 4: f_pdata->data_width = CQSPI_INST_TYPE_QUAD; break; - case SNOR_PROTO_1_1_8: + case 8: f_pdata->data_width = CQSPI_INST_TYPE_OCTAL; break; default: @@ -921,36 +861,32 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read) } } - cqspi_configure(nor); - return 0; } -static ssize_t cqspi_write(struct spi_nor *nor, loff_t to, - size_t len, const u_char *buf) +static ssize_t cqspi_write(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op) { - struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; + loff_t to = op->addr.val; + size_t len = op->data.nbytes; + const u_char *buf = op->data.buf.out; int ret; - ret = cqspi_set_protocol(nor, 0); + ret = cqspi_set_protocol(f_pdata, op); if (ret) return ret; - ret = cqspi_write_setup(nor); + ret = cqspi_write_setup(f_pdata, op); if (ret) return ret; - if (f_pdata->use_direct_mode) { + if (cqspi->use_direct_mode && ((to + len) <= cqspi->ahb_size)) { memcpy_toio(cqspi->ahb_base + to, buf, len); - ret = cqspi_wait_idle(cqspi); - } else { - ret = cqspi_indirect_write_execute(nor, to, buf, len); + return cqspi_wait_idle(cqspi); } - if (ret) - return ret; - return len; + return cqspi_indirect_write_execute(f_pdata, to, buf, len); } static void cqspi_rx_dma_callback(void *param) @@ -960,11 +896,11 @@ static void cqspi_rx_dma_callback(void *param) complete(&cqspi->rx_dma_complete); } -static int cqspi_direct_read_execute(struct spi_nor *nor, u_char *buf, - loff_t from, size_t len) +static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata, + u_char *buf, loff_t from, size_t len) { - struct cqspi_flash_pdata *f_pdata = nor->priv; struct cqspi_st *cqspi = f_pdata->cqspi; + struct device *dev = &cqspi->pdev->dev; enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; dma_addr_t dma_src = (dma_addr_t)cqspi->mmap_phys_base + from; int ret = 0; @@ -977,15 +913,15 @@ static int cqspi_direct_read_execute(struct spi_nor *nor, u_char *buf, return 0; } - dma_dst = dma_map_single(nor->dev, buf, len, DMA_FROM_DEVICE); - if (dma_mapping_error(nor->dev, dma_dst)) { - dev_err(nor->dev, "dma mapping failed\n"); + dma_dst = dma_map_single(dev, buf, len, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, dma_dst)) { + dev_err(dev, "dma mapping failed\n"); return -ENOMEM; } tx = dmaengine_prep_dma_memcpy(cqspi->rx_chan, dma_dst, dma_src, len, flags); if (!tx) { - dev_err(nor->dev, "device_prep_dma_memcpy error\n"); + dev_err(dev, "device_prep_dma_memcpy error\n"); ret = -EIO; goto err_unmap; } @@ -997,7 +933,7 @@ static int cqspi_direct_read_execute(struct spi_nor *nor, u_char *buf, ret = dma_submit_error(cookie); if (ret) { - dev_err(nor->dev, "dma_submit_error %d\n", cookie); + dev_err(dev, "dma_submit_error %d\n", cookie); ret = -EIO; goto err_unmap; } @@ -1006,99 +942,68 @@ static int cqspi_direct_read_execute(struct spi_nor *nor, u_char *buf, if (!wait_for_completion_timeout(&cqspi->rx_dma_complete, msecs_to_jiffies(len))) { dmaengine_terminate_sync(cqspi->rx_chan); - dev_err(nor->dev, "DMA wait_for_completion_timeout\n"); + dev_err(dev, "DMA wait_for_completion_timeout\n"); ret = -ETIMEDOUT; goto err_unmap; } err_unmap: - dma_unmap_single(nor->dev, dma_dst, len, DMA_FROM_DEVICE); + dma_unmap_single(dev, dma_dst, len, DMA_FROM_DEVICE); return ret; } -static ssize_t cqspi_read(struct spi_nor *nor, loff_t from, - size_t len, u_char *buf) -{ - struct cqspi_flash_pdata *f_pdata = nor->priv; - int ret; - - ret = cqspi_set_protocol(nor, 1); - if (ret) - return ret; - - ret = cqspi_read_setup(nor); - if (ret) - return ret; - - if (f_pdata->use_direct_mode) - ret = cqspi_direct_read_execute(nor, buf, from, len); - else - ret = cqspi_indirect_read_execute(nor, buf, from, len); - if (ret) - return ret; - - return len; -} - -static int cqspi_erase(struct spi_nor *nor, loff_t offs) +static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op) { + struct cqspi_st *cqspi = f_pdata->cqspi; + loff_t from = op->addr.val; + size_t len = op->data.nbytes; + u_char *buf = op->data.buf.in; int ret; - ret = cqspi_set_protocol(nor, 0); + ret = cqspi_set_protocol(f_pdata, op); if (ret) return ret; - /* Send write enable, then erase commands. */ - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREN, NULL, 0); + ret = cqspi_read_setup(f_pdata, op); if (ret) return ret; - /* Set up command buffer. */ - ret = cqspi_command_write_addr(nor, nor->erase_opcode, offs); - if (ret) - return ret; + if (cqspi->use_direct_mode && ((from + len) <= cqspi->ahb_size)) + return cqspi_direct_read_execute(f_pdata, buf, from, len); - return 0; + return cqspi_indirect_read_execute(f_pdata, buf, from, len); } -static int cqspi_prep(struct spi_nor *nor) +static int cqspi_mem_process(struct spi_mem *mem, const struct spi_mem_op *op) { - struct cqspi_flash_pdata *f_pdata = nor->priv; - struct cqspi_st *cqspi = f_pdata->cqspi; - - mutex_lock(&cqspi->bus_mutex); - - return 0; -} + struct cqspi_st *cqspi = spi_master_get_devdata(mem->spi->master); + struct cqspi_flash_pdata *f_pdata; -static void cqspi_unprep(struct spi_nor *nor) -{ - struct cqspi_flash_pdata *f_pdata = nor->priv; - struct cqspi_st *cqspi = f_pdata->cqspi; + f_pdata = &cqspi->f_pdata[mem->spi->chip_select]; + cqspi_configure(f_pdata, mem->spi->max_speed_hz); - mutex_unlock(&cqspi->bus_mutex); -} + if (op->data.dir == SPI_MEM_DATA_IN && op->data.buf.in) { + if (!op->addr.nbytes) + return cqspi_command_read(f_pdata, op); -static int cqspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, size_t len) -{ - int ret; + return cqspi_read(f_pdata, op); + } - ret = cqspi_set_protocol(nor, 0); - if (!ret) - ret = cqspi_command_read(nor, opcode, buf, len); + if (!op->addr.nbytes || !op->data.buf.out) + return cqspi_command_write(f_pdata, op); - return ret; + return cqspi_write(f_pdata, op); } -static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, - size_t len) +static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) { int ret; - ret = cqspi_set_protocol(nor, 0); - if (!ret) - ret = cqspi_command_write(nor, opcode, buf, len); + ret = cqspi_mem_process(mem, op); + if (ret) + dev_err(&mem->spi->dev, "operation failed with %d\n", ret); return ret; } @@ -1140,26 +1045,26 @@ static int cqspi_of_get_flash_pdata(struct platform_device *pdev, return 0; } -static int cqspi_of_get_pdata(struct platform_device *pdev) +static int cqspi_of_get_pdata(struct cqspi_st *cqspi) { - struct device_node *np = pdev->dev.of_node; - struct cqspi_st *cqspi = platform_get_drvdata(pdev); + struct device *dev = &cqspi->pdev->dev; + struct device_node *np = dev->of_node; cqspi->is_decoded_cs = of_property_read_bool(np, "cdns,is-decoded-cs"); if (of_property_read_u32(np, "cdns,fifo-depth", &cqspi->fifo_depth)) { - dev_err(&pdev->dev, "couldn't determine fifo-depth\n"); + dev_err(dev, "couldn't determine fifo-depth\n"); return -ENXIO; } if (of_property_read_u32(np, "cdns,fifo-width", &cqspi->fifo_width)) { - dev_err(&pdev->dev, "couldn't determine fifo-width\n"); + dev_err(dev, "couldn't determine fifo-width\n"); return -ENXIO; } if (of_property_read_u32(np, "cdns,trigger-address", &cqspi->trigger_address)) { - dev_err(&pdev->dev, "couldn't determine trigger-address\n"); + dev_err(dev, "couldn't determine trigger-address\n"); return -ENXIO; } @@ -1202,7 +1107,7 @@ static void cqspi_controller_init(struct cqspi_st *cqspi) cqspi_controller_enable(cqspi, 1); } -static void cqspi_request_mmap_dma(struct cqspi_st *cqspi) +static int cqspi_request_mmap_dma(struct cqspi_st *cqspi) { dma_cap_mask_t mask; @@ -1211,53 +1116,42 @@ static void cqspi_request_mmap_dma(struct cqspi_st *cqspi) cqspi->rx_chan = dma_request_chan_by_mask(&mask); if (IS_ERR(cqspi->rx_chan)) { - dev_err(&cqspi->pdev->dev, "No Rx DMA available\n"); + int ret = PTR_ERR(cqspi->rx_chan); + + if (ret != -EPROBE_DEFER) + dev_err(&cqspi->pdev->dev, "No Rx DMA available\n"); cqspi->rx_chan = NULL; + return ret; } init_completion(&cqspi->rx_dma_complete); + + return 0; } -static const struct spi_nor_controller_ops cqspi_controller_ops = { - .prepare = cqspi_prep, - .unprepare = cqspi_unprep, - .read_reg = cqspi_read_reg, - .write_reg = cqspi_write_reg, - .read = cqspi_read, - .write = cqspi_write, - .erase = cqspi_erase, +static const struct spi_controller_mem_ops cqspi_mem_ops = { + .exec_op = cqspi_exec_mem_op, }; -static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np) +static int cqspi_setup_flash(struct cqspi_st *cqspi) { struct platform_device *pdev = cqspi->pdev; struct device *dev = &pdev->dev; - const struct cqspi_driver_platdata *ddata; - struct spi_nor_hwcaps hwcaps; + struct device_node *np = dev->of_node; struct cqspi_flash_pdata *f_pdata; - struct spi_nor *nor; - struct mtd_info *mtd; unsigned int cs; - int i, ret; - - ddata = of_device_get_match_data(dev); - if (!ddata) { - dev_err(dev, "Couldn't find driver data\n"); - return -EINVAL; - } - hwcaps.mask = ddata->hwcaps_mask; + int ret; /* Get flash device data */ for_each_available_child_of_node(dev->of_node, np) { ret = of_property_read_u32(np, "reg", &cs); if (ret) { dev_err(dev, "Couldn't determine chip select.\n"); - goto err; + return ret; } if (cs >= CQSPI_MAX_CHIPSELECT) { - ret = -EINVAL; dev_err(dev, "Chip select %d out of range.\n", cs); - goto err; + return -EINVAL; } f_pdata = &cqspi->f_pdata[cs]; @@ -1266,86 +1160,51 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np) ret = cqspi_of_get_flash_pdata(pdev, f_pdata, np); if (ret) - goto err; - - nor = &f_pdata->nor; - mtd = &nor->mtd; - - mtd->priv = nor; - - nor->dev = dev; - spi_nor_set_flash_node(nor, np); - nor->priv = f_pdata; - nor->controller_ops = &cqspi_controller_ops; - - mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", - dev_name(dev), cs); - if (!mtd->name) { - ret = -ENOMEM; - goto err; - } - - ret = spi_nor_scan(nor, NULL, &hwcaps); - if (ret) - goto err; - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) - goto err; - - f_pdata->registered = true; - - if (mtd->size <= cqspi->ahb_size) { - f_pdata->use_direct_mode = true; - dev_dbg(nor->dev, "using direct mode for %s\n", - mtd->name); - - if (!cqspi->rx_chan) - cqspi_request_mmap_dma(cqspi); - } + return ret; } return 0; - -err: - for (i = 0; i < CQSPI_MAX_CHIPSELECT; i++) - if (cqspi->f_pdata[i].registered) - mtd_device_unregister(&cqspi->f_pdata[i].nor.mtd); - return ret; } static int cqspi_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + const struct cqspi_driver_platdata *ddata; + struct reset_control *rstc, *rstc_ocp; struct device *dev = &pdev->dev; + struct spi_master *master; + struct resource *res_ahb; struct cqspi_st *cqspi; struct resource *res; - struct resource *res_ahb; - struct reset_control *rstc, *rstc_ocp; - const struct cqspi_driver_platdata *ddata; int ret; int irq; - cqspi = devm_kzalloc(dev, sizeof(*cqspi), GFP_KERNEL); - if (!cqspi) + master = spi_alloc_master(&pdev->dev, sizeof(*cqspi)); + if (!master) { + dev_err(&pdev->dev, "spi_alloc_master failed\n"); return -ENOMEM; + } + master->mode_bits = SPI_RX_QUAD | SPI_RX_DUAL; + master->mem_ops = &cqspi_mem_ops; + master->dev.of_node = pdev->dev.of_node; + + cqspi = spi_master_get_devdata(master); - mutex_init(&cqspi->bus_mutex); cqspi->pdev = pdev; - platform_set_drvdata(pdev, cqspi); /* Obtain configuration from OF. */ - ret = cqspi_of_get_pdata(pdev); + ret = cqspi_of_get_pdata(cqspi); if (ret) { dev_err(dev, "Cannot get mandatory OF data.\n"); - return -ENODEV; + ret = -ENODEV; + goto probe_master_put; } /* Obtain QSPI clock. */ cqspi->clk = devm_clk_get(dev, NULL); if (IS_ERR(cqspi->clk)) { dev_err(dev, "Cannot claim QSPI clock.\n"); - return PTR_ERR(cqspi->clk); + ret = PTR_ERR(cqspi->clk); + goto probe_master_put; } /* Obtain and remap controller address. */ @@ -1353,7 +1212,8 @@ static int cqspi_probe(struct platform_device *pdev) cqspi->iobase = devm_ioremap_resource(dev, res); if (IS_ERR(cqspi->iobase)) { dev_err(dev, "Cannot remap controller address.\n"); - return PTR_ERR(cqspi->iobase); + ret = PTR_ERR(cqspi->iobase); + goto probe_master_put; } /* Obtain and remap AHB address. */ @@ -1361,7 +1221,8 @@ static int cqspi_probe(struct platform_device *pdev) cqspi->ahb_base = devm_ioremap_resource(dev, res_ahb); if (IS_ERR(cqspi->ahb_base)) { dev_err(dev, "Cannot remap AHB address.\n"); - return PTR_ERR(cqspi->ahb_base); + ret = PTR_ERR(cqspi->ahb_base); + goto probe_master_put; } cqspi->mmap_phys_base = (dma_addr_t)res_ahb->start; cqspi->ahb_size = resource_size(res_ahb); @@ -1370,14 +1231,16 @@ static int cqspi_probe(struct platform_device *pdev) /* Obtain IRQ line. */ irq = platform_get_irq(pdev, 0); - if (irq < 0) - return -ENXIO; + if (irq < 0) { + ret = -ENXIO; + goto probe_master_put; + } pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { pm_runtime_put_noidle(dev); - return ret; + goto probe_master_put; } ret = clk_prepare_enable(cqspi->clk); @@ -1390,13 +1253,13 @@ static int cqspi_probe(struct platform_device *pdev) rstc = devm_reset_control_get_optional_exclusive(dev, "qspi"); if (IS_ERR(rstc)) { dev_err(dev, "Cannot get QSPI reset.\n"); - return PTR_ERR(rstc); + goto probe_reset_failed; } rstc_ocp = devm_reset_control_get_optional_exclusive(dev, "qspi-ocp"); if (IS_ERR(rstc_ocp)) { dev_err(dev, "Cannot get QSPI OCP reset.\n"); - return PTR_ERR(rstc_ocp); + goto probe_reset_failed; } reset_control_assert(rstc); @@ -1407,15 +1270,21 @@ static int cqspi_probe(struct platform_device *pdev) cqspi->master_ref_clk_hz = clk_get_rate(cqspi->clk); ddata = of_device_get_match_data(dev); - if (ddata && (ddata->quirks & CQSPI_NEEDS_WR_DELAY)) - cqspi->wr_delay = 5 * DIV_ROUND_UP(NSEC_PER_SEC, - cqspi->master_ref_clk_hz); + if (ddata) { + if (ddata->quirks & CQSPI_NEEDS_WR_DELAY) + cqspi->wr_delay = 5 * DIV_ROUND_UP(NSEC_PER_SEC, + cqspi->master_ref_clk_hz); + if (ddata->hwcaps_mask & CQSPI_SUPPORTS_OCTAL) + master->mode_bits |= SPI_RX_OCTAL; + if (!(ddata->quirks & CQSPI_DISABLE_DAC_MODE)) + cqspi->use_direct_mode = true; + } ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0, pdev->name, cqspi); if (ret) { dev_err(dev, "Cannot request IRQ.\n"); - goto probe_irq_failed; + goto probe_reset_failed; } cqspi_wait_idle(cqspi); @@ -1423,31 +1292,40 @@ static int cqspi_probe(struct platform_device *pdev) cqspi->current_cs = -1; cqspi->sclk = 0; - ret = cqspi_setup_flash(cqspi, np); + ret = cqspi_setup_flash(cqspi); if (ret) { - dev_err(dev, "Cadence QSPI NOR probe failed %d\n", ret); + dev_err(dev, "failed to setup flash parameters %d\n", ret); goto probe_setup_failed; } - return ret; + if (cqspi->use_direct_mode) { + ret = cqspi_request_mmap_dma(cqspi); + if (ret == -EPROBE_DEFER) + goto probe_setup_failed; + } + + ret = devm_spi_register_master(dev, master); + if (ret) { + dev_err(&pdev->dev, "failed to register SPI ctlr %d\n", ret); + goto probe_setup_failed; + } + + return 0; probe_setup_failed: cqspi_controller_enable(cqspi, 0); -probe_irq_failed: +probe_reset_failed: clk_disable_unprepare(cqspi->clk); probe_clk_failed: pm_runtime_put_sync(dev); pm_runtime_disable(dev); +probe_master_put: + spi_master_put(master); return ret; } static int cqspi_remove(struct platform_device *pdev) { struct cqspi_st *cqspi = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < CQSPI_MAX_CHIPSELECT; i++) - if (cqspi->f_pdata[i].registered) - mtd_device_unregister(&cqspi->f_pdata[i].nor.mtd); cqspi_controller_enable(cqspi, 0); @@ -1490,16 +1368,15 @@ static const struct dev_pm_ops cqspi__dev_pm_ops = { #endif static const struct cqspi_driver_platdata cdns_qspi = { - .hwcaps_mask = CQSPI_BASE_HWCAPS_MASK, + .quirks = CQSPI_DISABLE_DAC_MODE, }; static const struct cqspi_driver_platdata k2g_qspi = { - .hwcaps_mask = CQSPI_BASE_HWCAPS_MASK, .quirks = CQSPI_NEEDS_WR_DELAY, }; static const struct cqspi_driver_platdata am654_ospi = { - .hwcaps_mask = CQSPI_BASE_HWCAPS_MASK | SNOR_HWCAPS_READ_1_1_8, + .hwcaps_mask = CQSPI_SUPPORTS_OCTAL, .quirks = CQSPI_NEEDS_WR_DELAY, }; @@ -1538,3 +1415,5 @@ MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" CQSPI_NAME); MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>"); MODULE_AUTHOR("Graham Moore <grmoore@opensource.altera.com>"); +MODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@intel.com>"); +MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>"); diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index 9a86cc27fcc0..ef53290b7d24 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -156,6 +156,12 @@ bool spi_mem_default_supports_op(struct spi_mem *mem, op->data.dir == SPI_MEM_DATA_OUT)) return false; + if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) + return false; + + if (op->cmd.nbytes != 1) + return false; + return true; } EXPORT_SYMBOL_GPL(spi_mem_default_supports_op); @@ -170,7 +176,7 @@ static bool spi_mem_buswidth_is_valid(u8 buswidth) static int spi_mem_check_op(const struct spi_mem_op *op) { - if (!op->cmd.buswidth) + if (!op->cmd.buswidth || !op->cmd.nbytes) return -EINVAL; if ((op->addr.nbytes && !op->addr.buswidth) || @@ -306,8 +312,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) return ret; } - tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes + - op->dummy.nbytes; + tmpbufsize = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; /* * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so @@ -322,7 +327,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) tmpbuf[0] = op->cmd.opcode; xfers[xferpos].tx_buf = tmpbuf; - xfers[xferpos].len = sizeof(op->cmd.opcode); + xfers[xferpos].len = op->cmd.nbytes; xfers[xferpos].tx_nbits = op->cmd.buswidth; spi_message_add_tail(&xfers[xferpos], &msg); xferpos++; @@ -424,8 +429,7 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) return ctlr->mem_ops->adjust_op_size(mem, op); if (!ctlr->mem_ops || !ctlr->mem_ops->exec_op) { - len = sizeof(op->cmd.opcode) + op->addr.nbytes + - op->dummy.nbytes; + len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; if (len > spi_max_transfer_size(mem->spi)) return -EINVAL; diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 7bc302b50396..b08d8e9a8ee9 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -195,7 +195,7 @@ static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) } } - len = MTK_NOR_PRG_MAX_SIZE - sizeof(op->cmd.opcode) - op->addr.nbytes - + len = MTK_NOR_PRG_MAX_SIZE - op->cmd.nbytes - op->addr.nbytes - op->dummy.nbytes; if (op->data.nbytes > len) op->data.nbytes = len; @@ -211,6 +211,12 @@ static bool mtk_nor_supports_op(struct spi_mem *mem, if (op->cmd.buswidth != 1) return false; + /* DTR ops not supported. */ + if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) + return false; + if (op->cmd.nbytes != 1) + return false; + if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) { if ((op->data.dir == SPI_MEM_DATA_IN) && mtk_nor_match_read(op)) return true; @@ -219,7 +225,7 @@ static bool mtk_nor_supports_op(struct spi_mem *mem, (op->dummy.buswidth == 0) && (op->data.buswidth == 1); } - len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes; + len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; if ((len > MTK_NOR_PRG_MAX_SIZE) || ((op->data.nbytes) && (len == MTK_NOR_PRG_MAX_SIZE))) return false; diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c index 69491f3a515d..8c630acb0110 100644 --- a/drivers/spi/spi-mxic.c +++ b/drivers/spi/spi-mxic.c @@ -356,6 +356,7 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem, int nio = 1, i, ret; u32 ss_ctrl; u8 addr[8]; + u8 opcode = op->cmd.opcode; ret = mxic_spi_set_freq(mxic, mem->spi->max_speed_hz); if (ret) @@ -393,7 +394,7 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem, writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT, mxic->regs + HC_CFG); - ret = mxic_spi_data_xfer(mxic, &op->cmd.opcode, NULL, 1); + ret = mxic_spi_data_xfer(mxic, &opcode, NULL, 1); if (ret) goto out; diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c index 17641157354d..bbf3d90561f5 100644 --- a/drivers/spi/spi-zynq-qspi.c +++ b/drivers/spi/spi-zynq-qspi.c @@ -527,20 +527,21 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem, struct zynq_qspi *xqspi = spi_controller_get_devdata(mem->spi->master); int err = 0, i; u8 *tmpbuf; + u8 opcode = op->cmd.opcode; dev_dbg(xqspi->dev, "cmd:%#x mode:%d.%d.%d.%d\n", - op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + opcode, op->cmd.buswidth, op->addr.buswidth, op->dummy.buswidth, op->data.buswidth); zynq_qspi_chipselect(mem->spi, true); zynq_qspi_config_op(xqspi, mem->spi); - if (op->cmd.opcode) { + if (op->cmd.nbytes) { reinit_completion(&xqspi->data_completion); - xqspi->txbuf = (u8 *)&op->cmd.opcode; + xqspi->txbuf = &opcode; xqspi->rxbuf = NULL; - xqspi->tx_bytes = sizeof(op->cmd.opcode); - xqspi->rx_bytes = sizeof(op->cmd.opcode); + xqspi->tx_bytes = op->cmd.nbytes; + xqspi->rx_bytes = op->cmd.nbytes; zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true); zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET, ZYNQ_QSPI_IXR_RXTX_MASK); diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index af9ff2f0f1b2..159463cc659c 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -17,6 +17,7 @@ { \ .buswidth = __buswidth, \ .opcode = __opcode, \ + .nbytes = 1, \ } #define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \ @@ -69,11 +70,15 @@ enum spi_mem_data_dir { /** * struct spi_mem_op - describes a SPI memory operation + * @cmd.nbytes: number of opcode bytes (only 1 or 2 are valid). The opcode is + * sent MSB-first. * @cmd.buswidth: number of IO lines used to transmit the command * @cmd.opcode: operation opcode + * @cmd.dtr: whether the command opcode should be sent in DTR mode or not * @addr.nbytes: number of address bytes to send. Can be zero if the operation * does not need to send an address * @addr.buswidth: number of IO lines used to transmit the address cycles + * @addr.dtr: whether the address should be sent in DTR mode or not * @addr.val: address value. This value is always sent MSB first on the bus. * Note that only @addr.nbytes are taken into account in this * address value, so users should make sure the value fits in the @@ -81,7 +86,9 @@ enum spi_mem_data_dir { * @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can * be zero if the operation does not require dummy bytes * @dummy.buswidth: number of IO lanes used to transmit the dummy bytes + * @dummy.dtr: whether the dummy bytes should be sent in DTR mode or not * @data.buswidth: number of IO lanes used to send/receive the data + * @data.dtr: whether the data should be sent in DTR mode or not * @data.dir: direction of the transfer * @data.nbytes: number of data bytes to send/receive. Can be zero if the * operation does not involve transferring data @@ -90,23 +97,28 @@ enum spi_mem_data_dir { */ struct spi_mem_op { struct { + u8 nbytes; u8 buswidth; - u8 opcode; + u8 dtr : 1; + u16 opcode; } cmd; struct { u8 nbytes; u8 buswidth; + u8 dtr : 1; u64 val; } addr; struct { u8 nbytes; u8 buswidth; + u8 dtr : 1; } dummy; struct { u8 buswidth; + u8 dtr : 1; enum spi_mem_data_dir dir; unsigned int nbytes; union { |