summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2025-12-04 11:24:24 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2025-12-04 11:24:24 -0800
commitfa5ef105618ae9b5aaa51b3f09e41d88d4514207 (patch)
tree30780efabf844cc55a8d27082e6a7810d311d68b /drivers
parent0a9431fa74ac9b744bff5b65082ff96fd3d80297 (diff)
parentcb5c2eb459f4c98d584eaf3d3ea7c3612385d081 (diff)
Merge tag 'spi-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown: "This release is almost entirely new drivers, with a couple of small changes in generic code. The biggest individual update is a rename of the existing Microchip driver and the addition of a new driver for the silicon SPI controller in their PolarFire SoCs. The overlap between the soft IP supported by the current driver and this new one is regrettably all in the IP and not in the register interface offered to software. - Add a time offset parameter for offloads, allowing them to be defined in relation to each other. This is useful for IIO type applcations where you trigger an operation then read the result after a delay. - Add a tracepoint for flash exec_ops, bringing the flash support more in line with the debuggability of vanilla SPI. - Support for Airoha EN7523, Arduino MCUs, Aspeed AST2700, Microchip PolarFire SPI controllers, NXP i.MX51 ECSPI target mode, Qualcomm IPQ5414 and IPQ5332, Renesas RZ/T2H, RZ/V2N and RZ/2NH and SpacemiT K1 QuadSPI. There's also a small set of ASoC cleanups that I mistakenly applied to the SPI tree and then put more stuff on top of before it was brought to my attention, sorry about that" * tag 'spi-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (80 commits) spi: microchip-core: Refactor FIFO read and write handlers spi: ch341: fix out-of-bounds memory access in ch341_transfer_one spi: microchip-core: Remove unneeded PM related macro spi: microchip-core: Use SPI_MODE_X_MASK spi: microchip-core: Utilise temporary variable for struct device spi: microchip-core: Replace dead code (-ENOMEM error message) spi: microchip-core: use min() instead of min_t() spi: dt-bindings: airoha: add compatible for EN7523 spi: airoha-snfi: en7523: workaround flash damaging if UART_TXD was short to GND spi: dt-bindings: renesas,rzv2h-rspi: Document RZ/V2N SoC support spi: dt-bindings: renesas,rzv2h-rspi: Document RZ/V2N SoC support spi: microchip: Enable compile-testing for FPGA SPI controllers spi: Fix potential uninitialized variable in probe() spi: rzv2h-rspi: add support for RZ/T2H and RZ/N2H spi: dt-bindings: renesas,rzv2h-rspi: document RZ/T2H and RZ/N2H spi: rzv2h-rspi: add support for loopback mode spi: rzv2h-rspi: add support for variable transfer clock spi: rzv2h-rspi: add support for using PCLK for transfer clock spi: rzv2h-rspi: make transfer clock rate finding chip-specific spi: rzv2h-rspi: avoid recomputing transfer frequency ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/spi/Kconfig31
-rw-r--r--drivers/spi/Makefile3
-rw-r--r--drivers/spi/spi-airoha-snfi.c410
-rw-r--r--drivers/spi/spi-aspeed-smc.c747
-rw-r--r--drivers/spi/spi-bcm63xx.c4
-rw-r--r--drivers/spi/spi-cadence.c106
-rw-r--r--drivers/spi/spi-ch341.c2
-rw-r--r--drivers/spi/spi-davinci.c64
-rw-r--r--drivers/spi/spi-dw-bt1.c4
-rw-r--r--drivers/spi/spi-dw-core.c188
-rw-r--r--drivers/spi/spi-dw-dma.c22
-rw-r--r--drivers/spi/spi-dw-mmio.c9
-rw-r--r--drivers/spi/spi-dw-pci.c8
-rw-r--r--drivers/spi/spi-dw.h12
-rw-r--r--drivers/spi/spi-fsl-qspi.c88
-rw-r--r--drivers/spi/spi-imx.c58
-rw-r--r--drivers/spi/spi-mem.c5
-rw-r--r--drivers/spi/spi-microchip-core-spi.c429
-rw-r--r--drivers/spi/spi-mpfs.c (renamed from drivers/spi/spi-microchip-core.c)207
-rw-r--r--drivers/spi/spi-offload-trigger-pwm.c3
-rw-r--r--drivers/spi/spi-qpic-snand.c2
-rw-r--r--drivers/spi/spi-rzv2h-rspi.c303
-rw-r--r--drivers/spi/spi-sg2044-nor.c4
-rw-r--r--drivers/spi/spi-tegra210-quad.c174
-rw-r--r--drivers/spi/spi-tle62x0.c2
-rw-r--r--drivers/spi/spidev.c2
26 files changed, 2129 insertions, 758 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 55675750182e..5520403896fc 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -435,7 +435,8 @@ config SPI_FSL_LPSPI
config SPI_FSL_QUADSPI
tristate "Freescale QSPI controller"
- depends on ARCH_MXC || SOC_LS1021A || ARCH_LAYERSCAPE || COMPILE_TEST
+ depends on ARCH_MXC || SOC_LS1021A || ARCH_LAYERSCAPE || \
+ ARCH_SPACEMIT || COMPILE_TEST
depends on HAS_IOMEM
help
This enables support for the Quad SPI controller in master mode.
@@ -706,15 +707,6 @@ config SPI_MESON_SPIFC
This enables master mode support for the SPIFC (SPI flash
controller) available in Amlogic Meson SoCs.
-config SPI_MICROCHIP_CORE
- tristate "Microchip FPGA SPI controllers"
- depends on SPI_MASTER
- help
- This enables the SPI driver for Microchip FPGA SPI controllers.
- Say Y or M here if you want to use the "hard" controllers on
- PolarFire SoC.
- If built as a module, it will be called spi-microchip-core.
-
config SPI_MICROCHIP_CORE_QSPI
tristate "Microchip FPGA QSPI controllers"
depends on SPI_MASTER
@@ -724,6 +716,15 @@ config SPI_MICROCHIP_CORE_QSPI
PolarFire SoC.
If built as a module, it will be called spi-microchip-core-qspi.
+config SPI_MICROCHIP_CORE_SPI
+ tristate "Microchip FPGA CoreSPI controller"
+ depends on SPI_MASTER
+ help
+ This enables the SPI driver for Microchip FPGA CoreSPI controller.
+ Say Y or M here if you want to use the "soft" controllers on
+ PolarFire SoC.
+ If built as a module, it will be called spi-microchip-core-spi.
+
config SPI_MT65XX
tristate "MediaTek SPI controller"
depends on ARCH_MEDIATEK || COMPILE_TEST
@@ -871,6 +872,16 @@ config SPI_PL022
controller. If you have an embedded system with an AMBA(R)
bus and a PL022 controller, say Y or M here.
+config SPI_POLARFIRE_SOC
+ tristate "Microchip FPGA SPI controllers"
+ depends on SPI_MASTER
+ depends on ARCH_MICROCHIP || COMPILE_TEST
+ help
+ This enables the SPI driver for Microchip FPGA SPI controllers.
+ Say Y or M here if you want to use the "hard" controllers on
+ PolarFire SoC.
+ If built as a module, it will be called spi-mpfs.
+
config SPI_PPC4xx
tristate "PPC4xx SPI Controller"
depends on PPC32 && 4xx
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 8ff74a13faaa..863b628ff1ec 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -86,8 +86,8 @@ obj-$(CONFIG_SPI_LOONGSON_PLATFORM) += spi-loongson-plat.o
obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o
obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o
obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
-obj-$(CONFIG_SPI_MICROCHIP_CORE) += spi-microchip-core.o
obj-$(CONFIG_SPI_MICROCHIP_CORE_QSPI) += spi-microchip-core-qspi.o
+obj-$(CONFIG_SPI_MICROCHIP_CORE_SPI) += spi-microchip-core-spi.o
obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
@@ -97,6 +97,7 @@ obj-$(CONFIG_SPI_MTK_NOR) += spi-mtk-nor.o
obj-$(CONFIG_SPI_MTK_SNFI) += spi-mtk-snfi.o
obj-$(CONFIG_SPI_MXIC) += spi-mxic.o
obj-$(CONFIG_SPI_MXS) += spi-mxs.o
+obj-$(CONFIG_SPI_POLARFIRE_SOC) += spi-mpfs.o
obj-$(CONFIG_SPI_WPCM_FIU) += spi-wpcm-fiu.o
obj-$(CONFIG_SPI_NPCM_FIU) += spi-npcm-fiu.o
obj-$(CONFIG_SPI_NPCM_PSPI) += spi-npcm-pspi.o
diff --git a/drivers/spi/spi-airoha-snfi.c b/drivers/spi/spi-airoha-snfi.c
index b78163eaed61..70327aebc26b 100644
--- a/drivers/spi/spi-airoha-snfi.c
+++ b/drivers/spi/spi-airoha-snfi.c
@@ -147,6 +147,8 @@
#define SPI_NFI_CUS_SEC_SIZE_EN BIT(16)
#define REG_SPI_NFI_RD_CTL2 0x0510
+#define SPI_NFI_DATA_READ_CMD GENMASK(7, 0)
+
#define REG_SPI_NFI_RD_CTL3 0x0514
#define REG_SPI_NFI_PG_CTL1 0x0524
@@ -179,7 +181,9 @@
#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE 0x03
#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST 0x0b
#define SPI_NAND_OP_READ_FROM_CACHE_DUAL 0x3b
+#define SPI_NAND_OP_READ_FROM_CACHE_DUALIO 0xbb
#define SPI_NAND_OP_READ_FROM_CACHE_QUAD 0x6b
+#define SPI_NAND_OP_READ_FROM_CACHE_QUADIO 0xeb
#define SPI_NAND_OP_WRITE_ENABLE 0x06
#define SPI_NAND_OP_WRITE_DISABLE 0x04
#define SPI_NAND_OP_PROGRAM_LOAD_SINGLE 0x02
@@ -219,13 +223,6 @@ struct airoha_snand_ctrl {
struct regmap *regmap_ctrl;
struct regmap *regmap_nfi;
struct clk *spi_clk;
-
- struct {
- size_t page_size;
- size_t sec_size;
- u8 sec_num;
- u8 spare_size;
- } nfi_cfg;
};
static int airoha_snand_set_fifo_op(struct airoha_snand_ctrl *as_ctrl,
@@ -486,92 +483,6 @@ static int airoha_snand_nfi_init(struct airoha_snand_ctrl *as_ctrl)
SPI_NFI_ALL_IRQ_EN, SPI_NFI_AHB_DONE_EN);
}
-static int airoha_snand_nfi_config(struct airoha_snand_ctrl *as_ctrl)
-{
- int err;
- u32 val;
-
- err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
- SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
- if (err)
- return err;
-
- /* auto FDM */
- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
- SPI_NFI_AUTO_FDM_EN);
- if (err)
- return err;
-
- /* HW ECC */
- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
- SPI_NFI_HW_ECC_EN);
- if (err)
- return err;
-
- /* DMA Burst */
- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
- SPI_NFI_DMA_BURST_EN);
- if (err)
- return err;
-
- /* page format */
- switch (as_ctrl->nfi_cfg.spare_size) {
- case 26:
- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x1);
- break;
- case 27:
- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x2);
- break;
- case 28:
- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x3);
- break;
- default:
- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x0);
- break;
- }
-
- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT,
- SPI_NFI_SPARE_SIZE, val);
- if (err)
- return err;
-
- switch (as_ctrl->nfi_cfg.page_size) {
- case 2048:
- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x1);
- break;
- case 4096:
- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x2);
- break;
- default:
- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x0);
- break;
- }
-
- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT,
- SPI_NFI_PAGE_SIZE, val);
- if (err)
- return err;
-
- /* sec num */
- val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
- SPI_NFI_SEC_NUM, val);
- if (err)
- return err;
-
- /* enable cust sec size */
- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
- SPI_NFI_CUS_SEC_SIZE_EN);
- if (err)
- return err;
-
- /* set cust sec size */
- val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, as_ctrl->nfi_cfg.sec_size);
- return regmap_update_bits(as_ctrl->regmap_nfi,
- REG_SPI_NFI_SECCUS_SIZE,
- SPI_NFI_CUS_SEC_SIZE, val);
-}
-
static bool airoha_snand_is_page_ops(const struct spi_mem_op *op)
{
if (op->addr.nbytes != 2)
@@ -604,33 +515,6 @@ static bool airoha_snand_is_page_ops(const struct spi_mem_op *op)
}
}
-static int airoha_snand_adjust_op_size(struct spi_mem *mem,
- struct spi_mem_op *op)
-{
- size_t max_len;
-
- if (airoha_snand_is_page_ops(op)) {
- struct airoha_snand_ctrl *as_ctrl;
-
- as_ctrl = spi_controller_get_devdata(mem->spi->controller);
- max_len = as_ctrl->nfi_cfg.sec_size;
- max_len += as_ctrl->nfi_cfg.spare_size;
- max_len *= as_ctrl->nfi_cfg.sec_num;
-
- if (op->data.nbytes > max_len)
- op->data.nbytes = max_len;
- } else {
- max_len = 1 + op->addr.nbytes + op->dummy.nbytes;
- if (max_len >= 160)
- return -EOPNOTSUPP;
-
- if (op->data.nbytes > 160 - max_len)
- op->data.nbytes = 160 - max_len;
- }
-
- return 0;
-}
-
static bool airoha_snand_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
@@ -671,32 +555,89 @@ static int airoha_snand_dirmap_create(struct spi_mem_dirmap_desc *desc)
static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
- struct spi_mem_op *op = &desc->info.op_tmpl;
struct spi_device *spi = desc->mem->spi;
struct airoha_snand_ctrl *as_ctrl;
u8 *txrx_buf = spi_get_ctldata(spi);
dma_addr_t dma_addr;
- u32 val, rd_mode;
+ u32 val, rd_mode, opcode;
+ size_t bytes;
int err;
- switch (op->cmd.opcode) {
+ as_ctrl = spi_controller_get_devdata(spi->controller);
+
+ /* minimum oob size is 64 */
+ bytes = round_up(offs + len, 64);
+
+ /*
+ * DUALIO and QUADIO opcodes are not supported by the spi controller,
+ * replace them with supported opcodes.
+ */
+ opcode = desc->info.op_tmpl.cmd.opcode;
+ switch (opcode) {
+ case SPI_NAND_OP_READ_FROM_CACHE_SINGLE:
+ case SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST:
+ rd_mode = 0;
+ break;
case SPI_NAND_OP_READ_FROM_CACHE_DUAL:
+ case SPI_NAND_OP_READ_FROM_CACHE_DUALIO:
+ opcode = SPI_NAND_OP_READ_FROM_CACHE_DUAL;
rd_mode = 1;
break;
case SPI_NAND_OP_READ_FROM_CACHE_QUAD:
+ case SPI_NAND_OP_READ_FROM_CACHE_QUADIO:
+ opcode = SPI_NAND_OP_READ_FROM_CACHE_QUAD;
rd_mode = 2;
break;
default:
- rd_mode = 0;
- break;
+ /* unknown opcode */
+ return -EOPNOTSUPP;
}
- as_ctrl = spi_controller_get_devdata(spi->controller);
err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
if (err < 0)
return err;
- err = airoha_snand_nfi_config(as_ctrl);
+ /* NFI reset */
+ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+ SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
+ if (err)
+ goto error_dma_mode_off;
+
+ /* NFI configure:
+ * - No AutoFDM (custom sector size (SECCUS) register will be used)
+ * - No SoC's hardware ECC (flash internal ECC will be used)
+ * - Use burst mode (faster, but requires 16 byte alignment for addresses)
+ * - Setup for reading (SPI_NFI_READ_MODE)
+ * - Setup reading command: FIELD_PREP(SPI_NFI_OPMODE, 6)
+ * - Use DMA instead of PIO for data reading
+ */
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
+ SPI_NFI_DMA_MODE |
+ SPI_NFI_READ_MODE |
+ SPI_NFI_DMA_BURST_EN |
+ SPI_NFI_HW_ECC_EN |
+ SPI_NFI_AUTO_FDM_EN |
+ SPI_NFI_OPMODE,
+ SPI_NFI_DMA_MODE |
+ SPI_NFI_READ_MODE |
+ SPI_NFI_DMA_BURST_EN |
+ FIELD_PREP(SPI_NFI_OPMODE, 6));
+ if (err)
+ goto error_dma_mode_off;
+
+ /* Set number of sector will be read */
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+ SPI_NFI_SEC_NUM,
+ FIELD_PREP(SPI_NFI_SEC_NUM, 1));
+ if (err)
+ goto error_dma_mode_off;
+
+ /* Set custom sector size */
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
+ SPI_NFI_CUS_SEC_SIZE |
+ SPI_NFI_CUS_SEC_SIZE_EN,
+ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) |
+ SPI_NFI_CUS_SEC_SIZE_EN);
if (err)
goto error_dma_mode_off;
@@ -712,18 +653,24 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
if (err)
goto error_dma_unmap;
- /* set cust sec size */
- val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num;
- val = FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, val);
+ /*
+ * Setup transfer length
+ * ---------------------
+ * The following rule MUST be met:
+ * transfer_length =
+ * = NFI_SNF_MISC_CTL2.read_data_byte_number =
+ * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
+ */
err = regmap_update_bits(as_ctrl->regmap_nfi,
REG_SPI_NFI_SNF_MISC_CTL2,
- SPI_NFI_READ_DATA_BYTE_NUM, val);
+ SPI_NFI_READ_DATA_BYTE_NUM,
+ FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, bytes));
if (err)
goto error_dma_unmap;
/* set read command */
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL2,
- op->cmd.opcode);
+ FIELD_PREP(SPI_NFI_DATA_READ_CMD, opcode));
if (err)
goto error_dma_unmap;
@@ -739,23 +686,11 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
if (err)
goto error_dma_unmap;
- /* set nfi read */
- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
- SPI_NFI_OPMODE,
- FIELD_PREP(SPI_NFI_OPMODE, 6));
- if (err)
- goto error_dma_unmap;
-
- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
- SPI_NFI_READ_MODE | SPI_NFI_DMA_MODE);
- if (err)
- goto error_dma_unmap;
-
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x0);
if (err)
goto error_dma_unmap;
- /* trigger dma start read */
+ /* trigger dma reading */
err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
SPI_NFI_RD_TRIG);
if (err)
@@ -813,59 +748,122 @@ error_dma_mode_off:
static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf)
{
- struct spi_mem_op *op = &desc->info.op_tmpl;
struct spi_device *spi = desc->mem->spi;
u8 *txrx_buf = spi_get_ctldata(spi);
struct airoha_snand_ctrl *as_ctrl;
dma_addr_t dma_addr;
- u32 wr_mode, val;
+ u32 wr_mode, val, opcode;
+ size_t bytes;
int err;
as_ctrl = spi_controller_get_devdata(spi->controller);
- err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
- if (err < 0)
- return err;
+ /* minimum oob size is 64 */
+ bytes = round_up(offs + len, 64);
+
+ opcode = desc->info.op_tmpl.cmd.opcode;
+ switch (opcode) {
+ case SPI_NAND_OP_PROGRAM_LOAD_SINGLE:
+ case SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE:
+ wr_mode = 0;
+ break;
+ case SPI_NAND_OP_PROGRAM_LOAD_QUAD:
+ case SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD:
+ wr_mode = 2;
+ break;
+ default:
+ /* unknown opcode */
+ return -EOPNOTSUPP;
+ }
+
+ if (offs > 0)
+ memset(txrx_buf, 0xff, offs);
memcpy(txrx_buf + offs, buf, len);
- dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
- DMA_TO_DEVICE);
- err = dma_mapping_error(as_ctrl->dev, dma_addr);
- if (err)
- return err;
+ if (bytes > offs + len)
+ memset(txrx_buf + offs + len, 0xff, bytes - offs - len);
err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
if (err < 0)
- goto error_dma_unmap;
+ return err;
+
+ /* NFI reset */
+ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+ SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
+ if (err)
+ goto error_dma_mode_off;
- err = airoha_snand_nfi_config(as_ctrl);
+ /*
+ * NFI configure:
+ * - No AutoFDM (custom sector size (SECCUS) register will be used)
+ * - No SoC's hardware ECC (flash internal ECC will be used)
+ * - Use burst mode (faster, but requires 16 byte alignment for addresses)
+ * - Setup for writing (SPI_NFI_READ_MODE bit is cleared)
+ * - Setup writing command: FIELD_PREP(SPI_NFI_OPMODE, 3)
+ * - Use DMA instead of PIO for data writing
+ */
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
+ SPI_NFI_DMA_MODE |
+ SPI_NFI_READ_MODE |
+ SPI_NFI_DMA_BURST_EN |
+ SPI_NFI_HW_ECC_EN |
+ SPI_NFI_AUTO_FDM_EN |
+ SPI_NFI_OPMODE,
+ SPI_NFI_DMA_MODE |
+ SPI_NFI_DMA_BURST_EN |
+ FIELD_PREP(SPI_NFI_OPMODE, 3));
if (err)
- goto error_dma_unmap;
+ goto error_dma_mode_off;
- if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD ||
- op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD)
- wr_mode = BIT(1);
- else
- wr_mode = 0;
+ /* Set number of sector will be written */
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+ SPI_NFI_SEC_NUM,
+ FIELD_PREP(SPI_NFI_SEC_NUM, 1));
+ if (err)
+ goto error_dma_mode_off;
+
+ /* Set custom sector size */
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
+ SPI_NFI_CUS_SEC_SIZE |
+ SPI_NFI_CUS_SEC_SIZE_EN,
+ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) |
+ SPI_NFI_CUS_SEC_SIZE_EN);
+ if (err)
+ goto error_dma_mode_off;
+
+ dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
+ DMA_TO_DEVICE);
+ err = dma_mapping_error(as_ctrl->dev, dma_addr);
+ if (err)
+ goto error_dma_mode_off;
+ /* set dma addr */
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR,
dma_addr);
if (err)
goto error_dma_unmap;
- val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM,
- as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num);
+ /*
+ * Setup transfer length
+ * ---------------------
+ * The following rule MUST be met:
+ * transfer_length =
+ * = NFI_SNF_MISC_CTL2.write_data_byte_number =
+ * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
+ */
err = regmap_update_bits(as_ctrl->regmap_nfi,
REG_SPI_NFI_SNF_MISC_CTL2,
- SPI_NFI_PROG_LOAD_BYTE_NUM, val);
+ SPI_NFI_PROG_LOAD_BYTE_NUM,
+ FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, bytes));
if (err)
goto error_dma_unmap;
+ /* set write command */
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1,
- FIELD_PREP(SPI_NFI_PG_LOAD_CMD,
- op->cmd.opcode));
+ FIELD_PREP(SPI_NFI_PG_LOAD_CMD, opcode));
if (err)
goto error_dma_unmap;
+ /* set write mode */
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL,
FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode));
if (err)
@@ -877,26 +875,11 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
if (err)
goto error_dma_unmap;
- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
- SPI_NFI_READ_MODE);
- if (err)
- goto error_dma_unmap;
-
- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
- SPI_NFI_OPMODE,
- FIELD_PREP(SPI_NFI_OPMODE, 3));
- if (err)
- goto error_dma_unmap;
-
- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
- SPI_NFI_DMA_MODE);
- if (err)
- goto error_dma_unmap;
-
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x80);
if (err)
goto error_dma_unmap;
+ /* trigger dma writing */
err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
SPI_NFI_WR_TRIG);
if (err)
@@ -941,6 +924,7 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
error_dma_unmap:
dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE,
DMA_TO_DEVICE);
+error_dma_mode_off:
airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
return err;
}
@@ -1022,7 +1006,6 @@ static int airoha_snand_exec_op(struct spi_mem *mem,
}
static const struct spi_controller_mem_ops airoha_snand_mem_ops = {
- .adjust_op_size = airoha_snand_adjust_op_size,
.supports_op = airoha_snand_supports_op,
.exec_op = airoha_snand_exec_op,
.dirmap_create = airoha_snand_dirmap_create,
@@ -1030,6 +1013,11 @@ static const struct spi_controller_mem_ops airoha_snand_mem_ops = {
.dirmap_write = airoha_snand_dirmap_write,
};
+static const struct spi_controller_mem_ops airoha_snand_nodma_mem_ops = {
+ .supports_op = airoha_snand_supports_op,
+ .exec_op = airoha_snand_exec_op,
+};
+
static int airoha_snand_setup(struct spi_device *spi)
{
struct airoha_snand_ctrl *as_ctrl;
@@ -1047,36 +1035,6 @@ static int airoha_snand_setup(struct spi_device *spi)
return 0;
}
-static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl)
-{
- u32 val, sec_size, sec_num;
- int err;
-
- err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, &val);
- if (err)
- return err;
-
- sec_num = FIELD_GET(SPI_NFI_SEC_NUM, val);
-
- err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, &val);
- if (err)
- return err;
-
- sec_size = FIELD_GET(SPI_NFI_CUS_SEC_SIZE, val);
-
- /* init default value */
- as_ctrl->nfi_cfg.sec_size = sec_size;
- as_ctrl->nfi_cfg.sec_num = sec_num;
- as_ctrl->nfi_cfg.page_size = round_down(sec_size * sec_num, 1024);
- as_ctrl->nfi_cfg.spare_size = 16;
-
- err = airoha_snand_nfi_init(as_ctrl);
- if (err)
- return err;
-
- return airoha_snand_nfi_config(as_ctrl);
-}
-
static const struct regmap_config spi_ctrl_regmap_config = {
.name = "ctrl",
.reg_bits = 32,
@@ -1104,7 +1062,9 @@ static int airoha_snand_probe(struct platform_device *pdev)
struct airoha_snand_ctrl *as_ctrl;
struct device *dev = &pdev->dev;
struct spi_controller *ctrl;
+ bool dma_enable = true;
void __iomem *base;
+ u32 sfc_strap;
int err;
ctrl = devm_spi_alloc_host(dev, sizeof(*as_ctrl));
@@ -1139,18 +1099,34 @@ static int airoha_snand_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(as_ctrl->spi_clk),
"unable to get spi clk\n");
+ if (device_is_compatible(dev, "airoha,en7523-snand")) {
+ err = regmap_read(as_ctrl->regmap_ctrl,
+ REG_SPI_CTRL_SFC_STRAP, &sfc_strap);
+ if (err)
+ return err;
+
+ if (!(sfc_strap & 0x04)) {
+ dma_enable = false;
+ dev_warn(dev, "Detected booting in RESERVED mode (UART_TXD was short to GND).\n");
+ dev_warn(dev, "This mode is known for incorrect DMA reading of some flashes.\n");
+ dev_warn(dev, "Much slower PIO mode will be used to prevent flash data damage.\n");
+ dev_warn(dev, "Unplug UART cable and power cycle board to get full performance.\n");
+ }
+ }
+
err = dma_set_mask(as_ctrl->dev, DMA_BIT_MASK(32));
if (err)
return err;
ctrl->num_chipselect = 2;
- ctrl->mem_ops = &airoha_snand_mem_ops;
+ ctrl->mem_ops = dma_enable ? &airoha_snand_mem_ops
+ : &airoha_snand_nodma_mem_ops;
ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
ctrl->mode_bits = SPI_RX_DUAL;
ctrl->setup = airoha_snand_setup;
device_set_node(&ctrl->dev, dev_fwnode(dev));
- err = airoha_snand_nfi_setup(as_ctrl);
+ err = airoha_snand_nfi_init(as_ctrl);
if (err)
return err;
diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index 62a11142bd63..db3e096f2eb0 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -7,6 +7,7 @@
*/
#include <linux/clk.h>
+#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@@ -67,6 +68,7 @@ struct aspeed_spi_chip {
u32 ahb_window_size;
u32 ctl_val[ASPEED_SPI_MAX];
u32 clk_freq;
+ bool force_user_mode;
};
struct aspeed_spi_data {
@@ -78,10 +80,14 @@ struct aspeed_spi_data {
u32 timing;
u32 hclk_mask;
u32 hdiv_max;
-
- u32 (*segment_start)(struct aspeed_spi *aspi, u32 reg);
- u32 (*segment_end)(struct aspeed_spi *aspi, u32 reg);
- u32 (*segment_reg)(struct aspeed_spi *aspi, u32 start, u32 end);
+ u32 min_window_size;
+
+ phys_addr_t (*segment_start)(struct aspeed_spi *aspi, u32 reg);
+ phys_addr_t (*segment_end)(struct aspeed_spi *aspi, u32 reg);
+ u32 (*segment_reg)(struct aspeed_spi *aspi, phys_addr_t start,
+ phys_addr_t end);
+ int (*adjust_window)(struct aspeed_spi *aspi);
+ u32 (*get_clk_div)(struct aspeed_spi_chip *chip, u32 hz);
int (*calibrate)(struct aspeed_spi_chip *chip, u32 hdiv,
const u8 *golden_buf, u8 *test_buf);
};
@@ -92,9 +98,9 @@ struct aspeed_spi {
const struct aspeed_spi_data *data;
void __iomem *regs;
- void __iomem *ahb_base;
- u32 ahb_base_phy;
+ phys_addr_t ahb_base_phy;
u32 ahb_window_size;
+ u32 num_cs;
struct device *dev;
struct clk *clk;
@@ -258,11 +264,15 @@ static ssize_t aspeed_spi_write_user(struct aspeed_spi_chip *chip,
const struct spi_mem_op *op)
{
int ret;
+ int io_mode = aspeed_spi_get_io_mode(op);
aspeed_spi_start_user(chip);
ret = aspeed_spi_send_cmd_addr(chip, op->addr.nbytes, op->addr.val, op->cmd.opcode);
if (ret < 0)
goto stop_user;
+
+ aspeed_spi_set_io_mode(chip, io_mode);
+
aspeed_spi_write_to_ahb(chip->ahb_base, op->data.buf.out, op->data.nbytes);
stop_user:
aspeed_spi_stop_user(chip);
@@ -376,89 +386,270 @@ static const char *aspeed_spi_get_name(struct spi_mem *mem)
spi_get_chipselect(mem->spi, 0));
}
-struct aspeed_spi_window {
+static int aspeed_spi_set_window(struct aspeed_spi *aspi)
+{
+ struct device *dev = aspi->dev;
+ off_t offset = 0;
+ phys_addr_t start;
+ phys_addr_t end;
+ void __iomem *seg_reg_base = aspi->regs + CE0_SEGMENT_ADDR_REG;
+ void __iomem *seg_reg;
+ u32 seg_val_backup;
+ u32 seg_val;
u32 cs;
- u32 offset;
- u32 size;
-};
+ size_t window_size;
+
+ for (cs = 0; cs < aspi->data->max_cs; cs++) {
+ if (aspi->chips[cs].ahb_base) {
+ devm_iounmap(dev, aspi->chips[cs].ahb_base);
+ aspi->chips[cs].ahb_base = NULL;
+ }
+ }
+
+ for (cs = 0; cs < aspi->data->max_cs; cs++) {
+ seg_reg = seg_reg_base + cs * 4;
+ seg_val_backup = readl(seg_reg);
+
+ start = aspi->ahb_base_phy + offset;
+ window_size = aspi->chips[cs].ahb_window_size;
+ end = start + window_size;
+
+ seg_val = aspi->data->segment_reg(aspi, start, end);
+ writel(seg_val, seg_reg);
+
+ /*
+ * Restore initial value if something goes wrong or the segment
+ * register is written protected.
+ */
+ if (seg_val != readl(seg_reg)) {
+ dev_warn(dev, "CE%d expected window [ 0x%.9llx - 0x%.9llx ] %zdMB\n",
+ cs, (u64)start, (u64)end - 1, window_size >> 20);
+ writel(seg_val_backup, seg_reg);
+ window_size = aspi->data->segment_end(aspi, seg_val_backup) -
+ aspi->data->segment_start(aspi, seg_val_backup);
+ aspi->chips[cs].ahb_window_size = window_size;
+ end = start + window_size;
+ }
+
+ if (window_size != 0)
+ dev_dbg(dev, "CE%d window [ 0x%.9llx - 0x%.9llx ] %zdMB\n",
+ cs, (u64)start, (u64)end - 1, window_size >> 20);
+ else
+ dev_dbg(dev, "CE%d window closed\n", cs);
+
+ offset += window_size;
+ if (offset > aspi->ahb_window_size) {
+ dev_err(dev, "CE%d offset value 0x%llx is too large.\n",
+ cs, (u64)offset);
+ return -ENOSPC;
+ }
+
+ /*
+ * No need to map the address deocding range when
+ * - window size is 0.
+ * - the CS is unused.
+ */
+ if (window_size == 0 || cs >= aspi->num_cs)
+ continue;
+
+ aspi->chips[cs].ahb_base =
+ devm_ioremap(aspi->dev, start, window_size);
+ if (!aspi->chips[cs].ahb_base) {
+ dev_err(aspi->dev,
+ "Fail to remap window [0x%.9llx - 0x%.9llx]\n",
+ (u64)start, (u64)end - 1);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static const struct aspeed_spi_data ast2500_spi_data;
+static const struct aspeed_spi_data ast2600_spi_data;
+static const struct aspeed_spi_data ast2600_fmc_data;
-static void aspeed_spi_get_windows(struct aspeed_spi *aspi,
- struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS])
+static int aspeed_spi_chip_set_default_window(struct aspeed_spi *aspi)
{
- const struct aspeed_spi_data *data = aspi->data;
- u32 reg_val;
u32 cs;
- for (cs = 0; cs < aspi->data->max_cs; cs++) {
- reg_val = readl(aspi->regs + CE0_SEGMENT_ADDR_REG + cs * 4);
- windows[cs].cs = cs;
- windows[cs].size = data->segment_end(aspi, reg_val) -
- data->segment_start(aspi, reg_val);
- windows[cs].offset = data->segment_start(aspi, reg_val) - aspi->ahb_base_phy;
- dev_vdbg(aspi->dev, "CE%d offset=0x%.8x size=0x%x\n", cs,
- windows[cs].offset, windows[cs].size);
+ /* No segment registers for the AST2400 SPI controller */
+ if (aspi->data == &ast2400_spi_data) {
+ aspi->chips[0].ahb_base = devm_ioremap(aspi->dev,
+ aspi->ahb_base_phy,
+ aspi->ahb_window_size);
+ aspi->chips[0].ahb_window_size = aspi->ahb_window_size;
+ return 0;
}
+
+ /* Assign the minimum window size to each CS */
+ for (cs = 0; cs < aspi->num_cs; cs++) {
+ aspi->chips[cs].ahb_window_size = aspi->data->min_window_size;
+ dev_dbg(aspi->dev, "CE%d default window [ 0x%.9llx - 0x%.9llx ]",
+ cs, (u64)(aspi->ahb_base_phy + aspi->data->min_window_size * cs),
+ (u64)(aspi->ahb_base_phy + aspi->data->min_window_size * cs - 1));
+ }
+
+ /* Close unused CS */
+ for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++)
+ aspi->chips[cs].ahb_window_size = 0;
+
+ if (aspi->data->adjust_window)
+ aspi->data->adjust_window(aspi);
+
+ return aspeed_spi_set_window(aspi);
}
/*
- * On the AST2600, some CE windows are closed by default at reset but
- * U-Boot should open all.
+ * As the flash size grows up, we need to trim some decoding
+ * size if needed for the sake of conforming the maximum
+ * decoding size. We trim the decoding size from the rear CS
+ * to avoid affecting the default boot up sequence, usually,
+ * from CS0. Notice, if a CS decoding size is trimmed,
+ * command mode may not work perfectly on that CS, but it only
+ * affect performance and the debug function.
*/
-static int aspeed_spi_chip_set_default_window(struct aspeed_spi_chip *chip)
+static int aspeed_spi_trim_window_size(struct aspeed_spi *aspi)
{
- struct aspeed_spi *aspi = chip->aspi;
- struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS] = { 0 };
- struct aspeed_spi_window *win = &windows[chip->cs];
+ struct aspeed_spi_chip *chips = aspi->chips;
+ size_t total_sz;
+ int cs = aspi->data->max_cs - 1;
+ u32 i;
+ bool trimmed = false;
+
+ do {
+ total_sz = 0;
+ for (i = 0; i < aspi->data->max_cs; i++)
+ total_sz += chips[i].ahb_window_size;
+
+ if (cs < 0)
+ return -ENOMEM;
+
+ if (chips[cs].ahb_window_size <= aspi->data->min_window_size) {
+ cs--;
+ continue;
+ }
- /* No segment registers for the AST2400 SPI controller */
- if (aspi->data == &ast2400_spi_data) {
- win->offset = 0;
- win->size = aspi->ahb_window_size;
- } else {
- aspeed_spi_get_windows(aspi, windows);
+ if (total_sz > aspi->ahb_window_size) {
+ chips[cs].ahb_window_size -=
+ aspi->data->min_window_size;
+ total_sz -= aspi->data->min_window_size;
+ /*
+ * If the ahb window size is ever trimmed, only user
+ * mode can be adopted to access the whole flash.
+ */
+ chips[cs].force_user_mode = true;
+ trimmed = true;
+ }
+ } while (total_sz > aspi->ahb_window_size);
+
+ if (trimmed) {
+ dev_warn(aspi->dev, "Window size after trimming:\n");
+ for (cs = 0; cs < aspi->data->max_cs; cs++) {
+ dev_warn(aspi->dev, "CE%d: 0x%08x\n",
+ cs, chips[cs].ahb_window_size);
+ }
}
- chip->ahb_base = aspi->ahb_base + win->offset;
- chip->ahb_window_size = win->size;
+ return 0;
+}
+
+static int aspeed_adjust_window_ast2400(struct aspeed_spi *aspi)
+{
+ int ret;
+ int cs;
+ struct aspeed_spi_chip *chips = aspi->chips;
+
+ /* Close unused CS. */
+ for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++)
+ chips[cs].ahb_window_size = 0;
- dev_dbg(aspi->dev, "CE%d default window [ 0x%.8x - 0x%.8x ] %dMB",
- chip->cs, aspi->ahb_base_phy + win->offset,
- aspi->ahb_base_phy + win->offset + win->size - 1,
- win->size >> 20);
+ ret = aspeed_spi_trim_window_size(aspi);
+ if (ret != 0)
+ return ret;
- return chip->ahb_window_size ? 0 : -1;
+ return 0;
}
-static int aspeed_spi_set_window(struct aspeed_spi *aspi,
- const struct aspeed_spi_window *win)
+/*
+ * For AST2500, the minimum address decoding size for each CS
+ * is 8MB. This address decoding size is mandatory for each
+ * CS no matter whether it will be used. This is a HW limitation.
+ */
+static int aspeed_adjust_window_ast2500(struct aspeed_spi *aspi)
{
- u32 start = aspi->ahb_base_phy + win->offset;
- u32 end = start + win->size;
- void __iomem *seg_reg = aspi->regs + CE0_SEGMENT_ADDR_REG + win->cs * 4;
- u32 seg_val_backup = readl(seg_reg);
- u32 seg_val = aspi->data->segment_reg(aspi, start, end);
+ int ret;
+ int cs, i;
+ u32 cum_size, rem_size;
+ struct aspeed_spi_chip *chips = aspi->chips;
+
+ /* Assign min_window_sz to unused CS. */
+ for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++) {
+ if (chips[cs].ahb_window_size < aspi->data->min_window_size)
+ chips[cs].ahb_window_size =
+ aspi->data->min_window_size;
+ }
- if (seg_val == seg_val_backup)
- return 0;
+ /*
+ * If command mode or normal mode is used by dirmap read, the start
+ * address of a window should be multiple of its related flash size.
+ * Namely, the total windows size from flash 0 to flash N should
+ * be multiple of the size of flash (N + 1).
+ */
+ for (cs = aspi->num_cs - 1; cs >= 0; cs--) {
+ cum_size = 0;
+ for (i = 0; i < cs; i++)
+ cum_size += chips[i].ahb_window_size;
+
+ rem_size = cum_size % chips[cs].ahb_window_size;
+ if (chips[cs].ahb_window_size != 0 && rem_size != 0)
+ chips[0].ahb_window_size +=
+ chips[cs].ahb_window_size - rem_size;
+ }
+
+ ret = aspeed_spi_trim_window_size(aspi);
+ if (ret != 0)
+ return ret;
+
+ /* The total window size of AST2500 SPI1 CS0 and CS1 must be 128MB */
+ if (aspi->data == &ast2500_spi_data)
+ chips[1].ahb_window_size =
+ 0x08000000 - chips[0].ahb_window_size;
+
+ return 0;
+}
+
+static int aspeed_adjust_window_ast2600(struct aspeed_spi *aspi)
+{
+ int ret;
+ int cs, i;
+ u32 cum_size, rem_size;
+ struct aspeed_spi_chip *chips = aspi->chips;
- writel(seg_val, seg_reg);
+ /* Close unused CS. */
+ for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++)
+ chips[cs].ahb_window_size = 0;
/*
- * Restore initial value if something goes wrong else we could
- * loose access to the chip.
+ * If command mode or normal mode is used by dirmap read, the start
+ * address of a window should be multiple of its related flash size.
+ * Namely, the total windows size from flash 0 to flash N should
+ * be multiple of the size of flash (N + 1).
*/
- if (seg_val != readl(seg_reg)) {
- dev_err(aspi->dev, "CE%d invalid window [ 0x%.8x - 0x%.8x ] %dMB",
- win->cs, start, end - 1, win->size >> 20);
- writel(seg_val_backup, seg_reg);
- return -EIO;
+ for (cs = aspi->num_cs - 1; cs >= 0; cs--) {
+ cum_size = 0;
+ for (i = 0; i < cs; i++)
+ cum_size += chips[i].ahb_window_size;
+
+ rem_size = cum_size % chips[cs].ahb_window_size;
+ if (chips[cs].ahb_window_size != 0 && rem_size != 0)
+ chips[0].ahb_window_size +=
+ chips[cs].ahb_window_size - rem_size;
}
- if (win->size)
- dev_dbg(aspi->dev, "CE%d new window [ 0x%.8x - 0x%.8x ] %dMB",
- win->cs, start, end - 1, win->size >> 20);
- else
- dev_dbg(aspi->dev, "CE%d window closed", win->cs);
+ ret = aspeed_spi_trim_window_size(aspi);
+ if (ret != 0)
+ return ret;
return 0;
}
@@ -469,78 +660,27 @@ static int aspeed_spi_set_window(struct aspeed_spi *aspi,
* - ioremap each window, not strictly necessary since the overall window
* is correct.
*/
-static const struct aspeed_spi_data ast2500_spi_data;
-static const struct aspeed_spi_data ast2600_spi_data;
-static const struct aspeed_spi_data ast2600_fmc_data;
-
static int aspeed_spi_chip_adjust_window(struct aspeed_spi_chip *chip,
u32 local_offset, u32 size)
{
struct aspeed_spi *aspi = chip->aspi;
- struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS] = { 0 };
- struct aspeed_spi_window *win = &windows[chip->cs];
int ret;
/* No segment registers for the AST2400 SPI controller */
if (aspi->data == &ast2400_spi_data)
return 0;
- /*
- * Due to an HW issue on the AST2500 SPI controller, the CE0
- * window size should be smaller than the maximum 128MB.
- */
- if (aspi->data == &ast2500_spi_data && chip->cs == 0 && size == SZ_128M) {
- size = 120 << 20;
- dev_info(aspi->dev, "CE%d window resized to %dMB (AST2500 HW quirk)",
- chip->cs, size >> 20);
- }
-
- /*
- * The decoding size of AST2600 SPI controller should set at
- * least 2MB.
- */
- if ((aspi->data == &ast2600_spi_data || aspi->data == &ast2600_fmc_data) &&
- size < SZ_2M) {
- size = SZ_2M;
- dev_info(aspi->dev, "CE%d window resized to %dMB (AST2600 Decoding)",
- chip->cs, size >> 20);
- }
-
- aspeed_spi_get_windows(aspi, windows);
-
/* Adjust this chip window */
- win->offset += local_offset;
- win->size = size;
+ aspi->chips[chip->cs].ahb_window_size = size;
- if (win->offset + win->size > aspi->ahb_window_size) {
- win->size = aspi->ahb_window_size - win->offset;
- dev_warn(aspi->dev, "CE%d window resized to %dMB", chip->cs, win->size >> 20);
- }
+ /* Adjust the overall windows size regarding each platform */
+ if (aspi->data->adjust_window)
+ aspi->data->adjust_window(aspi);
- ret = aspeed_spi_set_window(aspi, win);
+ ret = aspeed_spi_set_window(aspi);
if (ret)
return ret;
- /* Update chip mapping info */
- chip->ahb_base = aspi->ahb_base + win->offset;
- chip->ahb_window_size = win->size;
-
- /*
- * Also adjust next chip window to make sure that it does not
- * overlap with the current window.
- */
- if (chip->cs < aspi->data->max_cs - 1) {
- struct aspeed_spi_window *next = &windows[chip->cs + 1];
-
- /* Change offset and size to keep the same end address */
- if ((next->offset + next->size) > (win->offset + win->size))
- next->size = (next->offset + next->size) - (win->offset + win->size);
- else
- next->size = 0;
- next->offset = win->offset + win->size;
-
- aspeed_spi_set_window(aspi, next);
- }
return 0;
}
@@ -619,7 +759,7 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc,
struct aspeed_spi_chip *chip = &aspi->chips[spi_get_chipselect(desc->mem->spi, 0)];
/* Switch to USER command mode if mapping window is too small */
- if (chip->ahb_window_size < offset + len) {
+ if (chip->ahb_window_size < offset + len || chip->force_user_mode) {
int ret;
ret = aspeed_spi_read_user(chip, &desc->info.op_tmpl, offset, len, buf);
@@ -677,11 +817,6 @@ static int aspeed_spi_setup(struct spi_device *spi)
if (data->hastype)
aspeed_spi_chip_set_type(aspi, cs, CONFIG_TYPE_SPI);
- if (aspeed_spi_chip_set_default_window(chip) < 0) {
- dev_warn(aspi->dev, "CE%d window invalid", cs);
- return -EINVAL;
- }
-
aspeed_spi_chip_enable(aspi, cs, true);
chip->ctl_val[ASPEED_SPI_BASE] = CTRL_CE_STOP_ACTIVE | CTRL_IO_MODE_USER;
@@ -734,10 +869,10 @@ static int aspeed_spi_probe(struct platform_device *pdev)
if (IS_ERR(aspi->regs))
return PTR_ERR(aspi->regs);
- aspi->ahb_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
- if (IS_ERR(aspi->ahb_base)) {
- dev_err(dev, "missing AHB mapping window\n");
- return PTR_ERR(aspi->ahb_base);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(dev, "missing AHB memory\n");
+ return -EINVAL;
}
aspi->ahb_window_size = resource_size(res);
@@ -762,9 +897,17 @@ static int aspeed_spi_probe(struct platform_device *pdev)
ctlr->mem_ops = &aspeed_spi_mem_ops;
ctlr->setup = aspeed_spi_setup;
ctlr->cleanup = aspeed_spi_cleanup;
- ctlr->num_chipselect = data->max_cs;
+ ctlr->num_chipselect = of_get_available_child_count(dev->of_node);
ctlr->dev.of_node = dev->of_node;
+ aspi->num_cs = ctlr->num_chipselect;
+
+ ret = aspeed_spi_chip_set_default_window(aspi);
+ if (ret) {
+ dev_err(&pdev->dev, "fail to set default window\n");
+ return ret;
+ }
+
ret = devm_spi_register_controller(dev, ctlr);
if (ret)
dev_err(&pdev->dev, "spi_register_controller failed\n");
@@ -788,17 +931,18 @@ static void aspeed_spi_remove(struct platform_device *pdev)
* The address range is encoded with absolute addresses in the overall
* mapping window.
*/
-static u32 aspeed_spi_segment_start(struct aspeed_spi *aspi, u32 reg)
+static phys_addr_t aspeed_spi_segment_start(struct aspeed_spi *aspi, u32 reg)
{
return ((reg >> 16) & 0xFF) << 23;
}
-static u32 aspeed_spi_segment_end(struct aspeed_spi *aspi, u32 reg)
+static phys_addr_t aspeed_spi_segment_end(struct aspeed_spi *aspi, u32 reg)
{
return ((reg >> 24) & 0xFF) << 23;
}
-static u32 aspeed_spi_segment_reg(struct aspeed_spi *aspi, u32 start, u32 end)
+static u32 aspeed_spi_segment_reg(struct aspeed_spi *aspi,
+ phys_addr_t start, phys_addr_t end)
{
return (((start >> 23) & 0xFF) << 16) | (((end >> 23) & 0xFF) << 24);
}
@@ -810,16 +954,16 @@ static u32 aspeed_spi_segment_reg(struct aspeed_spi *aspi, u32 start, u32 end)
#define AST2600_SEG_ADDR_MASK 0x0ff00000
-static u32 aspeed_spi_segment_ast2600_start(struct aspeed_spi *aspi,
- u32 reg)
+static phys_addr_t aspeed_spi_segment_ast2600_start(struct aspeed_spi *aspi,
+ u32 reg)
{
u32 start_offset = (reg << 16) & AST2600_SEG_ADDR_MASK;
return aspi->ahb_base_phy + start_offset;
}
-static u32 aspeed_spi_segment_ast2600_end(struct aspeed_spi *aspi,
- u32 reg)
+static phys_addr_t aspeed_spi_segment_ast2600_end(struct aspeed_spi *aspi,
+ u32 reg)
{
u32 end_offset = reg & AST2600_SEG_ADDR_MASK;
@@ -831,7 +975,7 @@ static u32 aspeed_spi_segment_ast2600_end(struct aspeed_spi *aspi,
}
static u32 aspeed_spi_segment_ast2600_reg(struct aspeed_spi *aspi,
- u32 start, u32 end)
+ phys_addr_t start, phys_addr_t end)
{
/* disable zero size segments */
if (start == end)
@@ -841,6 +985,41 @@ static u32 aspeed_spi_segment_ast2600_reg(struct aspeed_spi *aspi,
((end - 1) & AST2600_SEG_ADDR_MASK);
}
+/* The Segment Registers of the AST2700 use a 64KB unit. */
+#define AST2700_SEG_ADDR_MASK 0x7fff0000
+
+static phys_addr_t aspeed_spi_segment_ast2700_start(struct aspeed_spi *aspi,
+ u32 reg)
+{
+ u64 start_offset = (reg << 16) & AST2700_SEG_ADDR_MASK;
+
+ if (!start_offset)
+ return aspi->ahb_base_phy;
+
+ return aspi->ahb_base_phy + start_offset;
+}
+
+static phys_addr_t aspeed_spi_segment_ast2700_end(struct aspeed_spi *aspi,
+ u32 reg)
+{
+ u64 end_offset = reg & AST2700_SEG_ADDR_MASK;
+
+ if (!end_offset)
+ return aspi->ahb_base_phy;
+
+ return aspi->ahb_base_phy + end_offset;
+}
+
+static u32 aspeed_spi_segment_ast2700_reg(struct aspeed_spi *aspi,
+ phys_addr_t start, phys_addr_t end)
+{
+ if (start == end)
+ return 0;
+
+ return (u32)(((start & AST2700_SEG_ADDR_MASK) >> 16) |
+ (end & AST2700_SEG_ADDR_MASK));
+}
+
/*
* Read timing compensation sequences
*/
@@ -942,26 +1121,149 @@ static bool aspeed_spi_check_calib_data(const u8 *test_buf, u32 size)
}
static const u32 aspeed_spi_hclk_divs[] = {
- 0xf, /* HCLK */
- 0x7, /* HCLK/2 */
- 0xe, /* HCLK/3 */
- 0x6, /* HCLK/4 */
- 0xd, /* HCLK/5 */
+ /* HCLK, HCLK/2, HCLK/3, HCLK/4, HCLK/5, ..., HCLK/16 */
+ 0xf, 0x7, 0xe, 0x6, 0xd,
+ 0x5, 0xc, 0x4, 0xb, 0x3,
+ 0xa, 0x2, 0x9, 0x1, 0x8,
+ 0x0
};
#define ASPEED_SPI_HCLK_DIV(i) \
(aspeed_spi_hclk_divs[(i) - 1] << CTRL_FREQ_SEL_SHIFT)
+/* Transfer maximum clock frequency to register setting */
+static u32 aspeed_get_clk_div_ast2400(struct aspeed_spi_chip *chip,
+ u32 max_hz)
+{
+ struct device *dev = chip->aspi->dev;
+ u32 hclk_clk = chip->aspi->clk_freq;
+ u32 div_ctl = 0;
+ u32 i;
+ bool found = false;
+
+ /* FMC/SPIR10[11:8] */
+ for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) {
+ if (hclk_clk / i <= max_hz) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ div_ctl = ASPEED_SPI_HCLK_DIV(i);
+ chip->clk_freq = hclk_clk / i;
+ }
+
+ dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n",
+ found ? "yes" : "no", hclk_clk, max_hz);
+
+ if (found) {
+ dev_dbg(dev, "h_div: 0x%08x, speed: %d\n",
+ div_ctl, chip->clk_freq);
+ }
+
+ return div_ctl;
+}
+
+static u32 aspeed_get_clk_div_ast2500(struct aspeed_spi_chip *chip,
+ u32 max_hz)
+{
+ struct device *dev = chip->aspi->dev;
+ u32 hclk_clk = chip->aspi->clk_freq;
+ u32 div_ctl = 0;
+ u32 i;
+ bool found = false;
+
+ /* FMC/SPIR10[11:8] */
+ for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) {
+ if (hclk_clk / i <= max_hz) {
+ found = true;
+ chip->clk_freq = hclk_clk / i;
+ break;
+ }
+ }
+
+ if (found) {
+ div_ctl = ASPEED_SPI_HCLK_DIV(i);
+ goto end;
+ }
+
+ for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) {
+ if (hclk_clk / (i * 4) <= max_hz) {
+ found = true;
+ chip->clk_freq = hclk_clk / (i * 4);
+ break;
+ }
+ }
+
+ if (found)
+ div_ctl = BIT(13) | ASPEED_SPI_HCLK_DIV(i);
+
+end:
+ dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n",
+ found ? "yes" : "no", hclk_clk, max_hz);
+
+ if (found) {
+ dev_dbg(dev, "h_div: 0x%08x, speed: %d\n",
+ div_ctl, chip->clk_freq);
+ }
+
+ return div_ctl;
+}
+
+static u32 aspeed_get_clk_div_ast2600(struct aspeed_spi_chip *chip,
+ u32 max_hz)
+{
+ struct device *dev = chip->aspi->dev;
+ u32 hclk_clk = chip->aspi->clk_freq;
+ u32 div_ctl = 0;
+ u32 i, j;
+ bool found = false;
+
+ /* FMC/SPIR10[27:24] */
+ for (j = 0; j < 16; j++) {
+ /* FMC/SPIR10[11:8] */
+ for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) {
+ if (j == 0 && i == 1)
+ continue;
+
+ if (hclk_clk / (j * 16 + i) <= max_hz) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ div_ctl = ((j << 24) | ASPEED_SPI_HCLK_DIV(i));
+ chip->clk_freq = hclk_clk / (j * 16 + i);
+ break;
+ }
+ }
+
+ dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n",
+ found ? "yes" : "no", hclk_clk, max_hz);
+
+ if (found) {
+ dev_dbg(dev, "h_div: 0x%08x, speed: %d\n",
+ div_ctl, chip->clk_freq);
+ }
+
+ return div_ctl;
+}
+
static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip)
{
struct aspeed_spi *aspi = chip->aspi;
const struct aspeed_spi_data *data = aspi->data;
u32 ahb_freq = aspi->clk_freq;
u32 max_freq = chip->clk_freq;
+ bool exec_calib = false;
+ u32 best_freq = 0;
u32 ctl_val;
u8 *golden_buf = NULL;
u8 *test_buf = NULL;
- int i, rc, best_div = -1;
+ int i, rc;
+ u32 div_ctl;
dev_dbg(aspi->dev, "calculate timing compensation - AHB freq: %d MHz",
ahb_freq / 1000000);
@@ -982,7 +1284,7 @@ static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip)
memcpy_fromio(golden_buf, chip->ahb_base, CALIBRATE_BUF_SIZE);
if (!aspeed_spi_check_calib_data(golden_buf, CALIBRATE_BUF_SIZE)) {
dev_info(aspi->dev, "Calibration area too uniform, using low speed");
- goto no_calib;
+ goto end_calib;
}
#if defined(VERBOSE_DEBUG)
@@ -991,7 +1293,7 @@ static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip)
#endif
/* Now we iterate the HCLK dividers until we find our breaking point */
- for (i = ARRAY_SIZE(aspeed_spi_hclk_divs); i > data->hdiv_max - 1; i--) {
+ for (i = 5; i > data->hdiv_max - 1; i--) {
u32 tv, freq;
freq = ahb_freq / i;
@@ -1004,22 +1306,33 @@ static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip)
dev_dbg(aspi->dev, "Trying HCLK/%d [%08x] ...", i, tv);
rc = data->calibrate(chip, i, golden_buf, test_buf);
if (rc == 0)
- best_div = i;
+ best_freq = freq;
+
+ exec_calib = true;
}
- /* Nothing found ? */
- if (best_div < 0) {
- dev_warn(aspi->dev, "No good frequency, using dumb slow");
+end_calib:
+ if (!exec_calib) {
+ /* calibration process is not executed */
+ dev_warn(aspi->dev, "Force to dts configuration %dkHz.\n",
+ max_freq / 1000);
+ div_ctl = data->get_clk_div(chip, max_freq);
+ } else if (best_freq == 0) {
+ /* calibration process is executed, but no good frequency */
+ dev_warn(aspi->dev, "No good frequency, using dumb slow\n");
+ div_ctl = 0;
} else {
- dev_dbg(aspi->dev, "Found good read timings at HCLK/%d", best_div);
+ dev_dbg(aspi->dev, "Found good read timings at %dMHz.\n",
+ best_freq / 1000000);
+ div_ctl = data->get_clk_div(chip, best_freq);
+ }
- /* Record the freq */
- for (i = 0; i < ASPEED_SPI_MAX; i++)
- chip->ctl_val[i] = (chip->ctl_val[i] & data->hclk_mask) |
- ASPEED_SPI_HCLK_DIV(best_div);
+ /* Record the freq */
+ for (i = 0; i < ASPEED_SPI_MAX; i++) {
+ chip->ctl_val[i] = (chip->ctl_val[i] & data->hclk_mask) |
+ div_ctl;
}
-no_calib:
writel(chip->ctl_val[ASPEED_SPI_READ], chip->ctl);
kfree(test_buf);
return 0;
@@ -1027,21 +1340,57 @@ no_calib:
#define TIMING_DELAY_DI BIT(3)
#define TIMING_DELAY_HCYCLE_MAX 5
+#define TIMING_DELAY_INPUT_MAX 16
#define TIMING_REG_AST2600(chip) \
((chip)->aspi->regs + (chip)->aspi->data->timing + \
(chip)->cs * 4)
+/*
+ * This function returns the center point of the longest
+ * continuous "pass" interval within the buffer. The interval
+ * must contains the highest number of consecutive "pass"
+ * results and not span across multiple rows.
+ */
+static u32 aspeed_spi_ast2600_optimized_timing(u32 rows, u32 cols,
+ u8 buf[rows][cols])
+{
+ int r = 0, c = 0;
+ int max = 0;
+ int i, j;
+
+ for (i = 0; i < rows; i++) {
+ for (j = 0; j < cols;) {
+ int k = j;
+
+ while (k < cols && buf[i][k])
+ k++;
+
+ if (k - j > max) {
+ max = k - j;
+ r = i;
+ c = j + (k - j) / 2;
+ }
+
+ j = k + 1;
+ }
+ }
+
+ return max > 4 ? r * cols + c : 0;
+}
+
static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv,
const u8 *golden_buf, u8 *test_buf)
{
struct aspeed_spi *aspi = chip->aspi;
int hcycle;
+ int delay_ns;
u32 shift = (hdiv - 2) << 3;
- u32 mask = ~(0xfu << shift);
+ u32 mask = ~(0xffu << shift);
u32 fread_timing_val = 0;
+ u8 calib_res[6][17] = {0};
+ u32 calib_point;
for (hcycle = 0; hcycle <= TIMING_DELAY_HCYCLE_MAX; hcycle++) {
- int delay_ns;
bool pass = false;
fread_timing_val &= mask;
@@ -1054,14 +1403,14 @@ static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv,
" * [%08x] %d HCLK delay, DI delay none : %s",
fread_timing_val, hcycle, pass ? "PASS" : "FAIL");
if (pass)
- return 0;
+ calib_res[hcycle][0] = 1;
/* Add DI input delays */
fread_timing_val &= mask;
fread_timing_val |= (TIMING_DELAY_DI | hcycle) << shift;
- for (delay_ns = 0; delay_ns < 0x10; delay_ns++) {
- fread_timing_val &= ~(0xf << (4 + shift));
+ for (delay_ns = 0; delay_ns < TIMING_DELAY_INPUT_MAX; delay_ns++) {
+ fread_timing_val &= ~(0xfu << (4 + shift));
fread_timing_val |= delay_ns << (4 + shift);
writel(fread_timing_val, TIMING_REG_AST2600(chip));
@@ -1070,18 +1419,28 @@ static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv,
" * [%08x] %d HCLK delay, DI delay %d.%dns : %s",
fread_timing_val, hcycle, (delay_ns + 1) / 2,
(delay_ns + 1) & 1 ? 5 : 5, pass ? "PASS" : "FAIL");
- /*
- * TODO: This is optimistic. We should look
- * for a working interval and save the middle
- * value in the read timing register.
- */
+
if (pass)
- return 0;
+ calib_res[hcycle][delay_ns + 1] = 1;
}
}
+ calib_point = aspeed_spi_ast2600_optimized_timing(6, 17, calib_res);
/* No good setting for this frequency */
- return -1;
+ if (calib_point == 0)
+ return -1;
+
+ hcycle = calib_point / 17;
+ delay_ns = calib_point % 17;
+
+ fread_timing_val = (TIMING_DELAY_DI | hcycle | (delay_ns << 4)) << shift;
+
+ dev_dbg(aspi->dev, "timing val: %08x, final hcycle: %d, delay_ns: %d\n",
+ fread_timing_val, hcycle, delay_ns);
+
+ writel(fread_timing_val, TIMING_REG_AST2600(chip));
+
+ return 0;
}
/*
@@ -1095,10 +1454,13 @@ static const struct aspeed_spi_data ast2400_fmc_data = {
.timing = CE0_TIMING_COMPENSATION_REG,
.hclk_mask = 0xfffff0ff,
.hdiv_max = 1,
+ .min_window_size = 0x800000,
.calibrate = aspeed_spi_calibrate,
+ .get_clk_div = aspeed_get_clk_div_ast2400,
.segment_start = aspeed_spi_segment_start,
.segment_end = aspeed_spi_segment_end,
.segment_reg = aspeed_spi_segment_reg,
+ .adjust_window = aspeed_adjust_window_ast2400,
};
static const struct aspeed_spi_data ast2400_spi_data = {
@@ -1109,6 +1471,7 @@ static const struct aspeed_spi_data ast2400_spi_data = {
.timing = 0x14,
.hclk_mask = 0xfffff0ff,
.hdiv_max = 1,
+ .get_clk_div = aspeed_get_clk_div_ast2400,
.calibrate = aspeed_spi_calibrate,
/* No segment registers */
};
@@ -1121,10 +1484,13 @@ static const struct aspeed_spi_data ast2500_fmc_data = {
.timing = CE0_TIMING_COMPENSATION_REG,
.hclk_mask = 0xffffd0ff,
.hdiv_max = 1,
+ .min_window_size = 0x800000,
+ .get_clk_div = aspeed_get_clk_div_ast2500,
.calibrate = aspeed_spi_calibrate,
.segment_start = aspeed_spi_segment_start,
.segment_end = aspeed_spi_segment_end,
.segment_reg = aspeed_spi_segment_reg,
+ .adjust_window = aspeed_adjust_window_ast2500,
};
static const struct aspeed_spi_data ast2500_spi_data = {
@@ -1135,10 +1501,13 @@ static const struct aspeed_spi_data ast2500_spi_data = {
.timing = CE0_TIMING_COMPENSATION_REG,
.hclk_mask = 0xffffd0ff,
.hdiv_max = 1,
+ .min_window_size = 0x800000,
+ .get_clk_div = aspeed_get_clk_div_ast2500,
.calibrate = aspeed_spi_calibrate,
.segment_start = aspeed_spi_segment_start,
.segment_end = aspeed_spi_segment_end,
.segment_reg = aspeed_spi_segment_reg,
+ .adjust_window = aspeed_adjust_window_ast2500,
};
static const struct aspeed_spi_data ast2600_fmc_data = {
@@ -1150,10 +1519,13 @@ static const struct aspeed_spi_data ast2600_fmc_data = {
.timing = CE0_TIMING_COMPENSATION_REG,
.hclk_mask = 0xf0fff0ff,
.hdiv_max = 2,
+ .min_window_size = 0x200000,
+ .get_clk_div = aspeed_get_clk_div_ast2600,
.calibrate = aspeed_spi_ast2600_calibrate,
.segment_start = aspeed_spi_segment_ast2600_start,
.segment_end = aspeed_spi_segment_ast2600_end,
.segment_reg = aspeed_spi_segment_ast2600_reg,
+ .adjust_window = aspeed_adjust_window_ast2600,
};
static const struct aspeed_spi_data ast2600_spi_data = {
@@ -1165,10 +1537,47 @@ static const struct aspeed_spi_data ast2600_spi_data = {
.timing = CE0_TIMING_COMPENSATION_REG,
.hclk_mask = 0xf0fff0ff,
.hdiv_max = 2,
+ .min_window_size = 0x200000,
+ .get_clk_div = aspeed_get_clk_div_ast2600,
.calibrate = aspeed_spi_ast2600_calibrate,
.segment_start = aspeed_spi_segment_ast2600_start,
.segment_end = aspeed_spi_segment_ast2600_end,
.segment_reg = aspeed_spi_segment_ast2600_reg,
+ .adjust_window = aspeed_adjust_window_ast2600,
+};
+
+static const struct aspeed_spi_data ast2700_fmc_data = {
+ .max_cs = 3,
+ .hastype = false,
+ .mode_bits = SPI_RX_QUAD | SPI_TX_QUAD,
+ .we0 = 16,
+ .ctl0 = CE0_CTRL_REG,
+ .timing = CE0_TIMING_COMPENSATION_REG,
+ .hclk_mask = 0xf0fff0ff,
+ .hdiv_max = 2,
+ .min_window_size = 0x10000,
+ .get_clk_div = aspeed_get_clk_div_ast2600,
+ .calibrate = aspeed_spi_ast2600_calibrate,
+ .segment_start = aspeed_spi_segment_ast2700_start,
+ .segment_end = aspeed_spi_segment_ast2700_end,
+ .segment_reg = aspeed_spi_segment_ast2700_reg,
+};
+
+static const struct aspeed_spi_data ast2700_spi_data = {
+ .max_cs = 2,
+ .hastype = false,
+ .mode_bits = SPI_RX_QUAD | SPI_TX_QUAD,
+ .we0 = 16,
+ .ctl0 = CE0_CTRL_REG,
+ .timing = CE0_TIMING_COMPENSATION_REG,
+ .hclk_mask = 0xf0fff0ff,
+ .hdiv_max = 2,
+ .min_window_size = 0x10000,
+ .get_clk_div = aspeed_get_clk_div_ast2600,
+ .calibrate = aspeed_spi_ast2600_calibrate,
+ .segment_start = aspeed_spi_segment_ast2700_start,
+ .segment_end = aspeed_spi_segment_ast2700_end,
+ .segment_reg = aspeed_spi_segment_ast2700_reg,
};
static const struct of_device_id aspeed_spi_matches[] = {
@@ -1178,6 +1587,8 @@ static const struct of_device_id aspeed_spi_matches[] = {
{ .compatible = "aspeed,ast2500-spi", .data = &ast2500_spi_data },
{ .compatible = "aspeed,ast2600-fmc", .data = &ast2600_fmc_data },
{ .compatible = "aspeed,ast2600-spi", .data = &ast2600_spi_data },
+ { .compatible = "aspeed,ast2700-fmc", .data = &ast2700_fmc_data },
+ { .compatible = "aspeed,ast2700-spi", .data = &ast2700_spi_data },
{ }
};
MODULE_DEVICE_TABLE(of, aspeed_spi_matches);
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 2e3c62f12bef..4c549f166b0f 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -582,8 +582,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
host->auto_runtime_pm = true;
bs->msg_type_shift = bs->reg_offsets[SPI_MSG_TYPE_SHIFT];
bs->msg_ctl_width = bs->reg_offsets[SPI_MSG_CTL_WIDTH];
- bs->tx_io = (u8 *)(bs->regs + bs->reg_offsets[SPI_MSG_DATA]);
- bs->rx_io = (const u8 *)(bs->regs + bs->reg_offsets[SPI_RX_DATA]);
+ bs->tx_io = bs->regs + bs->reg_offsets[SPI_MSG_DATA];
+ bs->rx_io = bs->regs + bs->reg_offsets[SPI_RX_DATA];
/* Initialize hardware */
ret = clk_prepare_enable(bs->clk);
diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
index 5ae09b21d23a..47054da630d0 100644
--- a/drivers/spi/spi-cadence.c
+++ b/drivers/spi/spi-cadence.c
@@ -109,6 +109,7 @@
* @rxbuf: Pointer to the RX buffer
* @tx_bytes: Number of bytes left to transfer
* @rx_bytes: Number of bytes requested
+ * @n_bytes: Number of bytes per word
* @dev_busy: Device busy flag
* @is_decoded_cs: Flag for decoder property set or not
* @tx_fifo_depth: Depth of the TX FIFO
@@ -120,16 +121,24 @@ struct cdns_spi {
struct clk *pclk;
unsigned int clk_rate;
u32 speed_hz;
- const u8 *txbuf;
- u8 *rxbuf;
+ const void *txbuf;
+ void *rxbuf;
int tx_bytes;
int rx_bytes;
+ u8 n_bytes;
u8 dev_busy;
u32 is_decoded_cs;
unsigned int tx_fifo_depth;
struct reset_control *rstc;
};
+enum cdns_spi_frame_n_bytes {
+ CDNS_SPI_N_BYTES_NULL = 0,
+ CDNS_SPI_N_BYTES_U8 = 1,
+ CDNS_SPI_N_BYTES_U16 = 2,
+ CDNS_SPI_N_BYTES_U32 = 4
+};
+
/* Macros for the SPI controller read/write */
static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset)
{
@@ -305,6 +314,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi,
return 0;
}
+static u8 cdns_spi_n_bytes(struct spi_transfer *transfer)
+{
+ if (transfer->bits_per_word <= 8)
+ return CDNS_SPI_N_BYTES_U8;
+ else if (transfer->bits_per_word <= 16)
+ return CDNS_SPI_N_BYTES_U16;
+ else
+ return CDNS_SPI_N_BYTES_U32;
+}
+
+static inline void cdns_spi_reader(struct cdns_spi *xspi)
+{
+ u32 rxw = 0;
+
+ if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) {
+ pr_err("%s: rxbuf address is not aligned for %d bytes\n",
+ __func__, xspi->n_bytes);
+ return;
+ }
+
+ rxw = cdns_spi_read(xspi, CDNS_SPI_RXD);
+ if (xspi->rxbuf) {
+ switch (xspi->n_bytes) {
+ case CDNS_SPI_N_BYTES_U8:
+ *(u8 *)xspi->rxbuf = rxw;
+ break;
+ case CDNS_SPI_N_BYTES_U16:
+ *(u16 *)xspi->rxbuf = rxw;
+ break;
+ case CDNS_SPI_N_BYTES_U32:
+ *(u32 *)xspi->rxbuf = rxw;
+ break;
+ default:
+ pr_err("%s invalid n_bytes %d\n", __func__,
+ xspi->n_bytes);
+ return;
+ }
+ xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes;
+ }
+}
+
+static inline void cdns_spi_writer(struct cdns_spi *xspi)
+{
+ u32 txw = 0;
+
+ if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) {
+ pr_err("%s: txbuf address is not aligned for %d bytes\n",
+ __func__, xspi->n_bytes);
+ return;
+ }
+
+ if (xspi->txbuf) {
+ switch (xspi->n_bytes) {
+ case CDNS_SPI_N_BYTES_U8:
+ txw = *(u8 *)xspi->txbuf;
+ break;
+ case CDNS_SPI_N_BYTES_U16:
+ txw = *(u16 *)xspi->txbuf;
+ break;
+ case CDNS_SPI_N_BYTES_U32:
+ txw = *(u32 *)xspi->txbuf;
+ break;
+ default:
+ pr_err("%s invalid n_bytes %d\n", __func__,
+ xspi->n_bytes);
+ return;
+ }
+ cdns_spi_write(xspi, CDNS_SPI_TXD, txw);
+ xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes;
+ }
+}
+
/**
* cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
* @xspi: Pointer to the cdns_spi structure
@@ -321,23 +402,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
while (ntx || nrx) {
if (nrx) {
- u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD);
-
- if (xspi->rxbuf)
- *xspi->rxbuf++ = data;
-
+ cdns_spi_reader(xspi);
nrx--;
}
if (ntx) {
- if (xspi->txbuf)
- cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++);
- else
- cdns_spi_write(xspi, CDNS_SPI_TXD, 0);
-
+ cdns_spi_writer(xspi);
ntx--;
}
-
}
}
@@ -454,6 +526,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
udelay(10);
+ xspi->n_bytes = cdns_spi_n_bytes(transfer);
+ xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
+ xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
+
cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
@@ -654,6 +730,9 @@ static int cdns_spi_probe(struct platform_device *pdev)
ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
+ if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6"))
+ ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
+
if (!spi_controller_is_target(ctlr)) {
ctlr->mode_bits |= SPI_CS_HIGH;
ctlr->set_cs = cdns_spi_chipselect;
@@ -797,6 +876,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = {
static const struct of_device_id cdns_spi_of_match[] = {
{ .compatible = "xlnx,zynq-spi-r1p6" },
+ { .compatible = "cix,sky1-spi-r1p6" },
{ .compatible = "cdns,spi-r1p6" },
{ /* end of table */ }
};
diff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c
index 46bc208f2d05..79d2f9ab4ef0 100644
--- a/drivers/spi/spi-ch341.c
+++ b/drivers/spi/spi-ch341.c
@@ -78,7 +78,7 @@ static int ch341_transfer_one(struct spi_controller *host,
ch341->tx_buf[0] = CH341A_CMD_SPI_STREAM;
- memcpy(ch341->tx_buf + 1, trans->tx_buf, len);
+ memcpy(ch341->tx_buf + 1, trans->tx_buf, len - 1);
ret = usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf, len,
NULL, CH341_DEFAULT_TIMEOUT);
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index a29934422356..21a14e800eed 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -9,6 +9,7 @@
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/platform_data/edma.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/clk.h>
@@ -19,8 +20,6 @@
#include <linux/spi/spi_bitbang.h>
#include <linux/slab.h>
-#include <linux/platform_data/spi-davinci.h>
-
#define CS_DEFAULT 0xFF
#define SPIFMT_PHASE_MASK BIT(16)
@@ -98,8 +97,69 @@
#define SPIDEF 0x4c
#define SPIFMT0 0x50
+#define SPI_IO_TYPE_POLL 1
+#define SPI_IO_TYPE_DMA 2
+
#define DMA_MIN_BYTES 16
+enum {
+ SPI_VERSION_1, /* For DM355/DM365/DM6467 */
+ SPI_VERSION_2, /* For DA8xx */
+};
+
+/**
+ * struct davinci_spi_platform_data - Platform data for SPI master device on DaVinci
+ *
+ * @version: version of the SPI IP. Different DaVinci devices have slightly
+ * varying versions of the same IP.
+ * @num_chipselect: number of chipselects supported by this SPI master
+ * @intr_line: interrupt line used to connect the SPI IP to the ARM interrupt
+ * controller withn the SoC. Possible values are 0 and 1.
+ * @prescaler_limit: max clock prescaler value
+ * @cshold_bug: set this to true if the SPI controller on your chip requires
+ * a write to CSHOLD bit in between transfers (like in DM355).
+ * @dma_event_q: DMA event queue to use if SPI_IO_TYPE_DMA is used for any
+ * device on the bus.
+ */
+struct davinci_spi_platform_data {
+ u8 version;
+ u8 num_chipselect;
+ u8 intr_line;
+ u8 prescaler_limit;
+ bool cshold_bug;
+ enum dma_event_q dma_event_q;
+};
+
+/**
+ * struct davinci_spi_config - Per-chip-select configuration for SPI slave devices
+ *
+ * @wdelay: amount of delay between transmissions. Measured in number of
+ * SPI module clocks.
+ * @odd_parity: polarity of parity flag at the end of transmit data stream.
+ * 0 - odd parity, 1 - even parity.
+ * @parity_enable: enable transmission of parity at end of each transmit
+ * data stream.
+ * @io_type: type of IO transfer. Choose between polled, interrupt and DMA.
+ * @timer_disable: disable chip-select timers (setup and hold)
+ * @c2tdelay: chip-select setup time. Measured in number of SPI module clocks.
+ * @t2cdelay: chip-select hold time. Measured in number of SPI module clocks.
+ * @t2edelay: transmit data finished to SPI ENAn pin inactive time. Measured
+ * in number of SPI clocks.
+ * @c2edelay: chip-select active to SPI ENAn signal active time. Measured in
+ * number of SPI clocks.
+ */
+struct davinci_spi_config {
+ u8 wdelay;
+ u8 odd_parity;
+ u8 parity_enable;
+ u8 io_type;
+ u8 timer_disable;
+ u8 c2tdelay;
+ u8 t2cdelay;
+ u8 t2edelay;
+ u8 c2edelay;
+};
+
/* SPI Controller driver's private data. */
struct davinci_spi {
struct spi_bitbang bitbang;
diff --git a/drivers/spi/spi-dw-bt1.c b/drivers/spi/spi-dw-bt1.c
index 4a5be813efa7..91642e05ac60 100644
--- a/drivers/spi/spi-dw-bt1.c
+++ b/drivers/spi/spi-dw-bt1.c
@@ -288,7 +288,7 @@ static int dw_spi_bt1_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
- ret = dw_spi_add_host(&pdev->dev, dws);
+ ret = dw_spi_add_controller(&pdev->dev, dws);
if (ret) {
pm_runtime_disable(&pdev->dev);
return ret;
@@ -303,7 +303,7 @@ static void dw_spi_bt1_remove(struct platform_device *pdev)
{
struct dw_spi_bt1 *dwsbt1 = platform_get_drvdata(pdev);
- dw_spi_remove_host(&dwsbt1->dws);
+ dw_spi_remove_controller(&dwsbt1->dws);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index b3b883cb9541..9ebf244294f8 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -63,7 +63,7 @@ static void dw_spi_debugfs_init(struct dw_spi *dws)
{
char name[32];
- snprintf(name, 32, "dw_spi%d", dws->host->bus_num);
+ snprintf(name, 32, "dw_spi%d", dws->ctlr->bus_num);
dws->debugfs = debugfs_create_dir(name, NULL);
dws->regset.regs = dw_spi_dbgfs_regs;
@@ -185,25 +185,25 @@ int dw_spi_check_status(struct dw_spi *dws, bool raw)
irq_status = dw_readl(dws, DW_SPI_ISR);
if (irq_status & DW_SPI_INT_RXOI) {
- dev_err(&dws->host->dev, "RX FIFO overflow detected\n");
+ dev_err(&dws->ctlr->dev, "RX FIFO overflow detected\n");
ret = -EIO;
}
if (irq_status & DW_SPI_INT_RXUI) {
- dev_err(&dws->host->dev, "RX FIFO underflow detected\n");
+ dev_err(&dws->ctlr->dev, "RX FIFO underflow detected\n");
ret = -EIO;
}
if (irq_status & DW_SPI_INT_TXOI) {
- dev_err(&dws->host->dev, "TX FIFO overflow detected\n");
+ dev_err(&dws->ctlr->dev, "TX FIFO overflow detected\n");
ret = -EIO;
}
/* Generically handle the erroneous situation */
if (ret) {
dw_spi_reset_chip(dws);
- if (dws->host->cur_msg)
- dws->host->cur_msg->status = ret;
+ if (dws->ctlr->cur_msg)
+ dws->ctlr->cur_msg->status = ret;
}
return ret;
@@ -215,7 +215,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
u16 irq_status = dw_readl(dws, DW_SPI_ISR);
if (dw_spi_check_status(dws, false)) {
- spi_finalize_current_transfer(dws->host);
+ spi_finalize_current_transfer(dws->ctlr);
return IRQ_HANDLED;
}
@@ -229,7 +229,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
dw_reader(dws);
if (!dws->rx_len) {
dw_spi_mask_intr(dws, 0xff);
- spi_finalize_current_transfer(dws->host);
+ spi_finalize_current_transfer(dws->ctlr);
} else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) {
dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1);
}
@@ -250,14 +250,14 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
static irqreturn_t dw_spi_irq(int irq, void *dev_id)
{
- struct spi_controller *host = dev_id;
- struct dw_spi *dws = spi_controller_get_devdata(host);
+ struct spi_controller *ctlr = dev_id;
+ struct dw_spi *dws = spi_controller_get_devdata(ctlr);
u16 irq_status = dw_readl(dws, DW_SPI_ISR) & DW_SPI_INT_MASK;
if (!irq_status)
return IRQ_NONE;
- if (!host->cur_msg) {
+ if (!ctlr->cur_msg) {
dw_spi_mask_intr(dws, 0xff);
return IRQ_HANDLED;
}
@@ -332,6 +332,9 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
dw_writel(dws, DW_SPI_CTRLR0, cr0);
+ if (spi_controller_is_target(dws->ctlr))
+ return;
+
if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD ||
cfg->tmode == DW_SPI_CTRLR0_TMOD_RO)
dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0);
@@ -410,11 +413,11 @@ static int dw_spi_poll_transfer(struct dw_spi *dws,
return 0;
}
-static int dw_spi_transfer_one(struct spi_controller *host,
+static int dw_spi_transfer_one(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *transfer)
{
- struct dw_spi *dws = spi_controller_get_devdata(host);
+ struct dw_spi *dws = spi_controller_get_devdata(ctlr);
struct dw_spi_cfg cfg = {
.tmode = DW_SPI_CTRLR0_TMOD_TR,
.dfs = transfer->bits_per_word,
@@ -439,7 +442,7 @@ static int dw_spi_transfer_one(struct spi_controller *host,
transfer->effective_speed_hz = dws->current_freq;
/* Check if current transfer is a DMA transaction */
- dws->dma_mapped = spi_xfer_is_dma_mapped(host, spi, transfer);
+ dws->dma_mapped = spi_xfer_is_dma_mapped(ctlr, spi, transfer);
/* For poll mode just disable all interrupts */
dw_spi_mask_intr(dws, 0xff);
@@ -462,10 +465,9 @@ static int dw_spi_transfer_one(struct spi_controller *host,
return 1;
}
-static void dw_spi_handle_err(struct spi_controller *host,
- struct spi_message *msg)
+static inline void dw_spi_abort(struct spi_controller *ctlr)
{
- struct dw_spi *dws = spi_controller_get_devdata(host);
+ struct dw_spi *dws = spi_controller_get_devdata(ctlr);
if (dws->dma_mapped)
dws->dma_ops->dma_stop(dws);
@@ -473,6 +475,19 @@ static void dw_spi_handle_err(struct spi_controller *host,
dw_spi_reset_chip(dws);
}
+static void dw_spi_handle_err(struct spi_controller *ctlr,
+ struct spi_message *msg)
+{
+ dw_spi_abort(ctlr);
+}
+
+static int dw_spi_target_abort(struct spi_controller *ctlr)
+{
+ dw_spi_abort(ctlr);
+
+ return 0;
+}
+
static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
if (op->data.dir == SPI_MEM_DATA_IN)
@@ -574,7 +589,7 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi)
while (len) {
entries = readl_relaxed(dws->regs + DW_SPI_TXFLR);
if (!entries) {
- dev_err(&dws->host->dev, "CS de-assertion on Tx\n");
+ dev_err(&dws->ctlr->dev, "CS de-assertion on Tx\n");
return -EIO;
}
room = min(dws->fifo_len - entries, len);
@@ -594,7 +609,7 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi)
if (!entries) {
sts = readl_relaxed(dws->regs + DW_SPI_RISR);
if (sts & DW_SPI_INT_RXOI) {
- dev_err(&dws->host->dev, "FIFO overflow on Rx\n");
+ dev_err(&dws->ctlr->dev, "FIFO overflow on Rx\n");
return -EIO;
}
continue;
@@ -635,7 +650,7 @@ static int dw_spi_wait_mem_op_done(struct dw_spi *dws)
spi_delay_exec(&delay, NULL);
if (retry < 0) {
- dev_err(&dws->host->dev, "Mem op hanged up\n");
+ dev_err(&dws->ctlr->dev, "Mem op hanged up\n");
return -EIO;
}
@@ -834,18 +849,23 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
DW_SPI_GET_BYTE(dws->ver, 1));
}
- /*
- * Try to detect the number of native chip-selects if the platform
- * driver didn't set it up. There can be up to 16 lines configured.
- */
- if (!dws->num_cs) {
- u32 ser;
+ if (spi_controller_is_target(dws->ctlr)) {
+ /* There is only one CS input signal in target mode */
+ dws->num_cs = 1;
+ } else {
+ /*
+ * Try to detect the number of native chip-selects if the platform
+ * driver didn't set it up. There can be up to 16 lines configured.
+ */
+ if (!dws->num_cs) {
+ u32 ser;
- dw_writel(dws, DW_SPI_SER, 0xffff);
- ser = dw_readl(dws, DW_SPI_SER);
- dw_writel(dws, DW_SPI_SER, 0);
+ dw_writel(dws, DW_SPI_SER, 0xffff);
+ ser = dw_readl(dws, DW_SPI_SER);
+ dw_writel(dws, DW_SPI_SER, 0);
- dws->num_cs = hweight16(ser);
+ dws->num_cs = hweight16(ser);
+ }
}
/*
@@ -898,60 +918,72 @@ static const struct spi_controller_mem_caps dw_spi_mem_caps = {
.per_op_freq = true,
};
-int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
+int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
{
- struct spi_controller *host;
+ struct spi_controller *ctlr;
+ bool target;
int ret;
if (!dws)
return -EINVAL;
- host = spi_alloc_host(dev, 0);
- if (!host)
+ target = device_property_read_bool(dev, "spi-slave");
+ if (target)
+ ctlr = spi_alloc_target(dev, 0);
+ else
+ ctlr = spi_alloc_host(dev, 0);
+
+ if (!ctlr)
return -ENOMEM;
- device_set_node(&host->dev, dev_fwnode(dev));
+ device_set_node(&ctlr->dev, dev_fwnode(dev));
- dws->host = host;
+ dws->ctlr = ctlr;
dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR);
- spi_controller_set_devdata(host, dws);
+ spi_controller_set_devdata(ctlr, dws);
/* Basic HW init */
dw_spi_hw_init(dev, dws);
ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev),
- host);
+ ctlr);
if (ret < 0 && ret != -ENOTCONN) {
dev_err(dev, "can not get IRQ\n");
- goto err_free_host;
+ goto err_free_ctlr;
}
dw_spi_init_mem_ops(dws);
- host->use_gpio_descriptors = true;
- host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
+ ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
if (dws->caps & DW_SPI_CAP_DFS32)
- host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
- else
- host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
- host->bus_num = dws->bus_num;
- host->num_chipselect = dws->num_cs;
- host->setup = dw_spi_setup;
- host->cleanup = dw_spi_cleanup;
- if (dws->set_cs)
- host->set_cs = dws->set_cs;
+ ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
else
- host->set_cs = dw_spi_set_cs;
- host->transfer_one = dw_spi_transfer_one;
- host->handle_err = dw_spi_handle_err;
- if (dws->mem_ops.exec_op) {
- host->mem_ops = &dws->mem_ops;
- host->mem_caps = &dw_spi_mem_caps;
+ ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
+ ctlr->bus_num = dws->bus_num;
+ ctlr->num_chipselect = dws->num_cs;
+ ctlr->setup = dw_spi_setup;
+ ctlr->cleanup = dw_spi_cleanup;
+ ctlr->transfer_one = dw_spi_transfer_one;
+ ctlr->handle_err = dw_spi_handle_err;
+ ctlr->auto_runtime_pm = true;
+
+ if (!target) {
+ ctlr->use_gpio_descriptors = true;
+ ctlr->mode_bits |= SPI_LOOP;
+ if (dws->set_cs)
+ ctlr->set_cs = dws->set_cs;
+ else
+ ctlr->set_cs = dw_spi_set_cs;
+ if (dws->mem_ops.exec_op) {
+ ctlr->mem_ops = &dws->mem_ops;
+ ctlr->mem_caps = &dw_spi_mem_caps;
+ }
+ ctlr->max_speed_hz = dws->max_freq;
+ ctlr->flags = SPI_CONTROLLER_GPIO_SS;
+ } else {
+ ctlr->target_abort = dw_spi_target_abort;
}
- host->max_speed_hz = dws->max_freq;
- host->flags = SPI_CONTROLLER_GPIO_SS;
- host->auto_runtime_pm = true;
/* Get default rx sample delay */
device_property_read_u32(dev, "rx-sample-delay-ns",
@@ -964,14 +996,14 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
} else if (ret) {
dev_warn(dev, "DMA init failed\n");
} else {
- host->can_dma = dws->dma_ops->can_dma;
- host->flags |= SPI_CONTROLLER_MUST_TX;
+ ctlr->can_dma = dws->dma_ops->can_dma;
+ ctlr->flags |= SPI_CONTROLLER_MUST_TX;
}
}
- ret = spi_register_controller(host);
+ ret = spi_register_controller(ctlr);
if (ret) {
- dev_err_probe(dev, ret, "problem registering spi host\n");
+ dev_err_probe(dev, ret, "problem registering spi controller\n");
goto err_dma_exit;
}
@@ -983,47 +1015,47 @@ err_dma_exit:
dws->dma_ops->dma_exit(dws);
dw_spi_enable_chip(dws, 0);
err_free_irq:
- free_irq(dws->irq, host);
-err_free_host:
- spi_controller_put(host);
+ free_irq(dws->irq, ctlr);
+err_free_ctlr:
+ spi_controller_put(ctlr);
return ret;
}
-EXPORT_SYMBOL_NS_GPL(dw_spi_add_host, "SPI_DW_CORE");
+EXPORT_SYMBOL_NS_GPL(dw_spi_add_controller, "SPI_DW_CORE");
-void dw_spi_remove_host(struct dw_spi *dws)
+void dw_spi_remove_controller(struct dw_spi *dws)
{
dw_spi_debugfs_remove(dws);
- spi_unregister_controller(dws->host);
+ spi_unregister_controller(dws->ctlr);
if (dws->dma_ops && dws->dma_ops->dma_exit)
dws->dma_ops->dma_exit(dws);
dw_spi_shutdown_chip(dws);
- free_irq(dws->irq, dws->host);
+ free_irq(dws->irq, dws->ctlr);
}
-EXPORT_SYMBOL_NS_GPL(dw_spi_remove_host, "SPI_DW_CORE");
+EXPORT_SYMBOL_NS_GPL(dw_spi_remove_controller, "SPI_DW_CORE");
-int dw_spi_suspend_host(struct dw_spi *dws)
+int dw_spi_suspend_controller(struct dw_spi *dws)
{
int ret;
- ret = spi_controller_suspend(dws->host);
+ ret = spi_controller_suspend(dws->ctlr);
if (ret)
return ret;
dw_spi_shutdown_chip(dws);
return 0;
}
-EXPORT_SYMBOL_NS_GPL(dw_spi_suspend_host, "SPI_DW_CORE");
+EXPORT_SYMBOL_NS_GPL(dw_spi_suspend_controller, "SPI_DW_CORE");
-int dw_spi_resume_host(struct dw_spi *dws)
+int dw_spi_resume_controller(struct dw_spi *dws)
{
- dw_spi_hw_init(&dws->host->dev, dws);
- return spi_controller_resume(dws->host);
+ dw_spi_hw_init(&dws->ctlr->dev, dws);
+ return spi_controller_resume(dws->ctlr);
}
-EXPORT_SYMBOL_NS_GPL(dw_spi_resume_host, "SPI_DW_CORE");
+EXPORT_SYMBOL_NS_GPL(dw_spi_resume_controller, "SPI_DW_CORE");
MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
MODULE_DESCRIPTION("Driver for DesignWare SPI controller core");
diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c
index b5bed02b7e50..65adec7c7524 100644
--- a/drivers/spi/spi-dw-dma.c
+++ b/drivers/spi/spi-dw-dma.c
@@ -139,8 +139,8 @@ static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws)
if (!dws->txchan)
goto free_rxchan;
- dws->host->dma_rx = dws->rxchan;
- dws->host->dma_tx = dws->txchan;
+ dws->ctlr->dma_rx = dws->rxchan;
+ dws->ctlr->dma_tx = dws->txchan;
init_completion(&dws->dma_completion);
@@ -183,8 +183,8 @@ static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws)
goto free_rxchan;
}
- dws->host->dma_rx = dws->rxchan;
- dws->host->dma_tx = dws->txchan;
+ dws->ctlr->dma_rx = dws->rxchan;
+ dws->ctlr->dma_tx = dws->txchan;
init_completion(&dws->dma_completion);
@@ -242,10 +242,10 @@ static enum dma_slave_buswidth dw_spi_dma_convert_width(u8 n_bytes)
}
}
-static bool dw_spi_can_dma(struct spi_controller *host,
+static bool dw_spi_can_dma(struct spi_controller *ctlr,
struct spi_device *spi, struct spi_transfer *xfer)
{
- struct dw_spi *dws = spi_controller_get_devdata(host);
+ struct dw_spi *dws = spi_controller_get_devdata(ctlr);
enum dma_slave_buswidth dma_bus_width;
if (xfer->len <= dws->fifo_len)
@@ -271,7 +271,7 @@ static int dw_spi_dma_wait(struct dw_spi *dws, unsigned int len, u32 speed)
msecs_to_jiffies(ms));
if (ms == 0) {
- dev_err(&dws->host->cur_msg->spi->dev,
+ dev_err(&dws->ctlr->cur_msg->spi->dev,
"DMA transaction timed out\n");
return -ETIMEDOUT;
}
@@ -299,7 +299,7 @@ static int dw_spi_dma_wait_tx_done(struct dw_spi *dws,
spi_delay_exec(&delay, xfer);
if (retry < 0) {
- dev_err(&dws->host->dev, "Tx hanged up\n");
+ dev_err(&dws->ctlr->dev, "Tx hanged up\n");
return -EIO;
}
@@ -400,7 +400,7 @@ static int dw_spi_dma_wait_rx_done(struct dw_spi *dws)
spi_delay_exec(&delay, NULL);
if (retry < 0) {
- dev_err(&dws->host->dev, "Rx hanged up\n");
+ dev_err(&dws->ctlr->dev, "Rx hanged up\n");
return -EIO;
}
@@ -656,13 +656,13 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer)
if (ret)
return ret;
- if (dws->host->cur_msg->status == -EINPROGRESS) {
+ if (dws->ctlr->cur_msg->status == -EINPROGRESS) {
ret = dw_spi_dma_wait_tx_done(dws, xfer);
if (ret)
return ret;
}
- if (xfer->rx_buf && dws->host->cur_msg->status == -EINPROGRESS)
+ if (xfer->rx_buf && dws->ctlr->cur_msg->status == -EINPROGRESS)
ret = dw_spi_dma_wait_rx_done(dws);
return ret;
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index 7a5197586919..33239b4778cb 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -321,11 +321,6 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
struct dw_spi *dws;
int ret;
- if (device_property_read_bool(&pdev->dev, "spi-slave")) {
- dev_warn(&pdev->dev, "spi-slave is not yet supported\n");
- return -ENODEV;
- }
-
dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio),
GFP_KERNEL);
if (!dwsmmio)
@@ -382,7 +377,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
- ret = dw_spi_add_host(&pdev->dev, dws);
+ ret = dw_spi_add_controller(&pdev->dev, dws);
if (ret)
goto out;
@@ -401,7 +396,7 @@ static void dw_spi_mmio_remove(struct platform_device *pdev)
{
struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev);
- dw_spi_remove_host(&dwsmmio->dws);
+ dw_spi_remove_controller(&dwsmmio->dws);
pm_runtime_disable(&pdev->dev);
reset_control_assert(dwsmmio->rstc);
}
diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c
index b32d6648a32e..72d9f5bc87f7 100644
--- a/drivers/spi/spi-dw-pci.c
+++ b/drivers/spi/spi-dw-pci.c
@@ -127,7 +127,7 @@ static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *en
goto err_free_irq_vectors;
}
- ret = dw_spi_add_host(&pdev->dev, dws);
+ ret = dw_spi_add_controller(&pdev->dev, dws);
if (ret)
goto err_free_irq_vectors;
@@ -156,7 +156,7 @@ static void dw_spi_pci_remove(struct pci_dev *pdev)
pm_runtime_forbid(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
- dw_spi_remove_host(dws);
+ dw_spi_remove_controller(dws);
pci_free_irq_vectors(pdev);
}
@@ -165,14 +165,14 @@ static int dw_spi_pci_suspend(struct device *dev)
{
struct dw_spi *dws = dev_get_drvdata(dev);
- return dw_spi_suspend_host(dws);
+ return dw_spi_suspend_controller(dws);
}
static int dw_spi_pci_resume(struct device *dev)
{
struct dw_spi *dws = dev_get_drvdata(dev);
- return dw_spi_resume_host(dws);
+ return dw_spi_resume_controller(dws);
}
#endif
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index fc267c6437ae..9cc79c566a70 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -142,14 +142,14 @@ struct dw_spi_dma_ops {
int (*dma_init)(struct device *dev, struct dw_spi *dws);
void (*dma_exit)(struct dw_spi *dws);
int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer);
- bool (*can_dma)(struct spi_controller *host, struct spi_device *spi,
+ bool (*can_dma)(struct spi_controller *ctlr, struct spi_device *spi,
struct spi_transfer *xfer);
int (*dma_transfer)(struct dw_spi *dws, struct spi_transfer *xfer);
void (*dma_stop)(struct dw_spi *dws);
};
struct dw_spi {
- struct spi_controller *host;
+ struct spi_controller *ctlr;
u32 ip; /* Synopsys DW SSI IP-core ID */
u32 ver; /* Synopsys component version */
@@ -288,10 +288,10 @@ extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
struct dw_spi_cfg *cfg);
extern int dw_spi_check_status(struct dw_spi *dws, bool raw);
-extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
-extern void dw_spi_remove_host(struct dw_spi *dws);
-extern int dw_spi_suspend_host(struct dw_spi *dws);
-extern int dw_spi_resume_host(struct dw_spi *dws);
+extern int dw_spi_add_controller(struct device *dev, struct dw_spi *dws);
+extern void dw_spi_remove_controller(struct dw_spi *dws);
+extern int dw_spi_suspend_controller(struct dw_spi *dws);
+extern int dw_spi_resume_controller(struct dw_spi *dws);
#ifdef CONFIG_SPI_DW_DMA
diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c
index c887abb028d7..a223b4bc6e63 100644
--- a/drivers/spi/spi-fsl-qspi.c
+++ b/drivers/spi/spi-fsl-qspi.c
@@ -36,6 +36,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
+#include <linux/reset.h>
#include <linux/sizes.h>
#include <linux/spi/spi.h>
@@ -196,11 +197,17 @@
*/
#define QUADSPI_QUIRK_USE_TDH_SETTING BIT(5)
+/*
+ * Do not disable the "qspi" clock when changing its rate.
+ */
+#define QUADSPI_QUIRK_SKIP_CLK_DISABLE BIT(6)
+
struct fsl_qspi_devtype_data {
unsigned int rxfifo;
unsigned int txfifo;
int invalid_mstrid;
unsigned int ahb_buf_size;
+ unsigned int sfa_size;
unsigned int quirks;
bool little_endian;
};
@@ -261,12 +268,23 @@ static const struct fsl_qspi_devtype_data ls2080a_data = {
.little_endian = true,
};
+static const struct fsl_qspi_devtype_data spacemit_k1_data = {
+ .rxfifo = SZ_128,
+ .txfifo = SZ_256,
+ .ahb_buf_size = SZ_512,
+ .sfa_size = SZ_1K,
+ .invalid_mstrid = QUADSPI_BUFXCR_INVALID_MSTRID,
+ .quirks = QUADSPI_QUIRK_TKT253890 | QUADSPI_QUIRK_SKIP_CLK_DISABLE,
+ .little_endian = true,
+};
+
struct fsl_qspi {
void __iomem *iobase;
void __iomem *ahb_addr;
const struct fsl_qspi_devtype_data *devtype_data;
struct mutex lock;
struct completion c;
+ struct reset_control *resets;
struct clk *clk, *clk_en;
struct pm_qos_request pm_qos_req;
struct device *dev;
@@ -274,34 +292,39 @@ struct fsl_qspi {
u32 memmap_phy;
};
-static inline int needs_swap_endian(struct fsl_qspi *q)
+static bool needs_swap_endian(struct fsl_qspi *q)
{
- return q->devtype_data->quirks & QUADSPI_QUIRK_SWAP_ENDIAN;
+ return !!(q->devtype_data->quirks & QUADSPI_QUIRK_SWAP_ENDIAN);
}
-static inline int needs_4x_clock(struct fsl_qspi *q)
+static bool needs_4x_clock(struct fsl_qspi *q)
{
- return q->devtype_data->quirks & QUADSPI_QUIRK_4X_INT_CLK;
+ return !!(q->devtype_data->quirks & QUADSPI_QUIRK_4X_INT_CLK);
}
-static inline int needs_fill_txfifo(struct fsl_qspi *q)
+static bool needs_fill_txfifo(struct fsl_qspi *q)
{
- return q->devtype_data->quirks & QUADSPI_QUIRK_TKT253890;
+ return !!(q->devtype_data->quirks & QUADSPI_QUIRK_TKT253890);
}
-static inline int needs_wakeup_wait_mode(struct fsl_qspi *q)
+static bool needs_wakeup_wait_mode(struct fsl_qspi *q)
{
- return q->devtype_data->quirks & QUADSPI_QUIRK_TKT245618;
+ return !!(q->devtype_data->quirks & QUADSPI_QUIRK_TKT245618);
}
-static inline int needs_amba_base_offset(struct fsl_qspi *q)
+static bool needs_amba_base_offset(struct fsl_qspi *q)
{
return !(q->devtype_data->quirks & QUADSPI_QUIRK_BASE_INTERNAL);
}
-static inline int needs_tdh_setting(struct fsl_qspi *q)
+static bool needs_tdh_setting(struct fsl_qspi *q)
+{
+ return !!(q->devtype_data->quirks & QUADSPI_QUIRK_USE_TDH_SETTING);
+}
+
+static bool needs_clk_disable(struct fsl_qspi *q)
{
- return q->devtype_data->quirks & QUADSPI_QUIRK_USE_TDH_SETTING;
+ return !(q->devtype_data->quirks & QUADSPI_QUIRK_SKIP_CLK_DISABLE);
}
/*
@@ -534,15 +557,18 @@ static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi,
if (needs_4x_clock(q))
rate *= 4;
- fsl_qspi_clk_disable_unprep(q);
+ if (needs_clk_disable(q))
+ fsl_qspi_clk_disable_unprep(q);
ret = clk_set_rate(q->clk, rate);
if (ret)
return;
- ret = fsl_qspi_clk_prep_enable(q);
- if (ret)
- return;
+ if (needs_clk_disable(q)) {
+ ret = fsl_qspi_clk_prep_enable(q);
+ if (ret)
+ return;
+ }
q->selected = spi_get_chipselect(spi, 0);
@@ -722,6 +748,7 @@ static int fsl_qspi_default_setup(struct fsl_qspi *q)
{
void __iomem *base = q->iobase;
u32 reg, addr_offset = 0;
+ u32 sfa_size;
int ret;
/* disable and unprepare clock to avoid glitch pass to controller */
@@ -780,17 +807,17 @@ static int fsl_qspi_default_setup(struct fsl_qspi *q)
* In HW there can be a maximum of four chips on two buses with
* two chip selects on each bus. We use four chip selects in SW
* to differentiate between the four chips.
- * We use ahb_buf_size for each chip and set SFA1AD, SFA2AD, SFB1AD,
- * SFB2AD accordingly.
+ *
+ * By default we write the AHB buffer size to each chip, but
+ * a different size can be specified with devtype_data->sfa_size.
+ * The SFA1AD, SFA2AD, SFB1AD, and SFB2AD registers define the
+ * top (end) of these four regions.
*/
- qspi_writel(q, q->devtype_data->ahb_buf_size + addr_offset,
- base + QUADSPI_SFA1AD);
- qspi_writel(q, q->devtype_data->ahb_buf_size * 2 + addr_offset,
- base + QUADSPI_SFA2AD);
- qspi_writel(q, q->devtype_data->ahb_buf_size * 3 + addr_offset,
- base + QUADSPI_SFB1AD);
- qspi_writel(q, q->devtype_data->ahb_buf_size * 4 + addr_offset,
- base + QUADSPI_SFB2AD);
+ sfa_size = q->devtype_data->sfa_size ? : q->devtype_data->ahb_buf_size;
+ qspi_writel(q, addr_offset + 1 * sfa_size, base + QUADSPI_SFA1AD);
+ qspi_writel(q, addr_offset + 2 * sfa_size, base + QUADSPI_SFA2AD);
+ qspi_writel(q, addr_offset + 3 * sfa_size, base + QUADSPI_SFB1AD);
+ qspi_writel(q, addr_offset + 4 * sfa_size, base + QUADSPI_SFB2AD);
q->selected = -1;
@@ -857,6 +884,8 @@ static void fsl_qspi_cleanup(void *data)
{
struct fsl_qspi *q = data;
+ reset_control_assert(q->resets);
+
fsl_qspi_clk_disable_unprep(q);
mutex_destroy(&q->lock);
@@ -902,6 +931,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (!q->ahb_addr)
return -ENOMEM;
+ q->resets = devm_reset_control_array_get_optional_exclusive(dev);
+ if (IS_ERR(q->resets))
+ return PTR_ERR(q->resets);
+
/* find the clocks */
q->clk_en = devm_clk_get(dev, "qspi_en");
if (IS_ERR(q->clk_en))
@@ -923,6 +956,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ret = reset_control_deassert(q->resets);
+ if (ret)
+ return ret;
+
/* find the irq */
ret = platform_get_irq(pdev, 0);
if (ret < 0)
@@ -976,6 +1013,7 @@ static const struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,imx6ul-qspi", .data = &imx6ul_data, },
{ .compatible = "fsl,ls1021a-qspi", .data = &ls1021a_data, },
{ .compatible = "fsl,ls2080a-qspi", .data = &ls2080a_data, },
+ { .compatible = "spacemit,k1-qspi", .data = &spacemit_k1_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index bbf1fd4fe1e9..b8b79bb7fec3 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -42,6 +42,7 @@ MODULE_PARM_DESC(polling_limit_us,
"time in us to run a transfer in polling mode\n");
#define MXC_RPM_TIMEOUT 2000 /* 2000ms */
+#define MXC_SPI_DEFAULT_SPEED 500000 /* 500KHz */
#define MXC_CSPIRXDATA 0x00
#define MXC_CSPITXDATA 0x04
@@ -424,8 +425,15 @@ static void spi_imx_buf_tx_swap(struct spi_imx_data *spi_imx)
static void mx53_ecspi_rx_target(struct spi_imx_data *spi_imx)
{
- u32 val = ioread32be(spi_imx->base + MXC_CSPIRXDATA);
+ u32 val = readl(spi_imx->base + MXC_CSPIRXDATA);
+#ifdef __LITTLE_ENDIAN
+ unsigned int bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word);
+ if (bytes_per_word == 1)
+ swab32s(&val);
+ else if (bytes_per_word == 2)
+ swahw32s(&val);
+#endif
if (spi_imx->rx_buf) {
int n_bytes = spi_imx->target_burst % sizeof(val);
@@ -446,6 +454,9 @@ static void mx53_ecspi_tx_target(struct spi_imx_data *spi_imx)
{
u32 val = 0;
int n_bytes = spi_imx->count % sizeof(val);
+#ifdef __LITTLE_ENDIAN
+ unsigned int bytes_per_word;
+#endif
if (!n_bytes)
n_bytes = sizeof(val);
@@ -458,7 +469,14 @@ static void mx53_ecspi_tx_target(struct spi_imx_data *spi_imx)
spi_imx->count -= n_bytes;
- iowrite32be(val, spi_imx->base + MXC_CSPITXDATA);
+#ifdef __LITTLE_ENDIAN
+ bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word);
+ if (bytes_per_word == 1)
+ swab32s(&val);
+ else if (bytes_per_word == 2)
+ swahw32s(&val);
+#endif
+ writel(val, spi_imx->base + MXC_CSPITXDATA);
}
/* MX51 eCSPI */
@@ -591,7 +609,7 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx,
* is not functional for imx53 Soc, config SPI burst completed when
* BURST_LENGTH + 1 bits are received
*/
- if (spi_imx->target_mode && is_imx53_ecspi(spi_imx))
+ if (spi_imx->target_mode)
cfg &= ~MX51_ECSPI_CONFIG_SBBCTRL(channel);
else
cfg |= MX51_ECSPI_CONFIG_SBBCTRL(channel);
@@ -679,7 +697,7 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
/* Clear BL field and set the right value */
ctrl &= ~MX51_ECSPI_CTRL_BL_MASK;
- if (spi_imx->target_mode && is_imx53_ecspi(spi_imx))
+ if (spi_imx->target_mode)
ctrl |= (spi_imx->target_burst * 8 - 1)
<< MX51_ECSPI_CTRL_BL_OFFSET;
else {
@@ -690,8 +708,11 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
/* set clock speed */
ctrl &= ~(0xf << MX51_ECSPI_CTRL_POSTDIV_OFFSET |
0xf << MX51_ECSPI_CTRL_PREDIV_OFFSET);
- ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->spi_bus_clk, &clk);
- spi_imx->spi_bus_clk = clk;
+
+ if (!spi_imx->target_mode) {
+ ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->spi_bus_clk, &clk);
+ spi_imx->spi_bus_clk = clk;
+ }
mx51_configure_cpha(spi_imx, spi);
@@ -1313,15 +1334,18 @@ static int spi_imx_setupxfer(struct spi_device *spi,
if (!t)
return 0;
- if (!t->speed_hz) {
- if (!spi->max_speed_hz) {
- dev_err(&spi->dev, "no speed_hz provided!\n");
- return -EINVAL;
+ if (!spi_imx->target_mode) {
+ if (!t->speed_hz) {
+ if (!spi->max_speed_hz) {
+ dev_err(&spi->dev, "no speed_hz provided!\n");
+ return -EINVAL;
+ }
+ dev_dbg(&spi->dev, "using spi->max_speed_hz!\n");
+ spi_imx->spi_bus_clk = spi->max_speed_hz;
+ } else {
+ spi_imx->spi_bus_clk = t->speed_hz;
}
- dev_dbg(&spi->dev, "using spi->max_speed_hz!\n");
- spi_imx->spi_bus_clk = spi->max_speed_hz;
- } else
- spi_imx->spi_bus_clk = t->speed_hz;
+ }
spi_imx->bits_per_word = t->bits_per_word;
spi_imx->count = t->len;
@@ -1365,7 +1389,7 @@ static int spi_imx_setupxfer(struct spi_device *spi,
spi_imx->rx_only = ((t->tx_buf == NULL)
|| (t->tx_buf == spi->controller->dummy_tx));
- if (is_imx53_ecspi(spi_imx) && spi_imx->target_mode) {
+ if (spi_imx->target_mode) {
spi_imx->rx = mx53_ecspi_rx_target;
spi_imx->tx = mx53_ecspi_tx_target;
spi_imx->target_burst = t->len;
@@ -1641,8 +1665,7 @@ static int spi_imx_pio_transfer_target(struct spi_device *spi,
struct spi_imx_data *spi_imx = spi_controller_get_devdata(spi->controller);
int ret = 0;
- if (is_imx53_ecspi(spi_imx) &&
- transfer->len > MX53_MAX_TRANSFER_BYTES) {
+ if (transfer->len > MX53_MAX_TRANSFER_BYTES) {
dev_err(&spi->dev, "Transaction too big, max size is %d bytes\n",
MX53_MAX_TRANSFER_BYTES);
return -EMSGSIZE;
@@ -1838,6 +1861,7 @@ static int spi_imx_probe(struct platform_device *pdev)
controller->prepare_message = spi_imx_prepare_message;
controller->unprepare_message = spi_imx_unprepare_message;
controller->target_abort = spi_imx_target_abort;
+ spi_imx->spi_bus_clk = MXC_SPI_DEFAULT_SPEED;
controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS |
SPI_MOSI_IDLE_LOW;
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 064b99204d9a..c8b2add2640e 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -12,6 +12,9 @@
#include <linux/spi/spi-mem.h>
#include <linux/sched/task_stack.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/spi-mem.h>
+
#include "internals.h"
#define SPI_MEM_MAX_BUSWIDTH 8
@@ -403,7 +406,9 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
if (ret)
return ret;
+ trace_spi_mem_start_op(mem, op);
ret = ctlr->mem_ops->exec_op(mem, op);
+ trace_spi_mem_stop_op(mem, op);
spi_mem_access_end(mem);
diff --git a/drivers/spi/spi-microchip-core-spi.c b/drivers/spi/spi-microchip-core-spi.c
new file mode 100644
index 000000000000..98bf0e6cd00e
--- /dev/null
+++ b/drivers/spi/spi-microchip-core-spi.c
@@ -0,0 +1,429 @@
+// SPDX-License-Identifier: (GPL-2.0)
+//
+// Microchip CoreSPI controller driver
+//
+// Copyright (c) 2025 Microchip Technology Inc. and its subsidiaries
+//
+// Author: Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#define MCHP_CORESPI_MAX_CS (8)
+#define MCHP_CORESPI_DEFAULT_FIFO_DEPTH (4)
+#define MCHP_CORESPI_DEFAULT_MOTOROLA_MODE (3)
+
+#define MCHP_CORESPI_CONTROL_ENABLE BIT(0)
+#define MCHP_CORESPI_CONTROL_MASTER BIT(1)
+#define MCHP_CORESPI_CONTROL_TX_DATA_INT BIT(3)
+#define MCHP_CORESPI_CONTROL_RX_OVER_INT BIT(4)
+#define MCHP_CORESPI_CONTROL_TX_UNDER_INT BIT(5)
+#define MCHP_CORESPI_CONTROL_FRAMEURUN BIT(6)
+#define MCHP_CORESPI_CONTROL_OENOFF BIT(7)
+
+#define MCHP_CORESPI_STATUS_ACTIVE BIT(7)
+#define MCHP_CORESPI_STATUS_SSEL BIT(6)
+#define MCHP_CORESPI_STATUS_TXFIFO_UNDERFLOW BIT(5)
+#define MCHP_CORESPI_STATUS_RXFIFO_FULL BIT(4)
+#define MCHP_CORESPI_STATUS_TXFIFO_FULL BIT(3)
+#define MCHP_CORESPI_STATUS_RXFIFO_EMPTY BIT(2)
+#define MCHP_CORESPI_STATUS_DONE BIT(1)
+#define MCHP_CORESPI_STATUS_FIRSTFRAME BIT(0)
+
+#define MCHP_CORESPI_INT_TXDONE BIT(0)
+#define MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW BIT(2)
+#define MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN BIT(3)
+#define MCHP_CORESPI_INT_CMDINT BIT(4)
+#define MCHP_CORESPI_INT_SSEND BIT(5)
+#define MCHP_CORESPI_INT_DATA_RX BIT(6)
+#define MCHP_CORESPI_INT_TXRFM BIT(7)
+
+#define MCHP_CORESPI_CONTROL2_INTEN_TXRFMT BIT(7)
+#define MCHP_CORESPI_CONTROL2_INTEN_DATA_RX BIT(6)
+#define MCHP_CORESPI_CONTROL2_INTEN_SSEND BIT(5)
+#define MCHP_CORESPI_CONTROL2_INTEN_CMD BIT(4)
+
+#define INT_ENABLE_MASK (MCHP_CORESPI_CONTROL_TX_DATA_INT | MCHP_CORESPI_CONTROL_RX_OVER_INT | \
+ MCHP_CORESPI_CONTROL_TX_UNDER_INT)
+
+#define MCHP_CORESPI_REG_CONTROL (0x00)
+#define MCHP_CORESPI_REG_INTCLEAR (0x04)
+#define MCHP_CORESPI_REG_RXDATA (0x08)
+#define MCHP_CORESPI_REG_TXDATA (0x0c)
+#define MCHP_CORESPI_REG_INTMASK (0X10)
+#define MCHP_CORESPI_REG_INTRAW (0X14)
+#define MCHP_CORESPI_REG_CONTROL2 (0x18)
+#define MCHP_CORESPI_REG_COMMAND (0x1c)
+#define MCHP_CORESPI_REG_STAT (0x20)
+#define MCHP_CORESPI_REG_SSEL (0x24)
+#define MCHP_CORESPI_REG_TXDATA_LAST (0X28)
+#define MCHP_CORESPI_REG_CLK_DIV (0x2c)
+
+struct mchp_corespi {
+ void __iomem *regs;
+ struct clk *clk;
+ const u8 *tx_buf;
+ u8 *rx_buf;
+ u32 clk_gen;
+ int irq;
+ unsigned int tx_len;
+ unsigned int rx_len;
+ u32 fifo_depth;
+};
+
+static inline void mchp_corespi_disable(struct mchp_corespi *spi)
+{
+ u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
+
+ control &= ~MCHP_CORESPI_CONTROL_ENABLE;
+
+ writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
+}
+
+static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, u32 fifo_max)
+{
+ for (int i = 0; i < fifo_max; i++) {
+ u32 data;
+
+ while (readb(spi->regs + MCHP_CORESPI_REG_STAT) &
+ MCHP_CORESPI_STATUS_RXFIFO_EMPTY)
+ ;
+
+ /* On TX-only transfers always perform a dummy read */
+ data = readb(spi->regs + MCHP_CORESPI_REG_RXDATA);
+ if (spi->rx_buf)
+ *spi->rx_buf++ = data;
+
+ spi->rx_len--;
+ }
+}
+
+static void mchp_corespi_enable_ints(struct mchp_corespi *spi)
+{
+ u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
+
+ control |= INT_ENABLE_MASK;
+ writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
+}
+
+static void mchp_corespi_disable_ints(struct mchp_corespi *spi)
+{
+ u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
+
+ control &= ~INT_ENABLE_MASK;
+ writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
+}
+
+static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, u32 fifo_max)
+{
+ for (int i = 0; i < fifo_max; i++) {
+ if (readb(spi->regs + MCHP_CORESPI_REG_STAT) &
+ MCHP_CORESPI_STATUS_TXFIFO_FULL)
+ break;
+
+ /* On RX-only transfers always perform a dummy write */
+ if (spi->tx_buf)
+ writeb(*spi->tx_buf++, spi->regs + MCHP_CORESPI_REG_TXDATA);
+ else
+ writeb(0xaa, spi->regs + MCHP_CORESPI_REG_TXDATA);
+
+ spi->tx_len--;
+ }
+}
+
+static void mchp_corespi_set_cs(struct spi_device *spi, bool disable)
+{
+ struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller);
+ u32 reg;
+
+ reg = readb(corespi->regs + MCHP_CORESPI_REG_SSEL);
+ reg &= ~BIT(spi_get_chipselect(spi, 0));
+ reg |= !disable << spi_get_chipselect(spi, 0);
+
+ writeb(reg, corespi->regs + MCHP_CORESPI_REG_SSEL);
+}
+
+static int mchp_corespi_setup(struct spi_device *spi)
+{
+ if (spi_get_csgpiod(spi, 0))
+ return 0;
+
+ if (spi->mode & (SPI_CS_HIGH)) {
+ dev_err(&spi->dev, "unable to support active-high CS in Motorola mode\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (spi->mode & SPI_MODE_X_MASK & ~spi->controller->mode_bits) {
+ dev_err(&spi->dev, "incompatible CPOL/CPHA, must match controller's Motorola mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *spi)
+{
+ u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
+
+ /* Master mode changes require core to be disabled.*/
+ control = (control & ~MCHP_CORESPI_CONTROL_ENABLE) | MCHP_CORESPI_CONTROL_MASTER;
+
+ writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
+
+ mchp_corespi_enable_ints(spi);
+
+ control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
+ control |= MCHP_CORESPI_CONTROL_ENABLE;
+
+ writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
+}
+
+static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
+{
+ struct spi_controller *host = dev_id;
+ struct mchp_corespi *spi = spi_controller_get_devdata(host);
+ u8 intfield = readb(spi->regs + MCHP_CORESPI_REG_INTMASK) & 0xff;
+ bool finalise = false;
+
+ /* Interrupt line may be shared and not for us at all */
+ if (intfield == 0)
+ return IRQ_NONE;
+
+ if (intfield & MCHP_CORESPI_INT_TXDONE)
+ writeb(MCHP_CORESPI_INT_TXDONE, spi->regs + MCHP_CORESPI_REG_INTCLEAR);
+
+ if (intfield & MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW) {
+ writeb(MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW,
+ spi->regs + MCHP_CORESPI_REG_INTCLEAR);
+ finalise = true;
+ dev_err(&host->dev,
+ "RX OVERFLOW: rxlen: %u, txlen: %u\n",
+ spi->rx_len, spi->tx_len);
+ }
+
+ if (intfield & MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN) {
+ writeb(MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN,
+ spi->regs + MCHP_CORESPI_REG_INTCLEAR);
+ finalise = true;
+ dev_err(&host->dev,
+ "TX UNDERFLOW: rxlen: %u, txlen: %u\n",
+ spi->rx_len, spi->tx_len);
+ }
+
+ if (finalise)
+ spi_finalize_current_transfer(host);
+
+ return IRQ_HANDLED;
+}
+
+static int mchp_corespi_set_clk_div(struct mchp_corespi *spi,
+ unsigned long target_hz)
+{
+ unsigned long pclk_hz, spi_hz;
+ u32 clk_div;
+
+ /* Get peripheral clock rate */
+ pclk_hz = clk_get_rate(spi->clk);
+ if (!pclk_hz)
+ return -EINVAL;
+
+ /*
+ * Calculate clock rate generated by SPI master
+ * Formula: SPICLK = PCLK / (2 * (CLK_DIV + 1))
+ */
+ clk_div = DIV_ROUND_UP(pclk_hz, 2 * target_hz) - 1;
+
+ if (clk_div > 0xFF)
+ return -EINVAL;
+
+ spi_hz = pclk_hz / (2 * (clk_div + 1));
+
+ if (spi_hz > target_hz)
+ return -EINVAL;
+
+ writeb(clk_div, spi->regs + MCHP_CORESPI_REG_CLK_DIV);
+
+ return 0;
+}
+
+static int mchp_corespi_transfer_one(struct spi_controller *host,
+ struct spi_device *spi_dev,
+ struct spi_transfer *xfer)
+{
+ struct mchp_corespi *spi = spi_controller_get_devdata(host);
+ int ret;
+
+ ret = mchp_corespi_set_clk_div(spi, (unsigned long)xfer->speed_hz);
+ if (ret) {
+ dev_err(&host->dev, "failed to set clock divider for target %u Hz\n",
+ xfer->speed_hz);
+ return ret;
+ }
+
+ spi->tx_buf = xfer->tx_buf;
+ spi->rx_buf = xfer->rx_buf;
+ spi->tx_len = xfer->len;
+ spi->rx_len = xfer->len;
+
+ while (spi->tx_len) {
+ unsigned int fifo_max = min(spi->tx_len, spi->fifo_depth);
+
+ mchp_corespi_write_fifo(spi, fifo_max);
+ mchp_corespi_read_fifo(spi, fifo_max);
+ }
+
+ spi_finalize_current_transfer(host);
+ return 1;
+}
+
+static int mchp_corespi_probe(struct platform_device *pdev)
+{
+ const char *protocol = "motorola";
+ struct device *dev = &pdev->dev;
+ struct spi_controller *host;
+ struct mchp_corespi *spi;
+ struct resource *res;
+ u32 num_cs, mode, frame_size;
+ bool assert_ssel;
+ int ret = 0;
+
+ host = devm_spi_alloc_host(dev, sizeof(*spi));
+ if (!host)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, host);
+
+ if (of_property_read_u32(dev->of_node, "num-cs", &num_cs))
+ num_cs = MCHP_CORESPI_MAX_CS;
+
+ /*
+ * Protocol: CFG_MODE
+ * CoreSPI can be configured for Motorola, TI or NSC.
+ * The current driver supports only Motorola mode.
+ */
+ ret = of_property_read_string(dev->of_node, "microchip,protocol-configuration",
+ &protocol);
+ if (ret && ret != -EINVAL)
+ return dev_err_probe(dev, ret, "Error reading protocol-configuration\n");
+ if (strcmp(protocol, "motorola") != 0)
+ return dev_err_probe(dev, -EINVAL,
+ "CoreSPI: protocol '%s' not supported by this driver\n",
+ protocol);
+
+ /*
+ * Motorola mode (0-3): CFG_MOT_MODE
+ * Mode is fixed in the IP configurator.
+ */
+ ret = of_property_read_u32(dev->of_node, "microchip,motorola-mode", &mode);
+ if (ret)
+ mode = MCHP_CORESPI_DEFAULT_MOTOROLA_MODE;
+ else if (mode > 3)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid 'microchip,motorola-mode' value %u\n", mode);
+
+ /*
+ * Frame size: CFG_FRAME_SIZE
+ * The hardware allows frame sizes <= APB data width.
+ * However, this driver currently only supports 8-bit frames.
+ */
+ ret = of_property_read_u32(dev->of_node, "microchip,frame-size", &frame_size);
+ if (!ret && frame_size != 8)
+ return dev_err_probe(dev, -EINVAL,
+ "CoreSPI: frame size %u not supported by this driver\n",
+ frame_size);
+
+ /*
+ * SSEL: CFG_MOT_SSEL
+ * CoreSPI deasserts SSEL when the TX FIFO empties.
+ * To prevent CS deassertion when TX FIFO drains, the ssel-active property
+ * keeps CS asserted for the full SPI transfer.
+ */
+ assert_ssel = of_property_read_bool(dev->of_node, "microchip,ssel-active");
+ if (!assert_ssel)
+ return dev_err_probe(dev, -EINVAL,
+ "hardware must enable 'microchip,ssel-active' to keep CS asserted for the SPI transfer\n");
+
+ spi = spi_controller_get_devdata(host);
+
+ host->num_chipselect = num_cs;
+ host->mode_bits = mode;
+ host->setup = mchp_corespi_setup;
+ host->use_gpio_descriptors = true;
+ host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
+ host->transfer_one = mchp_corespi_transfer_one;
+ host->set_cs = mchp_corespi_set_cs;
+ host->dev.of_node = dev->of_node;
+
+ ret = of_property_read_u32(dev->of_node, "fifo-depth", &spi->fifo_depth);
+ if (ret)
+ spi->fifo_depth = MCHP_CORESPI_DEFAULT_FIFO_DEPTH;
+
+ spi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(spi->regs))
+ return PTR_ERR(spi->regs);
+
+ spi->irq = platform_get_irq(pdev, 0);
+ if (spi->irq < 0)
+ return spi->irq;
+
+ ret = devm_request_irq(dev, spi->irq, mchp_corespi_interrupt, IRQF_SHARED,
+ dev_name(dev), host);
+ if (ret)
+ return dev_err_probe(dev, ret, "could not request irq\n");
+
+ spi->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(spi->clk))
+ return dev_err_probe(dev, PTR_ERR(spi->clk), "could not get clk\n");
+
+ mchp_corespi_init(host, spi);
+
+ ret = devm_spi_register_controller(dev, host);
+ if (ret) {
+ mchp_corespi_disable(spi);
+ return dev_err_probe(dev, ret, "unable to register host for CoreSPI controller\n");
+ }
+
+ return 0;
+}
+
+static void mchp_corespi_remove(struct platform_device *pdev)
+{
+ struct spi_controller *host = platform_get_drvdata(pdev);
+ struct mchp_corespi *spi = spi_controller_get_devdata(host);
+
+ mchp_corespi_disable_ints(spi);
+ mchp_corespi_disable(spi);
+}
+
+/*
+ * Platform driver data structure
+ */
+
+#if defined(CONFIG_OF)
+static const struct of_device_id mchp_corespi_dt_ids[] = {
+ { .compatible = "microchip,corespi-rtl-v5" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids);
+#endif
+
+static struct platform_driver mchp_corespi_driver = {
+ .probe = mchp_corespi_probe,
+ .driver = {
+ .name = "microchip-corespi",
+ .of_match_table = of_match_ptr(mchp_corespi_dt_ids),
+ },
+ .remove = mchp_corespi_remove,
+};
+module_platform_driver(mchp_corespi_driver);
+MODULE_DESCRIPTION("Microchip CoreSPI controller driver");
+MODULE_AUTHOR("Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-microchip-core.c b/drivers/spi/spi-mpfs.c
index 9128b86c5366..9a14d1732a15 100644
--- a/drivers/spi/spi-microchip-core.c
+++ b/drivers/spi/spi-mpfs.c
@@ -99,7 +99,7 @@
#define REG_CTRL2 (0x48)
#define REG_FRAMESUP (0x50)
-struct mchp_corespi {
+struct mpfs_spi {
void __iomem *regs;
struct clk *clk;
const u8 *tx_buf;
@@ -113,34 +113,34 @@ struct mchp_corespi {
int n_bytes;
};
-static inline u32 mchp_corespi_read(struct mchp_corespi *spi, unsigned int reg)
+static inline u32 mpfs_spi_read(struct mpfs_spi *spi, unsigned int reg)
{
return readl(spi->regs + reg);
}
-static inline void mchp_corespi_write(struct mchp_corespi *spi, unsigned int reg, u32 val)
+static inline void mpfs_spi_write(struct mpfs_spi *spi, unsigned int reg, u32 val)
{
writel(val, spi->regs + reg);
}
-static inline void mchp_corespi_disable(struct mchp_corespi *spi)
+static inline void mpfs_spi_disable(struct mpfs_spi *spi)
{
- u32 control = mchp_corespi_read(spi, REG_CONTROL);
+ u32 control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~CONTROL_ENABLE;
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CONTROL, control);
}
-static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, int fifo_max)
+static inline void mpfs_spi_read_fifo(struct mpfs_spi *spi, int fifo_max)
{
for (int i = 0; i < fifo_max; i++) {
u32 data;
- while (mchp_corespi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)
+ while (mpfs_spi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)
;
- data = mchp_corespi_read(spi, REG_RX_DATA);
+ data = mpfs_spi_read(spi, REG_RX_DATA);
spi->rx_len -= spi->n_bytes;
@@ -158,34 +158,34 @@ static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, int fifo_max
}
}
-static void mchp_corespi_enable_ints(struct mchp_corespi *spi)
+static void mpfs_spi_enable_ints(struct mpfs_spi *spi)
{
- u32 control = mchp_corespi_read(spi, REG_CONTROL);
+ u32 control = mpfs_spi_read(spi, REG_CONTROL);
control |= INT_ENABLE_MASK;
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CONTROL, control);
}
-static void mchp_corespi_disable_ints(struct mchp_corespi *spi)
+static void mpfs_spi_disable_ints(struct mpfs_spi *spi)
{
- u32 control = mchp_corespi_read(spi, REG_CONTROL);
+ u32 control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~INT_ENABLE_MASK;
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CONTROL, control);
}
-static inline void mchp_corespi_set_xfer_size(struct mchp_corespi *spi, int len)
+static inline void mpfs_spi_set_xfer_size(struct mpfs_spi *spi, int len)
{
u32 control;
u32 lenpart;
- u32 frames = mchp_corespi_read(spi, REG_FRAMESUP);
+ u32 frames = mpfs_spi_read(spi, REG_FRAMESUP);
/*
* Writing to FRAMECNT in REG_CONTROL will reset the frame count, taking
* a shortcut requires an explicit clear.
*/
if (frames == len) {
- mchp_corespi_write(spi, REG_COMMAND, COMMAND_CLRFRAMECNT);
+ mpfs_spi_write(spi, REG_COMMAND, COMMAND_CLRFRAMECNT);
return;
}
@@ -208,20 +208,20 @@ static inline void mchp_corespi_set_xfer_size(struct mchp_corespi *spi, int len)
* that matches the documentation.
*/
lenpart = len & 0xffff;
- control = mchp_corespi_read(spi, REG_CONTROL);
+ control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~CONTROL_FRAMECNT_MASK;
control |= lenpart << CONTROL_FRAMECNT_SHIFT;
- mchp_corespi_write(spi, REG_CONTROL, control);
- mchp_corespi_write(spi, REG_FRAMESUP, len);
+ mpfs_spi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_FRAMESUP, len);
}
-static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_max)
+static inline void mpfs_spi_write_fifo(struct mpfs_spi *spi, int fifo_max)
{
int i = 0;
- mchp_corespi_set_xfer_size(spi, fifo_max);
+ mpfs_spi_set_xfer_size(spi, fifo_max);
- while ((i < fifo_max) && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) {
+ while ((i < fifo_max) && !(mpfs_spi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) {
u32 word;
if (spi->n_bytes == 4)
@@ -231,7 +231,7 @@ static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_ma
else
word = spi->tx_buf ? *spi->tx_buf : 0xaa;
- mchp_corespi_write(spi, REG_TX_DATA, word);
+ mpfs_spi_write(spi, REG_TX_DATA, word);
if (spi->tx_buf)
spi->tx_buf += spi->n_bytes;
i++;
@@ -240,9 +240,9 @@ static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_ma
spi->tx_len -= i * spi->n_bytes;
}
-static inline void mchp_corespi_set_framesize(struct mchp_corespi *spi, int bt)
+static inline void mpfs_spi_set_framesize(struct mpfs_spi *spi, int bt)
{
- u32 frame_size = mchp_corespi_read(spi, REG_FRAME_SIZE);
+ u32 frame_size = mpfs_spi_read(spi, REG_FRAME_SIZE);
u32 control;
if ((frame_size & FRAME_SIZE_MASK) == bt)
@@ -252,25 +252,25 @@ static inline void mchp_corespi_set_framesize(struct mchp_corespi *spi, int bt)
* Disable the SPI controller. Writes to the frame size have
* no effect when the controller is enabled.
*/
- control = mchp_corespi_read(spi, REG_CONTROL);
+ control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~CONTROL_ENABLE;
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CONTROL, control);
- mchp_corespi_write(spi, REG_FRAME_SIZE, bt);
+ mpfs_spi_write(spi, REG_FRAME_SIZE, bt);
control |= CONTROL_ENABLE;
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CONTROL, control);
}
-static void mchp_corespi_set_cs(struct spi_device *spi, bool disable)
+static void mpfs_spi_set_cs(struct spi_device *spi, bool disable)
{
u32 reg;
- struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller);
+ struct mpfs_spi *mspi = spi_controller_get_devdata(spi->controller);
- reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT);
+ reg = mpfs_spi_read(mspi, REG_SLAVE_SELECT);
reg &= ~BIT(spi_get_chipselect(spi, 0));
reg |= !disable << spi_get_chipselect(spi, 0);
- corespi->pending_slave_select = reg;
+ mspi->pending_slave_select = reg;
/*
* Only deassert chip select immediately. Writing to some registers
@@ -281,12 +281,12 @@ static void mchp_corespi_set_cs(struct spi_device *spi, bool disable)
* doesn't see any spurious clock transitions whilst CS is enabled.
*/
if (((spi->mode & SPI_CS_HIGH) == 0) == disable)
- mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
+ mpfs_spi_write(mspi, REG_SLAVE_SELECT, reg);
}
-static int mchp_corespi_setup(struct spi_device *spi)
+static int mpfs_spi_setup(struct spi_device *spi)
{
- struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller);
+ struct mpfs_spi *mspi = spi_controller_get_devdata(spi->controller);
u32 reg;
if (spi_is_csgpiod(spi))
@@ -298,21 +298,21 @@ static int mchp_corespi_setup(struct spi_device *spi)
* driving their select line low.
*/
if (spi->mode & SPI_CS_HIGH) {
- reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT);
+ reg = mpfs_spi_read(mspi, REG_SLAVE_SELECT);
reg |= BIT(spi_get_chipselect(spi, 0));
- corespi->pending_slave_select = reg;
- mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
+ mspi->pending_slave_select = reg;
+ mpfs_spi_write(mspi, REG_SLAVE_SELECT, reg);
}
return 0;
}
-static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *spi)
+static void mpfs_spi_init(struct spi_controller *host, struct mpfs_spi *spi)
{
unsigned long clk_hz;
- u32 control = mchp_corespi_read(spi, REG_CONTROL);
+ u32 control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~CONTROL_ENABLE;
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CONTROL, control);
control |= CONTROL_MASTER;
control &= ~CONTROL_MODE_MASK;
@@ -328,15 +328,15 @@ static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *
*/
control |= CONTROL_SPS | CONTROL_BIGFIFO;
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CONTROL, control);
- mchp_corespi_set_framesize(spi, DEFAULT_FRAMESIZE);
+ mpfs_spi_set_framesize(spi, DEFAULT_FRAMESIZE);
/* max. possible spi clock rate is the apb clock rate */
clk_hz = clk_get_rate(spi->clk);
host->max_speed_hz = clk_hz;
- mchp_corespi_enable_ints(spi);
+ mpfs_spi_enable_ints(spi);
/*
* It is required to enable direct mode, otherwise control over the chip
@@ -344,34 +344,34 @@ static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *
* can deal with active high targets.
*/
spi->pending_slave_select = SSELOUT | SSEL_DIRECT;
- mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
+ mpfs_spi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
- control = mchp_corespi_read(spi, REG_CONTROL);
+ control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~CONTROL_RESET;
control |= CONTROL_ENABLE;
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CONTROL, control);
}
-static inline void mchp_corespi_set_clk_gen(struct mchp_corespi *spi)
+static inline void mpfs_spi_set_clk_gen(struct mpfs_spi *spi)
{
u32 control;
- control = mchp_corespi_read(spi, REG_CONTROL);
+ control = mpfs_spi_read(spi, REG_CONTROL);
if (spi->clk_mode)
control |= CONTROL_CLKMODE;
else
control &= ~CONTROL_CLKMODE;
- mchp_corespi_write(spi, REG_CLK_GEN, spi->clk_gen);
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CLK_GEN, spi->clk_gen);
+ mpfs_spi_write(spi, REG_CONTROL, control);
}
-static inline void mchp_corespi_set_mode(struct mchp_corespi *spi, unsigned int mode)
+static inline void mpfs_spi_set_mode(struct mpfs_spi *spi, unsigned int mode)
{
u32 mode_val;
- u32 control = mchp_corespi_read(spi, REG_CONTROL);
+ u32 control = mpfs_spi_read(spi, REG_CONTROL);
switch (mode & SPI_MODE_X_MASK) {
case SPI_MODE_0:
@@ -394,22 +394,22 @@ static inline void mchp_corespi_set_mode(struct mchp_corespi *spi, unsigned int
*/
control &= ~CONTROL_ENABLE;
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CONTROL, control);
control &= ~(SPI_MODE_X_MASK << MODE_X_MASK_SHIFT);
control |= mode_val;
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CONTROL, control);
control |= CONTROL_ENABLE;
- mchp_corespi_write(spi, REG_CONTROL, control);
+ mpfs_spi_write(spi, REG_CONTROL, control);
}
-static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
+static irqreturn_t mpfs_spi_interrupt(int irq, void *dev_id)
{
struct spi_controller *host = dev_id;
- struct mchp_corespi *spi = spi_controller_get_devdata(host);
- u32 intfield = mchp_corespi_read(spi, REG_MIS) & 0xf;
+ struct mpfs_spi *spi = spi_controller_get_devdata(host);
+ u32 intfield = mpfs_spi_read(spi, REG_MIS) & 0xf;
bool finalise = false;
/* Interrupt line may be shared and not for us at all */
@@ -417,7 +417,7 @@ static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
return IRQ_NONE;
if (intfield & INT_RX_CHANNEL_OVERFLOW) {
- mchp_corespi_write(spi, REG_INT_CLEAR, INT_RX_CHANNEL_OVERFLOW);
+ mpfs_spi_write(spi, REG_INT_CLEAR, INT_RX_CHANNEL_OVERFLOW);
finalise = true;
dev_err(&host->dev,
"%s: RX OVERFLOW: rxlen: %d, txlen: %d\n", __func__,
@@ -425,7 +425,7 @@ static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
}
if (intfield & INT_TX_CHANNEL_UNDERRUN) {
- mchp_corespi_write(spi, REG_INT_CLEAR, INT_TX_CHANNEL_UNDERRUN);
+ mpfs_spi_write(spi, REG_INT_CLEAR, INT_TX_CHANNEL_UNDERRUN);
finalise = true;
dev_err(&host->dev,
"%s: TX UNDERFLOW: rxlen: %d, txlen: %d\n", __func__,
@@ -438,8 +438,8 @@ static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int mchp_corespi_calculate_clkgen(struct mchp_corespi *spi,
- unsigned long target_hz)
+static int mpfs_spi_calculate_clkgen(struct mpfs_spi *spi,
+ unsigned long target_hz)
{
unsigned long clk_hz, spi_hz, clk_gen;
@@ -475,20 +475,20 @@ static int mchp_corespi_calculate_clkgen(struct mchp_corespi *spi,
return 0;
}
-static int mchp_corespi_transfer_one(struct spi_controller *host,
- struct spi_device *spi_dev,
- struct spi_transfer *xfer)
+static int mpfs_spi_transfer_one(struct spi_controller *host,
+ struct spi_device *spi_dev,
+ struct spi_transfer *xfer)
{
- struct mchp_corespi *spi = spi_controller_get_devdata(host);
+ struct mpfs_spi *spi = spi_controller_get_devdata(host);
int ret;
- ret = mchp_corespi_calculate_clkgen(spi, (unsigned long)xfer->speed_hz);
+ ret = mpfs_spi_calculate_clkgen(spi, (unsigned long)xfer->speed_hz);
if (ret) {
dev_err(&host->dev, "failed to set clk_gen for target %u Hz\n", xfer->speed_hz);
return ret;
}
- mchp_corespi_set_clk_gen(spi);
+ mpfs_spi_set_clk_gen(spi);
spi->tx_buf = xfer->tx_buf;
spi->rx_buf = xfer->rx_buf;
@@ -496,45 +496,46 @@ static int mchp_corespi_transfer_one(struct spi_controller *host,
spi->rx_len = xfer->len;
spi->n_bytes = roundup_pow_of_two(DIV_ROUND_UP(xfer->bits_per_word, BITS_PER_BYTE));
- mchp_corespi_set_framesize(spi, xfer->bits_per_word);
+ mpfs_spi_set_framesize(spi, xfer->bits_per_word);
- mchp_corespi_write(spi, REG_COMMAND, COMMAND_RXFIFORST | COMMAND_TXFIFORST);
+ mpfs_spi_write(spi, REG_COMMAND, COMMAND_RXFIFORST | COMMAND_TXFIFORST);
- mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
+ mpfs_spi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
while (spi->tx_len) {
int fifo_max = DIV_ROUND_UP(min(spi->tx_len, FIFO_DEPTH), spi->n_bytes);
- mchp_corespi_write_fifo(spi, fifo_max);
- mchp_corespi_read_fifo(spi, fifo_max);
+ mpfs_spi_write_fifo(spi, fifo_max);
+ mpfs_spi_read_fifo(spi, fifo_max);
}
spi_finalize_current_transfer(host);
return 1;
}
-static int mchp_corespi_prepare_message(struct spi_controller *host,
- struct spi_message *msg)
+static int mpfs_spi_prepare_message(struct spi_controller *host,
+ struct spi_message *msg)
{
struct spi_device *spi_dev = msg->spi;
- struct mchp_corespi *spi = spi_controller_get_devdata(host);
+ struct mpfs_spi *spi = spi_controller_get_devdata(host);
- mchp_corespi_set_mode(spi, spi_dev->mode);
+ mpfs_spi_set_mode(spi, spi_dev->mode);
return 0;
}
-static int mchp_corespi_probe(struct platform_device *pdev)
+static int mpfs_spi_probe(struct platform_device *pdev)
{
struct spi_controller *host;
- struct mchp_corespi *spi;
+ struct mpfs_spi *spi;
struct resource *res;
u32 num_cs;
int ret = 0;
host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi));
if (!host)
- return -ENOMEM;
+ return dev_err_probe(&pdev->dev, -ENOMEM,
+ "unable to allocate host for SPI controller\n");
platform_set_drvdata(pdev, host);
@@ -544,11 +545,11 @@ static int mchp_corespi_probe(struct platform_device *pdev)
host->num_chipselect = num_cs;
host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
host->use_gpio_descriptors = true;
- host->setup = mchp_corespi_setup;
+ host->setup = mpfs_spi_setup;
host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
- host->transfer_one = mchp_corespi_transfer_one;
- host->prepare_message = mchp_corespi_prepare_message;
- host->set_cs = mchp_corespi_set_cs;
+ host->transfer_one = mpfs_spi_transfer_one;
+ host->prepare_message = mpfs_spi_prepare_message;
+ host->set_cs = mpfs_spi_set_cs;
host->dev.of_node = pdev->dev.of_node;
spi = spi_controller_get_devdata(host);
@@ -561,7 +562,7 @@ static int mchp_corespi_probe(struct platform_device *pdev)
if (spi->irq < 0)
return spi->irq;
- ret = devm_request_irq(&pdev->dev, spi->irq, mchp_corespi_interrupt,
+ ret = devm_request_irq(&pdev->dev, spi->irq, mpfs_spi_interrupt,
IRQF_SHARED, dev_name(&pdev->dev), host);
if (ret)
return dev_err_probe(&pdev->dev, ret,
@@ -572,11 +573,11 @@ static int mchp_corespi_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk),
"could not get clk\n");
- mchp_corespi_init(host, spi);
+ mpfs_spi_init(host, spi);
ret = devm_spi_register_controller(&pdev->dev, host);
if (ret) {
- mchp_corespi_disable(spi);
+ mpfs_spi_disable(spi);
return dev_err_probe(&pdev->dev, ret,
"unable to register host for SPI controller\n");
}
@@ -586,13 +587,13 @@ static int mchp_corespi_probe(struct platform_device *pdev)
return 0;
}
-static void mchp_corespi_remove(struct platform_device *pdev)
+static void mpfs_spi_remove(struct platform_device *pdev)
{
struct spi_controller *host = platform_get_drvdata(pdev);
- struct mchp_corespi *spi = spi_controller_get_devdata(host);
+ struct mpfs_spi *spi = spi_controller_get_devdata(host);
- mchp_corespi_disable_ints(spi);
- mchp_corespi_disable(spi);
+ mpfs_spi_disable_ints(spi);
+ mpfs_spi_disable(spi);
}
#define MICROCHIP_SPI_PM_OPS (NULL)
@@ -602,23 +603,23 @@ static void mchp_corespi_remove(struct platform_device *pdev)
*/
#if defined(CONFIG_OF)
-static const struct of_device_id mchp_corespi_dt_ids[] = {
+static const struct of_device_id mpfs_spi_dt_ids[] = {
{ .compatible = "microchip,mpfs-spi" },
{ /* sentinel */ }
};
-MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids);
+MODULE_DEVICE_TABLE(of, mpfs_spi_dt_ids);
#endif
-static struct platform_driver mchp_corespi_driver = {
- .probe = mchp_corespi_probe,
+static struct platform_driver mpfs_spi_driver = {
+ .probe = mpfs_spi_probe,
.driver = {
- .name = "microchip-corespi",
+ .name = "microchip-spi",
.pm = MICROCHIP_SPI_PM_OPS,
- .of_match_table = of_match_ptr(mchp_corespi_dt_ids),
+ .of_match_table = of_match_ptr(mpfs_spi_dt_ids),
},
- .remove = mchp_corespi_remove,
+ .remove = mpfs_spi_remove,
};
-module_platform_driver(mchp_corespi_driver);
+module_platform_driver(mpfs_spi_driver);
MODULE_DESCRIPTION("Microchip coreSPI SPI controller driver");
MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
diff --git a/drivers/spi/spi-offload-trigger-pwm.c b/drivers/spi/spi-offload-trigger-pwm.c
index 805ed41560df..3e8c19227edb 100644
--- a/drivers/spi/spi-offload-trigger-pwm.c
+++ b/drivers/spi/spi-offload-trigger-pwm.c
@@ -51,12 +51,14 @@ static int spi_offload_trigger_pwm_validate(struct spi_offload_trigger *trigger,
wf.period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, periodic->frequency_hz);
/* REVISIT: 50% duty-cycle for now - may add config parameter later */
wf.duty_length_ns = wf.period_length_ns / 2;
+ wf.duty_offset_ns = periodic->offset_ns;
ret = pwm_round_waveform_might_sleep(st->pwm, &wf);
if (ret < 0)
return ret;
periodic->frequency_hz = DIV_ROUND_UP_ULL(NSEC_PER_SEC, wf.period_length_ns);
+ periodic->offset_ns = wf.duty_offset_ns;
return 0;
}
@@ -77,6 +79,7 @@ static int spi_offload_trigger_pwm_enable(struct spi_offload_trigger *trigger,
wf.period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, periodic->frequency_hz);
/* REVISIT: 50% duty-cycle for now - may add config parameter later */
wf.duty_length_ns = wf.period_length_ns / 2;
+ wf.duty_offset_ns = periodic->offset_ns;
return pwm_set_waveform_might_sleep(st->pwm, &wf, false);
}
diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c
index 58ceea1ea8fb..7681a91d67d5 100644
--- a/drivers/spi/spi-qpic-snand.c
+++ b/drivers/spi/spi-qpic-snand.c
@@ -448,7 +448,7 @@ static int qcom_spi_ecc_finish_io_req_pipelined(struct nand_device *nand,
return snandc->qspi->ecc_stats.bitflips;
}
-static struct nand_ecc_engine_ops qcom_spi_ecc_engine_ops_pipelined = {
+static const struct nand_ecc_engine_ops qcom_spi_ecc_engine_ops_pipelined = {
.init_ctx = qcom_spi_ecc_init_ctx_pipelined,
.cleanup_ctx = qcom_spi_ecc_cleanup_ctx_pipelined,
.prepare_io_req = qcom_spi_ecc_prepare_io_req_pipelined,
diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c
index dcc431ba60a9..1db7e4e5d64e 100644
--- a/drivers/spi/spi-rzv2h-rspi.c
+++ b/drivers/spi/spi-rzv2h-rspi.c
@@ -24,6 +24,7 @@
/* Registers */
#define RSPI_SPDR 0x00
#define RSPI_SPCR 0x08
+#define RSPI_SPPCR 0x0e
#define RSPI_SSLP 0x10
#define RSPI_SPBR 0x11
#define RSPI_SPSCR 0x13
@@ -34,13 +35,18 @@
#define RSPI_SPFCR 0x6c
/* Register SPCR */
+#define RSPI_SPCR_BPEN BIT(31)
#define RSPI_SPCR_MSTR BIT(30)
#define RSPI_SPCR_SPRIE BIT(17)
#define RSPI_SPCR_SCKASE BIT(12)
#define RSPI_SPCR_SPE BIT(0)
+/* Register SPPCR */
+#define RSPI_SPPCR_SPLP2 BIT(1)
+
/* Register SPBR */
#define RSPI_SPBR_SPR_MIN 0
+#define RSPI_SPBR_SPR_PCLK_MIN 1
#define RSPI_SPBR_SPR_MAX 255
/* Register SPCMD */
@@ -58,7 +64,6 @@
/* Register SPDCR2 */
#define RSPI_SPDCR2_TTRG GENMASK(11, 8)
#define RSPI_SPDCR2_RTRG GENMASK(3, 0)
-#define RSPI_FIFO_SIZE 16
/* Register SPSR */
#define RSPI_SPSR_SPRF BIT(15)
@@ -67,17 +72,41 @@
#define RSPI_SPSRC_CLEAR 0xfd80
#define RSPI_RESET_NUM 2
-#define RSPI_CLK_NUM 3
+
+struct rzv2h_rspi_best_clock {
+ struct clk *clk;
+ unsigned long clk_rate;
+ unsigned long error;
+ u32 actual_hz;
+ u8 brdv;
+ u8 spr;
+};
+
+struct rzv2h_rspi_info {
+ void (*find_tclk_rate)(struct clk *clk, u32 hz, u8 spr_min, u8 spr_max,
+ struct rzv2h_rspi_best_clock *best_clk);
+ void (*find_pclk_rate)(struct clk *clk, u32 hz, u8 spr_low, u8 spr_high,
+ struct rzv2h_rspi_best_clock *best_clk);
+ const char *tclk_name;
+ unsigned int fifo_size;
+ unsigned int num_clks;
+};
struct rzv2h_rspi_priv {
struct reset_control_bulk_data resets[RSPI_RESET_NUM];
struct spi_controller *controller;
+ const struct rzv2h_rspi_info *info;
void __iomem *base;
struct clk *tclk;
+ struct clk *pclk;
wait_queue_head_t wait;
unsigned int bytes_per_word;
+ u32 last_speed_hz;
u32 freq;
u16 status;
+ u8 spr;
+ u8 brdv;
+ bool use_pclk;
};
#define RZV2H_RSPI_TX(func, type) \
@@ -232,9 +261,112 @@ static inline u32 rzv2h_rspi_calc_bitrate(unsigned long tclk_rate, u8 spr,
return DIV_ROUND_UP(tclk_rate, (2 * (spr + 1) * (1 << brdv)));
}
-static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz)
+static void rzv2h_rspi_find_rate_variable(struct clk *clk, u32 hz,
+ u8 spr_min, u8 spr_max,
+ struct rzv2h_rspi_best_clock *best)
+{
+ long clk_rate, clk_min_rate, clk_max_rate;
+ int min_rate_spr, max_rate_spr;
+ unsigned long error;
+ u32 actual_hz;
+ u8 brdv;
+ int spr;
+
+ /*
+ * On T2H / N2H, the source for the SPI clock is PCLKSPIn, which is a
+ * 1/32, 1/30, 1/25 or 1/24 divider of PLL4, which is 2400MHz,
+ * resulting in either 75MHz, 80MHz, 96MHz or 100MHz.
+ */
+ clk_min_rate = clk_round_rate(clk, 0);
+ if (clk_min_rate < 0)
+ return;
+
+ clk_max_rate = clk_round_rate(clk, ULONG_MAX);
+ if (clk_max_rate < 0)
+ return;
+
+ /*
+ * From the manual:
+ * Bit rate = f(PCLKSPIn) / (2 * (n + 1) * 2^N)
+ *
+ * If we adapt it to the current context, we get the following:
+ * hz = rate / ((spr + 1) * (1 << (brdv + 1)))
+ *
+ * This can be written in multiple forms depending on what we want to
+ * determine.
+ *
+ * To find the rate, having hz, spr and brdv:
+ * rate = hz * (spr + 1) * (1 << (brdv + 1)
+ *
+ * To find the spr, having rate, hz, and spr:
+ * spr = rate / (hz * (1 << (brdv + 1)) - 1
+ */
+
+ for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) {
+ /* Calculate the divisor needed to find the SPR from a rate. */
+ u32 rate_div = hz * (1 << (brdv + 1));
+
+ /*
+ * If the SPR for the minimum rate is greater than the maximum
+ * allowed value skip this BRDV. The divisor increases with each
+ * BRDV iteration, so the following BRDV might result in a
+ * minimum SPR that is in the valid range.
+ */
+ min_rate_spr = DIV_ROUND_CLOSEST(clk_min_rate, rate_div) - 1;
+ if (min_rate_spr > spr_max)
+ continue;
+
+ /*
+ * If the SPR for the maximum rate is less than the minimum
+ * allowed value, exit. The divisor only increases with each
+ * BRDV iteration, so the following BRDV cannot result in a
+ * maximum SPR that is in the valid range.
+ */
+ max_rate_spr = DIV_ROUND_CLOSEST(clk_max_rate, rate_div) - 1;
+ if (max_rate_spr < spr_min)
+ break;
+
+ if (min_rate_spr < spr_min)
+ min_rate_spr = spr_min;
+
+ if (max_rate_spr > spr_max)
+ max_rate_spr = spr_max;
+
+ for (spr = min_rate_spr; spr <= max_rate_spr; spr++) {
+ clk_rate = (spr + 1) * rate_div;
+
+ clk_rate = clk_round_rate(clk, clk_rate);
+ if (clk_rate <= 0)
+ continue;
+
+ actual_hz = rzv2h_rspi_calc_bitrate(clk_rate, spr, brdv);
+ error = abs((long)hz - (long)actual_hz);
+
+ if (error >= best->error)
+ continue;
+
+ *best = (struct rzv2h_rspi_best_clock) {
+ .clk = clk,
+ .clk_rate = clk_rate,
+ .error = error,
+ .actual_hz = actual_hz,
+ .brdv = brdv,
+ .spr = spr,
+ };
+
+ if (!error)
+ return;
+ }
+ }
+}
+
+static void rzv2h_rspi_find_rate_fixed(struct clk *clk, u32 hz,
+ u8 spr_min, u8 spr_max,
+ struct rzv2h_rspi_best_clock *best)
{
- unsigned long tclk_rate;
+ unsigned long clk_rate;
+ unsigned long error;
+ u32 actual_hz;
int spr;
u8 brdv;
@@ -247,21 +379,63 @@ static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz)
* * n = SPR - is RSPI_SPBR.SPR (from 0 to 255)
* * N = BRDV - is RSPI_SPCMD.BRDV (from 0 to 3)
*/
- tclk_rate = clk_get_rate(rspi->tclk);
+ clk_rate = clk_get_rate(clk);
for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) {
- spr = DIV_ROUND_UP(tclk_rate, hz * (1 << (brdv + 1)));
+ spr = DIV_ROUND_UP(clk_rate, hz * (1 << (brdv + 1)));
spr--;
- if (spr >= RSPI_SPBR_SPR_MIN && spr <= RSPI_SPBR_SPR_MAX)
+ if (spr >= spr_min && spr <= spr_max)
goto clock_found;
}
- return 0;
+ return;
clock_found:
- rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_BRDV, brdv);
- writeb(spr, rspi->base + RSPI_SPBR);
+ actual_hz = rzv2h_rspi_calc_bitrate(clk_rate, spr, brdv);
+ error = abs((long)hz - (long)actual_hz);
+
+ if (error >= best->error)
+ return;
+
+ *best = (struct rzv2h_rspi_best_clock) {
+ .clk = clk,
+ .clk_rate = clk_rate,
+ .error = error,
+ .actual_hz = actual_hz,
+ .brdv = brdv,
+ .spr = spr,
+ };
+}
+
+static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz)
+{
+ struct rzv2h_rspi_best_clock best_clock = {
+ .error = ULONG_MAX,
+ };
+ int ret;
+
+ rspi->info->find_tclk_rate(rspi->tclk, hz, RSPI_SPBR_SPR_MIN,
+ RSPI_SPBR_SPR_MAX, &best_clock);
+
+ /*
+ * T2H and N2H can also use PCLK as a source, which is 125MHz, but not
+ * when both SPR and BRDV are 0.
+ */
+ if (best_clock.error && rspi->info->find_pclk_rate)
+ rspi->info->find_pclk_rate(rspi->pclk, hz, RSPI_SPBR_SPR_PCLK_MIN,
+ RSPI_SPBR_SPR_MAX, &best_clock);
+
+ if (!best_clock.clk_rate)
+ return -EINVAL;
- return rzv2h_rspi_calc_bitrate(tclk_rate, spr, brdv);
+ ret = clk_set_rate(best_clock.clk, best_clock.clk_rate);
+ if (ret)
+ return 0;
+
+ rspi->use_pclk = best_clock.clk == rspi->pclk;
+ rspi->spr = best_clock.spr;
+ rspi->brdv = best_clock.brdv;
+
+ return best_clock.actual_hz;
}
static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr,
@@ -274,10 +448,34 @@ static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr,
u8 bits_per_word;
u32 conf32;
u16 conf16;
+ u8 conf8;
/* Make sure SPCR.SPE is 0 before amending the configuration */
rzv2h_rspi_spe_disable(rspi);
+ list_for_each_entry(xfer, &message->transfers, transfer_list) {
+ if (!xfer->speed_hz)
+ continue;
+
+ speed_hz = min(xfer->speed_hz, speed_hz);
+ bits_per_word = xfer->bits_per_word;
+ }
+
+ if (speed_hz == U32_MAX)
+ return -EINVAL;
+
+ rspi->bytes_per_word = roundup_pow_of_two(BITS_TO_BYTES(bits_per_word));
+
+ if (speed_hz != rspi->last_speed_hz) {
+ rspi->freq = rzv2h_rspi_setup_clock(rspi, speed_hz);
+ if (!rspi->freq)
+ return -EINVAL;
+
+ rspi->last_speed_hz = speed_hz;
+ }
+
+ writeb(rspi->spr, rspi->base + RSPI_SPBR);
+
/* Configure the device to work in "host" mode */
conf32 = RSPI_SPCR_MSTR;
@@ -287,15 +485,24 @@ static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr,
/* SPI receive buffer full interrupt enable */
conf32 |= RSPI_SPCR_SPRIE;
+ /* Bypass synchronization circuit */
+ conf32 |= FIELD_PREP(RSPI_SPCR_BPEN, rspi->use_pclk);
+
writel(conf32, rspi->base + RSPI_SPCR);
/* Use SPCMD0 only */
writeb(0x0, rspi->base + RSPI_SPSCR);
+ /* Setup loopback */
+ conf8 = FIELD_PREP(RSPI_SPPCR_SPLP2, !!(spi->mode & SPI_LOOP));
+ writeb(conf8, rspi->base + RSPI_SPPCR);
+
/* Setup mode */
conf32 = FIELD_PREP(RSPI_SPCMD_CPOL, !!(spi->mode & SPI_CPOL));
conf32 |= FIELD_PREP(RSPI_SPCMD_CPHA, !!(spi->mode & SPI_CPHA));
conf32 |= FIELD_PREP(RSPI_SPCMD_LSBF, !!(spi->mode & SPI_LSB_FIRST));
+ conf32 |= FIELD_PREP(RSPI_SPCMD_SPB, bits_per_word - 1);
+ conf32 |= FIELD_PREP(RSPI_SPCMD_BRDV, rspi->brdv);
conf32 |= FIELD_PREP(RSPI_SPCMD_SSLKP, 1);
conf32 |= FIELD_PREP(RSPI_SPCMD_SSLA, spi_get_chipselect(spi, 0));
writel(conf32, rspi->base + RSPI_SPCMD);
@@ -305,30 +512,12 @@ static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr,
writeb(0, rspi->base + RSPI_SSLP);
/* Setup FIFO thresholds */
- conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, RSPI_FIFO_SIZE - 1);
+ conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, rspi->info->fifo_size - 1);
conf16 |= FIELD_PREP(RSPI_SPDCR2_RTRG, 0);
writew(conf16, rspi->base + RSPI_SPDCR2);
rzv2h_rspi_clear_fifos(rspi);
- list_for_each_entry(xfer, &message->transfers, transfer_list) {
- if (!xfer->speed_hz)
- continue;
-
- speed_hz = min(xfer->speed_hz, speed_hz);
- bits_per_word = xfer->bits_per_word;
- }
-
- if (speed_hz == U32_MAX)
- return -EINVAL;
-
- rspi->bytes_per_word = roundup_pow_of_two(BITS_TO_BYTES(bits_per_word));
- rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_SPB, bits_per_word - 1);
-
- rspi->freq = rzv2h_rspi_setup_clock(rspi, speed_hz);
- if (!rspi->freq)
- return -EINVAL;
-
rzv2h_rspi_spe_enable(rspi);
return 0;
@@ -350,8 +539,8 @@ static int rzv2h_rspi_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct rzv2h_rspi_priv *rspi;
struct clk_bulk_data *clks;
- unsigned long tclk_rate;
int irq_rx, ret, i;
+ long tclk_rate;
controller = devm_spi_alloc_host(dev, sizeof(*rspi));
if (!controller)
@@ -362,30 +551,32 @@ static int rzv2h_rspi_probe(struct platform_device *pdev)
rspi->controller = controller;
+ rspi->info = device_get_match_data(dev);
+
rspi->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rspi->base))
return PTR_ERR(rspi->base);
ret = devm_clk_bulk_get_all_enabled(dev, &clks);
- if (ret != RSPI_CLK_NUM)
+ if (ret != rspi->info->num_clks)
return dev_err_probe(dev, ret >= 0 ? -EINVAL : ret,
"cannot get clocks\n");
- for (i = 0; i < RSPI_CLK_NUM; i++) {
- if (!strcmp(clks[i].id, "tclk")) {
+ for (i = 0; i < rspi->info->num_clks; i++) {
+ if (!strcmp(clks[i].id, rspi->info->tclk_name)) {
rspi->tclk = clks[i].clk;
- break;
+ } else if (rspi->info->find_pclk_rate &&
+ !strcmp(clks[i].id, "pclk")) {
+ rspi->pclk = clks[i].clk;
}
}
if (!rspi->tclk)
return dev_err_probe(dev, -EINVAL, "Failed to get tclk\n");
- tclk_rate = clk_get_rate(rspi->tclk);
-
rspi->resets[0].id = "presetn";
rspi->resets[1].id = "tresetn";
- ret = devm_reset_control_bulk_get_exclusive(dev, RSPI_RESET_NUM,
- rspi->resets);
+ ret = devm_reset_control_bulk_get_optional_exclusive(dev, RSPI_RESET_NUM,
+ rspi->resets);
if (ret)
return dev_err_probe(dev, ret, "cannot get resets\n");
@@ -407,15 +598,29 @@ static int rzv2h_rspi_probe(struct platform_device *pdev)
}
controller->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH |
- SPI_LSB_FIRST;
+ SPI_LSB_FIRST | SPI_LOOP;
controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
controller->prepare_message = rzv2h_rspi_prepare_message;
controller->unprepare_message = rzv2h_rspi_unprepare_message;
controller->num_chipselect = 4;
controller->transfer_one = rzv2h_rspi_transfer_one;
+
+ tclk_rate = clk_round_rate(rspi->tclk, 0);
+ if (tclk_rate < 0) {
+ ret = tclk_rate;
+ goto quit_resets;
+ }
+
controller->min_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate,
RSPI_SPBR_SPR_MAX,
RSPI_SPCMD_BRDV_MAX);
+
+ tclk_rate = clk_round_rate(rspi->tclk, ULONG_MAX);
+ if (tclk_rate < 0) {
+ ret = tclk_rate;
+ goto quit_resets;
+ }
+
controller->max_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate,
RSPI_SPBR_SPR_MIN,
RSPI_SPCMD_BRDV_MIN);
@@ -445,8 +650,24 @@ static void rzv2h_rspi_remove(struct platform_device *pdev)
reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets);
}
+static const struct rzv2h_rspi_info rzv2h_info = {
+ .find_tclk_rate = rzv2h_rspi_find_rate_fixed,
+ .tclk_name = "tclk",
+ .fifo_size = 16,
+ .num_clks = 3,
+};
+
+static const struct rzv2h_rspi_info rzt2h_info = {
+ .find_tclk_rate = rzv2h_rspi_find_rate_variable,
+ .find_pclk_rate = rzv2h_rspi_find_rate_fixed,
+ .tclk_name = "pclkspi",
+ .fifo_size = 4,
+ .num_clks = 2,
+};
+
static const struct of_device_id rzv2h_rspi_match[] = {
- { .compatible = "renesas,r9a09g057-rspi" },
+ { .compatible = "renesas,r9a09g057-rspi", &rzv2h_info },
+ { .compatible = "renesas,r9a09g077-rspi", &rzt2h_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzv2h_rspi_match);
diff --git a/drivers/spi/spi-sg2044-nor.c b/drivers/spi/spi-sg2044-nor.c
index af48b1fcda93..37f1cfe10be4 100644
--- a/drivers/spi/spi-sg2044-nor.c
+++ b/drivers/spi/spi-sg2044-nor.c
@@ -42,6 +42,7 @@
#define SPIFMC_TRAN_CSR_TRAN_MODE_RX BIT(0)
#define SPIFMC_TRAN_CSR_TRAN_MODE_TX BIT(1)
#define SPIFMC_TRAN_CSR_FAST_MODE BIT(3)
+#define SPIFMC_TRAN_CSR_BUS_WIDTH_MASK GENMASK(5, 4)
#define SPIFMC_TRAN_CSR_BUS_WIDTH_1_BIT (0x00 << 4)
#define SPIFMC_TRAN_CSR_BUS_WIDTH_2_BIT (0x01 << 4)
#define SPIFMC_TRAN_CSR_BUS_WIDTH_4_BIT (0x02 << 4)
@@ -122,8 +123,7 @@ static u32 sg2044_spifmc_init_reg(struct sg2044_spifmc *spifmc)
reg = readl(spifmc->io_base + SPIFMC_TRAN_CSR);
reg &= ~(SPIFMC_TRAN_CSR_TRAN_MODE_MASK |
SPIFMC_TRAN_CSR_FAST_MODE |
- SPIFMC_TRAN_CSR_BUS_WIDTH_2_BIT |
- SPIFMC_TRAN_CSR_BUS_WIDTH_4_BIT |
+ SPIFMC_TRAN_CSR_BUS_WIDTH_MASK |
SPIFMC_TRAN_CSR_DMA_EN |
SPIFMC_TRAN_CSR_ADDR_BYTES_MASK |
SPIFMC_TRAN_CSR_WITH_CMD |
diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c
index 3be7499db21e..cdc3cb7c01f9 100644
--- a/drivers/spi/spi-tegra210-quad.c
+++ b/drivers/spi/spi-tegra210-quad.c
@@ -1019,13 +1019,20 @@ static void tegra_qspi_dump_regs(struct tegra_qspi *tqspi)
tegra_qspi_readl(tqspi, QSPI_FIFO_STATUS));
}
+static void tegra_qspi_reset(struct tegra_qspi *tqspi)
+{
+ if (device_reset(tqspi->dev) < 0) {
+ dev_warn_once(tqspi->dev, "device reset failed\n");
+ tegra_qspi_mask_clear_irq(tqspi);
+ }
+}
+
static void tegra_qspi_handle_error(struct tegra_qspi *tqspi)
{
dev_err(tqspi->dev, "error in transfer, fifo status 0x%08x\n", tqspi->status_reg);
tegra_qspi_dump_regs(tqspi);
tegra_qspi_flush_fifos(tqspi, true);
- if (device_reset(tqspi->dev) < 0)
- dev_warn_once(tqspi->dev, "device reset failed\n");
+ tegra_qspi_reset(tqspi);
}
static void tegra_qspi_transfer_end(struct spi_device *spi)
@@ -1041,6 +1048,49 @@ static void tegra_qspi_transfer_end(struct spi_device *spi)
tegra_qspi_writel(tqspi, tqspi->def_command1_reg, QSPI_COMMAND1);
}
+static irqreturn_t handle_cpu_based_xfer(struct tegra_qspi *tqspi);
+static irqreturn_t handle_dma_based_xfer(struct tegra_qspi *tqspi);
+
+/**
+ * tegra_qspi_handle_timeout - Handle transfer timeout with hardware check
+ * @tqspi: QSPI controller instance
+ *
+ * When a timeout occurs but hardware has completed the transfer (interrupt
+ * was lost or delayed), manually trigger transfer completion processing.
+ * This avoids failing transfers that actually succeeded.
+ *
+ * Returns: 0 if transfer was completed, -ETIMEDOUT if real timeout
+ */
+static int tegra_qspi_handle_timeout(struct tegra_qspi *tqspi)
+{
+ irqreturn_t ret;
+ u32 status;
+
+ /* Check if hardware actually completed the transfer */
+ status = tegra_qspi_readl(tqspi, QSPI_TRANS_STATUS);
+ if (!(status & QSPI_RDY))
+ return -ETIMEDOUT;
+
+ /*
+ * Hardware completed but interrupt was lost/delayed. Manually
+ * process the completion by calling the appropriate handler.
+ */
+ dev_warn_ratelimited(tqspi->dev,
+ "QSPI interrupt timeout, but transfer complete\n");
+
+ /* Clear the transfer status */
+ status = tegra_qspi_readl(tqspi, QSPI_TRANS_STATUS);
+ tegra_qspi_writel(tqspi, status, QSPI_TRANS_STATUS);
+
+ /* Manually trigger completion handler */
+ if (!tqspi->is_curr_dma_xfer)
+ ret = handle_cpu_based_xfer(tqspi);
+ else
+ ret = handle_dma_based_xfer(tqspi);
+
+ return (ret == IRQ_HANDLED) ? 0 : -EIO;
+}
+
static u32 tegra_qspi_cmd_config(bool is_ddr, u8 bus_width, u8 len)
{
u32 cmd_config = 0;
@@ -1072,6 +1122,30 @@ static u32 tegra_qspi_addr_config(bool is_ddr, u8 bus_width, u8 len)
return addr_config;
}
+static void tegra_qspi_dma_stop(struct tegra_qspi *tqspi)
+{
+ u32 value;
+
+ if ((tqspi->cur_direction & DATA_DIR_TX) && tqspi->tx_dma_chan)
+ dmaengine_terminate_all(tqspi->tx_dma_chan);
+
+ if ((tqspi->cur_direction & DATA_DIR_RX) && tqspi->rx_dma_chan)
+ dmaengine_terminate_all(tqspi->rx_dma_chan);
+
+ value = tegra_qspi_readl(tqspi, QSPI_DMA_CTL);
+ value &= ~QSPI_DMA_EN;
+ tegra_qspi_writel(tqspi, value, QSPI_DMA_CTL);
+}
+
+static void tegra_qspi_pio_stop(struct tegra_qspi *tqspi)
+{
+ u32 value;
+
+ value = tegra_qspi_readl(tqspi, QSPI_COMMAND1);
+ value &= ~QSPI_PIO;
+ tegra_qspi_writel(tqspi, value, QSPI_COMMAND1);
+}
+
static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi,
struct spi_message *msg)
{
@@ -1079,7 +1153,7 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi,
struct spi_transfer *xfer;
struct spi_device *spi = msg->spi;
u8 transfer_phase = 0;
- u32 cmd1 = 0, dma_ctl = 0;
+ u32 cmd1 = 0;
int ret = 0;
u32 address_value = 0;
u32 cmd_config = 0, addr_config = 0;
@@ -1146,41 +1220,28 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi,
QSPI_DMA_TIMEOUT);
if (WARN_ON_ONCE(ret == 0)) {
- dev_err_ratelimited(tqspi->dev,
- "QSPI Transfer failed with timeout\n");
- if (tqspi->is_curr_dma_xfer) {
- if ((tqspi->cur_direction & DATA_DIR_TX) &&
- tqspi->tx_dma_chan)
- dmaengine_terminate_all(tqspi->tx_dma_chan);
- if ((tqspi->cur_direction & DATA_DIR_RX) &&
- tqspi->rx_dma_chan)
- dmaengine_terminate_all(tqspi->rx_dma_chan);
- }
-
- /* Abort transfer by resetting pio/dma bit */
- if (!tqspi->is_curr_dma_xfer) {
- cmd1 = tegra_qspi_readl
- (tqspi,
- QSPI_COMMAND1);
- cmd1 &= ~QSPI_PIO;
- tegra_qspi_writel
- (tqspi, cmd1,
- QSPI_COMMAND1);
- } else {
- dma_ctl = tegra_qspi_readl
- (tqspi,
- QSPI_DMA_CTL);
- dma_ctl &= ~QSPI_DMA_EN;
- tegra_qspi_writel(tqspi, dma_ctl,
- QSPI_DMA_CTL);
+ /*
+ * Check if hardware completed the transfer
+ * even though interrupt was lost or delayed.
+ * If so, process the completion and continue.
+ */
+ ret = tegra_qspi_handle_timeout(tqspi);
+ if (ret < 0) {
+ /* Real timeout - clean up and fail */
+ dev_err(tqspi->dev, "transfer timeout\n");
+
+ /* Abort transfer by resetting pio/dma bit */
+ if (tqspi->is_curr_dma_xfer)
+ tegra_qspi_dma_stop(tqspi);
+ else
+ tegra_qspi_pio_stop(tqspi);
+
+ /* Reset controller if timeout happens */
+ tegra_qspi_reset(tqspi);
+
+ ret = -EIO;
+ goto exit;
}
-
- /* Reset controller if timeout happens */
- if (device_reset(tqspi->dev) < 0)
- dev_warn_once(tqspi->dev,
- "device reset failed\n");
- ret = -EIO;
- goto exit;
}
if (tqspi->tx_status || tqspi->rx_status) {
@@ -1200,11 +1261,13 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi,
tegra_qspi_transfer_end(spi);
spi_transfer_delay_exec(xfer);
}
+ tqspi->curr_xfer = NULL;
transfer_phase++;
}
ret = 0;
exit:
+ tqspi->curr_xfer = NULL;
msg->status = ret;
return ret;
@@ -1269,16 +1332,23 @@ static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi,
ret = wait_for_completion_timeout(&tqspi->xfer_completion,
QSPI_DMA_TIMEOUT);
if (WARN_ON(ret == 0)) {
- dev_err(tqspi->dev, "transfer timeout\n");
- if (tqspi->is_curr_dma_xfer) {
- if ((tqspi->cur_direction & DATA_DIR_TX) && tqspi->tx_dma_chan)
- dmaengine_terminate_all(tqspi->tx_dma_chan);
- if ((tqspi->cur_direction & DATA_DIR_RX) && tqspi->rx_dma_chan)
- dmaengine_terminate_all(tqspi->rx_dma_chan);
+ /*
+ * Check if hardware completed the transfer even though
+ * interrupt was lost or delayed. If so, process the
+ * completion and continue.
+ */
+ ret = tegra_qspi_handle_timeout(tqspi);
+ if (ret < 0) {
+ /* Real timeout - clean up and fail */
+ dev_err(tqspi->dev, "transfer timeout\n");
+
+ if (tqspi->is_curr_dma_xfer)
+ tegra_qspi_dma_stop(tqspi);
+
+ tegra_qspi_handle_error(tqspi);
+ ret = -EIO;
+ goto complete_xfer;
}
- tegra_qspi_handle_error(tqspi);
- ret = -EIO;
- goto complete_xfer;
}
if (tqspi->tx_status || tqspi->rx_status) {
@@ -1290,6 +1360,8 @@ static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi,
msg->actual_length += xfer->len + dummy_bytes;
complete_xfer:
+ tqspi->curr_xfer = NULL;
+
if (ret < 0) {
tegra_qspi_transfer_end(spi);
spi_transfer_delay_exec(xfer);
@@ -1395,6 +1467,7 @@ static irqreturn_t handle_cpu_based_xfer(struct tegra_qspi *tqspi)
tegra_qspi_calculate_curr_xfer_param(tqspi, t);
tegra_qspi_start_cpu_based_transfer(tqspi, t);
exit:
+ tqspi->curr_xfer = NULL;
spin_unlock_irqrestore(&tqspi->lock, flags);
return IRQ_HANDLED;
}
@@ -1480,6 +1553,15 @@ static irqreturn_t tegra_qspi_isr_thread(int irq, void *context_data)
{
struct tegra_qspi *tqspi = context_data;
+ /*
+ * Occasionally the IRQ thread takes a long time to wake up (usually
+ * when the CPU that it's running on is excessively busy) and we have
+ * already reached the timeout before and cleaned up the timed out
+ * transfer. Avoid any processing in that case and bail out early.
+ */
+ if (!tqspi->curr_xfer)
+ return IRQ_NONE;
+
tqspi->status_reg = tegra_qspi_readl(tqspi, QSPI_FIFO_STATUS);
if (tqspi->cur_direction & DATA_DIR_TX)
diff --git a/drivers/spi/spi-tle62x0.c b/drivers/spi/spi-tle62x0.c
index a565352f6381..663c0136d119 100644
--- a/drivers/spi/spi-tle62x0.c
+++ b/drivers/spi/spi-tle62x0.c
@@ -141,7 +141,7 @@ static ssize_t tle62x0_gpio_show(struct device *dev,
value = (st->gpio_state >> gpio_num) & 1;
mutex_unlock(&st->lock);
- return sysfs_emit(buf, "%d", value);
+ return sysfs_emit(buf, "%d\n", value);
}
static ssize_t tle62x0_gpio_store(struct device *dev,
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 5300c942a2a4..9a0160f6dc3d 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -704,6 +704,7 @@ static const struct class spidev_class = {
*/
static const struct spi_device_id spidev_spi_ids[] = {
{ .name = /* abb */ "spi-sensor" },
+ { .name = /* arduino */ "unoq-mcu" },
{ .name = /* cisco */ "spi-petra" },
{ .name = /* dh */ "dhcom-board" },
{ .name = /* elgin */ "jg10309-01" },
@@ -737,6 +738,7 @@ static int spidev_of_check(struct device *dev)
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "abb,spi-sensor", .data = &spidev_of_check },
+ { .compatible = "arduino,unoq-mcu", .data = &spidev_of_check },
{ .compatible = "cisco,spi-petra", .data = &spidev_of_check },
{ .compatible = "dh,dhcom-board", .data = &spidev_of_check },
{ .compatible = "elgin,jg10309-01", .data = &spidev_of_check },