summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig52
-rw-r--r--drivers/spi/Makefile5
-rw-r--r--drivers/spi/atmel-quadspi.c187
-rw-r--r--drivers/spi/spi-altera-platform.c1
-rw-r--r--drivers/spi/spi-amd-pci.c5
-rw-r--r--drivers/spi/spi-amd.c2
-rw-r--r--drivers/spi/spi-amlogic-spifc-a4.c1222
-rw-r--r--drivers/spi/spi-amlogic-spisg.c888
-rw-r--r--drivers/spi/spi-apple.c1
-rw-r--r--drivers/spi/spi-atmel.c78
-rw-r--r--drivers/spi/spi-axi-spi-engine.c19
-rw-r--r--drivers/spi/spi-bcm2835.c2
-rw-r--r--drivers/spi/spi-cadence-quadspi.c124
-rw-r--r--drivers/spi/spi-cadence.c1
-rw-r--r--drivers/spi/spi-cs42l43.c2
-rw-r--r--drivers/spi/spi-falcon.c5
-rw-r--r--drivers/spi/spi-fsl-dspi.c588
-rw-r--r--drivers/spi/spi-fsl-espi.c2
-rw-r--r--drivers/spi/spi-fsl-lpspi.c65
-rw-r--r--drivers/spi/spi-geni-qcom.c6
-rw-r--r--drivers/spi/spi-gpio.c16
-rw-r--r--drivers/spi/spi-imx.c3
-rw-r--r--drivers/spi/spi-intel.c13
-rw-r--r--drivers/spi/spi-ljca.c2
-rw-r--r--drivers/spi/spi-loopback-test.c12
-rw-r--r--drivers/spi/spi-mem.c31
-rw-r--r--drivers/spi/spi-microchip-core-qspi.c241
-rw-r--r--drivers/spi/spi-microchip-core.c3
-rw-r--r--drivers/spi/spi-mt65xx.c41
-rw-r--r--drivers/spi/spi-mtk-nor.c1
-rw-r--r--drivers/spi/spi-mtk-snfi.c1
-rw-r--r--drivers/spi/spi-mxs.c2
-rw-r--r--drivers/spi/spi-npcm-fiu.c6
-rw-r--r--drivers/spi/spi-nxp-fspi.c122
-rw-r--r--drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c62
-rw-r--r--drivers/spi/spi-omap2-mcspi.c4
-rw-r--r--drivers/spi/spi-pci1xxxx.c285
-rw-r--r--drivers/spi/spi-pl022.c13
-rw-r--r--drivers/spi/spi-pxa2xx.c2
-rw-r--r--drivers/spi/spi-qpic-snand.c160
-rw-r--r--drivers/spi/spi-rb4xx.c36
-rw-r--r--drivers/spi/spi-rockchip-sfc.c3
-rw-r--r--drivers/spi/spi-rpc-if.c12
-rw-r--r--drivers/spi/spi-rspi.c9
-rw-r--r--drivers/spi/spi-rzv2h-rspi.c466
-rw-r--r--drivers/spi/spi-s3c64xx.c22
-rw-r--r--drivers/spi/spi-sg2044-nor.c29
-rw-r--r--drivers/spi/spi-sh-msiof.c11
-rw-r--r--drivers/spi/spi-sprd.c1
-rw-r--r--drivers/spi/spi-st-ssc4.c14
-rw-r--r--drivers/spi/spi-stm32-ospi.c31
-rw-r--r--drivers/spi/spi-stm32-qspi.c7
-rw-r--r--drivers/spi/spi-stm32.c318
-rw-r--r--drivers/spi/spi-sunplus-sp7021.c6
-rw-r--r--drivers/spi/spi-ti-qspi.c2
-rw-r--r--drivers/spi/spi-virtio.c431
-rw-r--r--drivers/spi/spi-xcomm.c2
-rw-r--r--drivers/spi/spi-xilinx.c5
-rw-r--r--drivers/spi/spi-zynqmp-gqspi.c1
-rw-r--r--drivers/spi/spi.c102
-rw-r--r--drivers/spi/spidev.c2
61 files changed, 4907 insertions, 878 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index c51da3fc3604..4d8f00c850c1 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -99,6 +99,25 @@ config SPI_AMLOGIC_SPIFC_A1
This enables master mode support for the SPIFC (SPI flash
controller) available in Amlogic A1 (A113L SoC).
+config SPI_AMLOGIC_SPIFC_A4
+ tristate "Amlogic A4 SPI Flash controller"
+ depends on ARCH_MESON || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ This enables SPI mode on the NAND Flash Controller of Amlogic
+ ARM SoCs. It supports SPI Nor Flash and SPI NAND Flash (Could
+ enable Host ECC HW engine). The controller implements the
+ SPI-MEM interface, it doesn't support generic SPI.
+
+config SPI_AMLOGIC_SPISG
+ tristate "Amlogic SPISG controller"
+ depends on COMMON_CLK
+ depends on ARCH_MESON || COMPILE_TEST
+ help
+ This enables master mode support for the SPISG (SPI scatter-gather
+ communication controller), which is available on platforms such as
+ Amlogic A4 SoCs.
+
config SPI_APPLE
tristate "Apple SoC SPI Controller platform driver"
depends on ARCH_APPLE || COMPILE_TEST
@@ -145,7 +164,7 @@ config SPI_ASPEED_SMC
config SPI_ATMEL
tristate "Atmel SPI Controller"
- depends on ARCH_AT91 || COMPILE_TEST
+ depends on ARCH_MICROCHIP || COMPILE_TEST
depends on OF
help
This selects a driver for the Atmel SPI Controller, present on
@@ -647,10 +666,10 @@ config SPI_FSL_SPI
config SPI_FSL_DSPI
tristate "Freescale DSPI controller"
select REGMAP_MMIO
- depends on SOC_VF610 || SOC_LS1021A || ARCH_LAYERSCAPE || M5441x || COMPILE_TEST
+ depends on ARCH_MXC || ARCH_NXP || M5441x || COMPILE_TEST
help
This enables support for the Freescale DSPI controller in master
- mode. VF610, LS1021A and ColdFire platforms uses the controller.
+ mode. S32, VF610, LS1021A and ColdFire platforms uses the controller.
config SPI_FSL_ESPI
tristate "Freescale eSPI controller"
@@ -907,7 +926,8 @@ config SPI_ROCKCHIP_SFC
config SPI_RB4XX
tristate "Mikrotik RB4XX SPI master"
- depends on SPI_MASTER && ATH79
+ depends on SPI_MASTER && (ATH79 || COMPILE_TEST)
+ depends on OF
help
SPI controller driver for the Mikrotik RB4xx series boards.
@@ -923,6 +943,14 @@ config SPI_RSPI
help
SPI driver for Renesas RSPI and QSPI blocks.
+config SPI_RZV2H_RSPI
+ tristate "Renesas RZ/V2H RSPI controller"
+ depends on ARCH_RENESAS || COMPILE_TEST
+ help
+ RSPI driver for the Renesas RZ/V2H Serial Peripheral Interface (RSPI).
+ RSPI supports both SPI host and SPI target roles. This option only
+ enables the SPI host role.
+
config SPI_RZV2M_CSI
tristate "Renesas RZ/V2M CSI controller"
depends on ARCH_RENESAS || COMPILE_TEST
@@ -1207,6 +1235,17 @@ config SPI_UNIPHIER
If your SoC supports SCSSI, say Y here.
+config SPI_VIRTIO
+ tristate "Virtio SPI Controller"
+ depends on SPI_MASTER && VIRTIO
+ help
+ If you say yes to this option, support will be included for the virtio
+ SPI controller driver. The hardware can be emulated by any device model
+ software according to the virtio protocol.
+
+ This driver can also be built as a module. If so, the module
+ will be called spi-virtio.
+
config SPI_XCOMM
tristate "Analog Devices AD-FMCOMMS1-EBZ SPI-I2C-bridge driver"
depends on I2C
@@ -1355,6 +1394,11 @@ if SPI_OFFLOAD
comment "SPI Offload triggers"
+config SPI_OFFLOAD_TRIGGER_ADI_UTIL_SD
+ tristate "SPI offload trigger using ADI sigma-delta utility"
+ help
+ SPI offload trigger from ADI sigma-delta utility FPGA IP block.
+
config SPI_OFFLOAD_TRIGGER_PWM
tristate "SPI offload trigger using PWM"
depends on PWM
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 4ea89f6fc531..8ff74a13faaa 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -20,6 +20,8 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o
obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o
obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o
obj-$(CONFIG_SPI_AMLOGIC_SPIFC_A1) += spi-amlogic-spifc-a1.o
+obj-$(CONFIG_SPI_AMLOGIC_SPIFC_A4) += spi-amlogic-spifc-a4.o
+obj-$(CONFIG_SPI_AMLOGIC_SPISG) += spi-amlogic-spisg.o
obj-$(CONFIG_SPI_APPLE) += spi-apple.o
obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o
obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
@@ -126,6 +128,7 @@ obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
obj-$(CONFIG_SPI_REALTEK_SNAND) += spi-realtek-rtl-snand.o
obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
+obj-$(CONFIG_SPI_RZV2H_RSPI) += spi-rzv2h-rspi.o
obj-$(CONFIG_SPI_RZV2M_CSI) += spi-rzv2m-csi.o
obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o
obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o
@@ -156,6 +159,7 @@ spi-thunderx-objs := spi-cavium.o spi-cavium-thunderx.o
obj-$(CONFIG_SPI_THUNDERX) += spi-thunderx.o
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
obj-$(CONFIG_SPI_UNIPHIER) += spi-uniphier.o
+obj-$(CONFIG_SPI_VIRTIO) += spi-virtio.o
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
obj-$(CONFIG_SPI_XLP) += spi-xlp.o
@@ -170,3 +174,4 @@ obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o
# SPI offload triggers
obj-$(CONFIG_SPI_OFFLOAD_TRIGGER_PWM) += spi-offload-trigger-pwm.o
+obj-$(CONFIG_SPI_OFFLOAD_TRIGGER_ADI_UTIL_SD) += spi-offload-trigger-adi-util-sigma-delta.o
diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c
index 2f6797324227..d7a3d85d00c2 100644
--- a/drivers/spi/atmel-quadspi.c
+++ b/drivers/spi/atmel-quadspi.c
@@ -63,6 +63,7 @@
#define SAMA7G5_QSPI0_MAX_SPEED_HZ 200000000
#define SAMA7G5_QSPI1_SDR_MAX_SPEED_HZ 133000000
+#define SAM9X7_QSPI_MAX_SPEED_HZ 100000000
/* Bitfields in QSPI_CR (Control Register) */
#define QSPI_CR_QSPIEN BIT(0)
@@ -262,6 +263,9 @@ struct atmel_qspi_caps {
bool has_ricr;
bool octal;
bool has_dma;
+ bool has_2xgclk;
+ bool has_padcalib;
+ bool has_dllon;
};
struct atmel_qspi_ops;
@@ -965,7 +969,6 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
err = aq->ops->transfer(mem, op, offset);
pm_runtime_put:
- pm_runtime_mark_last_busy(&aq->pdev->dev);
pm_runtime_put_autosuspend(&aq->pdev->dev);
return err;
}
@@ -1028,13 +1031,25 @@ static int atmel_qspi_set_pad_calibration(struct atmel_qspi *aq)
aq, QSPI_PCALCFG);
/* DLL On + start calibration. */
- atmel_qspi_write(QSPI_CR_DLLON | QSPI_CR_STPCAL, aq, QSPI_CR);
+ if (aq->caps->has_dllon)
+ atmel_qspi_write(QSPI_CR_DLLON | QSPI_CR_STPCAL, aq, QSPI_CR);
+ /* If there is no DLL support only start calibration. */
+ else
+ atmel_qspi_write(QSPI_CR_STPCAL, aq, QSPI_CR);
- /* Check synchronization status before updating configuration. */
- ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
- (val & QSPI_SR2_DLOCK) &&
- !(val & QSPI_SR2_CALBSY), 40,
- ATMEL_QSPI_TIMEOUT);
+ /*
+ * Check DLL clock lock and synchronization status before updating
+ * configuration.
+ */
+ if (aq->caps->has_dllon)
+ ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
+ (val & QSPI_SR2_DLOCK) &&
+ !(val & QSPI_SR2_CALBSY), 40,
+ ATMEL_QSPI_TIMEOUT);
+ else
+ ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
+ !(val & QSPI_SR2_CALBSY), 40,
+ ATMEL_QSPI_TIMEOUT);
/* Refresh analogic blocks every 1 ms.*/
atmel_qspi_write(FIELD_PREP(QSPI_REFRESH_DELAY_COUNTER,
@@ -1050,23 +1065,28 @@ static int atmel_qspi_set_gclk(struct atmel_qspi *aq)
int ret;
/* Disable DLL before setting GCLK */
- status = atmel_qspi_read(aq, QSPI_SR2);
- if (status & QSPI_SR2_DLOCK) {
- atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR);
+ if (aq->caps->has_dllon) {
+ status = atmel_qspi_read(aq, QSPI_SR2);
+ if (status & QSPI_SR2_DLOCK) {
+ atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR);
+ ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
+ !(val & QSPI_SR2_DLOCK), 40,
+ ATMEL_QSPI_TIMEOUT);
+ if (ret)
+ return ret;
+ }
- ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
- !(val & QSPI_SR2_DLOCK), 40,
- ATMEL_QSPI_TIMEOUT);
- if (ret)
- return ret;
+ if (aq->target_max_speed_hz > QSPI_DLLCFG_THRESHOLD_FREQ)
+ atmel_qspi_write(QSPI_DLLCFG_RANGE, aq, QSPI_DLLCFG);
+ else
+ atmel_qspi_write(0, aq, QSPI_DLLCFG);
}
- if (aq->target_max_speed_hz > QSPI_DLLCFG_THRESHOLD_FREQ)
- atmel_qspi_write(QSPI_DLLCFG_RANGE, aq, QSPI_DLLCFG);
+ if (aq->caps->has_2xgclk)
+ ret = clk_set_rate(aq->gclk, 2 * aq->target_max_speed_hz);
else
- atmel_qspi_write(0, aq, QSPI_DLLCFG);
+ ret = clk_set_rate(aq->gclk, aq->target_max_speed_hz);
- ret = clk_set_rate(aq->gclk, aq->target_max_speed_hz);
if (ret) {
dev_err(&aq->pdev->dev, "Failed to set generic clock rate.\n");
return ret;
@@ -1089,11 +1109,16 @@ static int atmel_qspi_sama7g5_init(struct atmel_qspi *aq)
if (ret)
return ret;
- if (aq->caps->octal) {
+ /*
+ * Check if the SoC supports pad calibration in Octal SPI mode.
+ * Proceed only if both the capabilities are true.
+ */
+ if (aq->caps->octal && aq->caps->has_padcalib) {
ret = atmel_qspi_set_pad_calibration(aq);
if (ret)
return ret;
- } else {
+ /* Start DLL on only if the SoC supports the same */
+ } else if (aq->caps->has_dllon) {
atmel_qspi_write(QSPI_CR_DLLON, aq, QSPI_CR);
ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
(val & QSPI_SR2_DLOCK), 40,
@@ -1168,7 +1193,6 @@ static int atmel_qspi_setup(struct spi_device *spi)
aq->scr |= QSPI_SCR_SCBR(scbr);
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
- pm_runtime_mark_last_busy(ctrl->dev.parent);
pm_runtime_put_autosuspend(ctrl->dev.parent);
return 0;
@@ -1230,7 +1254,6 @@ static int atmel_qspi_set_cs_timing(struct spi_device *spi)
aq->mr |= QSPI_MR_DLYBCT(cs_hold) | QSPI_MR_DLYCS(cs_inactive);
atmel_qspi_write(aq->mr, aq, QSPI_MR);
- pm_runtime_mark_last_busy(ctrl->dev.parent);
pm_runtime_put_autosuspend(ctrl->dev.parent);
return 0;
@@ -1285,18 +1308,21 @@ static int atmel_qspi_dma_init(struct spi_controller *ctrl)
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
int ret;
- aq->rx_chan = dma_request_chan(&aq->pdev->dev, "rx");
+ aq->rx_chan = devm_dma_request_chan(&aq->pdev->dev, "rx");
if (IS_ERR(aq->rx_chan)) {
ret = dev_err_probe(&aq->pdev->dev, PTR_ERR(aq->rx_chan),
"RX DMA channel is not available\n");
- goto null_rx_chan;
+ aq->rx_chan = NULL;
+ return ret;
}
- aq->tx_chan = dma_request_chan(&aq->pdev->dev, "tx");
+ aq->tx_chan = devm_dma_request_chan(&aq->pdev->dev, "tx");
if (IS_ERR(aq->tx_chan)) {
ret = dev_err_probe(&aq->pdev->dev, PTR_ERR(aq->tx_chan),
"TX DMA channel is not available\n");
- goto release_rx_chan;
+ aq->rx_chan = NULL;
+ aq->tx_chan = NULL;
+ return ret;
}
ctrl->dma_rx = aq->rx_chan;
@@ -1307,21 +1333,6 @@ static int atmel_qspi_dma_init(struct spi_controller *ctrl)
dma_chan_name(aq->tx_chan), dma_chan_name(aq->rx_chan));
return 0;
-
-release_rx_chan:
- dma_release_channel(aq->rx_chan);
- aq->tx_chan = NULL;
-null_rx_chan:
- aq->rx_chan = NULL;
- return ret;
-}
-
-static void atmel_qspi_dma_release(struct atmel_qspi *aq)
-{
- if (aq->rx_chan)
- dma_release_channel(aq->rx_chan);
- if (aq->tx_chan)
- dma_release_channel(aq->tx_chan);
}
static const struct atmel_qspi_ops atmel_qspi_ops = {
@@ -1426,14 +1437,13 @@ static int atmel_qspi_probe(struct platform_device *pdev)
/* Request the IRQ */
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- err = irq;
- goto dma_release;
- }
+ if (irq < 0)
+ return irq;
+
err = devm_request_irq(&pdev->dev, irq, atmel_qspi_interrupt,
0, dev_name(&pdev->dev), aq);
if (err)
- goto dma_release;
+ return err;
pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
pm_runtime_use_autosuspend(&pdev->dev);
@@ -1442,22 +1452,15 @@ static int atmel_qspi_probe(struct platform_device *pdev)
err = atmel_qspi_init(aq);
if (err)
- goto dma_release;
+ return err;
err = spi_register_controller(ctrl);
if (err)
- goto dma_release;
+ return err;
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
-
-dma_release:
- if (aq->caps->has_dma)
- atmel_qspi_dma_release(aq);
-
- return err;
}
static int atmel_qspi_sama7g5_suspend(struct atmel_qspi *aq)
@@ -1481,19 +1484,19 @@ static int atmel_qspi_sama7g5_suspend(struct atmel_qspi *aq)
clk_disable_unprepare(aq->gclk);
- atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR);
- ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
- !(val & QSPI_SR2_DLOCK), 40,
- ATMEL_QSPI_TIMEOUT);
- if (ret)
- return ret;
-
- ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
- !(val & QSPI_SR2_CALBSY), 40,
- ATMEL_QSPI_TIMEOUT);
- if (ret)
- return ret;
+ if (aq->caps->has_dllon) {
+ atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR);
+ ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
+ !(val & QSPI_SR2_DLOCK), 40,
+ ATMEL_QSPI_TIMEOUT);
+ if (ret)
+ return ret;
+ }
+ if (aq->caps->has_padcalib)
+ return readl_poll_timeout(aq->regs + QSPI_SR2, val,
+ !(val & QSPI_SR2_CALBSY), 40,
+ ATMEL_QSPI_TIMEOUT);
return 0;
}
@@ -1507,9 +1510,6 @@ static void atmel_qspi_remove(struct platform_device *pdev)
ret = pm_runtime_get_sync(&pdev->dev);
if (ret >= 0) {
- if (aq->caps->has_dma)
- atmel_qspi_dma_release(aq);
-
if (aq->caps->has_gclk) {
ret = atmel_qspi_sama7g5_suspend(aq);
if (ret)
@@ -1582,7 +1582,6 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
@@ -1629,17 +1628,48 @@ static const struct atmel_qspi_caps atmel_sam9x60_qspi_caps = {
.has_ricr = true,
};
+static const struct atmel_qspi_caps atmel_sam9x7_ospi_caps = {
+ .max_speed_hz = SAM9X7_QSPI_MAX_SPEED_HZ,
+ .has_gclk = true,
+ .octal = true,
+ .has_dma = true,
+ .has_2xgclk = true,
+ .has_padcalib = false,
+ .has_dllon = false,
+};
+
+static const struct atmel_qspi_caps atmel_sama7d65_ospi_caps = {
+ .max_speed_hz = SAMA7G5_QSPI0_MAX_SPEED_HZ,
+ .has_gclk = true,
+ .octal = true,
+ .has_dma = true,
+ .has_2xgclk = true,
+ .has_padcalib = true,
+ .has_dllon = false,
+};
+
+static const struct atmel_qspi_caps atmel_sama7d65_qspi_caps = {
+ .max_speed_hz = SAMA7G5_QSPI1_SDR_MAX_SPEED_HZ,
+ .has_gclk = true,
+ .has_dma = true,
+ .has_2xgclk = true,
+ .has_dllon = false,
+};
+
static const struct atmel_qspi_caps atmel_sama7g5_ospi_caps = {
.max_speed_hz = SAMA7G5_QSPI0_MAX_SPEED_HZ,
.has_gclk = true,
.octal = true,
.has_dma = true,
+ .has_padcalib = true,
+ .has_dllon = true,
};
static const struct atmel_qspi_caps atmel_sama7g5_qspi_caps = {
.max_speed_hz = SAMA7G5_QSPI1_SDR_MAX_SPEED_HZ,
.has_gclk = true,
.has_dma = true,
+ .has_dllon = true,
};
static const struct of_device_id atmel_qspi_dt_ids[] = {
@@ -1659,6 +1689,19 @@ static const struct of_device_id atmel_qspi_dt_ids[] = {
.compatible = "microchip,sama7g5-qspi",
.data = &atmel_sama7g5_qspi_caps,
},
+ {
+ .compatible = "microchip,sam9x7-ospi",
+ .data = &atmel_sam9x7_ospi_caps,
+ },
+ {
+ .compatible = "microchip,sama7d65-ospi",
+ .data = &atmel_sama7d65_ospi_caps,
+ },
+ {
+ .compatible = "microchip,sama7d65-qspi",
+ .data = &atmel_sama7d65_qspi_caps,
+ },
+
{ /* sentinel */ }
};
diff --git a/drivers/spi/spi-altera-platform.c b/drivers/spi/spi-altera-platform.c
index 585393802e9f..e163774fd65b 100644
--- a/drivers/spi/spi-altera-platform.c
+++ b/drivers/spi/spi-altera-platform.c
@@ -30,7 +30,6 @@ static const struct regmap_config spi_altera_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .fast_io = true,
};
static int altera_spi_probe(struct platform_device *pdev)
diff --git a/drivers/spi/spi-amd-pci.c b/drivers/spi/spi-amd-pci.c
index e5faab414c17..d48c3a5da303 100644
--- a/drivers/spi/spi-amd-pci.c
+++ b/drivers/spi/spi-amd-pci.c
@@ -38,7 +38,7 @@ static int amd_spi_pci_probe(struct pci_dev *pdev,
/* Allocate storage for host and driver private data */
host = devm_spi_alloc_host(dev, sizeof(struct amd_spi));
if (!host)
- return dev_err_probe(dev, -ENOMEM, "Error allocating SPI host\n");
+ return -ENOMEM;
amd_spi = spi_controller_get_devdata(host);
@@ -47,8 +47,7 @@ static int amd_spi_pci_probe(struct pci_dev *pdev,
amd_spi->io_remap_addr = devm_ioremap(dev, io_base_addr, AMD_HID2_MEM_SIZE);
if (!amd_spi->io_remap_addr)
- return dev_err_probe(dev, -ENOMEM,
- "ioremap of SPI registers failed\n");
+ return -ENOMEM;
dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr);
diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c
index 02e7fe095a0b..4d1dce4f4974 100644
--- a/drivers/spi/spi-amd.c
+++ b/drivers/spi/spi-amd.c
@@ -857,7 +857,7 @@ static int amd_spi_probe(struct platform_device *pdev)
/* Allocate storage for host and driver private data */
host = devm_spi_alloc_host(dev, sizeof(struct amd_spi));
if (!host)
- return dev_err_probe(dev, -ENOMEM, "Error allocating SPI host\n");
+ return -ENOMEM;
amd_spi = spi_controller_get_devdata(host);
amd_spi->io_remap_addr = devm_platform_ioremap_resource(pdev, 0);
diff --git a/drivers/spi/spi-amlogic-spifc-a4.c b/drivers/spi/spi-amlogic-spifc-a4.c
new file mode 100644
index 000000000000..4338d00e56a6
--- /dev/null
+++ b/drivers/spi/spi-amlogic-spifc-a4.c
@@ -0,0 +1,1222 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ *
+ * Driver for the SPI Mode of Amlogic Flash Controller
+ * Authors:
+ * Liang Yang <liang.yang@amlogic.com>
+ * Feng Chen <feng.chen@amlogic.com>
+ * Xianwei Zhao <xianwei.zhao@amlogic.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/dma-mapping.h>
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/mtd/spinand.h>
+#include <linux/spi/spi-mem.h>
+
+#define SFC_CMD 0x00
+#define SFC_CFG 0x04
+#define SFC_DADR 0x08
+#define SFC_IADR 0x0c
+#define SFC_BUF 0x10
+#define SFC_INFO 0x14
+#define SFC_DC 0x18
+#define SFC_ADR 0x1c
+#define SFC_DL 0x20
+#define SFC_DH 0x24
+#define SFC_CADR 0x28
+#define SFC_SADR 0x2c
+#define SFC_RX_IDX 0x34
+#define SFC_RX_DAT 0x38
+#define SFC_SPI_CFG 0x40
+
+/* settings in SFC_CMD */
+
+/* 4 bits support 4 chip select, high false, low select but spi support 2*/
+#define CHIP_SELECT_MASK GENMASK(13, 10)
+#define CS_NONE 0xf
+#define CS_0 0xe
+#define CS_1 0xd
+
+#define CLE (0x5 << 14)
+#define ALE (0x6 << 14)
+#define DWR (0x4 << 14)
+#define DRD (0x8 << 14)
+#define DUMMY (0xb << 14)
+#define IDLE (0xc << 14)
+#define IDLE_CYCLE_MASK GENMASK(9, 0)
+#define EXT_CYCLE_MASK GENMASK(9, 0)
+
+#define OP_M2N ((0 << 17) | (2 << 20))
+#define OP_N2M ((1 << 17) | (2 << 20))
+#define OP_STS ((3 << 17) | (2 << 20))
+#define OP_ADL ((0 << 16) | (3 << 20))
+#define OP_ADH ((1 << 16) | (3 << 20))
+#define OP_AIL ((2 << 16) | (3 << 20))
+#define OP_AIH ((3 << 16) | (3 << 20))
+#define OP_ASL ((4 << 16) | (3 << 20))
+#define OP_ASH ((5 << 16) | (3 << 20))
+#define OP_SEED ((8 << 16) | (3 << 20))
+#define SEED_MASK GENMASK(14, 0)
+#define ENABLE_RANDOM BIT(19)
+
+#define CMD_COMMAND(cs_sel, cmd) (CLE | ((cs_sel) << 10) | (cmd))
+#define CMD_ADDR(cs_sel, addr) (ALE | ((cs_sel) << 10) | (addr))
+#define CMD_DUMMY(cs_sel, cyc) (DUMMY | ((cs_sel) << 10) | ((cyc) & EXT_CYCLE_MASK))
+#define CMD_IDLE(cs_sel, cyc) (IDLE | ((cs_sel) << 10) | ((cyc) & IDLE_CYCLE_MASK))
+#define CMD_MEM2NAND(bch, pages) (OP_M2N | ((bch) << 14) | (pages))
+#define CMD_NAND2MEM(bch, pages) (OP_N2M | ((bch) << 14) | (pages))
+#define CMD_DATA_ADDRL(addr) (OP_ADL | ((addr) & 0xffff))
+#define CMD_DATA_ADDRH(addr) (OP_ADH | (((addr) >> 16) & 0xffff))
+#define CMD_INFO_ADDRL(addr) (OP_AIL | ((addr) & 0xffff))
+#define CMD_INFO_ADDRH(addr) (OP_AIH | (((addr) >> 16) & 0xffff))
+#define CMD_SEED(seed) (OP_SEED | ((seed) & SEED_MASK))
+
+#define GET_CMD_SIZE(x) (((x) >> 22) & GENMASK(4, 0))
+
+#define DEFAULT_PULLUP_CYCLE 2
+#define CS_SETUP_CYCLE 1
+#define CS_HOLD_CYCLE 2
+#define DEFAULT_BUS_CYCLE 4
+
+#define RAW_SIZE GENMASK(13, 0)
+#define RAW_SIZE_BW 14
+
+#define DMA_ADDR_ALIGN 8
+
+/* Bit fields in SFC_SPI_CFG */
+#define SPI_MODE_EN BIT(31)
+#define RAW_EXT_SIZE GENMASK(29, 18)
+#define ADDR_LANE GENMASK(17, 16)
+#define CPOL BIT(15)
+#define CPHA BIT(14)
+#define EN_HOLD BIT(13)
+#define EN_WP BIT(12)
+#define TXADJ GENMASK(11, 8)
+#define RXADJ GENMASK(7, 4)
+#define CMD_LANE GENMASK(3, 2)
+#define DATA_LANE GENMASK(1, 0)
+#define LANE_MAX 0x3
+
+/* raw ext size[25:14] + raw size[13:0] */
+#define RAW_MAX_RW_SIZE_MASK GENMASK(25, 0)
+
+/* Ecc fields */
+#define ECC_COMPLETE BIT(31)
+#define ECC_UNCORRECTABLE 0x3f
+#define ECC_ERR_CNT(x) (((x) >> 24) & 0x3f)
+#define ECC_ZERO_CNT(x) (((x) >> 16) & 0x3f)
+
+#define ECC_BCH8_512 1
+#define ECC_BCH8_1K 2
+#define ECC_BCH8_PARITY_BYTES 14
+#define ECC_BCH8_USER_BYTES 2
+#define ECC_BCH8_INFO_BYTES (ECC_BCH8_USER_BYTES + ECC_BCH8_PARITY_BYTES)
+#define ECC_BCH8_STRENGTH 8
+#define ECC_BCH8_DEFAULT_STEP 512
+#define ECC_DEFAULT_BCH_MODE ECC_BCH8_512
+#define ECC_PER_INFO_BYTE 8
+#define ECC_PATTERN 0x5a
+#define ECC_BCH_MAX_SECT_SIZE 63
+/* soft flags for sfc */
+#define SFC_HWECC BIT(0)
+#define SFC_DATA_RANDOM BIT(1)
+#define SFC_DATA_ONLY BIT(2)
+#define SFC_OOB_ONLY BIT(3)
+#define SFC_DATA_OOB BIT(4)
+#define SFC_AUTO_OOB BIT(5)
+#define SFC_RAW_RW BIT(6)
+#define SFC_XFER_MDOE_MASK GENMASK(6, 2)
+
+#define SFC_DATABUF_SIZE 8192
+#define SFC_INFOBUF_SIZE 256
+#define SFC_BUF_SIZE (SFC_DATABUF_SIZE + SFC_INFOBUF_SIZE)
+
+/* !!! PCB and SPI-NAND chip limitations */
+#define SFC_MAX_FREQUENCY (250 * 1000 * 1000)
+#define SFC_MIN_FREQUENCY (4 * 1000 * 1000)
+#define SFC_BUS_DEFAULT_CLK 40000000
+#define SFC_MAX_CS_NUM 2
+
+/* SPI-FLASH R/W operation cmd */
+#define SPIFLASH_RD_OCTALIO 0xcb
+#define SPIFLASH_RD_OCTAL 0x8b
+#define SPIFLASH_RD_QUADIO 0xeb
+#define SPIFLASH_RD_QUAD 0x6b
+#define SPIFLASH_RD_DUALIO 0xbb
+#define SPIFLASH_RD_DUAL 0x3b
+#define SPIFLASH_RD_FAST 0x0b
+#define SPIFLASH_RD 0x03
+#define SPIFLASH_WR_OCTALIO 0xC2
+#define SPIFLASH_WR_OCTAL 0x82
+#define SPIFLASH_WR_QUAD 0x32
+#define SPIFLASH_WR 0x02
+#define SPIFLASH_UP_QUAD 0x34
+#define SPIFLASH_UP 0x84
+
+struct aml_sfc_ecc_cfg {
+ u32 stepsize;
+ u32 nsteps;
+ u32 strength;
+ u32 oobsize;
+ u32 bch;
+};
+
+struct aml_ecc_stats {
+ u32 corrected;
+ u32 bitflips;
+ u32 failed;
+};
+
+struct aml_sfc_caps {
+ struct aml_sfc_ecc_cfg *ecc_caps;
+ u32 num_ecc_caps;
+};
+
+struct aml_sfc {
+ struct device *dev;
+ struct clk *gate_clk;
+ struct clk *core_clk;
+ struct spi_controller *ctrl;
+ struct regmap *regmap_base;
+ const struct aml_sfc_caps *caps;
+ struct nand_ecc_engine ecc_eng;
+ struct aml_ecc_stats ecc_stats;
+ dma_addr_t daddr;
+ dma_addr_t iaddr;
+ u32 info_bytes;
+ u32 bus_rate;
+ u32 flags;
+ u32 rx_adj;
+ u32 cs_sel;
+ u8 *data_buf;
+ __le64 *info_buf;
+ u8 *priv;
+};
+
+#define AML_ECC_DATA(sz, s, b) { .stepsize = (sz), .strength = (s), .bch = (b) }
+
+static struct aml_sfc_ecc_cfg aml_a113l2_ecc_caps[] = {
+ AML_ECC_DATA(512, 8, ECC_BCH8_512),
+ AML_ECC_DATA(1024, 8, ECC_BCH8_1K),
+};
+
+static const struct aml_sfc_caps aml_a113l2_sfc_caps = {
+ .ecc_caps = aml_a113l2_ecc_caps,
+ .num_ecc_caps = ARRAY_SIZE(aml_a113l2_ecc_caps)
+};
+
+static struct aml_sfc *nand_to_aml_sfc(struct nand_device *nand)
+{
+ struct nand_ecc_engine *eng = nand->ecc.engine;
+
+ return container_of(eng, struct aml_sfc, ecc_eng);
+}
+
+static inline void *aml_sfc_to_ecc_ctx(struct aml_sfc *sfc)
+{
+ return sfc->priv;
+}
+
+static int aml_sfc_wait_cmd_finish(struct aml_sfc *sfc, u64 timeout_ms)
+{
+ u32 cmd_size = 0;
+ int ret;
+
+ /*
+ * The SPINAND flash controller employs a two-stage pipeline:
+ * 1) command prefetch; 2) command execution.
+ *
+ * All commands are stored in the FIFO, with one prefetched for execution.
+ *
+ * There are cases where the FIFO is detected as empty, yet a command may
+ * still be in execution and a prefetched command pending execution.
+ *
+ * So, send two idle commands to ensure all previous commands have
+ * been executed.
+ */
+ regmap_write(sfc->regmap_base, SFC_CMD, CMD_IDLE(sfc->cs_sel, 0));
+ regmap_write(sfc->regmap_base, SFC_CMD, CMD_IDLE(sfc->cs_sel, 0));
+
+ /* Wait for the FIFO to empty. */
+ ret = regmap_read_poll_timeout(sfc->regmap_base, SFC_CMD, cmd_size,
+ !GET_CMD_SIZE(cmd_size),
+ 10, timeout_ms * 1000);
+ if (ret)
+ dev_err(sfc->dev, "wait for empty CMD FIFO time out\n");
+
+ return ret;
+}
+
+static int aml_sfc_pre_transfer(struct aml_sfc *sfc, u32 idle_cycle, u32 cs2clk_cycle)
+{
+ int ret;
+
+ ret = regmap_write(sfc->regmap_base, SFC_CMD, CMD_IDLE(CS_NONE, idle_cycle));
+ if (ret)
+ return ret;
+
+ return regmap_write(sfc->regmap_base, SFC_CMD, CMD_IDLE(sfc->cs_sel, cs2clk_cycle));
+}
+
+static int aml_sfc_end_transfer(struct aml_sfc *sfc, u32 clk2cs_cycle)
+{
+ int ret;
+
+ ret = regmap_write(sfc->regmap_base, SFC_CMD, CMD_IDLE(sfc->cs_sel, clk2cs_cycle));
+ if (ret)
+ return ret;
+
+ return aml_sfc_wait_cmd_finish(sfc, 0);
+}
+
+static int aml_sfc_set_bus_width(struct aml_sfc *sfc, u8 buswidth, u32 mask)
+{
+ int i;
+ u32 conf = 0;
+
+ for (i = 0; i <= LANE_MAX; i++) {
+ if (buswidth == 1 << i) {
+ conf = i << __bf_shf(mask);
+ return regmap_update_bits(sfc->regmap_base, SFC_SPI_CFG,
+ mask, conf);
+ }
+ }
+
+ return 0;
+}
+
+static int aml_sfc_send_cmd(struct aml_sfc *sfc, const struct spi_mem_op *op)
+{
+ int i, ret;
+ u8 val;
+
+ ret = aml_sfc_set_bus_width(sfc, op->cmd.buswidth, CMD_LANE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < op->cmd.nbytes; i++) {
+ val = (op->cmd.opcode >> ((op->cmd.nbytes - i - 1) * 8)) & 0xff;
+ ret = regmap_write(sfc->regmap_base, SFC_CMD, CMD_COMMAND(sfc->cs_sel, val));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aml_sfc_send_addr(struct aml_sfc *sfc, const struct spi_mem_op *op)
+{
+ int i, ret;
+ u8 val;
+
+ ret = aml_sfc_set_bus_width(sfc, op->addr.buswidth, ADDR_LANE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < op->addr.nbytes; i++) {
+ val = (op->addr.val >> ((op->addr.nbytes - i - 1) * 8)) & 0xff;
+
+ ret = regmap_write(sfc->regmap_base, SFC_CMD, CMD_ADDR(sfc->cs_sel, val));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool aml_sfc_is_xio_op(const struct spi_mem_op *op)
+{
+ switch (op->cmd.opcode) {
+ case SPIFLASH_RD_OCTALIO:
+ case SPIFLASH_RD_QUADIO:
+ case SPIFLASH_RD_DUALIO:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static int aml_sfc_send_cmd_addr_dummy(struct aml_sfc *sfc, const struct spi_mem_op *op)
+{
+ u32 dummy_cycle, cmd;
+ int ret;
+
+ ret = aml_sfc_send_cmd(sfc, op);
+ if (ret)
+ return ret;
+
+ ret = aml_sfc_send_addr(sfc, op);
+ if (ret)
+ return ret;
+
+ if (op->dummy.nbytes) {
+ /* Dummy buswidth configuration is not supported */
+ if (aml_sfc_is_xio_op(op))
+ dummy_cycle = op->dummy.nbytes * 8 / op->data.buswidth;
+ else
+ dummy_cycle = op->dummy.nbytes * 8;
+ cmd = CMD_DUMMY(sfc->cs_sel, dummy_cycle - 1);
+ return regmap_write(sfc->regmap_base, SFC_CMD, cmd);
+ }
+
+ return 0;
+}
+
+static bool aml_sfc_is_snand_hwecc_page_op(struct aml_sfc *sfc, const struct spi_mem_op *op)
+{
+ switch (op->cmd.opcode) {
+ /* SPINAND read from cache cmd */
+ case SPIFLASH_RD_QUADIO:
+ case SPIFLASH_RD_QUAD:
+ case SPIFLASH_RD_DUALIO:
+ case SPIFLASH_RD_DUAL:
+ case SPIFLASH_RD_FAST:
+ case SPIFLASH_RD:
+ /* SPINAND write to cache cmd */
+ case SPIFLASH_WR_QUAD:
+ case SPIFLASH_WR:
+ case SPIFLASH_UP_QUAD:
+ case SPIFLASH_UP:
+ if (sfc->flags & SFC_HWECC)
+ return true;
+ else
+ return false;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static int aml_sfc_dma_buffer_setup(struct aml_sfc *sfc, void *databuf,
+ int datalen, void *infobuf, int infolen,
+ enum dma_data_direction dir)
+{
+ u32 cmd = 0;
+ int ret;
+
+ sfc->daddr = dma_map_single(sfc->dev, databuf, datalen, dir);
+ ret = dma_mapping_error(sfc->dev, sfc->daddr);
+ if (ret) {
+ dev_err(sfc->dev, "DMA mapping error\n");
+ goto out_map_data;
+ }
+
+ cmd = CMD_DATA_ADDRL(sfc->daddr);
+ ret = regmap_write(sfc->regmap_base, SFC_CMD, cmd);
+ if (ret)
+ goto out_map_data;
+
+ cmd = CMD_DATA_ADDRH(sfc->daddr);
+ ret = regmap_write(sfc->regmap_base, SFC_CMD, cmd);
+ if (ret)
+ goto out_map_data;
+
+ if (infobuf) {
+ sfc->iaddr = dma_map_single(sfc->dev, infobuf, infolen, dir);
+ ret = dma_mapping_error(sfc->dev, sfc->iaddr);
+ if (ret) {
+ dev_err(sfc->dev, "DMA mapping error\n");
+ dma_unmap_single(sfc->dev, sfc->daddr, datalen, dir);
+ goto out_map_data;
+ }
+
+ sfc->info_bytes = infolen;
+ cmd = CMD_INFO_ADDRL(sfc->iaddr);
+ ret = regmap_write(sfc->regmap_base, SFC_CMD, cmd);
+ if (ret)
+ goto out_map_info;
+
+ cmd = CMD_INFO_ADDRH(sfc->iaddr);
+ ret = regmap_write(sfc->regmap_base, SFC_CMD, cmd);
+ if (ret)
+ goto out_map_info;
+ }
+
+ return 0;
+
+out_map_info:
+ dma_unmap_single(sfc->dev, sfc->iaddr, datalen, dir);
+out_map_data:
+ dma_unmap_single(sfc->dev, sfc->daddr, datalen, dir);
+
+ return ret;
+}
+
+static void aml_sfc_dma_buffer_release(struct aml_sfc *sfc,
+ int datalen, int infolen,
+ enum dma_data_direction dir)
+{
+ dma_unmap_single(sfc->dev, sfc->daddr, datalen, dir);
+ if (infolen) {
+ dma_unmap_single(sfc->dev, sfc->iaddr, infolen, dir);
+ sfc->info_bytes = 0;
+ }
+}
+
+static bool aml_sfc_dma_buffer_is_safe(const void *buffer)
+{
+ if ((uintptr_t)buffer % DMA_ADDR_ALIGN)
+ return false;
+
+ if (virt_addr_valid(buffer))
+ return true;
+
+ return false;
+}
+
+static void *aml_get_dma_safe_input_buf(const struct spi_mem_op *op)
+{
+ if (aml_sfc_dma_buffer_is_safe(op->data.buf.in))
+ return op->data.buf.in;
+
+ return kzalloc(op->data.nbytes, GFP_KERNEL);
+}
+
+static void aml_sfc_put_dma_safe_input_buf(const struct spi_mem_op *op, void *buf)
+{
+ if (WARN_ON(op->data.dir != SPI_MEM_DATA_IN) || WARN_ON(!buf))
+ return;
+
+ if (buf == op->data.buf.in)
+ return;
+
+ memcpy(op->data.buf.in, buf, op->data.nbytes);
+ kfree(buf);
+}
+
+static void *aml_sfc_get_dma_safe_output_buf(const struct spi_mem_op *op)
+{
+ if (aml_sfc_dma_buffer_is_safe(op->data.buf.out))
+ return (void *)op->data.buf.out;
+
+ return kmemdup(op->data.buf.out, op->data.nbytes, GFP_KERNEL);
+}
+
+static void aml_sfc_put_dma_safe_output_buf(const struct spi_mem_op *op, const void *buf)
+{
+ if (WARN_ON(op->data.dir != SPI_MEM_DATA_OUT) || WARN_ON(!buf))
+ return;
+
+ if (buf != op->data.buf.out)
+ kfree(buf);
+}
+
+static u64 aml_sfc_cal_timeout_cycle(struct aml_sfc *sfc, const struct spi_mem_op *op)
+{
+ u64 ms;
+
+ /* For each byte we wait for (8 cycles / buswidth) of the SPI clock. */
+ ms = 8 * MSEC_PER_SEC * op->data.nbytes / op->data.buswidth;
+ do_div(ms, sfc->bus_rate / DEFAULT_BUS_CYCLE);
+
+ /*
+ * Double the value and add a 200 ms tolerance to compensate for
+ * the impact of specific CS hold time, CS setup time sequences,
+ * controller burst gaps, and other related timing variations.
+ */
+ ms += ms + 200;
+
+ if (ms > UINT_MAX)
+ ms = UINT_MAX;
+
+ return ms;
+}
+
+static void aml_sfc_check_ecc_pages_valid(struct aml_sfc *sfc, bool raw)
+{
+ struct aml_sfc_ecc_cfg *ecc_cfg;
+ __le64 *info;
+ int ret;
+
+ info = sfc->info_buf;
+ ecc_cfg = aml_sfc_to_ecc_ctx(sfc);
+ info += raw ? 0 : ecc_cfg->nsteps - 1;
+
+ do {
+ usleep_range(10, 15);
+ /* info is updated by nfc dma engine*/
+ smp_rmb();
+ dma_sync_single_for_cpu(sfc->dev, sfc->iaddr, sfc->info_bytes,
+ DMA_FROM_DEVICE);
+ ret = le64_to_cpu(*info) & ECC_COMPLETE;
+ } while (!ret);
+}
+
+static int aml_sfc_raw_io_op(struct aml_sfc *sfc, const struct spi_mem_op *op)
+{
+ void *buf = NULL;
+ int ret;
+ bool is_datain = false;
+ u32 cmd = 0, conf;
+ u64 timeout_ms;
+
+ if (!op->data.nbytes)
+ goto end_xfer;
+
+ conf = (op->data.nbytes >> RAW_SIZE_BW) << __bf_shf(RAW_EXT_SIZE);
+ ret = regmap_update_bits(sfc->regmap_base, SFC_SPI_CFG, RAW_EXT_SIZE, conf);
+ if (ret)
+ goto err_out;
+
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ is_datain = true;
+
+ buf = aml_get_dma_safe_input_buf(op);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ cmd |= CMD_NAND2MEM(0, (op->data.nbytes & RAW_SIZE));
+ } else if (op->data.dir == SPI_MEM_DATA_OUT) {
+ is_datain = false;
+
+ buf = aml_sfc_get_dma_safe_output_buf(op);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ cmd |= CMD_MEM2NAND(0, (op->data.nbytes & RAW_SIZE));
+ } else {
+ goto end_xfer;
+ }
+
+ ret = aml_sfc_dma_buffer_setup(sfc, buf, op->data.nbytes,
+ is_datain ? sfc->info_buf : NULL,
+ is_datain ? ECC_PER_INFO_BYTE : 0,
+ is_datain ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ if (ret)
+ goto err_out;
+
+ ret = regmap_write(sfc->regmap_base, SFC_CMD, cmd);
+ if (ret)
+ goto err_out;
+
+ timeout_ms = aml_sfc_cal_timeout_cycle(sfc, op);
+ ret = aml_sfc_wait_cmd_finish(sfc, timeout_ms);
+ if (ret)
+ goto err_out;
+
+ if (is_datain)
+ aml_sfc_check_ecc_pages_valid(sfc, 1);
+
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ aml_sfc_put_dma_safe_input_buf(op, buf);
+ else if (op->data.dir == SPI_MEM_DATA_OUT)
+ aml_sfc_put_dma_safe_output_buf(op, buf);
+
+ aml_sfc_dma_buffer_release(sfc, op->data.nbytes,
+ is_datain ? ECC_PER_INFO_BYTE : 0,
+ is_datain ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+
+end_xfer:
+ return aml_sfc_end_transfer(sfc, CS_HOLD_CYCLE);
+
+err_out:
+ return ret;
+}
+
+static void aml_sfc_set_user_byte(struct aml_sfc *sfc, __le64 *info_buf, u8 *oob_buf, bool auto_oob)
+{
+ struct aml_sfc_ecc_cfg *ecc_cfg;
+ __le64 *info;
+ int i, count, step_size;
+
+ ecc_cfg = aml_sfc_to_ecc_ctx(sfc);
+
+ step_size = auto_oob ? ECC_BCH8_INFO_BYTES : ECC_BCH8_USER_BYTES;
+
+ for (i = 0, count = 0; i < ecc_cfg->nsteps; i++, count += step_size) {
+ info = &info_buf[i];
+ *info &= cpu_to_le64(~0xffff);
+ *info |= cpu_to_le64((oob_buf[count + 1] << 8) + oob_buf[count]);
+ }
+}
+
+static void aml_sfc_get_user_byte(struct aml_sfc *sfc, __le64 *info_buf, u8 *oob_buf)
+{
+ struct aml_sfc_ecc_cfg *ecc_cfg;
+ __le64 *info;
+ int i, count;
+
+ ecc_cfg = aml_sfc_to_ecc_ctx(sfc);
+
+ for (i = 0, count = 0; i < ecc_cfg->nsteps; i++, count += ECC_BCH8_INFO_BYTES) {
+ info = &info_buf[i];
+ oob_buf[count] = le64_to_cpu(*info);
+ oob_buf[count + 1] = le64_to_cpu(*info) >> 8;
+ }
+}
+
+static int aml_sfc_check_hwecc_status(struct aml_sfc *sfc, __le64 *info_buf)
+{
+ struct aml_sfc_ecc_cfg *ecc_cfg;
+ __le64 *info;
+ u32 i, max_bitflips = 0, per_sector_bitflips = 0;
+
+ ecc_cfg = aml_sfc_to_ecc_ctx(sfc);
+
+ sfc->ecc_stats.failed = 0;
+ sfc->ecc_stats.bitflips = 0;
+ sfc->ecc_stats.corrected = 0;
+
+ for (i = 0, info = info_buf; i < ecc_cfg->nsteps; i++, info++) {
+ if (ECC_ERR_CNT(le64_to_cpu(*info)) != ECC_UNCORRECTABLE) {
+ per_sector_bitflips = ECC_ERR_CNT(le64_to_cpu(*info));
+ max_bitflips = max_t(u32, max_bitflips, per_sector_bitflips);
+ sfc->ecc_stats.corrected += per_sector_bitflips;
+ continue;
+ }
+
+ return -EBADMSG;
+ }
+
+ return max_bitflips;
+}
+
+static int aml_sfc_read_page_hwecc(struct aml_sfc *sfc, const struct spi_mem_op *op)
+{
+ struct aml_sfc_ecc_cfg *ecc_cfg;
+ int ret, data_len, info_len;
+ u32 page_size, cmd = 0;
+ u64 timeout_ms;
+
+ ecc_cfg = aml_sfc_to_ecc_ctx(sfc);
+
+ page_size = ecc_cfg->stepsize * ecc_cfg->nsteps;
+ data_len = page_size + ecc_cfg->oobsize;
+ info_len = ecc_cfg->nsteps * ECC_PER_INFO_BYTE;
+
+ ret = aml_sfc_dma_buffer_setup(sfc, sfc->data_buf, data_len,
+ sfc->info_buf, info_len, DMA_FROM_DEVICE);
+ if (ret)
+ goto err_out;
+
+ cmd |= CMD_NAND2MEM(ecc_cfg->bch, ecc_cfg->nsteps);
+ ret = regmap_write(sfc->regmap_base, SFC_CMD, cmd);
+ if (ret)
+ goto err_out;
+
+ timeout_ms = aml_sfc_cal_timeout_cycle(sfc, op);
+ ret = aml_sfc_wait_cmd_finish(sfc, timeout_ms);
+ if (ret)
+ goto err_out;
+
+ aml_sfc_check_ecc_pages_valid(sfc, 0);
+ aml_sfc_dma_buffer_release(sfc, data_len, info_len, DMA_FROM_DEVICE);
+
+ /* check ecc status here */
+ ret = aml_sfc_check_hwecc_status(sfc, sfc->info_buf);
+ if (ret < 0)
+ sfc->ecc_stats.failed++;
+ else
+ sfc->ecc_stats.bitflips = ret;
+
+ if (sfc->flags & SFC_DATA_ONLY) {
+ memcpy(op->data.buf.in, sfc->data_buf, page_size);
+ } else if (sfc->flags & SFC_OOB_ONLY) {
+ aml_sfc_get_user_byte(sfc, sfc->info_buf, op->data.buf.in);
+ } else if (sfc->flags & SFC_DATA_OOB) {
+ memcpy(op->data.buf.in, sfc->data_buf, page_size);
+ aml_sfc_get_user_byte(sfc, sfc->info_buf, op->data.buf.in + page_size);
+ }
+
+ return aml_sfc_end_transfer(sfc, CS_HOLD_CYCLE);
+
+err_out:
+ return ret;
+}
+
+static int aml_sfc_write_page_hwecc(struct aml_sfc *sfc, const struct spi_mem_op *op)
+{
+ struct aml_sfc_ecc_cfg *ecc_cfg;
+ int ret, data_len, info_len;
+ u32 page_size, cmd = 0;
+ u64 timeout_ms;
+
+ ecc_cfg = aml_sfc_to_ecc_ctx(sfc);
+
+ page_size = ecc_cfg->stepsize * ecc_cfg->nsteps;
+ data_len = page_size + ecc_cfg->oobsize;
+ info_len = ecc_cfg->nsteps * ECC_PER_INFO_BYTE;
+
+ memset(sfc->info_buf, ECC_PATTERN, ecc_cfg->oobsize);
+ memcpy(sfc->data_buf, op->data.buf.out, page_size);
+
+ if (!(sfc->flags & SFC_DATA_ONLY)) {
+ if (sfc->flags & SFC_AUTO_OOB)
+ aml_sfc_set_user_byte(sfc, sfc->info_buf,
+ (u8 *)op->data.buf.out + page_size, 1);
+ else
+ aml_sfc_set_user_byte(sfc, sfc->info_buf,
+ (u8 *)op->data.buf.out + page_size, 0);
+ }
+
+ ret = aml_sfc_dma_buffer_setup(sfc, sfc->data_buf, data_len,
+ sfc->info_buf, info_len, DMA_TO_DEVICE);
+ if (ret)
+ goto err_out;
+
+ cmd |= CMD_MEM2NAND(ecc_cfg->bch, ecc_cfg->nsteps);
+ ret = regmap_write(sfc->regmap_base, SFC_CMD, cmd);
+ if (ret)
+ goto err_out;
+
+ timeout_ms = aml_sfc_cal_timeout_cycle(sfc, op);
+
+ ret = aml_sfc_wait_cmd_finish(sfc, timeout_ms);
+ if (ret)
+ goto err_out;
+
+ aml_sfc_dma_buffer_release(sfc, data_len, info_len, DMA_TO_DEVICE);
+
+ return aml_sfc_end_transfer(sfc, CS_HOLD_CYCLE);
+
+err_out:
+ return ret;
+}
+
+static int aml_sfc_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+ struct aml_sfc *sfc;
+ struct spi_device *spi;
+ struct aml_sfc_ecc_cfg *ecc_cfg;
+ int ret;
+
+ sfc = spi_controller_get_devdata(mem->spi->controller);
+ ecc_cfg = aml_sfc_to_ecc_ctx(sfc);
+ spi = mem->spi;
+ sfc->cs_sel = spi->chip_select[0] ? CS_1 : CS_0;
+
+ dev_dbg(sfc->dev, "cmd:0x%02x - addr:%08llX@%d:%u - dummy:%d:%u - data:%d:%u",
+ op->cmd.opcode, op->addr.val, op->addr.buswidth, op->addr.nbytes,
+ op->dummy.buswidth, op->dummy.nbytes, op->data.buswidth, op->data.nbytes);
+
+ ret = aml_sfc_pre_transfer(sfc, DEFAULT_PULLUP_CYCLE, CS_SETUP_CYCLE);
+ if (ret)
+ return ret;
+
+ ret = aml_sfc_send_cmd_addr_dummy(sfc, op);
+ if (ret)
+ return ret;
+
+ ret = aml_sfc_set_bus_width(sfc, op->data.buswidth, DATA_LANE);
+ if (ret)
+ return ret;
+
+ if (aml_sfc_is_snand_hwecc_page_op(sfc, op) &&
+ ecc_cfg && !(sfc->flags & SFC_RAW_RW)) {
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ return aml_sfc_read_page_hwecc(sfc, op);
+ else
+ return aml_sfc_write_page_hwecc(sfc, op);
+ }
+
+ return aml_sfc_raw_io_op(sfc, op);
+}
+
+static int aml_sfc_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+ struct aml_sfc *sfc;
+ struct aml_sfc_ecc_cfg *ecc_cfg;
+
+ sfc = spi_controller_get_devdata(mem->spi->controller);
+ ecc_cfg = aml_sfc_to_ecc_ctx(sfc);
+
+ if (aml_sfc_is_snand_hwecc_page_op(sfc, op) && ecc_cfg) {
+ if (op->data.nbytes > ecc_cfg->stepsize * ECC_BCH_MAX_SECT_SIZE)
+ return -EOPNOTSUPP;
+ } else if (op->data.nbytes & ~RAW_MAX_RW_SIZE_MASK) {
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static const struct spi_controller_mem_ops aml_sfc_mem_ops = {
+ .adjust_op_size = aml_sfc_adjust_op_size,
+ .exec_op = aml_sfc_exec_op,
+};
+
+static int aml_sfc_layout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+
+ if (section >= nand->ecc.ctx.nsteps)
+ return -ERANGE;
+
+ oobregion->offset = ECC_BCH8_USER_BYTES + (section * ECC_BCH8_INFO_BYTES);
+ oobregion->length = ECC_BCH8_PARITY_BYTES;
+
+ return 0;
+}
+
+static int aml_sfc_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+
+ if (section >= nand->ecc.ctx.nsteps)
+ return -ERANGE;
+
+ oobregion->offset = section * ECC_BCH8_INFO_BYTES;
+ oobregion->length = ECC_BCH8_USER_BYTES;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops aml_sfc_ooblayout_ops = {
+ .ecc = aml_sfc_layout_ecc,
+ .free = aml_sfc_ooblayout_free,
+};
+
+static int aml_spi_settings(struct aml_sfc *sfc, struct spi_device *spi)
+{
+ u32 conf = 0;
+
+ if (spi->mode & SPI_CPHA)
+ conf |= CPHA;
+
+ if (spi->mode & SPI_CPOL)
+ conf |= CPOL;
+
+ conf |= FIELD_PREP(RXADJ, sfc->rx_adj);
+ conf |= EN_HOLD | EN_WP;
+ return regmap_update_bits(sfc->regmap_base, SFC_SPI_CFG,
+ CPHA | CPOL | RXADJ |
+ EN_HOLD | EN_WP, conf);
+}
+
+static int aml_set_spi_clk(struct aml_sfc *sfc, struct spi_device *spi)
+{
+ u32 speed_hz;
+ int ret;
+
+ if (spi->max_speed_hz > SFC_MAX_FREQUENCY)
+ speed_hz = SFC_MAX_FREQUENCY;
+ else if (!spi->max_speed_hz)
+ speed_hz = SFC_BUS_DEFAULT_CLK;
+ else if (spi->max_speed_hz < SFC_MIN_FREQUENCY)
+ speed_hz = SFC_MIN_FREQUENCY;
+ else
+ speed_hz = spi->max_speed_hz;
+
+ /* The SPI clock is generated by dividing the bus clock by four by default. */
+ ret = regmap_write(sfc->regmap_base, SFC_CFG, (DEFAULT_BUS_CYCLE - 1));
+ if (ret) {
+ dev_err(sfc->dev, "failed to set bus cycle\n");
+ return ret;
+ }
+
+ return clk_set_rate(sfc->core_clk, speed_hz * DEFAULT_BUS_CYCLE);
+}
+
+static int aml_sfc_setup(struct spi_device *spi)
+{
+ struct aml_sfc *sfc;
+ int ret;
+
+ sfc = spi_controller_get_devdata(spi->controller);
+ ret = aml_spi_settings(sfc, spi);
+ if (ret)
+ return ret;
+
+ ret = aml_set_spi_clk(sfc, spi);
+ if (ret)
+ return ret;
+
+ sfc->bus_rate = clk_get_rate(sfc->core_clk);
+
+ return 0;
+}
+
+static int aml_sfc_ecc_init_ctx(struct nand_device *nand)
+{
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ struct aml_sfc *sfc = nand_to_aml_sfc(nand);
+ struct aml_sfc_ecc_cfg *ecc_cfg;
+ const struct aml_sfc_caps *caps = sfc->caps;
+ struct aml_sfc_ecc_cfg *ecc_caps = caps->ecc_caps;
+ int i, ecc_strength, ecc_step_size;
+
+ ecc_step_size = nand->ecc.user_conf.step_size;
+ ecc_strength = nand->ecc.user_conf.strength;
+
+ for (i = 0; i < caps->num_ecc_caps; i++) {
+ if (ecc_caps[i].stepsize == ecc_step_size) {
+ nand->ecc.ctx.conf.step_size = ecc_step_size;
+ nand->ecc.ctx.conf.flags |= BIT(ecc_caps[i].bch);
+ }
+
+ if (ecc_caps[i].strength == ecc_strength)
+ nand->ecc.ctx.conf.strength = ecc_strength;
+ }
+
+ if (!nand->ecc.ctx.conf.step_size) {
+ nand->ecc.ctx.conf.step_size = ECC_BCH8_DEFAULT_STEP;
+ nand->ecc.ctx.conf.flags |= BIT(ECC_DEFAULT_BCH_MODE);
+ }
+
+ if (!nand->ecc.ctx.conf.strength)
+ nand->ecc.ctx.conf.strength = ECC_BCH8_STRENGTH;
+
+ nand->ecc.ctx.nsteps = nand->memorg.pagesize / nand->ecc.ctx.conf.step_size;
+ nand->ecc.ctx.total = nand->ecc.ctx.nsteps * ECC_BCH8_PARITY_BYTES;
+
+ /* Verify the page size and OOB size against the SFC requirements. */
+ if ((nand->memorg.pagesize % nand->ecc.ctx.conf.step_size) ||
+ (nand->memorg.oobsize < (nand->ecc.ctx.total +
+ nand->ecc.ctx.nsteps * ECC_BCH8_USER_BYTES)))
+ return -EOPNOTSUPP;
+
+ nand->ecc.ctx.conf.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
+
+ ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL);
+ if (!ecc_cfg)
+ return -ENOMEM;
+
+ ecc_cfg->stepsize = nand->ecc.ctx.conf.step_size;
+ ecc_cfg->nsteps = nand->ecc.ctx.nsteps;
+ ecc_cfg->strength = nand->ecc.ctx.conf.strength;
+ ecc_cfg->oobsize = nand->memorg.oobsize;
+ ecc_cfg->bch = nand->ecc.ctx.conf.flags & BIT(ECC_DEFAULT_BCH_MODE) ? 1 : 2;
+
+ nand->ecc.ctx.priv = ecc_cfg;
+ sfc->priv = (void *)ecc_cfg;
+ mtd_set_ooblayout(mtd, &aml_sfc_ooblayout_ops);
+
+ sfc->flags |= SFC_HWECC;
+
+ return 0;
+}
+
+static void aml_sfc_ecc_cleanup_ctx(struct nand_device *nand)
+{
+ struct aml_sfc *sfc = nand_to_aml_sfc(nand);
+
+ sfc->flags &= ~(SFC_HWECC);
+ kfree(nand->ecc.ctx.priv);
+ sfc->priv = NULL;
+}
+
+static int aml_sfc_ecc_prepare_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ struct aml_sfc *sfc = nand_to_aml_sfc(nand);
+ struct spinand_device *spinand = nand_to_spinand(nand);
+
+ sfc->flags &= ~SFC_XFER_MDOE_MASK;
+
+ if (req->datalen && !req->ooblen)
+ sfc->flags |= SFC_DATA_ONLY;
+ else if (!req->datalen && req->ooblen)
+ sfc->flags |= SFC_OOB_ONLY;
+ else if (req->datalen && req->ooblen)
+ sfc->flags |= SFC_DATA_OOB;
+
+ if (req->mode == MTD_OPS_RAW)
+ sfc->flags |= SFC_RAW_RW;
+ else if (req->mode == MTD_OPS_AUTO_OOB)
+ sfc->flags |= SFC_AUTO_OOB;
+
+ memset(spinand->oobbuf, 0xff, nanddev_per_page_oobsize(nand));
+
+ return 0;
+}
+
+static int aml_sfc_ecc_finish_io_req(struct nand_device *nand,
+ struct nand_page_io_req *req)
+{
+ struct aml_sfc *sfc = nand_to_aml_sfc(nand);
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+
+ if (req->mode == MTD_OPS_RAW || req->type == NAND_PAGE_WRITE)
+ return 0;
+
+ if (sfc->ecc_stats.failed)
+ mtd->ecc_stats.failed++;
+
+ mtd->ecc_stats.corrected += sfc->ecc_stats.corrected;
+
+ return sfc->ecc_stats.failed ? -EBADMSG : sfc->ecc_stats.bitflips;
+}
+
+static const struct spi_controller_mem_caps aml_sfc_mem_caps = {
+ .ecc = true,
+};
+
+static const struct nand_ecc_engine_ops aml_sfc_ecc_engine_ops = {
+ .init_ctx = aml_sfc_ecc_init_ctx,
+ .cleanup_ctx = aml_sfc_ecc_cleanup_ctx,
+ .prepare_io_req = aml_sfc_ecc_prepare_io_req,
+ .finish_io_req = aml_sfc_ecc_finish_io_req,
+};
+
+static int aml_sfc_clk_init(struct aml_sfc *sfc)
+{
+ sfc->gate_clk = devm_clk_get_enabled(sfc->dev, "gate");
+ if (IS_ERR(sfc->gate_clk)) {
+ dev_err(sfc->dev, "unable to enable gate clk\n");
+ return PTR_ERR(sfc->gate_clk);
+ }
+
+ sfc->core_clk = devm_clk_get_enabled(sfc->dev, "core");
+ if (IS_ERR(sfc->core_clk)) {
+ dev_err(sfc->dev, "unable to enable core clk\n");
+ return PTR_ERR(sfc->core_clk);
+ }
+
+ return clk_set_rate(sfc->core_clk, SFC_BUS_DEFAULT_CLK);
+}
+
+static int aml_sfc_disable_clk(struct aml_sfc *sfc)
+{
+ clk_disable_unprepare(sfc->core_clk);
+ clk_disable_unprepare(sfc->gate_clk);
+
+ return 0;
+}
+
+static int aml_sfc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct spi_controller *ctrl;
+ struct aml_sfc *sfc;
+ void __iomem *reg_base;
+ int ret;
+ u32 val = 0;
+
+ const struct regmap_config core_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SFC_SPI_CFG,
+ };
+
+ ctrl = devm_spi_alloc_host(dev, sizeof(*sfc));
+ if (!ctrl)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, ctrl);
+
+ sfc = spi_controller_get_devdata(ctrl);
+ sfc->dev = dev;
+ sfc->ctrl = ctrl;
+
+ sfc->caps = of_device_get_match_data(dev);
+ if (!sfc->caps)
+ return dev_err_probe(dev, -ENODEV, "failed to get device data\n");
+
+ reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(reg_base))
+ return PTR_ERR(reg_base);
+
+ sfc->regmap_base = devm_regmap_init_mmio(dev, reg_base, &core_config);
+ if (IS_ERR(sfc->regmap_base))
+ return dev_err_probe(dev, PTR_ERR(sfc->regmap_base),
+ "failed to init sfc base regmap\n");
+
+ sfc->data_buf = devm_kzalloc(dev, SFC_BUF_SIZE, GFP_KERNEL);
+ if (!sfc->data_buf)
+ return -ENOMEM;
+ sfc->info_buf = (__le64 *)(sfc->data_buf + SFC_DATABUF_SIZE);
+
+ ret = aml_sfc_clk_init(sfc);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize SFC clock\n");
+
+ /* Enable Amlogic flash controller spi mode */
+ ret = regmap_write(sfc->regmap_base, SFC_SPI_CFG, SPI_MODE_EN);
+ if (ret) {
+ dev_err(dev, "failed to enable SPI mode\n");
+ goto err_out;
+ }
+
+ ret = dma_set_mask(sfc->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(sfc->dev, "failed to set dma mask\n");
+ goto err_out;
+ }
+
+ sfc->ecc_eng.dev = &pdev->dev;
+ sfc->ecc_eng.integration = NAND_ECC_ENGINE_INTEGRATION_PIPELINED;
+ sfc->ecc_eng.ops = &aml_sfc_ecc_engine_ops;
+ sfc->ecc_eng.priv = sfc;
+
+ ret = nand_ecc_register_on_host_hw_engine(&sfc->ecc_eng);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register Aml host ecc engine.\n");
+ goto err_out;
+ }
+
+ ret = of_property_read_u32(np, "amlogic,rx-adj", &val);
+ if (!ret)
+ sfc->rx_adj = val;
+
+ ctrl->dev.of_node = np;
+ ctrl->mem_ops = &aml_sfc_mem_ops;
+ ctrl->mem_caps = &aml_sfc_mem_caps;
+ ctrl->setup = aml_sfc_setup;
+ ctrl->mode_bits = SPI_TX_QUAD | SPI_TX_DUAL | SPI_RX_QUAD |
+ SPI_RX_DUAL | SPI_TX_OCTAL | SPI_RX_OCTAL;
+ ctrl->max_speed_hz = SFC_MAX_FREQUENCY;
+ ctrl->min_speed_hz = SFC_MIN_FREQUENCY;
+ ctrl->num_chipselect = SFC_MAX_CS_NUM;
+
+ ret = devm_spi_register_controller(dev, ctrl);
+ if (ret)
+ goto err_out;
+
+ return 0;
+
+err_out:
+ aml_sfc_disable_clk(sfc);
+
+ return ret;
+}
+
+static void aml_sfc_remove(struct platform_device *pdev)
+{
+ struct spi_controller *ctlr = platform_get_drvdata(pdev);
+ struct aml_sfc *sfc = spi_controller_get_devdata(ctlr);
+
+ aml_sfc_disable_clk(sfc);
+}
+
+static const struct of_device_id aml_sfc_of_match[] = {
+ {
+ .compatible = "amlogic,a4-spifc",
+ .data = &aml_a113l2_sfc_caps
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, aml_sfc_of_match);
+
+static struct platform_driver aml_sfc_driver = {
+ .driver = {
+ .name = "aml_sfc",
+ .of_match_table = aml_sfc_of_match,
+ },
+ .probe = aml_sfc_probe,
+ .remove = aml_sfc_remove,
+};
+module_platform_driver(aml_sfc_driver);
+
+MODULE_DESCRIPTION("Amlogic SPI Flash Controller driver");
+MODULE_AUTHOR("Feng Chen <feng.chen@amlogic.com>");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/spi/spi-amlogic-spisg.c b/drivers/spi/spi-amlogic-spisg.c
new file mode 100644
index 000000000000..bcd7ec291ad0
--- /dev/null
+++ b/drivers/spi/spi-amlogic-spisg.c
@@ -0,0 +1,888 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Amlogic SPI communication Scatter-Gather Controller
+ *
+ * Copyright (C) 2025 Amlogic, Inc. All rights reserved
+ *
+ * Author: Sunny Luo <sunny.luo@amlogic.com>
+ * Author: Xianwei Zhao <xianwei.zhao@amlogic.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+
+/* Register Map */
+#define SPISG_REG_CFG_READY 0x00
+
+#define SPISG_REG_CFG_SPI 0x04
+#define CFG_BUS64_EN BIT(0)
+#define CFG_SLAVE_EN BIT(1)
+#define CFG_SLAVE_SELECT GENMASK(3, 2)
+#define CFG_SFLASH_WP BIT(4)
+#define CFG_SFLASH_HD BIT(5)
+/* start on vsync rising */
+#define CFG_HW_POS BIT(6)
+/* start on vsync falling */
+#define CFG_HW_NEG BIT(7)
+
+#define SPISG_REG_CFG_START 0x08
+#define CFG_BLOCK_NUM GENMASK(19, 0)
+#define CFG_BLOCK_SIZE GENMASK(22, 20)
+#define CFG_DATA_COMMAND BIT(23)
+#define CFG_OP_MODE GENMASK(25, 24)
+#define CFG_RXD_MODE GENMASK(27, 26)
+#define CFG_TXD_MODE GENMASK(29, 28)
+#define CFG_EOC BIT(30)
+#define CFG_PEND BIT(31)
+
+#define SPISG_REG_CFG_BUS 0x0C
+#define CFG_CLK_DIV GENMASK(7, 0)
+#define CLK_DIV_WIDTH 8
+#define CFG_RX_TUNING GENMASK(11, 8)
+#define CFG_TX_TUNING GENMASK(15, 12)
+#define CFG_CS_SETUP GENMASK(19, 16)
+#define CFG_LANE GENMASK(21, 20)
+#define CFG_HALF_DUPLEX BIT(22)
+#define CFG_B_L_ENDIAN BIT(23)
+#define CFG_DC_MODE BIT(24)
+#define CFG_NULL_CTL BIT(25)
+#define CFG_DUMMY_CTL BIT(26)
+#define CFG_READ_TURN GENMASK(28, 27)
+#define CFG_KEEP_SS BIT(29)
+#define CFG_CPHA BIT(30)
+#define CFG_CPOL BIT(31)
+
+#define SPISG_REG_PIO_TX_DATA_L 0x10
+#define SPISG_REG_PIO_TX_DATA_H 0x14
+#define SPISG_REG_PIO_RX_DATA_L 0x18
+#define SPISG_REG_PIO_RX_DATA_H 0x1C
+#define SPISG_REG_MEM_TX_ADDR_L 0x10
+#define SPISG_REG_MEM_TX_ADDR_H 0x14
+#define SPISG_REG_MEM_RX_ADDR_L 0x18
+#define SPISG_REG_MEM_RX_ADDR_H 0x1C
+#define SPISG_REG_DESC_LIST_L 0x20
+#define SPISG_REG_DESC_LIST_H 0x24
+#define LIST_DESC_PENDING BIT(31)
+#define SPISG_REG_DESC_CURRENT_L 0x28
+#define SPISG_REG_DESC_CURRENT_H 0x2c
+#define SPISG_REG_IRQ_STS 0x30
+#define SPISG_REG_IRQ_ENABLE 0x34
+#define IRQ_RCH_DESC_EOC BIT(0)
+#define IRQ_RCH_DESC_INVALID BIT(1)
+#define IRQ_RCH_DESC_RESP BIT(2)
+#define IRQ_RCH_DATA_RESP BIT(3)
+#define IRQ_WCH_DESC_EOC BIT(4)
+#define IRQ_WCH_DESC_INVALID BIT(5)
+#define IRQ_WCH_DESC_RESP BIT(6)
+#define IRQ_WCH_DATA_RESP BIT(7)
+#define IRQ_DESC_ERR BIT(8)
+#define IRQ_SPI_READY BIT(9)
+#define IRQ_DESC_DONE BIT(10)
+#define IRQ_DESC_CHAIN_DONE BIT(11)
+
+#define SPISG_MAX_REG 0x40
+
+#define SPISG_BLOCK_MAX 0x100000
+
+#define SPISG_OP_MODE_WRITE_CMD 0
+#define SPISG_OP_MODE_READ_STS 1
+#define SPISG_OP_MODE_WRITE 2
+#define SPISG_OP_MODE_READ 3
+
+#define SPISG_DATA_MODE_NONE 0
+#define SPISG_DATA_MODE_PIO 1
+#define SPISG_DATA_MODE_MEM 2
+#define SPISG_DATA_MODE_SG 3
+
+#define SPISG_CLK_DIV_MAX 256
+/* recommended by specification */
+#define SPISG_CLK_DIV_MIN 4
+#define DIV_NUM (SPISG_CLK_DIV_MAX - SPISG_CLK_DIV_MIN + 1)
+
+#define SPISG_PCLK_RATE_MIN 24000000
+
+#define SPISG_SINGLE_SPI 0
+#define SPISG_DUAL_SPI 1
+#define SPISG_QUAD_SPI 2
+
+struct spisg_sg_link {
+#define LINK_ADDR_VALID BIT(0)
+#define LINK_ADDR_EOC BIT(1)
+#define LINK_ADDR_IRQ BIT(2)
+#define LINK_ADDR_ACT GENMASK(5, 3)
+#define LINK_ADDR_RING BIT(6)
+#define LINK_ADDR_LEN GENMASK(31, 8)
+ u32 addr;
+ u32 addr1;
+};
+
+struct spisg_descriptor {
+ u32 cfg_start;
+ u32 cfg_bus;
+ u64 tx_paddr;
+ u64 rx_paddr;
+};
+
+struct spisg_descriptor_extra {
+ struct spisg_sg_link *tx_ccsg;
+ struct spisg_sg_link *rx_ccsg;
+ int tx_ccsg_len;
+ int rx_ccsg_len;
+};
+
+struct spisg_device {
+ struct spi_controller *controller;
+ struct platform_device *pdev;
+ struct regmap *map;
+ struct clk *core;
+ struct clk *pclk;
+ struct clk *sclk;
+ struct clk_div_table *tbl;
+ struct completion completion;
+ u32 status;
+ u32 speed_hz;
+ u32 effective_speed_hz;
+ u32 bytes_per_word;
+ u32 cfg_spi;
+ u32 cfg_start;
+ u32 cfg_bus;
+};
+
+static int spi_delay_to_sclk(u32 slck_speed_hz, struct spi_delay *delay)
+{
+ s32 ns;
+
+ if (!delay)
+ return 0;
+
+ if (delay->unit == SPI_DELAY_UNIT_SCK)
+ return delay->value;
+
+ ns = spi_delay_to_ns(delay, NULL);
+ if (ns < 0)
+ return 0;
+
+ return DIV_ROUND_UP_ULL(slck_speed_hz * ns, NSEC_PER_SEC);
+}
+
+static inline u32 aml_spisg_sem_down_read(struct spisg_device *spisg)
+{
+ u32 ret;
+
+ regmap_read(spisg->map, SPISG_REG_CFG_READY, &ret);
+ if (ret)
+ regmap_write(spisg->map, SPISG_REG_CFG_READY, 0);
+
+ return ret;
+}
+
+static inline void aml_spisg_sem_up_write(struct spisg_device *spisg)
+{
+ regmap_write(spisg->map, SPISG_REG_CFG_READY, 1);
+}
+
+static int aml_spisg_set_speed(struct spisg_device *spisg, uint speed_hz)
+{
+ u32 cfg_bus;
+
+ if (!speed_hz || speed_hz == spisg->speed_hz)
+ return 0;
+
+ spisg->speed_hz = speed_hz;
+ clk_set_rate(spisg->sclk, speed_hz);
+ /* Store the div for the descriptor mode */
+ regmap_read(spisg->map, SPISG_REG_CFG_BUS, &cfg_bus);
+ spisg->cfg_bus &= ~CFG_CLK_DIV;
+ spisg->cfg_bus |= cfg_bus & CFG_CLK_DIV;
+ spisg->effective_speed_hz = clk_get_rate(spisg->sclk);
+ dev_dbg(&spisg->pdev->dev,
+ "desired speed %dHz, effective speed %dHz\n",
+ speed_hz, spisg->effective_speed_hz);
+
+ return 0;
+}
+
+static bool aml_spisg_can_dma(struct spi_controller *ctlr,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ return true;
+}
+
+static void aml_spisg_sg_xlate(struct sg_table *sgt, struct spisg_sg_link *ccsg)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ ccsg->addr = FIELD_PREP(LINK_ADDR_VALID, 1) |
+ FIELD_PREP(LINK_ADDR_RING, 0) |
+ FIELD_PREP(LINK_ADDR_EOC, sg_is_last(sg)) |
+ FIELD_PREP(LINK_ADDR_LEN, sg_dma_len(sg));
+ ccsg->addr1 = (u32)sg_dma_address(sg);
+ ccsg++;
+ }
+}
+
+static int nbits_to_lane[] = {
+ SPISG_SINGLE_SPI,
+ SPISG_SINGLE_SPI,
+ SPISG_DUAL_SPI,
+ -EINVAL,
+ SPISG_QUAD_SPI
+};
+
+static int aml_spisg_setup_transfer(struct spisg_device *spisg,
+ struct spi_transfer *xfer,
+ struct spisg_descriptor *desc,
+ struct spisg_descriptor_extra *exdesc)
+{
+ int block_size, blocks;
+ struct device *dev = &spisg->pdev->dev;
+ struct spisg_sg_link *ccsg;
+ int ccsg_len;
+ dma_addr_t paddr;
+ int ret;
+
+ memset(desc, 0, sizeof(*desc));
+ if (exdesc)
+ memset(exdesc, 0, sizeof(*exdesc));
+ aml_spisg_set_speed(spisg, xfer->speed_hz);
+ xfer->effective_speed_hz = spisg->effective_speed_hz;
+
+ desc->cfg_start = spisg->cfg_start;
+ desc->cfg_bus = spisg->cfg_bus;
+
+ block_size = xfer->bits_per_word >> 3;
+ blocks = xfer->len / block_size;
+
+ desc->cfg_start |= FIELD_PREP(CFG_EOC, 0);
+ desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, !xfer->cs_change);
+ desc->cfg_bus |= FIELD_PREP(CFG_NULL_CTL, 0);
+
+ if (xfer->tx_buf || xfer->tx_dma) {
+ desc->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->tx_nbits]);
+ desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE);
+ }
+ if (xfer->rx_buf || xfer->rx_dma) {
+ desc->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->rx_nbits]);
+ desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_READ);
+ }
+
+ if (FIELD_GET(CFG_OP_MODE, desc->cfg_start) == SPISG_OP_MODE_READ_STS) {
+ desc->cfg_start |= FIELD_PREP(CFG_BLOCK_SIZE, blocks) |
+ FIELD_PREP(CFG_BLOCK_NUM, 1);
+ } else {
+ blocks = min_t(int, blocks, SPISG_BLOCK_MAX);
+ desc->cfg_start |= FIELD_PREP(CFG_BLOCK_SIZE, block_size & 0x7) |
+ FIELD_PREP(CFG_BLOCK_NUM, blocks);
+ }
+
+ if (xfer->tx_sg.nents && xfer->tx_sg.sgl) {
+ ccsg_len = xfer->tx_sg.nents * sizeof(struct spisg_sg_link);
+ ccsg = kzalloc(ccsg_len, GFP_KERNEL | GFP_DMA);
+ if (!ccsg) {
+ dev_err(dev, "alloc tx_ccsg failed\n");
+ return -ENOMEM;
+ }
+
+ aml_spisg_sg_xlate(&xfer->tx_sg, ccsg);
+ paddr = dma_map_single(dev, (void *)ccsg,
+ ccsg_len, DMA_TO_DEVICE);
+ ret = dma_mapping_error(dev, paddr);
+ if (ret) {
+ kfree(ccsg);
+ dev_err(dev, "tx ccsg map failed\n");
+ return ret;
+ }
+
+ desc->tx_paddr = paddr;
+ desc->cfg_start |= FIELD_PREP(CFG_TXD_MODE, SPISG_DATA_MODE_SG);
+ exdesc->tx_ccsg = ccsg;
+ exdesc->tx_ccsg_len = ccsg_len;
+ dma_sync_sgtable_for_device(spisg->controller->cur_tx_dma_dev,
+ &xfer->tx_sg, DMA_TO_DEVICE);
+ } else if (xfer->tx_buf || xfer->tx_dma) {
+ paddr = xfer->tx_dma;
+ if (!paddr) {
+ paddr = dma_map_single(dev, (void *)xfer->tx_buf,
+ xfer->len, DMA_TO_DEVICE);
+ ret = dma_mapping_error(dev, paddr);
+ if (ret) {
+ dev_err(dev, "tx buf map failed\n");
+ return ret;
+ }
+ }
+ desc->tx_paddr = paddr;
+ desc->cfg_start |= FIELD_PREP(CFG_TXD_MODE, SPISG_DATA_MODE_MEM);
+ }
+
+ if (xfer->rx_sg.nents && xfer->rx_sg.sgl) {
+ ccsg_len = xfer->rx_sg.nents * sizeof(struct spisg_sg_link);
+ ccsg = kzalloc(ccsg_len, GFP_KERNEL | GFP_DMA);
+ if (!ccsg) {
+ dev_err(dev, "alloc rx_ccsg failed\n");
+ return -ENOMEM;
+ }
+
+ aml_spisg_sg_xlate(&xfer->rx_sg, ccsg);
+ paddr = dma_map_single(dev, (void *)ccsg,
+ ccsg_len, DMA_TO_DEVICE);
+ ret = dma_mapping_error(dev, paddr);
+ if (ret) {
+ kfree(ccsg);
+ dev_err(dev, "rx ccsg map failed\n");
+ return ret;
+ }
+
+ desc->rx_paddr = paddr;
+ desc->cfg_start |= FIELD_PREP(CFG_RXD_MODE, SPISG_DATA_MODE_SG);
+ exdesc->rx_ccsg = ccsg;
+ exdesc->rx_ccsg_len = ccsg_len;
+ dma_sync_sgtable_for_device(spisg->controller->cur_rx_dma_dev,
+ &xfer->rx_sg, DMA_FROM_DEVICE);
+ } else if (xfer->rx_buf || xfer->rx_dma) {
+ paddr = xfer->rx_dma;
+ if (!paddr) {
+ paddr = dma_map_single(dev, xfer->rx_buf,
+ xfer->len, DMA_FROM_DEVICE);
+ ret = dma_mapping_error(dev, paddr);
+ if (ret) {
+ dev_err(dev, "rx buf map failed\n");
+ return ret;
+ }
+ }
+
+ desc->rx_paddr = paddr;
+ desc->cfg_start |= FIELD_PREP(CFG_RXD_MODE, SPISG_DATA_MODE_MEM);
+ }
+
+ return 0;
+}
+
+static void aml_spisg_cleanup_transfer(struct spisg_device *spisg,
+ struct spi_transfer *xfer,
+ struct spisg_descriptor *desc,
+ struct spisg_descriptor_extra *exdesc)
+{
+ struct device *dev = &spisg->pdev->dev;
+
+ if (desc->tx_paddr) {
+ if (FIELD_GET(CFG_TXD_MODE, desc->cfg_start) == SPISG_DATA_MODE_SG) {
+ dma_unmap_single(dev, (dma_addr_t)desc->tx_paddr,
+ exdesc->tx_ccsg_len, DMA_TO_DEVICE);
+ kfree(exdesc->tx_ccsg);
+ dma_sync_sgtable_for_cpu(spisg->controller->cur_tx_dma_dev,
+ &xfer->tx_sg, DMA_TO_DEVICE);
+ } else if (!xfer->tx_dma) {
+ dma_unmap_single(dev, (dma_addr_t)desc->tx_paddr,
+ xfer->len, DMA_TO_DEVICE);
+ }
+ }
+
+ if (desc->rx_paddr) {
+ if (FIELD_GET(CFG_RXD_MODE, desc->cfg_start) == SPISG_DATA_MODE_SG) {
+ dma_unmap_single(dev, (dma_addr_t)desc->rx_paddr,
+ exdesc->rx_ccsg_len, DMA_TO_DEVICE);
+ kfree(exdesc->rx_ccsg);
+ dma_sync_sgtable_for_cpu(spisg->controller->cur_rx_dma_dev,
+ &xfer->rx_sg, DMA_FROM_DEVICE);
+ } else if (!xfer->rx_dma) {
+ dma_unmap_single(dev, (dma_addr_t)desc->rx_paddr,
+ xfer->len, DMA_FROM_DEVICE);
+ }
+ }
+}
+
+static void aml_spisg_setup_null_desc(struct spisg_device *spisg,
+ struct spisg_descriptor *desc,
+ u32 n_sclk)
+{
+ /* unit is the last xfer sclk */
+ desc->cfg_start = spisg->cfg_start;
+ desc->cfg_bus = spisg->cfg_bus;
+
+ desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE) |
+ FIELD_PREP(CFG_BLOCK_SIZE, 1) |
+ FIELD_PREP(CFG_BLOCK_NUM, DIV_ROUND_UP(n_sclk, 8));
+
+ desc->cfg_bus |= FIELD_PREP(CFG_NULL_CTL, 1);
+}
+
+static void aml_spisg_pending(struct spisg_device *spisg,
+ dma_addr_t desc_paddr,
+ bool trig,
+ bool irq_en)
+{
+ u32 desc_l, desc_h, cfg_spi, irq_enable;
+
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ desc_l = (u64)desc_paddr & 0xffffffff;
+ desc_h = (u64)desc_paddr >> 32;
+#else
+ desc_l = desc_paddr & 0xffffffff;
+ desc_h = 0;
+#endif
+
+ cfg_spi = spisg->cfg_spi;
+ if (trig)
+ cfg_spi |= CFG_HW_POS;
+ else
+ desc_h |= LIST_DESC_PENDING;
+
+ irq_enable = IRQ_RCH_DESC_INVALID | IRQ_RCH_DESC_RESP |
+ IRQ_RCH_DATA_RESP | IRQ_WCH_DESC_INVALID |
+ IRQ_WCH_DESC_RESP | IRQ_WCH_DATA_RESP |
+ IRQ_DESC_ERR | IRQ_DESC_CHAIN_DONE;
+ regmap_write(spisg->map, SPISG_REG_IRQ_ENABLE, irq_en ? irq_enable : 0);
+ regmap_write(spisg->map, SPISG_REG_CFG_SPI, cfg_spi);
+ regmap_write(spisg->map, SPISG_REG_DESC_LIST_L, desc_l);
+ regmap_write(spisg->map, SPISG_REG_DESC_LIST_H, desc_h);
+}
+
+static irqreturn_t aml_spisg_irq(int irq, void *data)
+{
+ struct spisg_device *spisg = (void *)data;
+ u32 sts;
+
+ spisg->status = 0;
+ regmap_read(spisg->map, SPISG_REG_IRQ_STS, &sts);
+ regmap_write(spisg->map, SPISG_REG_IRQ_STS, sts);
+ if (sts & (IRQ_RCH_DESC_INVALID |
+ IRQ_RCH_DESC_RESP |
+ IRQ_RCH_DATA_RESP |
+ IRQ_WCH_DESC_INVALID |
+ IRQ_WCH_DESC_RESP |
+ IRQ_WCH_DATA_RESP |
+ IRQ_DESC_ERR))
+ spisg->status = sts;
+ else if (sts & IRQ_DESC_CHAIN_DONE)
+ spisg->status = 0;
+ else
+ return IRQ_NONE;
+
+ complete(&spisg->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int aml_spisg_transfer_one_message(struct spi_controller *ctlr,
+ struct spi_message *msg)
+{
+ struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
+ struct device *dev = &spisg->pdev->dev;
+ unsigned long long ms = 0;
+ struct spi_transfer *xfer;
+ struct spisg_descriptor *descs, *desc;
+ struct spisg_descriptor_extra *exdescs, *exdesc;
+ dma_addr_t descs_paddr;
+ int desc_num = 1, descs_len;
+ u32 cs_hold_in_sclk = 0;
+ int ret = -EIO;
+
+ if (!aml_spisg_sem_down_read(spisg)) {
+ spi_finalize_current_message(ctlr);
+ dev_err(dev, "controller busy\n");
+ return -EBUSY;
+ }
+
+ /* calculate the desc num for all xfer */
+ list_for_each_entry(xfer, &msg->transfers, transfer_list)
+ desc_num++;
+
+ /* alloc descriptor/extra-descriptor table */
+ descs = kcalloc(desc_num, sizeof(*desc) + sizeof(*exdesc),
+ GFP_KERNEL | GFP_DMA);
+ if (!descs) {
+ spi_finalize_current_message(ctlr);
+ aml_spisg_sem_up_write(spisg);
+ return -ENOMEM;
+ }
+ descs_len = sizeof(*desc) * desc_num;
+ exdescs = (struct spisg_descriptor_extra *)(descs + desc_num);
+
+ /* config descriptor for each xfer */
+ desc = descs;
+ exdesc = exdescs;
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ ret = aml_spisg_setup_transfer(spisg, xfer, desc, exdesc);
+ if (ret) {
+ dev_err(dev, "config descriptor failed\n");
+ goto end;
+ }
+
+ /* calculate cs-setup delay with the first xfer speed */
+ if (list_is_first(&xfer->transfer_list, &msg->transfers))
+ desc->cfg_bus |= FIELD_PREP(CFG_CS_SETUP,
+ spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_setup));
+
+ /* calculate cs-hold delay with the last xfer speed */
+ if (list_is_last(&xfer->transfer_list, &msg->transfers))
+ cs_hold_in_sclk =
+ spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_hold);
+
+ desc++;
+ exdesc++;
+ ms += DIV_ROUND_UP_ULL(8LL * MSEC_PER_SEC * xfer->len,
+ xfer->effective_speed_hz);
+ }
+
+ if (cs_hold_in_sclk)
+ /* additional null-descriptor to achieve the cs-hold delay */
+ aml_spisg_setup_null_desc(spisg, desc, cs_hold_in_sclk);
+ else
+ desc--;
+
+ desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, 0);
+ desc->cfg_start |= FIELD_PREP(CFG_EOC, 1);
+
+ /* some tolerances */
+ ms += ms + 20;
+ if (ms > UINT_MAX)
+ ms = UINT_MAX;
+
+ descs_paddr = dma_map_single(dev, (void *)descs,
+ descs_len, DMA_TO_DEVICE);
+ ret = dma_mapping_error(dev, descs_paddr);
+ if (ret) {
+ dev_err(dev, "desc table map failed\n");
+ goto end;
+ }
+
+ reinit_completion(&spisg->completion);
+ aml_spisg_pending(spisg, descs_paddr, false, true);
+ if (wait_for_completion_timeout(&spisg->completion,
+ spi_controller_is_target(spisg->controller) ?
+ MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(ms)))
+ ret = spisg->status ? -EIO : 0;
+ else
+ ret = -ETIMEDOUT;
+
+ dma_unmap_single(dev, descs_paddr, descs_len, DMA_TO_DEVICE);
+end:
+ desc = descs;
+ exdesc = exdescs;
+ list_for_each_entry(xfer, &msg->transfers, transfer_list)
+ aml_spisg_cleanup_transfer(spisg, xfer, desc++, exdesc++);
+ kfree(descs);
+
+ if (!ret)
+ msg->actual_length = msg->frame_length;
+ msg->status = ret;
+ spi_finalize_current_message(ctlr);
+ aml_spisg_sem_up_write(spisg);
+
+ return ret;
+}
+
+static int aml_spisg_prepare_message(struct spi_controller *ctlr,
+ struct spi_message *message)
+{
+ struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
+ struct spi_device *spi = message->spi;
+
+ if (!spi->bits_per_word || spi->bits_per_word % 8) {
+ dev_err(&spisg->pdev->dev, "invalid wordlen %d\n", spi->bits_per_word);
+ return -EINVAL;
+ }
+
+ spisg->bytes_per_word = spi->bits_per_word >> 3;
+
+ spisg->cfg_spi &= ~CFG_SLAVE_SELECT;
+ spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_SELECT, spi_get_chipselect(spi, 0));
+
+ spisg->cfg_bus &= ~(CFG_CPOL | CFG_CPHA | CFG_B_L_ENDIAN | CFG_HALF_DUPLEX);
+ spisg->cfg_bus |= FIELD_PREP(CFG_CPOL, !!(spi->mode & SPI_CPOL)) |
+ FIELD_PREP(CFG_CPHA, !!(spi->mode & SPI_CPHA)) |
+ FIELD_PREP(CFG_B_L_ENDIAN, !!(spi->mode & SPI_LSB_FIRST)) |
+ FIELD_PREP(CFG_HALF_DUPLEX, !!(spi->mode & SPI_3WIRE));
+
+ return 0;
+}
+
+static int aml_spisg_setup(struct spi_device *spi)
+{
+ if (!spi->controller_state)
+ spi->controller_state = spi_controller_get_devdata(spi->controller);
+
+ return 0;
+}
+
+static void aml_spisg_cleanup(struct spi_device *spi)
+{
+ spi->controller_state = NULL;
+}
+
+static int aml_spisg_target_abort(struct spi_controller *ctlr)
+{
+ struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
+
+ spisg->status = 0;
+ regmap_write(spisg->map, SPISG_REG_DESC_LIST_H, 0);
+ complete(&spisg->completion);
+
+ return 0;
+}
+
+static int aml_spisg_clk_init(struct spisg_device *spisg, void __iomem *base)
+{
+ struct device *dev = &spisg->pdev->dev;
+ struct clk_init_data init;
+ struct clk_divider *div;
+ struct clk_div_table *tbl;
+ char name[32];
+ int ret, i;
+
+ spisg->core = devm_clk_get_enabled(dev, "core");
+ if (IS_ERR_OR_NULL(spisg->core)) {
+ dev_err(dev, "core clock request failed\n");
+ return PTR_ERR(spisg->core);
+ }
+
+ spisg->pclk = devm_clk_get_enabled(dev, "pclk");
+ if (IS_ERR_OR_NULL(spisg->pclk)) {
+ dev_err(dev, "pclk clock request failed\n");
+ return PTR_ERR(spisg->pclk);
+ }
+
+ clk_set_min_rate(spisg->pclk, SPISG_PCLK_RATE_MIN);
+
+ clk_disable_unprepare(spisg->pclk);
+
+ tbl = devm_kcalloc(dev, (DIV_NUM + 1), sizeof(*tbl), GFP_KERNEL);
+ if (!tbl)
+ return -ENOMEM;
+
+ for (i = 0; i < DIV_NUM; i++) {
+ tbl[i].val = i + SPISG_CLK_DIV_MIN - 1;
+ tbl[i].div = i + SPISG_CLK_DIV_MIN;
+ }
+ spisg->tbl = tbl;
+
+ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return -ENOMEM;
+
+ div->flags = CLK_DIVIDER_ROUND_CLOSEST;
+ div->reg = base + SPISG_REG_CFG_BUS;
+ div->shift = __bf_shf(CFG_CLK_DIV);
+ div->width = CLK_DIV_WIDTH;
+ div->table = tbl;
+
+ /* Register value should not be outside of the table */
+ regmap_update_bits(spisg->map, SPISG_REG_CFG_BUS, CFG_CLK_DIV,
+ FIELD_PREP(CFG_CLK_DIV, SPISG_CLK_DIV_MIN - 1));
+
+ /* Register clk-divider */
+ snprintf(name, sizeof(name), "%s_div", dev_name(dev));
+ init.name = name;
+ init.ops = &clk_divider_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_data = &(const struct clk_parent_data) {
+ .fw_name = "pclk",
+ };
+ init.num_parents = 1;
+ div->hw.init = &init;
+ ret = devm_clk_hw_register(dev, &div->hw);
+ if (ret) {
+ dev_err(dev, "clock registration failed\n");
+ return ret;
+ }
+
+ spisg->sclk = devm_clk_hw_get_clk(dev, &div->hw, NULL);
+ if (IS_ERR_OR_NULL(spisg->sclk)) {
+ dev_err(dev, "get clock failed\n");
+ return PTR_ERR(spisg->sclk);
+ }
+
+ clk_prepare_enable(spisg->sclk);
+
+ return 0;
+}
+
+static int aml_spisg_probe(struct platform_device *pdev)
+{
+ struct spi_controller *ctlr;
+ struct spisg_device *spisg;
+ struct device *dev = &pdev->dev;
+ void __iomem *base;
+ int ret, irq;
+
+ const struct regmap_config aml_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SPISG_MAX_REG,
+ };
+
+ if (of_property_read_bool(dev->of_node, "spi-slave"))
+ ctlr = spi_alloc_target(dev, sizeof(*spisg));
+ else
+ ctlr = spi_alloc_host(dev, sizeof(*spisg));
+ if (!ctlr)
+ return -ENOMEM;
+
+ spisg = spi_controller_get_devdata(ctlr);
+ spisg->controller = ctlr;
+
+ spisg->pdev = pdev;
+ platform_set_drvdata(pdev, spisg);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return dev_err_probe(dev, PTR_ERR(base), "resource ioremap failed\n");
+
+ spisg->map = devm_regmap_init_mmio(dev, base, &aml_regmap_config);
+ if (IS_ERR(spisg->map))
+ return dev_err_probe(dev, PTR_ERR(spisg->map), "regmap init failed\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto out_controller;
+ }
+
+ ret = device_reset_optional(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "reset dev failed\n");
+
+ ret = aml_spisg_clk_init(spisg, base);
+ if (ret)
+ return dev_err_probe(dev, ret, "clock init failed\n");
+
+ spisg->cfg_spi = 0;
+ spisg->cfg_start = 0;
+ spisg->cfg_bus = 0;
+
+ spisg->cfg_spi = FIELD_PREP(CFG_SFLASH_WP, 1) |
+ FIELD_PREP(CFG_SFLASH_HD, 1);
+ if (spi_controller_is_target(ctlr)) {
+ spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_EN, 1);
+ spisg->cfg_bus = FIELD_PREP(CFG_TX_TUNING, 0xf);
+ }
+ /* default pending */
+ spisg->cfg_start = FIELD_PREP(CFG_PEND, 1);
+
+ pm_runtime_set_active(&spisg->pdev->dev);
+ pm_runtime_enable(&spisg->pdev->dev);
+ pm_runtime_resume_and_get(&spisg->pdev->dev);
+
+ ctlr->num_chipselect = 4;
+ ctlr->dev.of_node = pdev->dev.of_node;
+ ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST |
+ SPI_3WIRE | SPI_TX_QUAD | SPI_RX_QUAD;
+ ctlr->max_speed_hz = 1000 * 1000 * 100;
+ ctlr->min_speed_hz = 1000 * 10;
+ ctlr->setup = aml_spisg_setup;
+ ctlr->cleanup = aml_spisg_cleanup;
+ ctlr->prepare_message = aml_spisg_prepare_message;
+ ctlr->transfer_one_message = aml_spisg_transfer_one_message;
+ ctlr->target_abort = aml_spisg_target_abort;
+ ctlr->can_dma = aml_spisg_can_dma;
+ ctlr->max_dma_len = SPISG_BLOCK_MAX;
+ ctlr->auto_runtime_pm = true;
+
+ dma_set_max_seg_size(&pdev->dev, SPISG_BLOCK_MAX);
+
+ ret = devm_request_irq(&pdev->dev, irq, aml_spisg_irq, 0, NULL, spisg);
+ if (ret) {
+ dev_err(&pdev->dev, "irq request failed\n");
+ goto out_clk;
+ }
+
+ ret = devm_spi_register_controller(dev, ctlr);
+ if (ret) {
+ dev_err(&pdev->dev, "spi controller registration failed\n");
+ goto out_clk;
+ }
+
+ init_completion(&spisg->completion);
+
+ pm_runtime_put(&spisg->pdev->dev);
+
+ return 0;
+out_clk:
+ if (spisg->core)
+ clk_disable_unprepare(spisg->core);
+ clk_disable_unprepare(spisg->pclk);
+out_controller:
+ spi_controller_put(ctlr);
+
+ return ret;
+}
+
+static void aml_spisg_remove(struct platform_device *pdev)
+{
+ struct spisg_device *spisg = platform_get_drvdata(pdev);
+
+ if (!pm_runtime_suspended(&pdev->dev)) {
+ pinctrl_pm_select_sleep_state(&spisg->pdev->dev);
+ clk_disable_unprepare(spisg->core);
+ clk_disable_unprepare(spisg->pclk);
+ }
+}
+
+static int spisg_suspend_runtime(struct device *dev)
+{
+ struct spisg_device *spisg = dev_get_drvdata(dev);
+
+ pinctrl_pm_select_sleep_state(&spisg->pdev->dev);
+ clk_disable_unprepare(spisg->sclk);
+ clk_disable_unprepare(spisg->core);
+
+ return 0;
+}
+
+static int spisg_resume_runtime(struct device *dev)
+{
+ struct spisg_device *spisg = dev_get_drvdata(dev);
+
+ clk_prepare_enable(spisg->core);
+ clk_prepare_enable(spisg->sclk);
+ pinctrl_pm_select_default_state(&spisg->pdev->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops amlogic_spisg_pm_ops = {
+ .runtime_suspend = spisg_suspend_runtime,
+ .runtime_resume = spisg_resume_runtime,
+};
+
+static const struct of_device_id amlogic_spisg_of_match[] = {
+ {
+ .compatible = "amlogic,a4-spisg",
+ },
+
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, amlogic_spisg_of_match);
+
+static struct platform_driver amlogic_spisg_driver = {
+ .probe = aml_spisg_probe,
+ .remove = aml_spisg_remove,
+ .driver = {
+ .name = "amlogic-spisg",
+ .of_match_table = amlogic_spisg_of_match,
+ .pm = &amlogic_spisg_pm_ops,
+ },
+};
+
+module_platform_driver(amlogic_spisg_driver);
+
+MODULE_DESCRIPTION("Amlogic SPI Scatter-Gather Controller driver");
+MODULE_AUTHOR("Sunny Luo <sunny.luo@amlogic.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-apple.c b/drivers/spi/spi-apple.c
index 6273352a2b28..2fee7057ecc9 100644
--- a/drivers/spi/spi-apple.c
+++ b/drivers/spi/spi-apple.c
@@ -511,6 +511,7 @@ static int apple_spi_probe(struct platform_device *pdev)
}
static const struct of_device_id apple_spi_of_match[] = {
+ { .compatible = "apple,t8103-spi", },
{ .compatible = "apple,spi", },
{}
};
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 89a6b46cd319..89977bff76d2 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -256,6 +256,7 @@ struct atmel_spi {
void __iomem *regs;
int irq;
struct clk *clk;
+ struct clk *gclk;
struct platform_device *pdev;
unsigned long spi_clk;
@@ -397,20 +398,10 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
* on CS1,2,3 needs SPI_CSR0.BITS config as SPI_CSR1,2,3.BITS
*/
spi_writel(as, CSR0, asd->csr);
- if (as->caps.has_wdrbt) {
- spi_writel(as, MR,
- SPI_BF(PCS, ~(0x01 << chip_select))
- | SPI_BIT(WDRBT)
- | SPI_BIT(MODFDIS)
- | SPI_BIT(MSTR));
- } else {
- spi_writel(as, MR,
- SPI_BF(PCS, ~(0x01 << chip_select))
- | SPI_BIT(MODFDIS)
- | SPI_BIT(MSTR));
- }
mr = spi_readl(as, MR);
+ mr = SPI_BFINS(PCS, ~(0x01 << chip_select), mr);
+ spi_writel(as, MR, mr);
/*
* Ensures the clock polarity is valid before we actually
@@ -1490,6 +1481,8 @@ static void atmel_get_caps(struct atmel_spi *as)
static void atmel_spi_init(struct atmel_spi *as)
{
+ u32 mr = 0;
+
spi_writel(as, CR, SPI_BIT(SWRST));
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
@@ -1497,12 +1490,17 @@ static void atmel_spi_init(struct atmel_spi *as)
if (as->fifo_size)
spi_writel(as, CR, SPI_BIT(FIFOEN));
- if (as->caps.has_wdrbt) {
- spi_writel(as, MR, SPI_BIT(WDRBT) | SPI_BIT(MODFDIS)
- | SPI_BIT(MSTR));
- } else {
- spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS));
- }
+ /*
+ * If GCLK is selected as the source clock for the bit rate generation
+ * Enable the BRSRCCLK/FDIV/DIV32 bit
+ */
+ if (as->gclk)
+ mr |= SPI_BIT(FDIV);
+
+ if (as->caps.has_wdrbt)
+ mr |= SPI_BIT(WDRBT);
+
+ spi_writel(as, MR, mr | SPI_BIT(MODFDIS) | SPI_BIT(MSTR));
if (as->use_pdc)
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
@@ -1565,6 +1563,11 @@ static int atmel_spi_probe(struct platform_device *pdev)
as->phybase = regs->start;
as->irq = irq;
as->clk = clk;
+ as->gclk = devm_clk_get_optional(&pdev->dev, "spi_gclk");
+ if (IS_ERR(as->gclk)) {
+ ret = PTR_ERR(as->gclk);
+ goto out_unmap_regs;
+ }
init_completion(&as->xfer_completion);
@@ -1625,7 +1628,19 @@ static int atmel_spi_probe(struct platform_device *pdev)
if (ret)
goto out_free_irq;
- as->spi_clk = clk_get_rate(clk);
+ /*
+ * In cases where the peripheral clock is higher,the FLEX_SPI_CSRx.SCBR
+ * exceeds the threshold (SCBR ≤ 255), the GCLK is used as the source clock
+ * for the SPCK (SPI Serial Clock) bit rate generation
+ */
+ if (as->gclk) {
+ ret = clk_prepare_enable(as->gclk);
+ if (ret)
+ goto out_disable_clk;
+ as->spi_clk = clk_get_rate(as->gclk);
+ } else {
+ as->spi_clk = clk_get_rate(clk);
+ }
as->fifo_size = 0;
if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
@@ -1660,6 +1675,8 @@ out_free_dma:
spi_writel(as, CR, SPI_BIT(SWRST));
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
+ clk_disable_unprepare(as->gclk);
+out_disable_clk:
clk_disable_unprepare(clk);
out_free_irq:
out_unmap_regs:
@@ -1695,6 +1712,8 @@ static void atmel_spi_remove(struct platform_device *pdev)
spin_unlock_irq(&as->lock);
clk_disable_unprepare(as->clk);
+ if (as->gclk)
+ clk_disable_unprepare(as->gclk);
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -1706,6 +1725,8 @@ static int atmel_spi_runtime_suspend(struct device *dev)
struct atmel_spi *as = spi_controller_get_devdata(host);
clk_disable_unprepare(as->clk);
+ if (as->gclk)
+ clk_disable_unprepare(as->gclk);
pinctrl_pm_select_sleep_state(dev);
return 0;
@@ -1715,10 +1736,20 @@ static int atmel_spi_runtime_resume(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct atmel_spi *as = spi_controller_get_devdata(host);
+ int ret;
pinctrl_pm_select_default_state(dev);
- return clk_prepare_enable(as->clk);
+ ret = clk_prepare_enable(as->clk);
+ if (ret)
+ return ret;
+ if (as->gclk) {
+ ret = clk_prepare_enable(as->gclk);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
static int atmel_spi_suspend(struct device *dev)
@@ -1746,10 +1777,17 @@ static int atmel_spi_resume(struct device *dev)
ret = clk_prepare_enable(as->clk);
if (ret)
return ret;
+ if (as->gclk) {
+ ret = clk_prepare_enable(as->gclk);
+ if (ret)
+ return ret;
+ }
atmel_spi_init(as);
clk_disable_unprepare(as->clk);
+ if (as->gclk)
+ clk_disable_unprepare(as->gclk);
if (!pm_runtime_suspended(dev)) {
ret = atmel_spi_runtime_resume(dev);
diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c
index 8cc19934b48b..e06f412190fd 100644
--- a/drivers/spi/spi-axi-spi-engine.c
+++ b/drivers/spi/spi-axi-spi-engine.c
@@ -6,12 +6,12 @@
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
+#include <linux/adi-axi-common.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/dmaengine.h>
-#include <linux/fpga/adi-axi-common.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@@ -1050,7 +1050,7 @@ static int spi_engine_probe(struct platform_device *pdev)
return -ENODEV;
}
- if (ADI_AXI_PCORE_VER_MINOR(version) >= 1) {
+ if (adi_axi_pcore_ver_gteq(version, 1, 1)) {
unsigned int sizes = readl(spi_engine->base +
SPI_ENGINE_REG_OFFLOAD_MEM_ADDR_WIDTH);
@@ -1064,7 +1064,7 @@ static int spi_engine_probe(struct platform_device *pdev)
}
/* IP v1.5 dropped the requirement for SYNC in offload messages. */
- spi_engine->offload_requires_sync = ADI_AXI_PCORE_VER_MINOR(version) < 5;
+ spi_engine->offload_requires_sync = !adi_axi_pcore_ver_gteq(version, 1, 5);
writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_RESET);
writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
@@ -1091,15 +1091,12 @@ static int spi_engine_probe(struct platform_device *pdev)
host->put_offload = spi_engine_put_offload;
host->num_chipselect = 8;
- /* Some features depend of the IP core version. */
- if (ADI_AXI_PCORE_VER_MAJOR(version) >= 1) {
- if (ADI_AXI_PCORE_VER_MINOR(version) >= 2) {
- host->mode_bits |= SPI_CS_HIGH;
- host->setup = spi_engine_setup;
- }
- if (ADI_AXI_PCORE_VER_MINOR(version) >= 3)
- host->mode_bits |= SPI_MOSI_IDLE_LOW | SPI_MOSI_IDLE_HIGH;
+ if (adi_axi_pcore_ver_gteq(version, 1, 2)) {
+ host->mode_bits |= SPI_CS_HIGH;
+ host->setup = spi_engine_setup;
}
+ if (adi_axi_pcore_ver_gteq(version, 1, 3))
+ host->mode_bits |= SPI_MOSI_IDLE_LOW | SPI_MOSI_IDLE_HIGH;
if (host->max_speed_hz == 0)
return dev_err_probe(&pdev->dev, -EINVAL, "spi_clk rate is 0");
diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c
index 77de5a07639a..192cc5ef65fb 100644
--- a/drivers/spi/spi-bcm2835.c
+++ b/drivers/spi/spi-bcm2835.c
@@ -622,7 +622,7 @@ static void bcm2835_spi_dma_rx_done(void *data)
/* reset fifo and HW */
bcm2835_spi_reset_hw(bs);
- /* and mark as completed */;
+ /* and mark as completed */
spi_finalize_current_transfer(ctlr);
}
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index d3c78f59b22c..8fb13df8ff87 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -33,7 +33,7 @@
#define CQSPI_NAME "cadence-qspi"
#define CQSPI_MAX_CHIPSELECT 4
-static_assert(CQSPI_MAX_CHIPSELECT <= SPI_CS_CNT_MAX);
+static_assert(CQSPI_MAX_CHIPSELECT <= SPI_DEVICE_CS_CNT_MAX);
/* Quirks */
#define CQSPI_NEEDS_WR_DELAY BIT(0)
@@ -46,6 +46,7 @@ static_assert(CQSPI_MAX_CHIPSELECT <= SPI_CS_CNT_MAX);
#define CQSPI_DMA_SET_MASK BIT(7)
#define CQSPI_SUPPORT_DEVICE_RESET BIT(8)
#define CQSPI_DISABLE_STIG_MODE BIT(9)
+#define CQSPI_DISABLE_RUNTIME_PM BIT(10)
/* Capabilities */
#define CQSPI_SUPPORTS_OCTAL BIT(0)
@@ -108,6 +109,8 @@ struct cqspi_st {
bool is_jh7110; /* Flag for StarFive JH7110 SoC */
bool disable_stig_mode;
+ refcount_t refcount;
+ refcount_t inflight_ops;
const struct cqspi_driver_platdata *ddata;
};
@@ -333,7 +336,7 @@ static bool cqspi_is_idle(struct cqspi_st *cqspi)
{
u32 reg = readl(cqspi->iobase + CQSPI_REG_CONFIG);
- return reg & (1UL << CQSPI_REG_CONFIG_IDLE_LSB);
+ return reg & BIT(CQSPI_REG_CONFIG_IDLE_LSB);
}
static u32 cqspi_get_rd_sram_level(struct cqspi_st *cqspi)
@@ -569,7 +572,7 @@ static int cqspi_command_read(struct cqspi_flash_pdata *f_pdata,
reg |= (dummy_clk & CQSPI_REG_CMDCTRL_DUMMY_MASK)
<< CQSPI_REG_CMDCTRL_DUMMY_LSB;
- reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB);
+ reg |= BIT(CQSPI_REG_CMDCTRL_RD_EN_LSB);
/* 0 means 1 byte. */
reg |= (((n_rx - 1) & CQSPI_REG_CMDCTRL_RD_BYTES_MASK)
@@ -577,7 +580,7 @@ static int cqspi_command_read(struct cqspi_flash_pdata *f_pdata,
/* setup ADDR BIT field */
if (op->addr.nbytes) {
- reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
+ reg |= BIT(CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
reg |= ((op->addr.nbytes - 1) &
CQSPI_REG_CMDCTRL_ADD_BYTES_MASK)
<< CQSPI_REG_CMDCTRL_ADD_BYTES_LSB;
@@ -644,7 +647,7 @@ static int cqspi_command_write(struct cqspi_flash_pdata *f_pdata,
reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
if (op->addr.nbytes) {
- reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
+ reg |= BIT(CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
reg |= ((op->addr.nbytes - 1) &
CQSPI_REG_CMDCTRL_ADD_BYTES_MASK)
<< CQSPI_REG_CMDCTRL_ADD_BYTES_LSB;
@@ -653,7 +656,7 @@ static int cqspi_command_write(struct cqspi_flash_pdata *f_pdata,
}
if (n_tx) {
- reg |= (0x1 << CQSPI_REG_CMDCTRL_WR_EN_LSB);
+ reg |= BIT(CQSPI_REG_CMDCTRL_WR_EN_LSB);
reg |= ((n_tx - 1) & CQSPI_REG_CMDCTRL_WR_BYTES_MASK)
<< CQSPI_REG_CMDCTRL_WR_BYTES_LSB;
data = 0;
@@ -717,6 +720,7 @@ static int cqspi_read_setup(struct cqspi_flash_pdata *f_pdata,
reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
reg |= (op->addr.nbytes - 1);
writel(reg, reg_base + CQSPI_REG_SIZE);
+ readl(reg_base + CQSPI_REG_SIZE); /* Flush posted write. */
return 0;
}
@@ -735,6 +739,9 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
u8 *rxbuf_end = rxbuf + n_rx;
int ret = 0;
+ if (!refcount_read(&cqspi->refcount))
+ return -ENODEV;
+
writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
writel(remaining, reg_base + CQSPI_REG_INDIRECTRDBYTES);
@@ -759,6 +766,7 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
reinit_completion(&cqspi->transfer_complete);
writel(CQSPI_REG_INDIRECTRD_START_MASK,
reg_base + CQSPI_REG_INDIRECTRD);
+ readl(reg_base + CQSPI_REG_INDIRECTRD); /* Flush posted write. */
while (remaining > 0) {
if (use_irq &&
@@ -1057,6 +1065,7 @@ static int cqspi_write_setup(struct cqspi_flash_pdata *f_pdata,
reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
reg |= (op->addr.nbytes - 1);
writel(reg, reg_base + CQSPI_REG_SIZE);
+ readl(reg_base + CQSPI_REG_SIZE); /* Flush posted write. */
return 0;
}
@@ -1071,6 +1080,9 @@ static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata,
unsigned int write_bytes;
int ret;
+ if (!refcount_read(&cqspi->refcount))
+ return -ENODEV;
+
writel(to_addr, reg_base + CQSPI_REG_INDIRECTWRSTARTADDR);
writel(remaining, reg_base + CQSPI_REG_INDIRECTWRBYTES);
@@ -1082,6 +1094,8 @@ static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata,
reinit_completion(&cqspi->transfer_complete);
writel(CQSPI_REG_INDIRECTWR_START_MASK,
reg_base + CQSPI_REG_INDIRECTWR);
+ readl(reg_base + CQSPI_REG_INDIRECTWR); /* Flush posted write. */
+
/*
* As per 66AK2G02 TRM SPRUHY8F section 11.15.5.3 Indirect Access
* Controller programming sequence, couple of cycles of
@@ -1178,7 +1192,7 @@ static void cqspi_chipselect(struct cqspi_flash_pdata *f_pdata)
* CS2 to 4b'1011
* CS3 to 4b'0111
*/
- chip_select = 0xF & ~(1 << chip_select);
+ chip_select = 0xF & ~BIT(chip_select);
}
reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK
@@ -1264,9 +1278,9 @@ static void cqspi_readdata_capture(struct cqspi_st *cqspi,
reg = readl(reg_base + CQSPI_REG_READCAPTURE);
if (bypass)
- reg |= (1 << CQSPI_REG_READCAPTURE_BYPASS_LSB);
+ reg |= BIT(CQSPI_REG_READCAPTURE_BYPASS_LSB);
else
- reg &= ~(1 << CQSPI_REG_READCAPTURE_BYPASS_LSB);
+ reg &= ~BIT(CQSPI_REG_READCAPTURE_BYPASS_LSB);
reg &= ~(CQSPI_REG_READCAPTURE_DELAY_MASK
<< CQSPI_REG_READCAPTURE_DELAY_LSB);
@@ -1460,21 +1474,41 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
int ret;
struct cqspi_st *cqspi = spi_controller_get_devdata(mem->spi->controller);
struct device *dev = &cqspi->pdev->dev;
+ const struct cqspi_driver_platdata *ddata = of_device_get_match_data(dev);
- ret = pm_runtime_resume_and_get(dev);
- if (ret) {
- dev_err(&mem->spi->dev, "resume failed with %d\n", ret);
- return ret;
+ if (refcount_read(&cqspi->inflight_ops) == 0)
+ return -ENODEV;
+
+ if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ dev_err(&mem->spi->dev, "resume failed with %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (!refcount_read(&cqspi->refcount))
+ return -EBUSY;
+
+ refcount_inc(&cqspi->inflight_ops);
+
+ if (!refcount_read(&cqspi->refcount)) {
+ if (refcount_read(&cqspi->inflight_ops))
+ refcount_dec(&cqspi->inflight_ops);
+ return -EBUSY;
}
ret = cqspi_mem_process(mem, op);
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
+ if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM)))
+ pm_runtime_put_autosuspend(dev);
if (ret)
dev_err(&mem->spi->dev, "operation failed with %d\n", ret);
+ if (refcount_read(&cqspi->inflight_ops) > 1)
+ refcount_dec(&cqspi->inflight_ops);
+
return ret;
}
@@ -1693,12 +1727,10 @@ static const struct spi_controller_mem_caps cqspi_mem_caps = {
static int cqspi_setup_flash(struct cqspi_st *cqspi)
{
- unsigned int max_cs = cqspi->num_chipselect - 1;
struct platform_device *pdev = cqspi->pdev;
struct device *dev = &pdev->dev;
struct cqspi_flash_pdata *f_pdata;
- unsigned int cs;
- int ret;
+ int ret, cs, max_cs = -1;
/* Get flash device data */
for_each_available_child_of_node_scoped(dev->of_node, np) {
@@ -1711,10 +1743,10 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi)
if (cs >= cqspi->num_chipselect) {
dev_err(dev, "Chip select %d out of range.\n", cs);
return -EINVAL;
- } else if (cs < max_cs) {
- max_cs = cs;
}
+ max_cs = max_t(int, cs, max_cs);
+
f_pdata = &cqspi->f_pdata[cs];
f_pdata->cqspi = cqspi;
f_pdata->cs = cs;
@@ -1724,6 +1756,11 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi)
return ret;
}
+ if (max_cs < 0) {
+ dev_err(dev, "No flash device declared\n");
+ return -ENODEV;
+ }
+
cqspi->num_chipselect = max_cs + 1;
return 0;
}
@@ -1926,6 +1963,9 @@ static int cqspi_probe(struct platform_device *pdev)
}
}
+ refcount_set(&cqspi->refcount, 1);
+ refcount_set(&cqspi->inflight_ops, 1);
+
ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0,
pdev->name, cqspi);
if (ret) {
@@ -1958,11 +1998,12 @@ static int cqspi_probe(struct platform_device *pdev)
goto probe_setup_failed;
}
- pm_runtime_enable(dev);
-
- pm_runtime_set_autosuspend_delay(dev, CQSPI_AUTOSUSPEND_TIMEOUT);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_get_noresume(dev);
+ if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, CQSPI_AUTOSUSPEND_TIMEOUT);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_get_noresume(dev);
+ }
ret = spi_register_controller(host);
if (ret) {
@@ -1970,13 +2011,17 @@ static int cqspi_probe(struct platform_device *pdev)
goto probe_setup_failed;
}
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
+ if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
+ pm_runtime_put_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
return 0;
probe_setup_failed:
cqspi_controller_enable(cqspi, 0);
- pm_runtime_disable(dev);
+ if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM)))
+ pm_runtime_disable(dev);
probe_reset_failed:
if (cqspi->is_jh7110)
cqspi_jh7110_disable_clk(pdev, cqspi);
@@ -1987,7 +2032,16 @@ probe_clk_failed:
static void cqspi_remove(struct platform_device *pdev)
{
+ const struct cqspi_driver_platdata *ddata;
struct cqspi_st *cqspi = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ ddata = of_device_get_match_data(dev);
+
+ refcount_set(&cqspi->refcount, 0);
+
+ if (!refcount_dec_and_test(&cqspi->inflight_ops))
+ cqspi_wait_idle(cqspi);
spi_unregister_controller(cqspi->host);
cqspi_controller_enable(cqspi, 0);
@@ -1995,14 +2049,17 @@ static void cqspi_remove(struct platform_device *pdev)
if (cqspi->rx_chan)
dma_release_channel(cqspi->rx_chan);
- if (pm_runtime_get_sync(&pdev->dev) >= 0)
- clk_disable(cqspi->clk);
+ if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM)))
+ if (pm_runtime_get_sync(&pdev->dev) >= 0)
+ clk_disable(cqspi->clk);
if (cqspi->is_jh7110)
cqspi_jh7110_disable_clk(pdev, cqspi);
- pm_runtime_put_sync(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
+ if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ }
}
static int cqspi_runtime_suspend(struct device *dev)
@@ -2081,7 +2138,8 @@ static const struct cqspi_driver_platdata socfpga_qspi = {
.quirks = CQSPI_DISABLE_DAC_MODE
| CQSPI_NO_SUPPORT_WR_COMPLETION
| CQSPI_SLOW_SRAM
- | CQSPI_DISABLE_STIG_MODE,
+ | CQSPI_DISABLE_STIG_MODE
+ | CQSPI_DISABLE_RUNTIME_PM,
};
static const struct cqspi_driver_platdata versal_ospi = {
diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
index 9e56bde87768..5ae09b21d23a 100644
--- a/drivers/spi/spi-cadence.c
+++ b/drivers/spi/spi-cadence.c
@@ -662,7 +662,6 @@ static int cdns_spi_probe(struct platform_device *pdev)
/* Set to default valid value */
ctlr->max_speed_hz = xspi->clk_rate / 4;
xspi->speed_hz = ctlr->max_speed_hz;
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
} else {
ctlr->mode_bits |= SPI_NO_CS;
diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c
index b28a840b3b04..14307dd800b7 100644
--- a/drivers/spi/spi-cs42l43.c
+++ b/drivers/spi/spi-cs42l43.c
@@ -295,7 +295,7 @@ static struct spi_board_info *cs42l43_create_bridge_amp(struct cs42l43_spi *priv
struct spi_board_info *info;
if (spkid >= 0) {
- props = devm_kmalloc(priv->dev, sizeof(*props), GFP_KERNEL);
+ props = devm_kcalloc(priv->dev, 2, sizeof(*props), GFP_KERNEL);
if (!props)
return NULL;
diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c
index 84279058f0f1..faa893f83dc5 100644
--- a/drivers/spi/spi-falcon.c
+++ b/drivers/spi/spi-falcon.c
@@ -94,8 +94,9 @@ struct falcon_sflash {
struct spi_controller *host;
};
-int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t,
- unsigned long flags)
+static int
+falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t,
+ unsigned long flags)
{
struct device *dev = &spi->dev;
struct falcon_sflash *priv = spi_controller_get_devdata(spi->controller);
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 0dcd49114095..83ea296597e9 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -24,6 +24,7 @@
#define SPI_MCR 0x00
#define SPI_MCR_HOST BIT(31)
+#define SPI_MCR_MTFE BIT(26)
#define SPI_MCR_PCSIS(x) ((x) << 16)
#define SPI_MCR_CLR_TXF BIT(11)
#define SPI_MCR_CLR_RXF BIT(10)
@@ -35,8 +36,9 @@
#define SPI_TCR 0x08
#define SPI_TCR_GET_TCNT(x) (((x) & GENMASK(31, 16)) >> 16)
-#define SPI_CTAR(x) (0x0c + (((x) & GENMASK(1, 0)) * 4))
+#define SPI_CTAR(x) (0x0c + (((x) & GENMASK(2, 0)) * 4))
#define SPI_CTAR_FMSZ(x) (((x) << 27) & GENMASK(30, 27))
+#define SPI_CTAR_DBR BIT(31)
#define SPI_CTAR_CPOL BIT(26)
#define SPI_CTAR_CPHA BIT(25)
#define SPI_CTAR_LSBFE BIT(24)
@@ -93,12 +95,14 @@
#define SPI_TXFR1 0x40
#define SPI_TXFR2 0x44
#define SPI_TXFR3 0x48
+#define SPI_TXFR4 0x4C
#define SPI_RXFR0 0x7c
#define SPI_RXFR1 0x80
#define SPI_RXFR2 0x84
#define SPI_RXFR3 0x88
+#define SPI_RXFR4 0x8C
-#define SPI_CTARE(x) (0x11c + (((x) & GENMASK(1, 0)) * 4))
+#define SPI_CTARE(x) (0x11c + (((x) & GENMASK(2, 0)) * 4))
#define SPI_CTARE_FMSZE(x) (((x) & 0x1) << 16)
#define SPI_CTARE_DTCP(x) ((x) & 0x7ff)
@@ -109,6 +113,8 @@
#define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000)
+#define SPI_25MHZ 25000000
+
struct chip_data {
u32 ctar_val;
};
@@ -122,6 +128,7 @@ struct fsl_dspi_devtype_data {
enum dspi_trans_mode trans_mode;
u8 max_clock_factor;
int fifo_size;
+ const struct regmap_config *regmap;
};
enum {
@@ -135,6 +142,102 @@ enum {
LX2160A,
MCF5441X,
VF610,
+ S32G,
+ S32G_TARGET,
+};
+
+static const struct regmap_range dspi_yes_ranges[] = {
+ regmap_reg_range(SPI_MCR, SPI_MCR),
+ regmap_reg_range(SPI_TCR, SPI_CTAR(3)),
+ regmap_reg_range(SPI_SR, SPI_TXFR3),
+ regmap_reg_range(SPI_RXFR0, SPI_RXFR3),
+ regmap_reg_range(SPI_CTARE(0), SPI_CTARE(3)),
+ regmap_reg_range(SPI_SREX, SPI_SREX),
+};
+
+static const struct regmap_range s32g_dspi_yes_ranges[] = {
+ regmap_reg_range(SPI_MCR, SPI_MCR),
+ regmap_reg_range(SPI_TCR, SPI_CTAR(5)),
+ regmap_reg_range(SPI_SR, SPI_TXFR4),
+ regmap_reg_range(SPI_RXFR0, SPI_RXFR4),
+ regmap_reg_range(SPI_CTARE(0), SPI_CTARE(5)),
+ regmap_reg_range(SPI_SREX, SPI_SREX),
+};
+
+static const struct regmap_access_table dspi_access_table = {
+ .yes_ranges = dspi_yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(dspi_yes_ranges),
+};
+
+static const struct regmap_access_table s32g_dspi_access_table = {
+ .yes_ranges = s32g_dspi_yes_ranges,
+ .n_yes_ranges = ARRAY_SIZE(s32g_dspi_yes_ranges),
+};
+
+static const struct regmap_range dspi_volatile_ranges[] = {
+ regmap_reg_range(SPI_MCR, SPI_TCR),
+ regmap_reg_range(SPI_SR, SPI_SR),
+ regmap_reg_range(SPI_PUSHR, SPI_RXFR4),
+ regmap_reg_range(SPI_SREX, SPI_SREX),
+};
+
+static const struct regmap_access_table dspi_volatile_table = {
+ .yes_ranges = dspi_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(dspi_volatile_ranges),
+};
+
+enum {
+ DSPI_REGMAP,
+ S32G_DSPI_REGMAP,
+ DSPI_XSPI_REGMAP,
+ S32G_DSPI_XSPI_REGMAP,
+ DSPI_PUSHR,
+};
+
+static const struct regmap_config dspi_regmap_config[] = {
+ [DSPI_REGMAP] = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SPI_RXFR3,
+ .volatile_table = &dspi_volatile_table,
+ .rd_table = &dspi_access_table,
+ .wr_table = &dspi_access_table,
+ },
+ [S32G_DSPI_REGMAP] = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SPI_RXFR4,
+ .volatile_table = &dspi_volatile_table,
+ .wr_table = &s32g_dspi_access_table,
+ .rd_table = &s32g_dspi_access_table,
+ },
+ [DSPI_XSPI_REGMAP] = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SPI_SREX,
+ .volatile_table = &dspi_volatile_table,
+ .rd_table = &dspi_access_table,
+ .wr_table = &dspi_access_table,
+ },
+ [S32G_DSPI_XSPI_REGMAP] = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SPI_SREX,
+ .volatile_table = &dspi_volatile_table,
+ .wr_table = &s32g_dspi_access_table,
+ .rd_table = &s32g_dspi_access_table,
+ },
+ [DSPI_PUSHR] = {
+ .name = "pushr",
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_stride = 2,
+ .max_register = 0x2,
+ },
};
static const struct fsl_dspi_devtype_data devtype_data[] = {
@@ -142,55 +245,77 @@ static const struct fsl_dspi_devtype_data devtype_data[] = {
.trans_mode = DSPI_DMA_MODE,
.max_clock_factor = 2,
.fifo_size = 4,
+ .regmap = &dspi_regmap_config[DSPI_REGMAP],
},
[LS1021A] = {
/* Has A-011218 DMA erratum */
.trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8,
.fifo_size = 4,
+ .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
},
[LS1012A] = {
/* Has A-011218 DMA erratum */
.trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8,
.fifo_size = 16,
+ .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
},
[LS1028A] = {
.trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8,
.fifo_size = 4,
+ .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
},
[LS1043A] = {
/* Has A-011218 DMA erratum */
.trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8,
.fifo_size = 16,
+ .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
},
[LS1046A] = {
/* Has A-011218 DMA erratum */
.trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8,
.fifo_size = 16,
+ .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
},
[LS2080A] = {
.trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8,
.fifo_size = 4,
+ .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
},
[LS2085A] = {
.trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8,
.fifo_size = 4,
+ .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
},
[LX2160A] = {
.trans_mode = DSPI_XSPI_MODE,
.max_clock_factor = 8,
.fifo_size = 4,
+ .regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
},
[MCF5441X] = {
.trans_mode = DSPI_DMA_MODE,
.max_clock_factor = 8,
.fifo_size = 16,
+ .regmap = &dspi_regmap_config[DSPI_REGMAP],
+ },
+ [S32G] = {
+ .trans_mode = DSPI_XSPI_MODE,
+ .max_clock_factor = 1,
+ .fifo_size = 5,
+ .regmap = &dspi_regmap_config[S32G_DSPI_XSPI_REGMAP],
+ },
+ [S32G_TARGET] = {
+ .trans_mode = DSPI_DMA_MODE,
+ .max_clock_factor = 1,
+ .fifo_size = 5,
+ .regmap = &dspi_regmap_config[S32G_DSPI_REGMAP],
},
};
@@ -206,6 +331,8 @@ struct fsl_dspi_dma {
dma_addr_t rx_dma_phys;
struct completion cmd_rx_complete;
struct dma_async_tx_descriptor *rx_desc;
+
+ size_t bufsize;
};
struct fsl_dspi {
@@ -225,6 +352,7 @@ struct fsl_dspi {
const void *tx;
void *rx;
u16 tx_cmd;
+ bool mtf_enabled;
const struct fsl_dspi_devtype_data *devtype_data;
struct completion xfer_done;
@@ -247,6 +375,14 @@ struct fsl_dspi {
void (*dev_to_host)(struct fsl_dspi *dspi, u32 rxdata);
};
+static void dspi_setup_accel(struct fsl_dspi *dspi);
+
+static bool is_s32g_dspi(struct fsl_dspi *data)
+{
+ return data->devtype_data == &devtype_data[S32G] ||
+ data->devtype_data == &devtype_data[S32G_TARGET];
+}
+
static void dspi_native_host_to_dev(struct fsl_dspi *dspi, u32 *txdata)
{
switch (dspi->oper_word_size) {
@@ -336,6 +472,27 @@ static u32 dspi_pop_tx(struct fsl_dspi *dspi)
return txdata;
}
+/* Push one word to the RX buffer from the POPR register (RX FIFO) */
+static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata)
+{
+ if (!dspi->rx)
+ return;
+ dspi->dev_to_host(dspi, rxdata);
+}
+
+static int dspi_fifo_error(struct fsl_dspi *dspi, u32 spi_sr)
+{
+ if (spi_sr & (SPI_SR_TFUF | SPI_SR_RFOF)) {
+ dev_err_ratelimited(&dspi->pdev->dev, "FIFO errors:%s%s\n",
+ spi_sr & SPI_SR_TFUF ? " TX underflow," : "",
+ spi_sr & SPI_SR_RFOF ? " RX overflow," : "");
+ return -EIO;
+ }
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_DMA_ENGINE)
+
/* Prepare one TX FIFO entry (txdata plus cmd) */
static u32 dspi_pop_tx_pushr(struct fsl_dspi *dspi)
{
@@ -349,19 +506,37 @@ static u32 dspi_pop_tx_pushr(struct fsl_dspi *dspi)
return cmd << 16 | data;
}
-/* Push one word to the RX buffer from the POPR register (RX FIFO) */
-static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata)
+static size_t dspi_dma_max_datawords(struct fsl_dspi *dspi)
{
- if (!dspi->rx)
- return;
- dspi->dev_to_host(dspi, rxdata);
+ /*
+ * Transfers look like one of these, so we always use a full DMA word
+ * regardless of SPI word size:
+ *
+ * 31 16 15 0
+ * -----------------------------------------
+ * | CONTROL WORD | 16-bit DATA |
+ * -----------------------------------------
+ * or
+ * -----------------------------------------
+ * | CONTROL WORD | UNUSED | 8-bit DATA |
+ * -----------------------------------------
+ */
+ return dspi->dma->bufsize / DMA_SLAVE_BUSWIDTH_4_BYTES;
+}
+
+static size_t dspi_dma_transfer_size(struct fsl_dspi *dspi)
+{
+ return dspi->words_in_flight * DMA_SLAVE_BUSWIDTH_4_BYTES;
}
static void dspi_tx_dma_callback(void *arg)
{
struct fsl_dspi *dspi = arg;
struct fsl_dspi_dma *dma = dspi->dma;
+ struct device *dev = &dspi->pdev->dev;
+ dma_sync_single_for_cpu(dev, dma->tx_dma_phys,
+ dspi_dma_transfer_size(dspi), DMA_TO_DEVICE);
complete(&dma->cmd_tx_complete);
}
@@ -369,9 +544,13 @@ static void dspi_rx_dma_callback(void *arg)
{
struct fsl_dspi *dspi = arg;
struct fsl_dspi_dma *dma = dspi->dma;
+ struct device *dev = &dspi->pdev->dev;
int i;
if (dspi->rx) {
+ dma_sync_single_for_cpu(dev, dma->rx_dma_phys,
+ dspi_dma_transfer_size(dspi),
+ DMA_FROM_DEVICE);
for (i = 0; i < dspi->words_in_flight; i++)
dspi_push_rx(dspi, dspi->dma->rx_dma_buf[i]);
}
@@ -381,20 +560,22 @@ static void dspi_rx_dma_callback(void *arg)
static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
{
+ size_t size = dspi_dma_transfer_size(dspi);
struct device *dev = &dspi->pdev->dev;
struct fsl_dspi_dma *dma = dspi->dma;
int time_left;
+ u32 spi_sr;
int i;
for (i = 0; i < dspi->words_in_flight; i++)
dspi->dma->tx_dma_buf[i] = dspi_pop_tx_pushr(dspi);
+ dma_sync_single_for_device(dev, dma->tx_dma_phys, size, DMA_TO_DEVICE);
dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx,
- dma->tx_dma_phys,
- dspi->words_in_flight *
- DMA_SLAVE_BUSWIDTH_4_BYTES,
- DMA_MEM_TO_DEV,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ dma->tx_dma_phys, size,
+ DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT |
+ DMA_CTRL_ACK);
if (!dma->tx_desc) {
dev_err(dev, "Not able to get desc for DMA xfer\n");
return -EIO;
@@ -407,12 +588,13 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
return -EINVAL;
}
+ dma_sync_single_for_device(dev, dma->rx_dma_phys, size,
+ DMA_FROM_DEVICE);
dma->rx_desc = dmaengine_prep_slave_single(dma->chan_rx,
- dma->rx_dma_phys,
- dspi->words_in_flight *
- DMA_SLAVE_BUSWIDTH_4_BYTES,
- DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ dma->rx_dma_phys, size,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT |
+ DMA_CTRL_ACK);
if (!dma->rx_desc) {
dev_err(dev, "Not able to get desc for DMA xfer\n");
return -EIO;
@@ -433,7 +615,8 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
if (spi_controller_is_target(dspi->ctlr)) {
wait_for_completion_interruptible(&dspi->dma->cmd_rx_complete);
- return 0;
+ regmap_read(dspi->regmap, SPI_SR, &spi_sr);
+ return dspi_fifo_error(dspi, spi_sr);
}
time_left = wait_for_completion_timeout(&dspi->dma->cmd_tx_complete,
@@ -457,13 +640,10 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
return 0;
}
-static void dspi_setup_accel(struct fsl_dspi *dspi);
-
-static int dspi_dma_xfer(struct fsl_dspi *dspi)
+static void dspi_dma_xfer(struct fsl_dspi *dspi)
{
struct spi_message *message = dspi->cur_msg;
struct device *dev = &dspi->pdev->dev;
- int ret = 0;
/*
* dspi->len gets decremented by dspi_pop_tx_pushr in
@@ -473,26 +653,22 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi)
/* Figure out operational bits-per-word for this chunk */
dspi_setup_accel(dspi);
- dspi->words_in_flight = dspi->len / dspi->oper_word_size;
- if (dspi->words_in_flight > dspi->devtype_data->fifo_size)
- dspi->words_in_flight = dspi->devtype_data->fifo_size;
+ dspi->words_in_flight = min(dspi->len / dspi->oper_word_size,
+ dspi_dma_max_datawords(dspi));
message->actual_length += dspi->words_in_flight *
dspi->oper_word_size;
- ret = dspi_next_xfer_dma_submit(dspi);
- if (ret) {
+ message->status = dspi_next_xfer_dma_submit(dspi);
+ if (message->status) {
dev_err(dev, "DMA transfer failed\n");
break;
}
}
-
- return ret;
}
static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
{
- int dma_bufsize = dspi->devtype_data->fifo_size * 2;
struct device *dev = &dspi->pdev->dev;
struct dma_slave_config cfg;
struct fsl_dspi_dma *dma;
@@ -512,17 +688,30 @@ static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
goto err_tx_channel;
}
- dma->tx_dma_buf = dma_alloc_coherent(dma->chan_tx->device->dev,
- dma_bufsize, &dma->tx_dma_phys,
- GFP_KERNEL);
+ if (spi_controller_is_target(dspi->ctlr)) {
+ /*
+ * In target mode we have to be ready to receive the maximum
+ * that can possibly be transferred at once by EDMA without any
+ * FIFO underflows.
+ */
+ dma->bufsize = min(dma_get_max_seg_size(dma->chan_rx->device->dev),
+ dma_get_max_seg_size(dma->chan_tx->device->dev)) *
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ } else {
+ dma->bufsize = PAGE_SIZE;
+ }
+
+ dma->tx_dma_buf = dma_alloc_noncoherent(dma->chan_tx->device->dev,
+ dma->bufsize, &dma->tx_dma_phys,
+ DMA_TO_DEVICE, GFP_KERNEL);
if (!dma->tx_dma_buf) {
ret = -ENOMEM;
goto err_tx_dma_buf;
}
- dma->rx_dma_buf = dma_alloc_coherent(dma->chan_rx->device->dev,
- dma_bufsize, &dma->rx_dma_phys,
- GFP_KERNEL);
+ dma->rx_dma_buf = dma_alloc_noncoherent(dma->chan_rx->device->dev,
+ dma->bufsize, &dma->rx_dma_phys,
+ DMA_FROM_DEVICE, GFP_KERNEL);
if (!dma->rx_dma_buf) {
ret = -ENOMEM;
goto err_rx_dma_buf;
@@ -557,11 +746,12 @@ static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
return 0;
err_slave_config:
- dma_free_coherent(dma->chan_rx->device->dev,
- dma_bufsize, dma->rx_dma_buf, dma->rx_dma_phys);
+ dma_free_noncoherent(dma->chan_rx->device->dev, dma->bufsize,
+ dma->rx_dma_buf, dma->rx_dma_phys,
+ DMA_FROM_DEVICE);
err_rx_dma_buf:
- dma_free_coherent(dma->chan_tx->device->dev,
- dma_bufsize, dma->tx_dma_buf, dma->tx_dma_phys);
+ dma_free_noncoherent(dma->chan_tx->device->dev, dma->bufsize,
+ dma->tx_dma_buf, dma->tx_dma_phys, DMA_TO_DEVICE);
err_tx_dma_buf:
dma_release_channel(dma->chan_tx);
err_tx_channel:
@@ -575,27 +765,40 @@ err_tx_channel:
static void dspi_release_dma(struct fsl_dspi *dspi)
{
- int dma_bufsize = dspi->devtype_data->fifo_size * 2;
struct fsl_dspi_dma *dma = dspi->dma;
if (!dma)
return;
if (dma->chan_tx) {
- dma_free_coherent(dma->chan_tx->device->dev, dma_bufsize,
- dma->tx_dma_buf, dma->tx_dma_phys);
+ dma_free_noncoherent(dma->chan_tx->device->dev, dma->bufsize,
+ dma->tx_dma_buf, dma->tx_dma_phys,
+ DMA_TO_DEVICE);
dma_release_channel(dma->chan_tx);
}
if (dma->chan_rx) {
- dma_free_coherent(dma->chan_rx->device->dev, dma_bufsize,
- dma->rx_dma_buf, dma->rx_dma_phys);
+ dma_free_noncoherent(dma->chan_rx->device->dev, dma->bufsize,
+ dma->rx_dma_buf, dma->rx_dma_phys,
+ DMA_FROM_DEVICE);
dma_release_channel(dma->chan_rx);
}
}
+#else
+static void dspi_dma_xfer(struct fsl_dspi *dspi)
+{
+ dspi->cur_msg->status = -EINVAL;
+}
+static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
+{
+ dev_err(&dspi->pdev->dev, "DMA support not enabled in kernel\n");
+ return -EINVAL;
+}
+static void dspi_release_dma(struct fsl_dspi *dspi) {}
+#endif
static void hz_to_spi_baud(char *pbr, char *br, int speed_hz,
- unsigned long clkrate)
+ unsigned long clkrate, bool mtf_enabled)
{
/* Valid baud rate pre-scaler values */
int pbr_tbl[4] = {2, 3, 5, 7};
@@ -612,7 +815,13 @@ static void hz_to_spi_baud(char *pbr, char *br, int speed_hz,
for (i = 0; i < ARRAY_SIZE(brs); i++)
for (j = 0; j < ARRAY_SIZE(pbr_tbl); j++) {
- scale = brs[i] * pbr_tbl[j];
+ if (mtf_enabled) {
+ /* In MTF mode DBR=1 so frequency is doubled */
+ scale = (brs[i] * pbr_tbl[j]) / 2;
+ } else {
+ scale = brs[i] * pbr_tbl[j];
+ }
+
if (scale >= scale_needed) {
if (scale < minscale) {
minscale = scale;
@@ -746,8 +955,12 @@ static void dspi_setup_accel(struct fsl_dspi *dspi)
struct spi_transfer *xfer = dspi->cur_transfer;
bool odd = !!(dspi->len & 1);
- /* No accel for frames not multiple of 8 bits at the moment */
- if (xfer->bits_per_word % 8)
+ /*
+ * No accel for DMA transfers or frames not multiples of 8 bits at the
+ * moment.
+ */
+ if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE ||
+ xfer->bits_per_word % 8)
goto no_accel;
if (!odd && dspi->len <= dspi->devtype_data->fifo_size * 2) {
@@ -756,10 +969,7 @@ static void dspi_setup_accel(struct fsl_dspi *dspi)
dspi->oper_bits_per_word = 8;
} else {
/* Start off with maximum supported by hardware */
- if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
- dspi->oper_bits_per_word = 32;
- else
- dspi->oper_bits_per_word = 16;
+ dspi->oper_bits_per_word = 32;
/*
* And go down only if the buffer can't be sent with
@@ -847,41 +1057,55 @@ static void dspi_fifo_write(struct fsl_dspi *dspi)
dspi->progress, !dspi->irq);
}
-static int dspi_rxtx(struct fsl_dspi *dspi)
+/*
+ * Read the previous transfer from the FIFO and transmit the next one.
+ *
+ * Returns false if the buffer to be transmitted is empty, and true if there is
+ * still data to transmit.
+ */
+static bool dspi_rxtx(struct fsl_dspi *dspi)
{
dspi_fifo_read(dspi);
if (!dspi->len)
/* Success! */
- return 0;
+ return false;
dspi_fifo_write(dspi);
- return -EINPROGRESS;
+ return true;
}
-static int dspi_poll(struct fsl_dspi *dspi)
+static void dspi_poll(struct fsl_dspi *dspi)
{
- int tries = 1000;
+ int tries;
+ int err = 0;
u32 spi_sr;
do {
- regmap_read(dspi->regmap, SPI_SR, &spi_sr);
- regmap_write(dspi->regmap, SPI_SR, spi_sr);
-
- if (spi_sr & SPI_SR_CMDTCF)
+ for (tries = 1000; tries > 0; --tries) {
+ regmap_read(dspi->regmap, SPI_SR, &spi_sr);
+ regmap_write(dspi->regmap, SPI_SR, spi_sr);
+
+ dspi->cur_msg->status = dspi_fifo_error(dspi, spi_sr);
+ if (dspi->cur_msg->status)
+ return;
+ if (spi_sr & SPI_SR_CMDTCF)
+ break;
+ }
+ if (!tries) {
+ err = -ETIMEDOUT;
break;
- } while (--tries);
-
- if (!tries)
- return -ETIMEDOUT;
+ }
+ } while (dspi_rxtx(dspi));
- return dspi_rxtx(dspi);
+ dspi->cur_msg->status = err;
}
static irqreturn_t dspi_interrupt(int irq, void *dev_id)
{
struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id;
+ int status;
u32 spi_sr;
regmap_read(dspi->regmap, SPI_SR, &spi_sr);
@@ -890,8 +1114,19 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id)
if (!(spi_sr & SPI_SR_CMDTCF))
return IRQ_NONE;
- if (dspi_rxtx(dspi) == 0)
+ status = dspi_fifo_error(dspi, spi_sr);
+ if (status) {
+ if (dspi->cur_msg)
+ WRITE_ONCE(dspi->cur_msg->status, status);
complete(&dspi->xfer_done);
+ return IRQ_HANDLED;
+ }
+
+ if (dspi_rxtx(dspi) == false) {
+ if (dspi->cur_msg)
+ WRITE_ONCE(dspi->cur_msg->status, 0);
+ complete(&dspi->xfer_done);
+ }
return IRQ_HANDLED;
}
@@ -921,7 +1156,6 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
struct spi_device *spi = message->spi;
struct spi_transfer *transfer;
bool cs = false;
- int status = 0;
u32 val = 0;
bool cs_change = false;
@@ -981,7 +1215,7 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
dspi->progress, !dspi->irq);
if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE) {
- status = dspi_dma_xfer(dspi);
+ dspi_dma_xfer(dspi);
} else {
/*
* Reinitialize the completion before transferring data
@@ -995,15 +1229,12 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
dspi_fifo_write(dspi);
- if (dspi->irq) {
+ if (dspi->irq)
wait_for_completion(&dspi->xfer_done);
- } else {
- do {
- status = dspi_poll(dspi);
- } while (status == -EINPROGRESS);
- }
+ else
+ dspi_poll(dspi);
}
- if (status)
+ if (READ_ONCE(message->status))
break;
spi_transfer_delay_exec(transfer);
@@ -1012,7 +1243,8 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
dspi_deassert_cs(spi, &cs);
}
- if (status || !cs_change) {
+ dspi->cur_msg = NULL;
+ if (message->status || !cs_change) {
/* Put DSPI in stop mode */
regmap_update_bits(dspi->regmap, SPI_MCR,
SPI_MCR_HALT, SPI_MCR_HALT);
@@ -1021,10 +1253,23 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
;
}
- message->status = status;
spi_finalize_current_message(ctlr);
- return status;
+ return message->status;
+}
+
+static int dspi_set_mtf(struct fsl_dspi *dspi)
+{
+ if (spi_controller_is_target(dspi->ctlr))
+ return 0;
+
+ if (dspi->mtf_enabled)
+ regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_MTFE,
+ SPI_MCR_MTFE);
+ else
+ regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_MTFE, 0);
+
+ return 0;
}
static int dspi_setup(struct spi_device *spi)
@@ -1085,7 +1330,16 @@ static int dspi_setup(struct spi_device *spi)
cs_sck_delay, sck_cs_delay);
clkrate = clk_get_rate(dspi->clk);
- hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate);
+
+ if (is_s32g_dspi(dspi) && spi->max_speed_hz > SPI_25MHZ)
+ dspi->mtf_enabled = true;
+ else
+ dspi->mtf_enabled = false;
+
+ dspi_set_mtf(dspi);
+
+ hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate,
+ dspi->mtf_enabled);
/* Set PCS to SCK delay scale values */
ns_delay_scale(&pcssck, &cssck, cs_sck_delay, clkrate);
@@ -1107,6 +1361,9 @@ static int dspi_setup(struct spi_device *spi)
SPI_CTAR_PBR(pbr) |
SPI_CTAR_BR(br);
+ if (dspi->mtf_enabled)
+ chip->ctar_val |= SPI_CTAR_DBR;
+
if (spi->mode & SPI_LSB_FIRST)
chip->ctar_val |= SPI_CTAR_LSBFE;
}
@@ -1160,112 +1417,14 @@ static const struct of_device_id fsl_dspi_dt_ids[] = {
}, {
.compatible = "fsl,lx2160a-dspi",
.data = &devtype_data[LX2160A],
+ }, {
+ .compatible = "nxp,s32g2-dspi",
+ .data = &devtype_data[S32G],
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids);
-#ifdef CONFIG_PM_SLEEP
-static int dspi_suspend(struct device *dev)
-{
- struct fsl_dspi *dspi = dev_get_drvdata(dev);
-
- if (dspi->irq)
- disable_irq(dspi->irq);
- spi_controller_suspend(dspi->ctlr);
- clk_disable_unprepare(dspi->clk);
-
- pinctrl_pm_select_sleep_state(dev);
-
- return 0;
-}
-
-static int dspi_resume(struct device *dev)
-{
- struct fsl_dspi *dspi = dev_get_drvdata(dev);
- int ret;
-
- pinctrl_pm_select_default_state(dev);
-
- ret = clk_prepare_enable(dspi->clk);
- if (ret)
- return ret;
- spi_controller_resume(dspi->ctlr);
- if (dspi->irq)
- enable_irq(dspi->irq);
-
- return 0;
-}
-#endif /* CONFIG_PM_SLEEP */
-
-static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume);
-
-static const struct regmap_range dspi_yes_ranges[] = {
- regmap_reg_range(SPI_MCR, SPI_MCR),
- regmap_reg_range(SPI_TCR, SPI_CTAR(3)),
- regmap_reg_range(SPI_SR, SPI_TXFR3),
- regmap_reg_range(SPI_RXFR0, SPI_RXFR3),
- regmap_reg_range(SPI_CTARE(0), SPI_CTARE(3)),
- regmap_reg_range(SPI_SREX, SPI_SREX),
-};
-
-static const struct regmap_access_table dspi_access_table = {
- .yes_ranges = dspi_yes_ranges,
- .n_yes_ranges = ARRAY_SIZE(dspi_yes_ranges),
-};
-
-static const struct regmap_range dspi_volatile_ranges[] = {
- regmap_reg_range(SPI_MCR, SPI_TCR),
- regmap_reg_range(SPI_SR, SPI_SR),
- regmap_reg_range(SPI_PUSHR, SPI_RXFR3),
-};
-
-static const struct regmap_access_table dspi_volatile_table = {
- .yes_ranges = dspi_volatile_ranges,
- .n_yes_ranges = ARRAY_SIZE(dspi_volatile_ranges),
-};
-
-static const struct regmap_config dspi_regmap_config = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
- .max_register = 0x88,
- .volatile_table = &dspi_volatile_table,
- .rd_table = &dspi_access_table,
- .wr_table = &dspi_access_table,
-};
-
-static const struct regmap_range dspi_xspi_volatile_ranges[] = {
- regmap_reg_range(SPI_MCR, SPI_TCR),
- regmap_reg_range(SPI_SR, SPI_SR),
- regmap_reg_range(SPI_PUSHR, SPI_RXFR3),
- regmap_reg_range(SPI_SREX, SPI_SREX),
-};
-
-static const struct regmap_access_table dspi_xspi_volatile_table = {
- .yes_ranges = dspi_xspi_volatile_ranges,
- .n_yes_ranges = ARRAY_SIZE(dspi_xspi_volatile_ranges),
-};
-
-static const struct regmap_config dspi_xspi_regmap_config[] = {
- {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
- .max_register = 0x13c,
- .volatile_table = &dspi_xspi_volatile_table,
- .rd_table = &dspi_access_table,
- .wr_table = &dspi_access_table,
- },
- {
- .name = "pushr",
- .reg_bits = 16,
- .val_bits = 16,
- .reg_stride = 2,
- .max_register = 0x2,
- },
-};
-
static int dspi_init(struct fsl_dspi *dspi)
{
unsigned int mcr;
@@ -1301,6 +1460,50 @@ static int dspi_init(struct fsl_dspi *dspi)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int dspi_suspend(struct device *dev)
+{
+ struct fsl_dspi *dspi = dev_get_drvdata(dev);
+
+ if (dspi->irq)
+ disable_irq(dspi->irq);
+ spi_controller_suspend(dspi->ctlr);
+ clk_disable_unprepare(dspi->clk);
+
+ pinctrl_pm_select_sleep_state(dev);
+
+ return 0;
+}
+
+static int dspi_resume(struct device *dev)
+{
+ struct fsl_dspi *dspi = dev_get_drvdata(dev);
+ int ret;
+
+ pinctrl_pm_select_default_state(dev);
+
+ ret = clk_prepare_enable(dspi->clk);
+ if (ret)
+ return ret;
+ spi_controller_resume(dspi->ctlr);
+
+ ret = dspi_init(dspi);
+ if (ret) {
+ dev_err(dev, "failed to initialize dspi during resume\n");
+ return ret;
+ }
+
+ dspi_set_mtf(dspi);
+
+ if (dspi->irq)
+ enable_irq(dspi->irq);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume);
+
static int dspi_target_abort(struct spi_controller *host)
{
struct fsl_dspi *dspi = spi_controller_get_devdata(host);
@@ -1325,7 +1528,6 @@ static int dspi_target_abort(struct spi_controller *host)
static int dspi_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- const struct regmap_config *regmap_config;
struct fsl_dspi_platform_data *pdata;
struct spi_controller *ctlr;
int ret, cs_num, bus_num = -1;
@@ -1338,7 +1540,10 @@ static int dspi_probe(struct platform_device *pdev)
if (!dspi)
return -ENOMEM;
- ctlr = spi_alloc_host(&pdev->dev, 0);
+ if (of_property_read_bool(np, "spi-slave"))
+ ctlr = spi_alloc_target(&pdev->dev, 0);
+ else
+ ctlr = spi_alloc_host(&pdev->dev, 0);
if (!ctlr)
return -ENOMEM;
@@ -1377,9 +1582,6 @@ static int dspi_probe(struct platform_device *pdev)
of_property_read_u32(np, "bus-num", &bus_num);
ctlr->bus_num = bus_num;
- if (of_property_read_bool(np, "spi-slave"))
- ctlr->target = true;
-
dspi->devtype_data = of_device_get_match_data(&pdev->dev);
if (!dspi->devtype_data) {
dev_err(&pdev->dev, "can't get devtype_data\n");
@@ -1397,6 +1599,9 @@ static int dspi_probe(struct platform_device *pdev)
dspi->pushr_tx = 0;
}
+ if (spi_controller_is_target(ctlr) && is_s32g_dspi(dspi))
+ dspi->devtype_data = &devtype_data[S32G_TARGET];
+
if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
else
@@ -1408,11 +1613,8 @@ static int dspi_probe(struct platform_device *pdev)
goto out_ctlr_put;
}
- if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
- regmap_config = &dspi_xspi_regmap_config[0];
- else
- regmap_config = &dspi_regmap_config;
- dspi->regmap = devm_regmap_init_mmio(&pdev->dev, base, regmap_config);
+ dspi->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ dspi->devtype_data->regmap);
if (IS_ERR(dspi->regmap)) {
dev_err(&pdev->dev, "failed to init regmap: %ld\n",
PTR_ERR(dspi->regmap));
@@ -1423,7 +1625,7 @@ static int dspi_probe(struct platform_device *pdev)
if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) {
dspi->regmap_pushr = devm_regmap_init_mmio(
&pdev->dev, base + SPI_PUSHR,
- &dspi_xspi_regmap_config[1]);
+ &dspi_regmap_config[DSPI_PUSHR]);
if (IS_ERR(dspi->regmap_pushr)) {
dev_err(&pdev->dev,
"failed to init pushr regmap: %ld\n",
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 6a73eaa34cf7..f2f1d3298e6c 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -513,7 +513,6 @@ static int fsl_espi_setup(struct spi_device *spi)
fsl_espi_setup_transfer(spi, NULL);
- pm_runtime_mark_last_busy(espi->dev);
pm_runtime_put_autosuspend(espi->dev);
return 0;
@@ -726,7 +725,6 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem,
dev_info(dev, "irq = %u\n", irq);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index 5e3818445234..8da66e101386 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -3,8 +3,9 @@
// Freescale i.MX7ULP LPSPI driver
//
// Copyright 2016 Freescale Semiconductor, Inc.
-// Copyright 2018 NXP Semiconductors
+// Copyright 2018, 2023, 2025 NXP
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
@@ -25,6 +26,7 @@
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/types.h>
+#include <linux/minmax.h>
#define DRIVER_NAME "fsl_lpspi"
@@ -70,7 +72,7 @@
#define DER_TDDE BIT(0)
#define CFGR1_PCSCFG BIT(27)
#define CFGR1_PINCFG (BIT(24)|BIT(25))
-#define CFGR1_PCSPOL BIT(8)
+#define CFGR1_PCSPOL_MASK GENMASK(11, 8)
#define CFGR1_NOSTALL BIT(3)
#define CFGR1_HOST BIT(0)
#define FSR_TXCOUNT (0xFF)
@@ -82,8 +84,11 @@
#define TCR_RXMSK BIT(19)
#define TCR_TXMSK BIT(18)
+#define SR_CLEAR_MASK GENMASK(13, 8)
+
struct fsl_lpspi_devtype_data {
- u8 prescale_max;
+ u8 prescale_max : 3; /* 0 == no limit */
+ bool query_hw_for_num_cs : 1;
};
struct lpspi_config {
@@ -129,20 +134,26 @@ struct fsl_lpspi_data {
};
/*
- * ERR051608 fixed or not:
- * https://www.nxp.com/docs/en/errata/i.MX93_1P87f.pdf
+ * Devices with ERR051608 have a max TCR_PRESCALE value of 1, otherwise there is
+ * no prescale limit: https://www.nxp.com/docs/en/errata/i.MX93_1P87f.pdf
*/
-static struct fsl_lpspi_devtype_data imx93_lpspi_devtype_data = {
+static const struct fsl_lpspi_devtype_data imx93_lpspi_devtype_data = {
.prescale_max = 1,
+ .query_hw_for_num_cs = true,
};
-static struct fsl_lpspi_devtype_data imx7ulp_lpspi_devtype_data = {
- .prescale_max = 7,
+static const struct fsl_lpspi_devtype_data imx7ulp_lpspi_devtype_data = {
+ /* All defaults */
+};
+
+static const struct fsl_lpspi_devtype_data s32g_lpspi_devtype_data = {
+ .query_hw_for_num_cs = true,
};
static const struct of_device_id fsl_lpspi_dt_ids[] = {
{ .compatible = "fsl,imx7ulp-spi", .data = &imx7ulp_lpspi_devtype_data,},
{ .compatible = "fsl,imx93-spi", .data = &imx93_lpspi_devtype_data,},
+ { .compatible = "nxp,s32g2-lpspi", .data = &s32g_lpspi_devtype_data,},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_lpspi_dt_ids);
@@ -233,7 +244,6 @@ static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller)
struct fsl_lpspi_data *fsl_lpspi =
spi_controller_get_devdata(controller);
- pm_runtime_mark_last_busy(fsl_lpspi->dev);
pm_runtime_put_autosuspend(fsl_lpspi->dev);
return 0;
@@ -322,7 +332,7 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi)
int scldiv;
perclk_rate = clk_get_rate(fsl_lpspi->clk_per);
- prescale_max = fsl_lpspi->devtype_data->prescale_max;
+ prescale_max = fsl_lpspi->devtype_data->prescale_max ?: 7;
if (!config.speed_hz) {
dev_err(fsl_lpspi->dev,
@@ -331,13 +341,11 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi)
}
if (config.speed_hz > perclk_rate / 2) {
- dev_err(fsl_lpspi->dev,
- "per-clk should be at least two times of transfer speed");
- return -EINVAL;
+ div = 2;
+ } else {
+ div = DIV_ROUND_UP(perclk_rate, config.speed_hz);
}
- div = DIV_ROUND_UP(perclk_rate, config.speed_hz);
-
for (prescale = 0; prescale <= prescale_max; prescale++) {
scldiv = div / (1 << prescale) - 2;
if (scldiv >= 0 && scldiv < 256) {
@@ -426,7 +434,9 @@ static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi)
else
temp = CFGR1_PINCFG;
if (fsl_lpspi->config.mode & SPI_CS_HIGH)
- temp |= CFGR1_PCSPOL;
+ temp |= FIELD_PREP(CFGR1_PCSPOL_MASK,
+ BIT(fsl_lpspi->config.chip_select));
+
writel(temp, fsl_lpspi->base + IMX7ULP_CFGR1);
temp = readl(fsl_lpspi->base + IMX7ULP_CR);
@@ -476,10 +486,9 @@ static int fsl_lpspi_setup_transfer(struct spi_controller *controller,
fsl_lpspi->tx = fsl_lpspi_buf_tx_u32;
}
- if (t->len <= fsl_lpspi->txfifosize)
- fsl_lpspi->watermark = t->len;
- else
- fsl_lpspi->watermark = fsl_lpspi->txfifosize;
+ fsl_lpspi->watermark = min_t(typeof(fsl_lpspi->watermark),
+ fsl_lpspi->txfifosize,
+ t->len);
if (fsl_lpspi_can_dma(controller, spi, t))
fsl_lpspi->usedma = true;
@@ -535,14 +544,13 @@ static int fsl_lpspi_reset(struct fsl_lpspi_data *fsl_lpspi)
fsl_lpspi_intctrl(fsl_lpspi, 0);
}
- /* W1C for all flags in SR */
- temp = 0x3F << 8;
- writel(temp, fsl_lpspi->base + IMX7ULP_SR);
-
/* Clear FIFO and disable module */
temp = CR_RRF | CR_RTF;
writel(temp, fsl_lpspi->base + IMX7ULP_CR);
+ /* W1C for all flags in SR */
+ writel(SR_CLEAR_MASK, fsl_lpspi->base + IMX7ULP_SR);
+
return 0;
}
@@ -733,12 +741,10 @@ static int fsl_lpspi_pio_transfer(struct spi_controller *controller,
fsl_lpspi_write_tx_fifo(fsl_lpspi);
ret = fsl_lpspi_wait_for_completion(controller);
- if (ret)
- return ret;
fsl_lpspi_reset(fsl_lpspi);
- return 0;
+ return ret;
}
static int fsl_lpspi_transfer_one(struct spi_controller *controller,
@@ -788,7 +794,7 @@ static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id)
if (temp_SR & SR_MBF ||
readl(fsl_lpspi->base + IMX7ULP_FSR) & FSR_TXCOUNT) {
writel(SR_FCF, fsl_lpspi->base + IMX7ULP_SR);
- fsl_lpspi_intctrl(fsl_lpspi, IER_FCIE);
+ fsl_lpspi_intctrl(fsl_lpspi, IER_FCIE | (temp_IER & IER_TDIE));
return IRQ_HANDLED;
}
@@ -933,7 +939,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
fsl_lpspi->rxfifosize = 1 << ((temp >> 8) & 0x0f);
if (of_property_read_u32((&pdev->dev)->of_node, "num-cs",
&num_cs)) {
- if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx93-spi"))
+ if (devtype_data->query_hw_for_num_cs)
num_cs = ((temp >> 16) & 0xf);
else
num_cs = 1;
@@ -966,7 +972,6 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
goto free_dma;
}
- pm_runtime_mark_last_busy(fsl_lpspi->dev);
pm_runtime_put_autosuspend(fsl_lpspi->dev);
return 0;
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 768d7482102a..a0d8d3425c6c 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -671,6 +671,12 @@ static int spi_geni_init(struct spi_geni_master *mas)
goto out_pm;
}
spi_slv_setup(mas);
+ } else if (proto == GENI_SE_INVALID_PROTO) {
+ ret = geni_load_se_firmware(se, GENI_SE_SPI);
+ if (ret) {
+ dev_err(mas->dev, "spi master firmware load failed ret: %d\n", ret);
+ goto out_pm;
+ }
} else if (proto != GENI_SE_SPI) {
dev_err(mas->dev, "Invalid proto %d\n", proto);
goto out_pm;
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index ea5f1b10b79e..c8dadb532c40 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -104,7 +104,7 @@ static inline int getmiso(const struct spi_device *spi)
*/
static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits, unsigned flags)
+ unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
{
if (unlikely(spi->mode & SPI_LSB_FIRST))
return bitbang_txrx_le_cpha0(spi, nsecs, 0, flags, word, bits);
@@ -113,7 +113,7 @@ static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
}
static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits, unsigned flags)
+ unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
{
if (unlikely(spi->mode & SPI_LSB_FIRST))
return bitbang_txrx_le_cpha1(spi, nsecs, 0, flags, word, bits);
@@ -122,7 +122,7 @@ static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi,
}
static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits, unsigned flags)
+ unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
{
if (unlikely(spi->mode & SPI_LSB_FIRST))
return bitbang_txrx_le_cpha0(spi, nsecs, 1, flags, word, bits);
@@ -131,7 +131,7 @@ static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi,
}
static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits, unsigned flags)
+ unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
{
if (unlikely(spi->mode & SPI_LSB_FIRST))
return bitbang_txrx_le_cpha1(spi, nsecs, 1, flags, word, bits);
@@ -150,7 +150,7 @@ static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi,
*/
static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits, unsigned flags)
+ unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
{
flags = spi->controller->flags;
if (unlikely(spi->mode & SPI_LSB_FIRST))
@@ -160,7 +160,7 @@ static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi,
}
static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits, unsigned flags)
+ unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
{
flags = spi->controller->flags;
if (unlikely(spi->mode & SPI_LSB_FIRST))
@@ -170,7 +170,7 @@ static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi,
}
static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits, unsigned flags)
+ unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
{
flags = spi->controller->flags;
if (unlikely(spi->mode & SPI_LSB_FIRST))
@@ -180,7 +180,7 @@ static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi,
}
static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits, unsigned flags)
+ unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
{
flags = spi->controller->flags;
if (unlikely(spi->mode & SPI_LSB_FIRST))
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index c93d80a4d734..155ddeb8fcd4 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -1748,7 +1748,6 @@ spi_imx_prepare_message(struct spi_controller *controller, struct spi_message *m
ret = spi_imx->devtype_data->prepare_message(spi_imx, msg);
if (ret) {
- pm_runtime_mark_last_busy(spi_imx->dev);
pm_runtime_put_autosuspend(spi_imx->dev);
}
@@ -1760,7 +1759,6 @@ spi_imx_unprepare_message(struct spi_controller *controller, struct spi_message
{
struct spi_imx_data *spi_imx = spi_controller_get_devdata(controller);
- pm_runtime_mark_last_busy(spi_imx->dev);
pm_runtime_put_autosuspend(spi_imx->dev);
return 0;
}
@@ -1933,7 +1931,6 @@ static int spi_imx_probe(struct platform_device *pdev)
goto out_register_controller;
}
- pm_runtime_mark_last_busy(spi_imx->dev);
pm_runtime_put_autosuspend(spi_imx->dev);
return ret;
diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c
index 5d5a546c62ea..13bbb2133507 100644
--- a/drivers/spi/spi-intel.c
+++ b/drivers/spi/spi-intel.c
@@ -189,6 +189,11 @@ struct intel_spi_mem_op {
static bool writeable;
module_param(writeable, bool, 0);
MODULE_PARM_DESC(writeable, "Enable write access to SPI flash chip (default=0)");
+static bool ignore_protection_status;
+module_param(ignore_protection_status, bool, 0);
+MODULE_PARM_DESC(
+ ignore_protection_status,
+ "Do not block SPI flash chip write access even if it is write-protected (default=0)");
static void intel_spi_dump_regs(struct intel_spi *ispi)
{
@@ -1248,13 +1253,15 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
continue;
/*
- * If any of the regions have protection bits set, make the
- * whole partition read-only to be on the safe side.
+ * If any of the regions have protection bits set and
+ * the ignore protection status parameter is not set,
+ * make the whole partition read-only to be on the safe side.
*
* Also if the user did not ask the chip to be writeable
* mask the bit too.
*/
- if (!writeable || intel_spi_is_protected(ispi, base, limit)) {
+ if (!writeable || (!ignore_protection_status &&
+ intel_spi_is_protected(ispi, base, limit))) {
part->mask_flags |= MTD_WRITEABLE;
ispi->protected = true;
}
diff --git a/drivers/spi/spi-ljca.c b/drivers/spi/spi-ljca.c
index 2cab79ad2b98..3f412cf8f1cd 100644
--- a/drivers/spi/spi-ljca.c
+++ b/drivers/spi/spi-ljca.c
@@ -289,7 +289,7 @@ static struct auxiliary_driver ljca_spi_driver = {
};
module_auxiliary_driver(ljca_spi_driver);
-MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
+MODULE_AUTHOR("Wentong Wu");
MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>");
MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB-SPI driver");
diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c
index 7dd92deffe3f..e0b131aa29b6 100644
--- a/drivers/spi/spi-loopback-test.c
+++ b/drivers/spi/spi-loopback-test.c
@@ -446,7 +446,7 @@ static void spi_test_dump_message(struct spi_device *spi,
int i;
u8 b;
- dev_info(&spi->dev, " spi_msg@%pK\n", msg);
+ dev_info(&spi->dev, " spi_msg@%p\n", msg);
if (msg->status)
dev_info(&spi->dev, " status: %i\n",
msg->status);
@@ -456,15 +456,15 @@ static void spi_test_dump_message(struct spi_device *spi,
msg->actual_length);
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
- dev_info(&spi->dev, " spi_transfer@%pK\n", xfer);
+ dev_info(&spi->dev, " spi_transfer@%p\n", xfer);
dev_info(&spi->dev, " len: %i\n", xfer->len);
- dev_info(&spi->dev, " tx_buf: %pK\n", xfer->tx_buf);
+ dev_info(&spi->dev, " tx_buf: %p\n", xfer->tx_buf);
if (dump_data && xfer->tx_buf)
spi_test_print_hex_dump(" TX: ",
xfer->tx_buf,
xfer->len);
- dev_info(&spi->dev, " rx_buf: %pK\n", xfer->rx_buf);
+ dev_info(&spi->dev, " rx_buf: %p\n", xfer->rx_buf);
if (dump_data && xfer->rx_buf)
spi_test_print_hex_dump(" RX: ",
xfer->rx_buf,
@@ -558,7 +558,7 @@ static int spi_check_rx_ranges(struct spi_device *spi,
/* if still not found then something has modified too much */
/* we could list the "closest" transfer here... */
dev_err(&spi->dev,
- "loopback strangeness - rx changed outside of allowed range at: %pK\n",
+ "loopback strangeness - rx changed outside of allowed range at: %p\n",
addr);
/* do not return, only set ret,
* so that we list all addresses
@@ -696,7 +696,7 @@ static int spi_test_translate(struct spi_device *spi,
}
dev_err(&spi->dev,
- "PointerRange [%pK:%pK[ not in range [%pK:%pK[ or [%pK:%pK[\n",
+ "PointerRange [%p:%p[ not in range [%p:%p[ or [%p:%p[\n",
*ptr, *ptr + len,
RX(0), RX(SPI_TEST_MAX_SIZE),
TX(0), TX(SPI_TEST_MAX_SIZE));
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 5db0639d3b01..064b99204d9a 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -265,6 +265,9 @@ static bool spi_mem_internal_supports_op(struct spi_mem *mem,
*/
bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
+ /* Make sure the operation frequency is correct before going futher */
+ spi_mem_adjust_op_freq(mem, (struct spi_mem_op *)op);
+
if (spi_mem_check_op(op))
return false;
@@ -577,6 +580,7 @@ EXPORT_SYMBOL_GPL(spi_mem_adjust_op_freq);
* spi_mem_calc_op_duration() - Derives the theoretical length (in ns) of an
* operation. This helps finding the best variant
* among a list of possible choices.
+ * @mem: the SPI memory
* @op: the operation to benchmark
*
* Some chips have per-op frequency limitations, PCBs usually have their own
@@ -586,14 +590,26 @@ EXPORT_SYMBOL_GPL(spi_mem_adjust_op_freq);
* accurate, all these combinations should be rated (eg. with a time estimate)
* and the best pick should be taken based on these calculations.
*
- * Returns a ns estimate for the time this op would take.
+ * Returns a ns estimate for the time this op would take, except if no
+ * frequency limit has been set, in this case we return the number of
+ * cycles nevertheless to allow callers to distinguish which operation
+ * would be the fastest at iso-frequency.
*/
-u64 spi_mem_calc_op_duration(struct spi_mem_op *op)
+u64 spi_mem_calc_op_duration(struct spi_mem *mem, struct spi_mem_op *op)
{
u64 ncycles = 0;
- u32 ns_per_cycles;
+ u64 ps_per_cycles, duration;
+
+ spi_mem_adjust_op_freq(mem, op);
+
+ if (op->max_freq) {
+ ps_per_cycles = 1000000000000ULL;
+ do_div(ps_per_cycles, op->max_freq);
+ } else {
+ /* In this case, the unit is no longer a time unit */
+ ps_per_cycles = 1;
+ }
- ns_per_cycles = 1000000000 / op->max_freq;
ncycles += ((op->cmd.nbytes * 8) / op->cmd.buswidth) / (op->cmd.dtr ? 2 : 1);
ncycles += ((op->addr.nbytes * 8) / op->addr.buswidth) / (op->addr.dtr ? 2 : 1);
@@ -603,7 +619,12 @@ u64 spi_mem_calc_op_duration(struct spi_mem_op *op)
ncycles += ((op->data.nbytes * 8) / op->data.buswidth) / (op->data.dtr ? 2 : 1);
- return ncycles * ns_per_cycles;
+ /* Derive the duration in ps */
+ duration = ncycles * ps_per_cycles;
+ /* Convert into ns */
+ do_div(duration, 1000);
+
+ return duration;
}
EXPORT_SYMBOL_GPL(spi_mem_calc_op_duration);
diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c
index fa828fcaaef2..aafe6cbf2aea 100644
--- a/drivers/spi/spi-microchip-core-qspi.c
+++ b/drivers/spi/spi-microchip-core-qspi.c
@@ -194,7 +194,7 @@ static inline void mchp_coreqspi_read_op(struct mchp_coreqspi *qspi)
}
}
-static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word)
+static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi)
{
u32 control, data;
@@ -222,6 +222,87 @@ static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word)
}
}
+static inline void mchp_coreqspi_write_read_op(struct mchp_coreqspi *qspi)
+{
+ u32 control, data;
+
+ qspi->rx_len = qspi->tx_len;
+
+ control = readl_relaxed(qspi->regs + REG_CONTROL);
+ control |= CONTROL_FLAGSX4;
+ writel_relaxed(control, qspi->regs + REG_CONTROL);
+
+ while (qspi->tx_len >= 4) {
+ while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
+ ;
+
+ data = qspi->txbuf ? *((u32 *)qspi->txbuf) : 0xaa;
+ if (qspi->txbuf)
+ qspi->txbuf += 4;
+ qspi->tx_len -= 4;
+ writel_relaxed(data, qspi->regs + REG_X4_TX_DATA);
+
+ /*
+ * The rx FIFO is twice the size of the tx FIFO, so there is
+ * no requirement to block transmission if receive data is not
+ * ready, and it is fine to let the tx FIFO completely fill
+ * without reading anything from the rx FIFO. Once the tx FIFO
+ * has been filled and becomes non-full due to a transmission
+ * occurring there will always be something to receive.
+ * IOW, this is safe as TX_FIFO_SIZE + 4 < 2 * TX_FIFO_SIZE
+ */
+ if (qspi->rx_len >= 4) {
+ if (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXAVAILABLE) {
+ data = readl_relaxed(qspi->regs + REG_X4_RX_DATA);
+ *(u32 *)qspi->rxbuf = data;
+ qspi->rxbuf += 4;
+ qspi->rx_len -= 4;
+ }
+ }
+ }
+
+ /*
+ * Since transmission is not being blocked by clearing the rx FIFO,
+ * loop here until all received data "leaked" by the loop above has
+ * been dealt with.
+ */
+ while (qspi->rx_len >= 4) {
+ while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY)
+ ;
+ data = readl_relaxed(qspi->regs + REG_X4_RX_DATA);
+ *(u32 *)qspi->rxbuf = data;
+ qspi->rxbuf += 4;
+ qspi->rx_len -= 4;
+ }
+
+ /*
+ * Since rx_len and tx_len must be < 4 bytes at this point, there's no
+ * concern about overflowing the rx or tx FIFOs any longer. It's
+ * therefore safe to loop over the remainder of the transmit data before
+ * handling the remaining receive data.
+ */
+ if (!qspi->tx_len)
+ return;
+
+ control &= ~CONTROL_FLAGSX4;
+ writel_relaxed(control, qspi->regs + REG_CONTROL);
+
+ while (qspi->tx_len--) {
+ while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
+ ;
+ data = qspi->txbuf ? *qspi->txbuf : 0xaa;
+ qspi->txbuf++;
+ writel_relaxed(data, qspi->regs + REG_TX_DATA);
+ }
+
+ while (qspi->rx_len--) {
+ while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY)
+ ;
+ data = readl_relaxed(qspi->regs + REG_RX_DATA);
+ *qspi->rxbuf++ = (data & 0xFF);
+ }
+}
+
static void mchp_coreqspi_enable_ints(struct mchp_coreqspi *qspi)
{
u32 mask = IEN_TXDONE |
@@ -266,7 +347,7 @@ static irqreturn_t mchp_coreqspi_isr(int irq, void *dev_id)
}
static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_device *spi,
- const struct spi_mem_op *op)
+ u32 max_freq)
{
unsigned long clk_hz;
u32 control, baud_rate_val = 0;
@@ -275,11 +356,11 @@ static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_devi
if (!clk_hz)
return -EINVAL;
- baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * op->max_freq);
+ baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * max_freq);
if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER) {
dev_err(&spi->dev,
"could not configure the clock for spi clock %d Hz & system clock %ld Hz\n",
- op->max_freq, clk_hz);
+ max_freq, clk_hz);
return -EINVAL;
}
@@ -367,23 +448,13 @@ static inline void mchp_coreqspi_config_op(struct mchp_coreqspi *qspi, const str
writel_relaxed(frames, qspi->regs + REG_FRAMES);
}
-static int mchp_qspi_wait_for_ready(struct spi_mem *mem)
+static int mchp_coreqspi_wait_for_ready(struct mchp_coreqspi *qspi)
{
- struct mchp_coreqspi *qspi = spi_controller_get_devdata
- (mem->spi->controller);
u32 status;
- int ret;
- ret = readl_poll_timeout(qspi->regs + REG_STATUS, status,
+ return readl_poll_timeout(qspi->regs + REG_STATUS, status,
(status & STATUS_READY), 0,
TIMEOUT_MS);
- if (ret) {
- dev_err(&mem->spi->dev,
- "Timeout waiting on QSPI ready.\n");
- return -ETIMEDOUT;
- }
-
- return ret;
}
static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
@@ -396,11 +467,13 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o
int err, i;
mutex_lock(&qspi->op_lock);
- err = mchp_qspi_wait_for_ready(mem);
- if (err)
+ err = mchp_coreqspi_wait_for_ready(qspi);
+ if (err) {
+ dev_err(&mem->spi->dev, "Timeout waiting on QSPI ready.\n");
goto error;
+ }
- err = mchp_coreqspi_setup_clock(qspi, mem->spi, op);
+ err = mchp_coreqspi_setup_clock(qspi, mem->spi, op->max_freq);
if (err)
goto error;
@@ -415,7 +488,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o
qspi->rxbuf = NULL;
qspi->tx_len = op->cmd.nbytes;
qspi->rx_len = 0;
- mchp_coreqspi_write_op(qspi, false);
+ mchp_coreqspi_write_op(qspi);
}
qspi->txbuf = &opaddr[0];
@@ -426,7 +499,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o
qspi->rxbuf = NULL;
qspi->tx_len = op->addr.nbytes;
qspi->rx_len = 0;
- mchp_coreqspi_write_op(qspi, false);
+ mchp_coreqspi_write_op(qspi);
}
if (op->data.nbytes) {
@@ -435,7 +508,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o
qspi->rxbuf = NULL;
qspi->rx_len = 0;
qspi->tx_len = op->data.nbytes;
- mchp_coreqspi_write_op(qspi, true);
+ mchp_coreqspi_write_op(qspi);
} else {
qspi->txbuf = NULL;
qspi->rxbuf = (u8 *)op->data.buf.in;
@@ -458,10 +531,6 @@ error:
static bool mchp_coreqspi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
- struct mchp_coreqspi *qspi = spi_controller_get_devdata(mem->spi->controller);
- unsigned long clk_hz;
- u32 baud_rate_val;
-
if (!spi_mem_default_supports_op(mem, op))
return false;
@@ -484,14 +553,6 @@ static bool mchp_coreqspi_supports_op(struct spi_mem *mem, const struct spi_mem_
return false;
}
- clk_hz = clk_get_rate(qspi->clk);
- if (!clk_hz)
- return false;
-
- baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * op->max_freq);
- if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER)
- return false;
-
return true;
}
@@ -515,6 +576,109 @@ static const struct spi_controller_mem_caps mchp_coreqspi_mem_caps = {
.per_op_freq = true,
};
+static int mchp_coreqspi_unprepare_message(struct spi_controller *ctlr, struct spi_message *m)
+{
+ struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr);
+
+ /*
+ * This delay is required for the driver to function correctly,
+ * but no explanation has been determined for why it is required.
+ */
+ udelay(750);
+
+ mutex_unlock(&qspi->op_lock);
+
+ return 0;
+}
+
+static int mchp_coreqspi_prepare_message(struct spi_controller *ctlr, struct spi_message *m)
+{
+ struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr);
+ struct spi_transfer *t = NULL;
+ u32 control, frames;
+ u32 total_bytes = 0, cmd_bytes = 0, idle_cycles = 0;
+ int ret;
+ bool quad = false, dual = false;
+
+ mutex_lock(&qspi->op_lock);
+ ret = mchp_coreqspi_wait_for_ready(qspi);
+ if (ret) {
+ mutex_unlock(&qspi->op_lock);
+ dev_err(&ctlr->dev, "Timeout waiting on QSPI ready.\n");
+ return ret;
+ }
+
+ ret = mchp_coreqspi_setup_clock(qspi, m->spi, m->spi->max_speed_hz);
+ if (ret) {
+ mutex_unlock(&qspi->op_lock);
+ return ret;
+ }
+
+ control = readl_relaxed(qspi->regs + REG_CONTROL);
+ control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0);
+ writel_relaxed(control, qspi->regs + REG_CONTROL);
+
+ reinit_completion(&qspi->data_completion);
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ total_bytes += t->len;
+ if (!cmd_bytes && !(t->tx_buf && t->rx_buf))
+ cmd_bytes = t->len;
+ if (!t->rx_buf)
+ cmd_bytes = total_bytes;
+ if (t->tx_nbits == SPI_NBITS_QUAD || t->rx_nbits == SPI_NBITS_QUAD)
+ quad = true;
+ else if (t->tx_nbits == SPI_NBITS_DUAL || t->rx_nbits == SPI_NBITS_DUAL)
+ dual = true;
+ }
+
+ control = readl_relaxed(qspi->regs + REG_CONTROL);
+ if (quad) {
+ control |= (CONTROL_MODE0 | CONTROL_MODE12_EX_RW);
+ } else if (dual) {
+ control &= ~CONTROL_MODE0;
+ control |= CONTROL_MODE12_FULL;
+ } else {
+ control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0);
+ }
+ writel_relaxed(control, qspi->regs + REG_CONTROL);
+
+ frames = total_bytes & BYTESUPPER_MASK;
+ writel_relaxed(frames, qspi->regs + REG_FRAMESUP);
+ frames = total_bytes & BYTESLOWER_MASK;
+ frames |= cmd_bytes << FRAMES_CMDBYTES_SHIFT;
+ frames |= idle_cycles << FRAMES_IDLE_SHIFT;
+ control = readl_relaxed(qspi->regs + REG_CONTROL);
+ if (control & CONTROL_MODE12_MASK)
+ frames |= (1 << FRAMES_SHIFT);
+
+ frames |= FRAMES_FLAGWORD;
+ writel_relaxed(frames, qspi->regs + REG_FRAMES);
+
+ return 0;
+};
+
+static int mchp_coreqspi_transfer_one(struct spi_controller *ctlr, struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr);
+
+ qspi->tx_len = t->len;
+
+ if (t->tx_buf)
+ qspi->txbuf = (u8 *)t->tx_buf;
+
+ if (!t->rx_buf) {
+ mchp_coreqspi_write_op(qspi);
+ } else {
+ qspi->rxbuf = (u8 *)t->rx_buf;
+ qspi->rx_len = t->len;
+ mchp_coreqspi_write_read_op(qspi);
+ }
+
+ return 0;
+}
+
static int mchp_coreqspi_probe(struct platform_device *pdev)
{
struct spi_controller *ctlr;
@@ -525,8 +689,7 @@ static int mchp_coreqspi_probe(struct platform_device *pdev)
ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*qspi));
if (!ctlr)
- return dev_err_probe(&pdev->dev, -ENOMEM,
- "unable to allocate host for QSPI controller\n");
+ return -ENOMEM;
qspi = spi_controller_get_devdata(ctlr);
platform_set_drvdata(pdev, qspi);
@@ -562,6 +725,12 @@ static int mchp_coreqspi_probe(struct platform_device *pdev)
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
SPI_TX_DUAL | SPI_TX_QUAD;
ctlr->dev.of_node = np;
+ ctlr->min_speed_hz = clk_get_rate(qspi->clk) / 30;
+ ctlr->prepare_message = mchp_coreqspi_prepare_message;
+ ctlr->unprepare_message = mchp_coreqspi_unprepare_message;
+ ctlr->transfer_one = mchp_coreqspi_transfer_one;
+ ctlr->num_chipselect = 2;
+ ctlr->use_gpio_descriptors = true;
ret = devm_spi_register_controller(&pdev->dev, ctlr);
if (ret)
diff --git a/drivers/spi/spi-microchip-core.c b/drivers/spi/spi-microchip-core.c
index 62ba0bd9cbb7..9128b86c5366 100644
--- a/drivers/spi/spi-microchip-core.c
+++ b/drivers/spi/spi-microchip-core.c
@@ -534,8 +534,7 @@ static int mchp_corespi_probe(struct platform_device *pdev)
host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi));
if (!host)
- return dev_err_probe(&pdev->dev, -ENOMEM,
- "unable to allocate host for SPI controller\n");
+ return -ENOMEM;
platform_set_drvdata(pdev, host);
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
index 4b0a1c0db041..4b40985af1ea 100644
--- a/drivers/spi/spi-mt65xx.c
+++ b/drivers/spi/spi-mt65xx.c
@@ -220,6 +220,14 @@ static const struct mtk_spi_compatible mt6893_compat = {
.no_need_unprepare = true,
};
+static const struct mtk_spi_compatible mt6991_compat = {
+ .need_pad_sel = true,
+ .must_tx = true,
+ .enhance_timing = true,
+ .dma_ext = true,
+ .ipm_design = true,
+};
+
/*
* A piece of default chip info unless the platform
* supplies it.
@@ -245,6 +253,9 @@ static const struct of_device_id mtk_spi_of_match[] = {
{ .compatible = "mediatek,mt6765-spi",
.data = (void *)&mt6765_compat,
},
+ { .compatible = "mediatek,mt6991-spi",
+ .data = (void *)&mt6991_compat,
+ },
{ .compatible = "mediatek,mt7622-spi",
.data = (void *)&mt7622_compat,
},
@@ -552,6 +563,22 @@ static void mtk_spi_setup_packet(struct spi_controller *host)
writel(reg_val, mdata->base + SPI_CFG1_REG);
}
+inline u32 mtk_spi_set_nbit(u32 nbit)
+{
+ switch (nbit) {
+ default:
+ pr_warn_once("unknown nbit mode %u. Falling back to single mode\n",
+ nbit);
+ fallthrough;
+ case SPI_NBITS_SINGLE:
+ return 0x0;
+ case SPI_NBITS_DUAL:
+ return 0x1;
+ case SPI_NBITS_QUAD:
+ return 0x2;
+ }
+}
+
static void mtk_spi_enable_transfer(struct spi_controller *host)
{
u32 cmd;
@@ -718,10 +745,16 @@ static int mtk_spi_transfer_one(struct spi_controller *host,
/* prepare xfer direction and duplex mode */
if (mdata->dev_comp->ipm_design) {
- if (!xfer->tx_buf || !xfer->rx_buf) {
+ if (xfer->tx_buf && xfer->rx_buf) {
+ reg_val &= ~SPI_CFG3_IPM_HALF_DUPLEX_EN;
+ } else if (xfer->tx_buf) {
reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN;
- if (xfer->rx_buf)
- reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR;
+ reg_val &= ~SPI_CFG3_IPM_HALF_DUPLEX_DIR;
+ reg_val |= mtk_spi_set_nbit(xfer->tx_nbits);
+ } else {
+ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN;
+ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR;
+ reg_val |= mtk_spi_set_nbit(xfer->rx_nbits);
}
writel(reg_val, mdata->base + SPI_CFG3_IPM_REG);
}
@@ -1148,7 +1181,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
host = devm_spi_alloc_host(dev, sizeof(*mdata));
if (!host)
- return dev_err_probe(dev, -ENOMEM, "failed to alloc spi host\n");
+ return -ENOMEM;
host->auto_runtime_pm = true;
host->dev.of_node = dev->of_node;
diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c
index 85ab5ce96c4d..5cc4632e13d7 100644
--- a/drivers/spi/spi-mtk-nor.c
+++ b/drivers/spi/spi-mtk-nor.c
@@ -918,7 +918,6 @@ static int mtk_nor_probe(struct platform_device *pdev)
if (ret < 0)
goto err_probe;
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
dev_info(&pdev->dev, "spi frequency: %d Hz\n", sp->spi_freq);
diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c
index e82ee6dcf498..ae38c244e258 100644
--- a/drivers/spi/spi-mtk-snfi.c
+++ b/drivers/spi/spi-mtk-snfi.c
@@ -1139,7 +1139,6 @@ static int mtk_snand_write_page_cache(struct mtk_snand *snf,
// Prepare for custom write interrupt
nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_PG);
reinit_completion(&snf->op_done);
- ;
// Trigger NFI into custom mode
nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_WRITE);
diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c
index 43455305fdf4..0ebcbdb1b1f7 100644
--- a/drivers/spi/spi-mxs.c
+++ b/drivers/spi/spi-mxs.c
@@ -388,7 +388,7 @@ static int mxs_spi_transfer_one(struct spi_controller *host,
TXRX_DEASSERT_CS : 0;
/*
- * Small blocks can be transfered via PIO.
+ * Small blocks can be transferred via PIO.
* Measured by empiric means:
*
* dd if=/dev/mtdblock0 of=/dev/null bs=1024k count=1
diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c
index 67cc1d86de42..cccd17f24775 100644
--- a/drivers/spi/spi-npcm-fiu.c
+++ b/drivers/spi/spi-npcm-fiu.c
@@ -13,6 +13,7 @@
#include <linux/vmalloc.h>
#include <linux/regmap.h>
#include <linux/of.h>
+#include <linux/minmax.h>
#include <linux/spi/spi-mem.h>
#include <linux/mfd/syscon.h>
@@ -498,10 +499,7 @@ static int npcm_fiu_read(struct spi_mem *mem, const struct spi_mem_op *op)
do {
addr = ((u32)op->addr.val + i);
- if (currlen < 16)
- readlen = currlen;
- else
- readlen = 16;
+ readlen = min_t(int, currlen, 16);
buf_ptr = data + i;
ret = npcm_fiu_uma_read(mem, op, addr, true, buf_ptr,
diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
index e63c77e41823..f9371f98a65b 100644
--- a/drivers/spi/spi-nxp-fspi.c
+++ b/drivers/spi/spi-nxp-fspi.c
@@ -330,6 +330,8 @@
/* Access flash memory using IP bus only */
#define FSPI_QUIRK_USE_IP_ONLY BIT(0)
+/* Disable DTR */
+#define FSPI_QUIRK_DISABLE_DTR BIT(1)
struct nxp_fspi_devtype_data {
unsigned int rxfifo;
@@ -344,7 +346,7 @@ static struct nxp_fspi_devtype_data lx2160a_data = {
.rxfifo = SZ_512, /* (64 * 64 bits) */
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
- .quirks = 0,
+ .quirks = FSPI_QUIRK_DISABLE_DTR,
.lut_num = 32,
.little_endian = true, /* little-endian */
};
@@ -399,7 +401,8 @@ struct nxp_fspi {
struct mutex lock;
struct pm_qos_request pm_qos_req;
int selected;
-#define FSPI_NEED_INIT (1 << 0)
+#define FSPI_NEED_INIT BIT(0)
+#define FSPI_DTR_MODE BIT(1)
int flags;
};
@@ -559,12 +562,21 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
u32 target_lut_reg;
/* cmd */
- lutval[0] |= LUT_DEF(0, LUT_CMD, LUT_PAD(op->cmd.buswidth),
- op->cmd.opcode);
+ if (op->cmd.dtr) {
+ lutval[0] |= LUT_DEF(0, LUT_CMD_DDR, LUT_PAD(op->cmd.buswidth),
+ op->cmd.opcode >> 8);
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_CMD_DDR,
+ LUT_PAD(op->cmd.buswidth),
+ op->cmd.opcode & 0xFF);
+ lutidx++;
+ } else {
+ lutval[0] |= LUT_DEF(0, LUT_CMD, LUT_PAD(op->cmd.buswidth),
+ op->cmd.opcode);
+ }
/* addr bytes */
if (op->addr.nbytes) {
- lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_ADDR,
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, op->addr.dtr ? LUT_ADDR_DDR : LUT_ADDR,
LUT_PAD(op->addr.buswidth),
op->addr.nbytes * 8);
lutidx++;
@@ -572,7 +584,7 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
/* dummy bytes, if needed */
if (op->dummy.nbytes) {
- lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_DUMMY,
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, op->dummy.dtr ? LUT_DUMMY_DDR : LUT_DUMMY,
/*
* Due to FlexSPI controller limitation number of PAD for dummy
* buswidth needs to be programmed as equal to data buswidth.
@@ -587,7 +599,8 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
if (op->data.nbytes) {
lutval[lutidx / 2] |= LUT_DEF(lutidx,
op->data.dir == SPI_MEM_DATA_IN ?
- LUT_NXP_READ : LUT_NXP_WRITE,
+ (op->data.dtr ? LUT_READ_DDR : LUT_NXP_READ) :
+ (op->data.dtr ? LUT_WRITE_DDR : LUT_NXP_WRITE),
LUT_PAD(op->data.buswidth),
0);
lutidx++;
@@ -645,6 +658,40 @@ static void nxp_fspi_clk_disable_unprep(struct nxp_fspi *f)
return;
}
+/*
+ * Sample Clock source selection for Flash Reading
+ * Four modes defined by fspi:
+ * mode 0: Dummy Read strobe generated by FlexSPI Controller
+ * and loopback internally
+ * mode 1: Dummy Read strobe generated by FlexSPI Controller
+ * and loopback from DQS pad
+ * mode 2: Reserved
+ * mode 3: Flash provided Read strobe and input from DQS pad
+ *
+ * fspi default use mode 0 after reset
+ */
+static void nxp_fspi_select_rx_sample_clk_source(struct nxp_fspi *f,
+ bool op_is_dtr)
+{
+ u32 reg;
+
+ /*
+ * For 8D-8D-8D mode, need to use mode 3 (Flash provided Read
+ * strobe and input from DQS pad), otherwise read operaton may
+ * meet issue.
+ * This mode require flash device connect the DQS pad on board.
+ * For other modes, still use mode 0, keep align with before.
+ * spi_nor_suspend will disable 8D-8D-8D mode, also need to
+ * change the mode back to mode 0.
+ */
+ reg = fspi_readl(f, f->iobase + FSPI_MCR0);
+ if (op_is_dtr)
+ reg |= FSPI_MCR0_RXCLKSRC(3);
+ else /*select mode 0 */
+ reg &= ~FSPI_MCR0_RXCLKSRC(3);
+ fspi_writel(f, reg, f->iobase + FSPI_MCR0);
+}
+
static void nxp_fspi_dll_calibration(struct nxp_fspi *f)
{
int ret;
@@ -675,6 +722,17 @@ static void nxp_fspi_dll_calibration(struct nxp_fspi *f)
}
/*
+ * Config the DLL register to default value, enable the target clock delay
+ * line delay cell override mode, and use 1 fixed delay cell in DLL delay
+ * chain, this is the suggested setting when clock rate < 100MHz.
+ */
+static void nxp_fspi_dll_override(struct nxp_fspi *f)
+{
+ fspi_writel(f, FSPI_DLLACR_OVRDEN, f->iobase + FSPI_DLLACR);
+ fspi_writel(f, FSPI_DLLBCR_OVRDEN, f->iobase + FSPI_DLLBCR);
+}
+
+/*
* In FlexSPI controller, flash access is based on value of FSPI_FLSHXXCR0
* register and start base address of the target device.
*
@@ -715,15 +773,18 @@ static void nxp_fspi_dll_calibration(struct nxp_fspi *f)
static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
const struct spi_mem_op *op)
{
+ /* flexspi only support one DTR mode: 8D-8D-8D */
+ bool op_is_dtr = op->cmd.dtr && op->addr.dtr && op->dummy.dtr && op->data.dtr;
unsigned long rate = op->max_freq;
int ret;
uint64_t size_kb;
/*
* Return, if previously selected target device is same as current
- * requested target device.
+ * requested target device. Also the DTR or STR mode do not change.
*/
- if (f->selected == spi_get_chipselect(spi, 0))
+ if ((f->selected == spi_get_chipselect(spi, 0)) &&
+ (!!(f->flags & FSPI_DTR_MODE) == op_is_dtr))
return;
/* Reset FLSHxxCR0 registers */
@@ -740,6 +801,18 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
dev_dbg(f->dev, "Target device [CS:%x] selected\n", spi_get_chipselect(spi, 0));
+ nxp_fspi_select_rx_sample_clk_source(f, op_is_dtr);
+
+ if (op_is_dtr) {
+ f->flags |= FSPI_DTR_MODE;
+ /* For DTR mode, flexspi will default div 2 and output to device.
+ * so here to config the root clock to 2 * device rate.
+ */
+ rate = rate * 2;
+ } else {
+ f->flags &= ~FSPI_DTR_MODE;
+ }
+
nxp_fspi_clk_disable_unprep(f);
ret = clk_set_rate(f->clk, rate);
@@ -756,6 +829,8 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
*/
if (rate > 100000000)
nxp_fspi_dll_calibration(f);
+ else
+ nxp_fspi_dll_override(f);
f->selected = spi_get_chipselect(spi, 0);
}
@@ -968,7 +1043,6 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
/* Invalidate the data in the AHB buffer. */
nxp_fspi_invalid(f);
- pm_runtime_mark_last_busy(f->dev);
pm_runtime_put_autosuspend(f->dev);
return err;
@@ -1072,13 +1146,7 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
/* Disable the module */
fspi_writel(f, FSPI_MCR0_MDIS, base + FSPI_MCR0);
- /*
- * Config the DLL register to default value, enable the target clock delay
- * line delay cell override mode, and use 1 fixed delay cell in DLL delay
- * chain, this is the suggested setting when clock rate < 100MHz.
- */
- fspi_writel(f, FSPI_DLLACR_OVRDEN, base + FSPI_DLLACR);
- fspi_writel(f, FSPI_DLLBCR_OVRDEN, base + FSPI_DLLBCR);
+ nxp_fspi_dll_override(f);
/* enable module */
fspi_writel(f, FSPI_MCR0_AHB_TIMEOUT(0xFF) |
@@ -1165,6 +1233,13 @@ static const struct spi_controller_mem_ops nxp_fspi_mem_ops = {
};
static const struct spi_controller_mem_caps nxp_fspi_mem_caps = {
+ .dtr = true,
+ .swap16 = false,
+ .per_op_freq = true,
+};
+
+static const struct spi_controller_mem_caps nxp_fspi_mem_caps_disable_dtr = {
+ .dtr = false,
.per_op_freq = true,
};
@@ -1273,17 +1348,24 @@ static int nxp_fspi_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "Failed to request irq\n");
- devm_mutex_init(dev, &f->lock);
+ ret = devm_mutex_init(dev, &f->lock);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize lock\n");
ctlr->bus_num = -1;
ctlr->num_chipselect = NXP_FSPI_MAX_CHIPSELECT;
ctlr->mem_ops = &nxp_fspi_mem_ops;
- ctlr->mem_caps = &nxp_fspi_mem_caps;
+
+ if (f->devtype_data->quirks & FSPI_QUIRK_DISABLE_DTR)
+ ctlr->mem_caps = &nxp_fspi_mem_caps_disable_dtr;
+ else
+ ctlr->mem_caps = &nxp_fspi_mem_caps;
+
ctlr->dev.of_node = np;
ret = devm_add_action_or_reset(dev, nxp_fspi_cleanup, f);
if (ret)
- return dev_err_probe(dev, ret, "Failed to register nxp_fspi_cleanup\n");
+ return ret;
return devm_spi_register_controller(&pdev->dev, ctlr);
}
diff --git a/drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c b/drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c
new file mode 100644
index 000000000000..8468c773713a
--- /dev/null
+++ b/drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Analog Devices Inc.
+ * Copyright (C) 2025 BayLibre, SAS
+ */
+
+#include <linux/clk.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/spi/offload/provider.h>
+#include <linux/spi/offload/types.h>
+#include <linux/types.h>
+
+static bool adi_util_sigma_delta_match(struct spi_offload_trigger *trigger,
+ enum spi_offload_trigger_type type,
+ u64 *args, u32 nargs)
+{
+ return type == SPI_OFFLOAD_TRIGGER_DATA_READY && nargs == 0;
+}
+
+static const struct spi_offload_trigger_ops adi_util_sigma_delta_ops = {
+ .match = adi_util_sigma_delta_match,
+};
+
+static int adi_util_sigma_delta_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct spi_offload_trigger_info info = {
+ .fwnode = dev_fwnode(dev),
+ .ops = &adi_util_sigma_delta_ops,
+ };
+ struct clk *clk;
+
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "Failed to get clock\n");
+
+ return devm_spi_offload_trigger_register(dev, &info);
+}
+
+static const struct of_device_id adi_util_sigma_delta_of_match_table[] = {
+ { .compatible = "adi,util-sigma-delta-spi", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adi_util_sigma_delta_of_match_table);
+
+static struct platform_driver adi_util_sigma_delta_driver = {
+ .probe = adi_util_sigma_delta_probe,
+ .driver = {
+ .name = "adi-util-sigma-delta-spi",
+ .of_match_table = adi_util_sigma_delta_of_match_table,
+ },
+};
+module_platform_driver(adi_util_sigma_delta_driver);
+
+MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>");
+MODULE_DESCRIPTION("ADI Sigma-Delta SPI offload trigger utility driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 70bb74b3bd9c..69c2e9d9be3c 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -272,7 +272,6 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable)
mcspi_write_chconf0(spi, l);
- pm_runtime_mark_last_busy(mcspi->dev);
pm_runtime_put_autosuspend(mcspi->dev);
}
}
@@ -989,6 +988,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
else
l &= ~OMAP2_MCSPI_CHCONF_PHA;
+ mcspi_write_chconf0(spi, l | OMAP2_MCSPI_CHCONF_FORCE);
mcspi_write_chconf0(spi, l);
cs->mode = spi->mode;
@@ -1102,7 +1102,6 @@ static int omap2_mcspi_setup(struct spi_device *spi)
if (ret && initial_setup)
omap2_mcspi_cleanup(spi);
- pm_runtime_mark_last_busy(mcspi->dev);
pm_runtime_put_autosuspend(mcspi->dev);
return ret;
@@ -1379,7 +1378,6 @@ static int omap2_mcspi_controller_setup(struct omap2_mcspi *mcspi)
ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN;
omap2_mcspi_set_mode(ctlr);
- pm_runtime_mark_last_busy(mcspi->dev);
pm_runtime_put_autosuspend(mcspi->dev);
return 0;
}
diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c
index e27642c4dea4..8577a19705de 100644
--- a/drivers/spi/spi-pci1xxxx.c
+++ b/drivers/spi/spi-pci1xxxx.c
@@ -23,6 +23,7 @@
#define SYS_FREQ_DEFAULT (62500000)
#define PCI1XXXX_SPI_MAX_CLOCK_HZ (30000000)
+#define PCI1XXXX_SPI_CLK_25MHZ (25000000)
#define PCI1XXXX_SPI_CLK_20MHZ (20000000)
#define PCI1XXXX_SPI_CLK_15MHZ (15000000)
#define PCI1XXXX_SPI_CLK_12MHZ (12000000)
@@ -96,8 +97,8 @@
#define SPI_DMA_CH1_DONE_INT BIT(1)
#define SPI_DMA_CH0_ABORT_INT BIT(16)
#define SPI_DMA_CH1_ABORT_INT BIT(17)
-#define SPI_DMA_DONE_INT_MASK (SPI_DMA_CH0_DONE_INT | SPI_DMA_CH1_DONE_INT)
-#define SPI_DMA_ABORT_INT_MASK (SPI_DMA_CH0_ABORT_INT | SPI_DMA_CH1_ABORT_INT)
+#define SPI_DMA_DONE_INT_MASK(x) (1 << (x))
+#define SPI_DMA_ABORT_INT_MASK(x) (1 << (16 + (x)))
#define DMA_CH_CONTROL_LIE BIT(3)
#define DMA_CH_CONTROL_RIE BIT(4)
#define DMA_INTR_EN (DMA_CH_CONTROL_RIE | DMA_CH_CONTROL_LIE)
@@ -131,12 +132,15 @@
#define SPI_SUSPEND_CONFIG 0x101
#define SPI_RESUME_CONFIG 0x203
+#define NUM_VEC_PER_INST 3
+
struct pci1xxxx_spi_internal {
u8 hw_inst;
u8 clkdiv;
- int irq;
+ int irq[NUM_VEC_PER_INST];
int mode;
bool spi_xfer_in_progress;
+ atomic_t dma_completion_count;
void *rx_buf;
bool dma_aborted_rd;
u32 bytes_recvd;
@@ -160,8 +164,10 @@ struct pci1xxxx_spi {
u8 dev_rev;
void __iomem *reg_base;
void __iomem *dma_offset_bar;
- /* lock to safely access the DMA registers in isr */
- spinlock_t dma_reg_lock;
+ /* lock to safely access the DMA RD registers in isr */
+ spinlock_t dma_rd_reg_lock;
+ /* lock to safely access the DMA RD registers in isr */
+ spinlock_t dma_wr_reg_lock;
bool can_dma;
struct pci1xxxx_spi_internal *spi_int[] __counted_by(total_hw_instances);
};
@@ -192,6 +198,9 @@ static const struct pci_device_id pci1xxxx_spi_pci_id_table[] = {
MODULE_DEVICE_TABLE(pci, pci1xxxx_spi_pci_id_table);
+static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev);
+static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev);
+
static int pci1xxxx_set_sys_lock(struct pci1xxxx_spi *par)
{
writel(SPI_SYSLOCK, par->reg_base + SPI_SYSLOCK_REG);
@@ -212,13 +221,16 @@ static void pci1xxxx_release_sys_lock(struct pci1xxxx_spi *par)
writel(0x0, par->reg_base + SPI_SYSLOCK_REG);
}
-static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq)
+static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int hw_inst, int num_vector)
{
struct pci_dev *pdev = spi_bus->dev;
u32 pf_num;
u32 regval;
int ret;
+ if (num_vector != hw_inst * NUM_VEC_PER_INST)
+ return -EOPNOTSUPP;
+
/*
* DEV REV Registers is a system register, HW Syslock bit
* should be acquired before accessing the register
@@ -246,16 +258,6 @@ static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq)
if (spi_bus->dev_rev < 0xC0 || pf_num)
return -EOPNOTSUPP;
- /*
- * DMA Supported only with MSI Interrupts
- * One of the SPI instance's MSI vector address and data
- * is used for DMA Interrupt
- */
- if (!irq_get_msi_desc(irq)) {
- dev_warn(&pdev->dev, "Error MSI Interrupt not supported, will operate in PIO mode\n");
- return -EOPNOTSUPP;
- }
-
spi_bus->dma_offset_bar = pcim_iomap(pdev, 2, pci_resource_len(pdev, 2));
if (!spi_bus->dma_offset_bar) {
dev_warn(&pdev->dev, "Error failed to map dma bar, will operate in PIO mode\n");
@@ -272,29 +274,91 @@ static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq)
return 0;
}
-static int pci1xxxx_spi_dma_init(struct pci1xxxx_spi *spi_bus, int irq)
+static void pci1xxxx_spi_dma_config(struct pci1xxxx_spi *spi_bus)
{
+ struct pci1xxxx_spi_internal *spi_sub_ptr;
+ u8 iter, irq_index;
struct msi_msg msi;
+ u32 regval;
+ u16 data;
+
+ irq_index = spi_bus->total_hw_instances;
+ for (iter = 0; iter < spi_bus->total_hw_instances; iter++) {
+ spi_sub_ptr = spi_bus->spi_int[iter];
+ get_cached_msi_msg(spi_sub_ptr->irq[1], &msi);
+ if (iter == 0) {
+ writel(msi.address_hi, spi_bus->dma_offset_bar +
+ SPI_DMA_INTR_IMWR_WDONE_HIGH);
+ writel(msi.address_hi, spi_bus->dma_offset_bar +
+ SPI_DMA_INTR_IMWR_WABORT_HIGH);
+ writel(msi.address_hi, spi_bus->dma_offset_bar +
+ SPI_DMA_INTR_IMWR_RDONE_HIGH);
+ writel(msi.address_hi, spi_bus->dma_offset_bar +
+ SPI_DMA_INTR_IMWR_RABORT_HIGH);
+ writel(msi.address_lo, spi_bus->dma_offset_bar +
+ SPI_DMA_INTR_IMWR_WDONE_LOW);
+ writel(msi.address_lo, spi_bus->dma_offset_bar +
+ SPI_DMA_INTR_IMWR_WABORT_LOW);
+ writel(msi.address_lo, spi_bus->dma_offset_bar +
+ SPI_DMA_INTR_IMWR_RDONE_LOW);
+ writel(msi.address_lo, spi_bus->dma_offset_bar +
+ SPI_DMA_INTR_IMWR_RABORT_LOW);
+ writel(0, spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA);
+ writel(0, spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA);
+ }
+ regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA);
+ data = msi.data + irq_index;
+ writel((regval | (data << (iter * 16))), spi_bus->dma_offset_bar +
+ SPI_DMA_INTR_WR_IMWR_DATA);
+ regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA);
+ irq_index++;
+
+ data = msi.data + irq_index;
+ regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA);
+ writel(regval | (data << (iter * 16)), spi_bus->dma_offset_bar +
+ SPI_DMA_INTR_RD_IMWR_DATA);
+ regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA);
+ irq_index++;
+ }
+}
+
+static int pci1xxxx_spi_dma_init(struct pci1xxxx_spi *spi_bus, int hw_inst, int num_vector)
+{
+ struct pci1xxxx_spi_internal *spi_sub_ptr;
+ u8 iter, irq_index;
int ret;
- ret = pci1xxxx_check_spi_can_dma(spi_bus, irq);
+ irq_index = hw_inst;
+ ret = pci1xxxx_check_spi_can_dma(spi_bus, hw_inst, num_vector);
if (ret)
return ret;
- spin_lock_init(&spi_bus->dma_reg_lock);
- get_cached_msi_msg(irq, &msi);
+ spin_lock_init(&spi_bus->dma_rd_reg_lock);
+ spin_lock_init(&spi_bus->dma_wr_reg_lock);
writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_WR_ENGINE_EN);
writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_RD_ENGINE_EN);
- writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WDONE_HIGH);
- writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WABORT_HIGH);
- writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RDONE_HIGH);
- writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RABORT_HIGH);
- writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WDONE_LOW);
- writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WABORT_LOW);
- writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RDONE_LOW);
- writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RABORT_LOW);
- writel(msi.data, spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA);
- writel(msi.data, spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA);
+
+ for (iter = 0; iter < hw_inst; iter++) {
+ spi_sub_ptr = spi_bus->spi_int[iter];
+ spi_sub_ptr->irq[1] = pci_irq_vector(spi_bus->dev, irq_index);
+ ret = devm_request_irq(&spi_bus->dev->dev, spi_sub_ptr->irq[1],
+ pci1xxxx_spi_isr_dma_wr, PCI1XXXX_IRQ_FLAGS,
+ pci_name(spi_bus->dev), spi_sub_ptr);
+ if (ret < 0)
+ return ret;
+
+ irq_index++;
+
+ spi_sub_ptr->irq[2] = pci_irq_vector(spi_bus->dev, irq_index);
+ ret = devm_request_irq(&spi_bus->dev->dev, spi_sub_ptr->irq[2],
+ pci1xxxx_spi_isr_dma_rd, PCI1XXXX_IRQ_FLAGS,
+ pci_name(spi_bus->dev), spi_sub_ptr);
+ if (ret < 0)
+ return ret;
+
+ irq_index++;
+ }
+ pci1xxxx_spi_dma_config(spi_bus);
dma_set_max_seg_size(&spi_bus->dev->dev, PCI1XXXX_SPI_BUFFER_SIZE);
spi_bus->can_dma = true;
return 0;
@@ -318,12 +382,14 @@ static void pci1xxxx_spi_set_cs(struct spi_device *spi, bool enable)
writel(regval, par->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst));
}
-static u8 pci1xxxx_get_clock_div(u32 hz)
+static u8 pci1xxxx_get_clock_div(struct pci1xxxx_spi *par, u32 hz)
{
u8 val = 0;
if (hz >= PCI1XXXX_SPI_MAX_CLOCK_HZ)
val = 2;
+ else if (par->dev_rev >= 0xC0 && hz >= PCI1XXXX_SPI_CLK_25MHZ)
+ val = 1;
else if ((hz < PCI1XXXX_SPI_MAX_CLOCK_HZ) && (hz >= PCI1XXXX_SPI_CLK_20MHZ))
val = 3;
else if ((hz < PCI1XXXX_SPI_CLK_20MHZ) && (hz >= PCI1XXXX_SPI_CLK_15MHZ))
@@ -398,13 +464,14 @@ static void pci1xxxx_spi_setup(struct pci1xxxx_spi *par, u8 hw_inst, u32 mode,
writel(regval, par->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst));
}
-static void pci1xxxx_start_spi_xfer(struct pci1xxxx_spi_internal *p, u8 hw_inst)
+static void pci1xxxx_start_spi_xfer(struct pci1xxxx_spi_internal *p)
{
u32 regval;
- regval = readl(p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst));
+ atomic_set(&p->dma_completion_count, 0);
+ regval = readl(p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst));
regval |= SPI_MST_CTL_GO;
- writel(regval, p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst));
+ writel(regval, p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst));
}
static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr,
@@ -423,7 +490,7 @@ static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr,
p->spi_xfer_in_progress = true;
p->bytes_recvd = 0;
- clkdiv = pci1xxxx_get_clock_div(xfer->speed_hz);
+ clkdiv = pci1xxxx_get_clock_div(par, xfer->speed_hz);
tx_buf = xfer->tx_buf;
rx_buf = xfer->rx_buf;
transfer_len = xfer->len;
@@ -448,7 +515,7 @@ static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr,
&tx_buf[bytes_transfered], len);
bytes_transfered += len;
pci1xxxx_spi_setup(par, p->hw_inst, spi->mode, clkdiv, len);
- pci1xxxx_start_spi_xfer(p, p->hw_inst);
+ pci1xxxx_start_spi_xfer(p);
/* Wait for DMA_TERM interrupt */
result = wait_for_completion_timeout(&p->spi_xfer_done,
@@ -474,7 +541,6 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr,
{
struct pci1xxxx_spi_internal *p = spi_controller_get_devdata(spi_ctlr);
struct pci1xxxx_spi *par = p->parent;
- dma_addr_t rx_dma_addr = 0;
dma_addr_t tx_dma_addr = 0;
int ret = 0;
u32 regval;
@@ -483,6 +549,7 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr,
p->tx_sgl = xfer->tx_sg.sgl;
p->rx_sgl = xfer->rx_sg.sgl;
p->rx_buf = xfer->rx_buf;
+ atomic_set(&p->dma_completion_count, 1);
regval = readl(par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
writel(regval, par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
@@ -492,20 +559,16 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr,
}
p->xfer = xfer;
p->mode = spi->mode;
- p->clkdiv = pci1xxxx_get_clock_div(xfer->speed_hz);
+ p->clkdiv = pci1xxxx_get_clock_div(par, xfer->speed_hz);
p->bytes_recvd = 0;
p->rx_buf = xfer->rx_buf;
regval = readl(par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
writel(regval, par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
tx_dma_addr = sg_dma_address(p->tx_sgl);
- rx_dma_addr = sg_dma_address(p->rx_sgl);
p->tx_sgl_len = sg_dma_len(p->tx_sgl);
- p->rx_sgl_len = sg_dma_len(p->rx_sgl);
pci1xxxx_spi_setup(par, p->hw_inst, p->mode, p->clkdiv, p->tx_sgl_len);
pci1xxxx_spi_setup_dma_to_io(p, (tx_dma_addr), p->tx_sgl_len);
- if (rx_dma_addr)
- pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len);
writel(p->hw_inst, par->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG);
reinit_completion(&p->spi_xfer_done);
@@ -595,83 +658,111 @@ static irqreturn_t pci1xxxx_spi_isr_io(int irq, void *dev)
return spi_int_fired;
}
-static void pci1xxxx_spi_setup_next_dma_transfer(struct pci1xxxx_spi_internal *p)
+static void pci1xxxx_spi_setup_next_dma_to_io_transfer(struct pci1xxxx_spi_internal *p)
{
dma_addr_t tx_dma_addr = 0;
- dma_addr_t rx_dma_addr = 0;
u32 prev_len;
p->tx_sgl = sg_next(p->tx_sgl);
- if (p->rx_sgl)
- p->rx_sgl = sg_next(p->rx_sgl);
- if (!p->tx_sgl) {
- /* Clear xfer_done */
- complete(&p->spi_xfer_done);
- } else {
+ if (p->tx_sgl) {
tx_dma_addr = sg_dma_address(p->tx_sgl);
prev_len = p->tx_sgl_len;
p->tx_sgl_len = sg_dma_len(p->tx_sgl);
+ pci1xxxx_spi_setup_dma_to_io(p, tx_dma_addr, p->tx_sgl_len);
+ writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG);
if (prev_len != p->tx_sgl_len)
pci1xxxx_spi_setup(p->parent,
p->hw_inst, p->mode, p->clkdiv, p->tx_sgl_len);
- pci1xxxx_spi_setup_dma_to_io(p, tx_dma_addr, p->tx_sgl_len);
- if (p->rx_sgl) {
- rx_dma_addr = sg_dma_address(p->rx_sgl);
- p->rx_sgl_len = sg_dma_len(p->rx_sgl);
- pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len);
- }
- writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG);
}
}
-static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev)
+static void pci1xxxx_spi_setup_next_dma_from_io_transfer(struct pci1xxxx_spi_internal *p)
+{
+ dma_addr_t rx_dma_addr = 0;
+
+ if (p->rx_sgl) {
+ rx_dma_addr = sg_dma_address(p->rx_sgl);
+ p->rx_sgl_len = sg_dma_len(p->rx_sgl);
+ pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len);
+ writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_WR_DOORBELL_REG);
+ }
+}
+
+static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev)
{
struct pci1xxxx_spi_internal *p = dev;
irqreturn_t spi_int_fired = IRQ_NONE;
unsigned long flags;
u32 regval;
- spin_lock_irqsave(&p->parent->dma_reg_lock, flags);
/* Clear the DMA RD INT and start spi xfer*/
regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_RD_STS);
- if (regval & SPI_DMA_DONE_INT_MASK) {
- if (regval & SPI_DMA_CH0_DONE_INT)
- pci1xxxx_start_spi_xfer(p, SPI0);
- if (regval & SPI_DMA_CH1_DONE_INT)
- pci1xxxx_start_spi_xfer(p, SPI1);
- spi_int_fired = IRQ_HANDLED;
- }
- if (regval & SPI_DMA_ABORT_INT_MASK) {
- p->dma_aborted_rd = true;
- spi_int_fired = IRQ_HANDLED;
+ if (regval) {
+ if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) {
+ /* Start the SPI transfer only if both DMA read and write are completed */
+ if (atomic_inc_return(&p->dma_completion_count) == 2)
+ pci1xxxx_start_spi_xfer(p);
+ spi_int_fired = IRQ_HANDLED;
+ }
+ if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) {
+ p->dma_aborted_rd = true;
+ spi_int_fired = IRQ_HANDLED;
+ }
+ spin_lock_irqsave(&p->parent->dma_rd_reg_lock, flags);
+ writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)),
+ p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR);
+ spin_unlock_irqrestore(&p->parent->dma_rd_reg_lock, flags);
}
- writel(regval, p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR);
+ return spi_int_fired;
+}
+
+static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev)
+{
+ struct pci1xxxx_spi_internal *p = dev;
+ irqreturn_t spi_int_fired = IRQ_NONE;
+ unsigned long flags;
+ u32 regval;
/* Clear the DMA WR INT */
regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_WR_STS);
- if (regval & SPI_DMA_DONE_INT_MASK) {
- if (regval & SPI_DMA_CH0_DONE_INT)
- pci1xxxx_spi_setup_next_dma_transfer(p->parent->spi_int[SPI0]);
-
- if (regval & SPI_DMA_CH1_DONE_INT)
- pci1xxxx_spi_setup_next_dma_transfer(p->parent->spi_int[SPI1]);
+ if (regval) {
+ if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) {
+ spi_int_fired = IRQ_HANDLED;
+ if (sg_is_last(p->rx_sgl)) {
+ complete(&p->spi_xfer_done);
+ } else {
+ p->rx_sgl = sg_next(p->rx_sgl);
+ if (atomic_inc_return(&p->dma_completion_count) == 2)
+ pci1xxxx_start_spi_xfer(p);
+ }
- spi_int_fired = IRQ_HANDLED;
- }
- if (regval & SPI_DMA_ABORT_INT_MASK) {
- p->dma_aborted_wr = true;
- spi_int_fired = IRQ_HANDLED;
+ }
+ if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) {
+ p->dma_aborted_wr = true;
+ spi_int_fired = IRQ_HANDLED;
+ }
+ spin_lock_irqsave(&p->parent->dma_wr_reg_lock, flags);
+ writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)),
+ p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR);
+ spin_unlock_irqrestore(&p->parent->dma_wr_reg_lock, flags);
}
- writel(regval, p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR);
- spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags);
+ return spi_int_fired;
+}
+
+static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev)
+{
+ struct pci1xxxx_spi_internal *p = dev;
+ irqreturn_t spi_int_fired = IRQ_NONE;
+ u32 regval;
/* Clear the SPI GO_BIT Interrupt */
regval = readl(p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
if (regval & SPI_INTR) {
- writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_WR_DOORBELL_REG);
+ pci1xxxx_spi_setup_next_dma_from_io_transfer(p);
+ pci1xxxx_spi_setup_next_dma_to_io_transfer(p);
spi_int_fired = IRQ_HANDLED;
+ writel(regval, p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
}
- writel(regval, p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
return spi_int_fired;
}
@@ -761,7 +852,7 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id *
if (!spi_bus->reg_base)
return -EINVAL;
- num_vector = pci_alloc_irq_vectors(pdev, 1, hw_inst_cnt,
+ num_vector = pci_alloc_irq_vectors(pdev, 1, hw_inst_cnt * NUM_VEC_PER_INST,
PCI_IRQ_INTX | PCI_IRQ_MSI);
if (num_vector < 0) {
dev_err(&pdev->dev, "Error allocating MSI vectors\n");
@@ -775,27 +866,23 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id *
regval &= ~SPI_INTR;
writel(regval, spi_bus->reg_base +
SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst));
- spi_sub_ptr->irq = pci_irq_vector(pdev, 0);
+ spi_sub_ptr->irq[0] = pci_irq_vector(pdev, 0);
if (num_vector >= hw_inst_cnt)
- ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq,
+ ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0],
pci1xxxx_spi_isr, PCI1XXXX_IRQ_FLAGS,
pci_name(pdev), spi_sub_ptr);
else
- ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq,
+ ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0],
pci1xxxx_spi_shared_isr,
PCI1XXXX_IRQ_FLAGS | IRQF_SHARED,
pci_name(pdev), spi_bus);
if (ret < 0) {
dev_err(&pdev->dev, "Unable to request irq : %d",
- spi_sub_ptr->irq);
+ spi_sub_ptr->irq[0]);
return -ENODEV;
}
- ret = pci1xxxx_spi_dma_init(spi_bus, spi_sub_ptr->irq);
- if (ret && ret != -EOPNOTSUPP)
- return ret;
-
/* This register is only applicable for 1st instance */
regval = readl(spi_bus->reg_base + SPI_PCI_CTRL_REG_OFFSET(0));
if (!only_sec_inst)
@@ -817,13 +904,13 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id *
writel(regval, spi_bus->reg_base +
SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst));
if (num_vector >= hw_inst_cnt) {
- spi_sub_ptr->irq = pci_irq_vector(pdev, iter);
- ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq,
+ spi_sub_ptr->irq[0] = pci_irq_vector(pdev, iter);
+ ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0],
pci1xxxx_spi_isr, PCI1XXXX_IRQ_FLAGS,
pci_name(pdev), spi_sub_ptr);
if (ret < 0) {
dev_err(&pdev->dev, "Unable to request irq : %d",
- spi_sub_ptr->irq);
+ spi_sub_ptr->irq[0]);
return -ENODEV;
}
}
@@ -846,6 +933,10 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id *
if (ret)
return ret;
}
+ ret = pci1xxxx_spi_dma_init(spi_bus, hw_inst_cnt, num_vector);
+ if (ret && ret != -EOPNOTSUPP)
+ return ret;
+
pci_set_drvdata(pdev, spi_bus);
return 0;
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index dd87cf4f70dd..9e56e8774614 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -33,6 +33,7 @@
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/minmax.h>
/*
* This macro is used to define some register default values.
@@ -760,10 +761,9 @@ static void setup_dma_scatter(struct pl022 *pl022,
* we just feed in this, else we stuff in as much
* as we can.
*/
- if (bytesleft < (PAGE_SIZE - offset_in_page(bufp)))
- mapbytes = bytesleft;
- else
- mapbytes = PAGE_SIZE - offset_in_page(bufp);
+ mapbytes = min_t(int, bytesleft,
+ PAGE_SIZE - offset_in_page(bufp));
+
sg_set_page(sg, virt_to_page(bufp),
mapbytes, offset_in_page(bufp));
bufp += mapbytes;
@@ -775,10 +775,7 @@ static void setup_dma_scatter(struct pl022 *pl022,
} else {
/* Map the dummy buffer on every page */
for_each_sg(sgtab->sgl, sg, sgtab->nents, i) {
- if (bytesleft < PAGE_SIZE)
- mapbytes = bytesleft;
- else
- mapbytes = PAGE_SIZE;
+ mapbytes = min_t(int, bytesleft, PAGE_SIZE);
sg_set_page(sg, virt_to_page(pl022->dummypage),
mapbytes, 0);
bytesleft -= mapbytes;
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index 06711a62fa3d..ec7117a94d5f 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -1283,7 +1283,7 @@ int pxa2xx_spi_probe(struct device *dev, struct ssp_device *ssp,
else
controller = devm_spi_alloc_host(dev, sizeof(*drv_data));
if (!controller)
- return dev_err_probe(dev, -ENOMEM, "cannot alloc spi_controller\n");
+ return -ENOMEM;
drv_data = spi_controller_get_devdata(controller);
drv_data->controller = controller;
diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c
index f2e1a27b410d..58ceea1ea8fb 100644
--- a/drivers/spi/spi-qpic-snand.c
+++ b/drivers/spi/spi-qpic-snand.c
@@ -59,12 +59,6 @@
#define OOB_BUF_SIZE 128
#define ecceng_to_qspi(eng) container_of(eng, struct qpic_spi_nand, ecc_eng)
-struct qpic_snand_op {
- u32 cmd_reg;
- u32 addr1_reg;
- u32 addr2_reg;
-};
-
struct snandc_read_status {
__le32 snandc_flash;
__le32 snandc_buffer;
@@ -84,7 +78,6 @@ struct qcom_ecc_stats {
};
struct qpic_ecc {
- struct device *dev;
int ecc_bytes_hw;
int spare_bytes;
int bbm_size;
@@ -101,8 +94,6 @@ struct qpic_ecc {
u32 cfg1_raw;
u32 ecc_buf_cfg;
u32 ecc_bch_cfg;
- u32 clrflashstatus;
- u32 clrreadstatus;
bool bch_enabled;
};
@@ -216,13 +207,21 @@ static int qcom_spi_ooblayout_ecc(struct mtd_info *mtd, int section,
struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand);
struct qpic_ecc *qecc = snandc->qspi->ecc;
- if (section > 1)
- return -ERANGE;
-
- oobregion->length = qecc->ecc_bytes_hw + qecc->spare_bytes;
- oobregion->offset = mtd->oobsize - oobregion->length;
+ switch (section) {
+ case 0:
+ oobregion->offset = 0;
+ oobregion->length = qecc->bytes * (qecc->steps - 1) +
+ qecc->bbm_size;
+ return 0;
+ case 1:
+ oobregion->offset = qecc->bytes * (qecc->steps - 1) +
+ qecc->bbm_size +
+ qecc->steps * 4;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+ return 0;
+ }
- return 0;
+ return -ERANGE;
}
static int qcom_spi_ooblayout_free(struct mtd_info *mtd, int section,
@@ -283,9 +282,22 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
goto err_free_ecc_cfg;
}
- if (ecc_cfg->strength != 4) {
+ switch (ecc_cfg->strength) {
+ case 4:
+ ecc_cfg->ecc_mode = ECC_MODE_4BIT;
+ ecc_cfg->ecc_bytes_hw = 7;
+ ecc_cfg->spare_bytes = 4;
+ break;
+
+ case 8:
+ ecc_cfg->ecc_mode = ECC_MODE_8BIT;
+ ecc_cfg->ecc_bytes_hw = 13;
+ ecc_cfg->spare_bytes = 2;
+ break;
+
+ default:
dev_err(snandc->dev,
- "only 4 bits ECC strength is supported\n");
+ "only 4 or 8 bits ECC strength is supported\n");
ret = -EOPNOTSUPP;
goto err_free_ecc_cfg;
}
@@ -302,13 +314,11 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
nand->ecc.ctx.priv = ecc_cfg;
snandc->qspi->mtd = mtd;
- ecc_cfg->ecc_bytes_hw = 7;
- ecc_cfg->spare_bytes = 4;
ecc_cfg->bbm_size = 1;
ecc_cfg->bch_enabled = true;
ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size;
- ecc_cfg->steps = 4;
+ ecc_cfg->steps = cwperpage;
ecc_cfg->cw_data = 516;
ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes;
bad_block_byte = mtd->writesize - ecc_cfg->cw_size * (cwperpage - 1) + 1;
@@ -365,16 +375,16 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
FIELD_PREP(ECC_SW_RESET, 0) |
FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) |
FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) |
- FIELD_PREP(ECC_MODE_MASK, 0) |
+ FIELD_PREP(ECC_MODE_MASK, ecc_cfg->ecc_mode) |
FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw);
ecc_cfg->ecc_buf_cfg = FIELD_PREP(NUM_STEPS_MASK, 0x203);
- ecc_cfg->clrflashstatus = FS_READY_BSY_N;
- ecc_cfg->clrreadstatus = 0xc0;
conf->step_size = ecc_cfg->step_size;
conf->strength = ecc_cfg->strength;
+ snandc->regs->clrflashstatus = cpu_to_le32(FS_READY_BSY_N);
+ snandc->regs->clrreadstatus = cpu_to_le32(0xc0);
snandc->regs->erased_cw_detect_cfg_clr = cpu_to_le32(CLR_ERASED_PAGE_DET);
snandc->regs->erased_cw_detect_cfg_set = cpu_to_le32(SET_ERASED_PAGE_DET);
@@ -481,9 +491,14 @@ qcom_spi_config_cw_read(struct qcom_nand_controller *snandc, bool use_ecc, int c
qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
- qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 2, 0);
- qcom_read_reg_dma(snandc, NAND_ERASED_CW_DETECT_STATUS, 1,
- NAND_BAM_NEXT_SGL);
+ if (use_ecc) {
+ qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 2, 0);
+ qcom_read_reg_dma(snandc, NAND_ERASED_CW_DETECT_STATUS, 1,
+ NAND_BAM_NEXT_SGL);
+ } else {
+ qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 1,
+ NAND_BAM_NEXT_SGL);
+ }
}
static int qcom_spi_block_erase(struct qcom_nand_controller *snandc)
@@ -586,8 +601,6 @@ static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc,
snandc->regs->cfg0 = cpu_to_le32(cfg0);
snandc->regs->cfg1 = cpu_to_le32(cfg1);
snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
- snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus);
- snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus);
snandc->regs->exec = cpu_to_le32(1);
qcom_spi_set_read_loc(snandc, num_cw - 1, 0, 0, ecc_cfg->cw_size, 1);
@@ -608,10 +621,16 @@ static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc,
bbpos = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1);
- if (snandc->data_buffer[bbpos] == 0xff)
- snandc->data_buffer[bbpos + 1] = 0xff;
- if (snandc->data_buffer[bbpos] != 0xff)
- snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos];
+ /*
+ * TODO: The SPINAND code expects two bad block marker bytes
+ * at the beginning of the OOB area, but the OOB layout used by
+ * the driver has only one. Duplicate that for now in order to
+ * avoid certain blocks to be marked as bad.
+ *
+ * This can be removed once single-byte bad block marker support
+ * gets implemented in the SPINAND code.
+ */
+ snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos];
memcpy(op->data.buf.in, snandc->data_buffer + bbpos, op->data.nbytes);
@@ -715,8 +734,6 @@ static int qcom_spi_read_cw_raw(struct qcom_nand_controller *snandc, u8 *data_bu
snandc->regs->cfg0 = cpu_to_le32(cfg0);
snandc->regs->cfg1 = cpu_to_le32(cfg1);
snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
- snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus);
- snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus);
snandc->regs->exec = cpu_to_le32(1);
qcom_spi_set_read_loc(snandc, raw_cw, 0, 0, ecc_cfg->cw_size, 1);
@@ -831,8 +848,6 @@ static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc,
snandc->regs->cfg0 = cpu_to_le32(cfg0);
snandc->regs->cfg1 = cpu_to_le32(cfg1);
snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
- snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus);
- snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus);
snandc->regs->exec = cpu_to_le32(1);
qcom_spi_set_read_loc(snandc, 0, 0, 0, ecc_cfg->cw_data, 1);
@@ -851,7 +866,7 @@ static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc,
int data_size, oob_size;
if (i == (num_cw - 1)) {
- data_size = 512 - ((num_cw - 1) << 2);
+ data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2);
oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw +
ecc_cfg->spare_bytes;
} else {
@@ -924,8 +939,6 @@ static int qcom_spi_read_page_oob(struct qcom_nand_controller *snandc,
snandc->regs->cfg0 = cpu_to_le32(cfg0);
snandc->regs->cfg1 = cpu_to_le32(cfg1);
snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
- snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus);
- snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus);
snandc->regs->exec = cpu_to_le32(1);
qcom_spi_set_read_loc(snandc, 0, 0, 0, ecc_cfg->cw_data, 1);
@@ -1045,8 +1058,6 @@ static int qcom_spi_program_raw(struct qcom_nand_controller *snandc,
snandc->regs->cfg0 = cpu_to_le32(cfg0);
snandc->regs->cfg1 = cpu_to_le32(cfg1);
snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
- snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus);
- snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus);
snandc->regs->exec = cpu_to_le32(1);
qcom_spi_config_page_write(snandc);
@@ -1185,7 +1196,7 @@ static int qcom_spi_program_oob(struct qcom_nand_controller *snandc,
u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg;
cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) |
- FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1);
+ FIELD_PREP(CW_PER_PAGE_MASK, 0);
cfg1 = ecc_cfg->cfg1;
ecc_bch_cfg = ecc_cfg->ecc_bch_cfg;
ecc_buf_cfg = ecc_cfg->ecc_buf_cfg;
@@ -1310,7 +1321,6 @@ static int qcom_spi_write_page(struct qcom_nand_controller *snandc,
static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc,
const struct spi_mem_op *op)
{
- struct qpic_snand_op s_op = {};
u32 cmd;
int ret, opcode;
@@ -1318,34 +1328,24 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc,
if (ret < 0)
return ret;
- s_op.cmd_reg = cmd;
- s_op.addr1_reg = op->addr.val;
- s_op.addr2_reg = 0;
-
opcode = op->cmd.opcode;
switch (opcode) {
case SPINAND_WRITE_EN:
return 0;
case SPINAND_PROGRAM_EXECUTE:
- s_op.addr1_reg = op->addr.val << 16;
- s_op.addr2_reg = op->addr.val >> 16 & 0xff;
- snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg);
- snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg);
+ snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16);
+ snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xff);
snandc->qspi->cmd = cpu_to_le32(cmd);
return qcom_spi_program_execute(snandc, op);
case SPINAND_READ:
- s_op.addr1_reg = (op->addr.val << 16);
- s_op.addr2_reg = op->addr.val >> 16 & 0xff;
- snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg);
- snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg);
+ snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16);
+ snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xff);
snandc->qspi->cmd = cpu_to_le32(cmd);
return 0;
case SPINAND_ERASE:
- s_op.addr2_reg = (op->addr.val >> 16) & 0xffff;
- s_op.addr1_reg = op->addr.val;
- snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg << 16);
- snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg);
+ snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16);
+ snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xffff);
snandc->qspi->cmd = cpu_to_le32(cmd);
return qcom_spi_block_erase(snandc);
default:
@@ -1357,10 +1357,10 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc,
qcom_clear_read_regs(snandc);
qcom_clear_bam_transaction(snandc);
- snandc->regs->cmd = cpu_to_le32(s_op.cmd_reg);
+ snandc->regs->cmd = cpu_to_le32(cmd);
snandc->regs->exec = cpu_to_le32(1);
- snandc->regs->addr0 = cpu_to_le32(s_op.addr1_reg);
- snandc->regs->addr1 = cpu_to_le32(s_op.addr2_reg);
+ snandc->regs->addr0 = cpu_to_le32(op->addr.val);
+ snandc->regs->addr1 = cpu_to_le32(0);
qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
@@ -1541,17 +1541,16 @@ static int qcom_spi_probe(struct platform_device *pdev)
}
snandc->props = dev_data;
- snandc->dev = &pdev->dev;
- snandc->core_clk = devm_clk_get(dev, "core");
+ snandc->core_clk = devm_clk_get_enabled(dev, "core");
if (IS_ERR(snandc->core_clk))
return PTR_ERR(snandc->core_clk);
- snandc->aon_clk = devm_clk_get(dev, "aon");
+ snandc->aon_clk = devm_clk_get_enabled(dev, "aon");
if (IS_ERR(snandc->aon_clk))
return PTR_ERR(snandc->aon_clk);
- snandc->qspi->iomacro_clk = devm_clk_get(dev, "iom");
+ snandc->qspi->iomacro_clk = devm_clk_get_enabled(dev, "iom");
if (IS_ERR(snandc->qspi->iomacro_clk))
return PTR_ERR(snandc->qspi->iomacro_clk);
@@ -1565,18 +1564,6 @@ static int qcom_spi_probe(struct platform_device *pdev)
if (dma_mapping_error(dev, snandc->base_dma))
return -ENXIO;
- ret = clk_prepare_enable(snandc->core_clk);
- if (ret)
- goto err_dis_core_clk;
-
- ret = clk_prepare_enable(snandc->aon_clk);
- if (ret)
- goto err_dis_aon_clk;
-
- ret = clk_prepare_enable(snandc->qspi->iomacro_clk);
- if (ret)
- goto err_dis_iom_clk;
-
ret = qcom_nandc_alloc(snandc);
if (ret)
goto err_snand_alloc;
@@ -1607,20 +1594,16 @@ static int qcom_spi_probe(struct platform_device *pdev)
ret = spi_register_controller(ctlr);
if (ret) {
dev_err(&pdev->dev, "spi_register_controller failed.\n");
- goto err_spi_init;
+ goto err_register_controller;
}
return 0;
+err_register_controller:
+ nand_ecc_unregister_on_host_hw_engine(&snandc->qspi->ecc_eng);
err_spi_init:
qcom_nandc_unalloc(snandc);
err_snand_alloc:
- clk_disable_unprepare(snandc->qspi->iomacro_clk);
-err_dis_iom_clk:
- clk_disable_unprepare(snandc->aon_clk);
-err_dis_aon_clk:
- clk_disable_unprepare(snandc->core_clk);
-err_dis_core_clk:
dma_unmap_resource(dev, res->start, resource_size(res),
DMA_BIDIRECTIONAL, 0);
return ret;
@@ -1633,13 +1616,8 @@ static void qcom_spi_remove(struct platform_device *pdev)
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi_unregister_controller(ctlr);
-
+ nand_ecc_unregister_on_host_hw_engine(&snandc->qspi->ecc_eng);
qcom_nandc_unalloc(snandc);
-
- clk_disable_unprepare(snandc->aon_clk);
- clk_disable_unprepare(snandc->core_clk);
- clk_disable_unprepare(snandc->qspi->iomacro_clk);
-
dma_unmap_resource(&pdev->dev, snandc->base_dma, resource_size(res),
DMA_BIDIRECTIONAL, 0);
}
diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c
index e71d3805b150..22b86fc89132 100644
--- a/drivers/spi/spi-rb4xx.c
+++ b/drivers/spi/spi-rb4xx.c
@@ -16,7 +16,16 @@
#include <linux/spi/spi.h>
#include <linux/of.h>
-#include <asm/mach-ath79/ar71xx_regs.h>
+#define AR71XX_SPI_REG_FS 0x00 /* Function Select */
+#define AR71XX_SPI_REG_CTRL 0x04 /* SPI Control */
+#define AR71XX_SPI_REG_IOC 0x08 /* SPI I/O Control */
+#define AR71XX_SPI_REG_RDS 0x0c /* Read Data Shift */
+
+#define AR71XX_SPI_FS_GPIO BIT(0) /* Enable GPIO mode */
+
+#define AR71XX_SPI_IOC_DO BIT(0) /* Data Out pin */
+#define AR71XX_SPI_IOC_CLK BIT(8) /* CLK pin */
+#define AR71XX_SPI_IOC_CS(n) BIT(16 + (n))
struct rb4xx_spi {
void __iomem *base;
@@ -63,7 +72,7 @@ static inline void do_spi_clk_two(struct rb4xx_spi *rbspi, u32 spi_ioc,
if (value & BIT(1))
regval |= AR71XX_SPI_IOC_DO;
if (value & BIT(0))
- regval |= AR71XX_SPI_IOC_CS2;
+ regval |= AR71XX_SPI_IOC_CS(2);
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
@@ -89,7 +98,7 @@ static void rb4xx_set_cs(struct spi_device *spi, bool enable)
*/
if (enable)
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC,
- AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1);
+ AR71XX_SPI_IOC_CS(0) | AR71XX_SPI_IOC_CS(1));
}
static int rb4xx_transfer_one(struct spi_controller *host,
@@ -109,10 +118,10 @@ static int rb4xx_transfer_one(struct spi_controller *host,
*/
if (spi_get_chipselect(spi, 0) == 2)
/* MMC */
- spi_ioc = AR71XX_SPI_IOC_CS0;
+ spi_ioc = AR71XX_SPI_IOC_CS(0);
else
/* Boot flash and CPLD */
- spi_ioc = AR71XX_SPI_IOC_CS1;
+ spi_ioc = AR71XX_SPI_IOC_CS(1);
tx_buf = t->tx_buf;
rx_buf = t->rx_buf;
@@ -147,7 +156,7 @@ static int rb4xx_spi_probe(struct platform_device *pdev)
if (!host)
return -ENOMEM;
- ahb_clk = devm_clk_get(&pdev->dev, "ahb");
+ ahb_clk = devm_clk_get_enabled(&pdev->dev, "ahb");
if (IS_ERR(ahb_clk))
return PTR_ERR(ahb_clk);
@@ -163,7 +172,6 @@ static int rb4xx_spi_probe(struct platform_device *pdev)
rbspi = spi_controller_get_devdata(host);
rbspi->base = spi_base;
rbspi->clk = ahb_clk;
- platform_set_drvdata(pdev, rbspi);
err = devm_spi_register_controller(&pdev->dev, host);
if (err) {
@@ -171,23 +179,12 @@ static int rb4xx_spi_probe(struct platform_device *pdev)
return err;
}
- err = clk_prepare_enable(ahb_clk);
- if (err)
- return err;
-
/* Enable SPI */
rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
return 0;
}
-static void rb4xx_spi_remove(struct platform_device *pdev)
-{
- struct rb4xx_spi *rbspi = platform_get_drvdata(pdev);
-
- clk_disable_unprepare(rbspi->clk);
-}
-
static const struct of_device_id rb4xx_spi_dt_match[] = {
{ .compatible = "mikrotik,rb4xx-spi" },
{ },
@@ -196,10 +193,9 @@ MODULE_DEVICE_TABLE(of, rb4xx_spi_dt_match);
static struct platform_driver rb4xx_spi_drv = {
.probe = rb4xx_spi_probe,
- .remove = rb4xx_spi_remove,
.driver = {
.name = "rb4xx-spi",
- .of_match_table = of_match_ptr(rb4xx_spi_dt_match),
+ .of_match_table = rb4xx_spi_dt_match,
},
};
diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c
index f3fe10eddb6a..9eba5c0a60f2 100644
--- a/drivers/spi/spi-rockchip-sfc.c
+++ b/drivers/spi/spi-rockchip-sfc.c
@@ -565,7 +565,6 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op
ret = rockchip_sfc_xfer_done(sfc, 100000);
out:
- pm_runtime_mark_last_busy(sfc->dev);
pm_runtime_put_autosuspend(sfc->dev);
return ret;
@@ -712,7 +711,6 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
if (ret)
goto err_register;
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
@@ -799,7 +797,6 @@ static int rockchip_sfc_resume(struct device *dev)
rockchip_sfc_init(sfc);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c
index 627cffea5d5c..6edc0c4db854 100644
--- a/drivers/spi/spi-rpc-if.c
+++ b/drivers/spi/spi-rpc-if.c
@@ -196,21 +196,23 @@ static void rpcif_spi_remove(struct platform_device *pdev)
pm_runtime_disable(rpc->dev);
}
-static int __maybe_unused rpcif_spi_suspend(struct device *dev)
+static int rpcif_spi_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
return spi_controller_suspend(ctlr);
}
-static int __maybe_unused rpcif_spi_resume(struct device *dev)
+static int rpcif_spi_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
+ rpcif_hw_init(dev, false);
+
return spi_controller_resume(ctlr);
}
-static SIMPLE_DEV_PM_OPS(rpcif_spi_pm_ops, rpcif_spi_suspend, rpcif_spi_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(rpcif_spi_pm_ops, rpcif_spi_suspend, rpcif_spi_resume);
static const struct platform_device_id rpc_if_spi_id_table[] = {
{ .name = "rpc-if-spi" },
@@ -224,9 +226,7 @@ static struct platform_driver rpcif_spi_driver = {
.id_table = rpc_if_spi_id_table,
.driver = {
.name = "rpc-if-spi",
-#ifdef CONFIG_PM_SLEEP
- .pm = &rpcif_spi_pm_ops,
-#endif
+ .pm = pm_sleep_ptr(&rpcif_spi_pm_ops),
},
};
module_platform_driver(rpcif_spi_driver);
diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c
index 92faaf614f8e..8e1d911b88b5 100644
--- a/drivers/spi/spi-rspi.c
+++ b/drivers/spi/spi-rspi.c
@@ -1404,7 +1404,6 @@ static const struct platform_device_id spi_driver_ids[] = {
MODULE_DEVICE_TABLE(platform, spi_driver_ids);
-#ifdef CONFIG_PM_SLEEP
static int rspi_suspend(struct device *dev)
{
struct rspi_data *rspi = dev_get_drvdata(dev);
@@ -1419,11 +1418,7 @@ static int rspi_resume(struct device *dev)
return spi_controller_resume(rspi->ctlr);
}
-static SIMPLE_DEV_PM_OPS(rspi_pm_ops, rspi_suspend, rspi_resume);
-#define DEV_PM_OPS &rspi_pm_ops
-#else
-#define DEV_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(rspi_pm_ops, rspi_suspend, rspi_resume);
static struct platform_driver rspi_driver = {
.probe = rspi_probe,
@@ -1431,7 +1426,7 @@ static struct platform_driver rspi_driver = {
.id_table = spi_driver_ids,
.driver = {
.name = "renesas_spi",
- .pm = DEV_PM_OPS,
+ .pm = pm_sleep_ptr(&rspi_pm_ops),
.of_match_table = of_match_ptr(rspi_of_match),
},
};
diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c
new file mode 100644
index 000000000000..dcc431ba60a9
--- /dev/null
+++ b/drivers/spi/spi-rzv2h-rspi.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Renesas RZ/V2H Renesas Serial Peripheral Interface (RSPI)
+ *
+ * Copyright (C) 2025 Renesas Electronics Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/limits.h>
+#include <linux/log2.h>
+#include <linux/math.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/wait.h>
+
+/* Registers */
+#define RSPI_SPDR 0x00
+#define RSPI_SPCR 0x08
+#define RSPI_SSLP 0x10
+#define RSPI_SPBR 0x11
+#define RSPI_SPSCR 0x13
+#define RSPI_SPCMD 0x14
+#define RSPI_SPDCR2 0x44
+#define RSPI_SPSR 0x52
+#define RSPI_SPSRC 0x6a
+#define RSPI_SPFCR 0x6c
+
+/* Register SPCR */
+#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 SPBR */
+#define RSPI_SPBR_SPR_MIN 0
+#define RSPI_SPBR_SPR_MAX 255
+
+/* Register SPCMD */
+#define RSPI_SPCMD_SSLA GENMASK(25, 24)
+#define RSPI_SPCMD_SPB GENMASK(20, 16)
+#define RSPI_SPCMD_LSBF BIT(12)
+#define RSPI_SPCMD_SSLKP BIT(7)
+#define RSPI_SPCMD_BRDV GENMASK(3, 2)
+#define RSPI_SPCMD_CPOL BIT(1)
+#define RSPI_SPCMD_CPHA BIT(0)
+
+#define RSPI_SPCMD_BRDV_MIN 0
+#define RSPI_SPCMD_BRDV_MAX 3
+
+/* 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)
+
+/* Register RSPI_SPSRC */
+#define RSPI_SPSRC_CLEAR 0xfd80
+
+#define RSPI_RESET_NUM 2
+#define RSPI_CLK_NUM 3
+
+struct rzv2h_rspi_priv {
+ struct reset_control_bulk_data resets[RSPI_RESET_NUM];
+ struct spi_controller *controller;
+ void __iomem *base;
+ struct clk *tclk;
+ wait_queue_head_t wait;
+ unsigned int bytes_per_word;
+ u32 freq;
+ u16 status;
+};
+
+#define RZV2H_RSPI_TX(func, type) \
+static inline void rzv2h_rspi_tx_##type(struct rzv2h_rspi_priv *rspi, \
+ const void *txbuf, \
+ unsigned int index) { \
+ type buf = 0; \
+ \
+ if (txbuf) \
+ buf = ((type *)txbuf)[index]; \
+ \
+ func(buf, rspi->base + RSPI_SPDR); \
+}
+
+#define RZV2H_RSPI_RX(func, type) \
+static inline void rzv2h_rspi_rx_##type(struct rzv2h_rspi_priv *rspi, \
+ void *rxbuf, \
+ unsigned int index) { \
+ type buf = func(rspi->base + RSPI_SPDR); \
+ \
+ if (rxbuf) \
+ ((type *)rxbuf)[index] = buf; \
+}
+
+RZV2H_RSPI_TX(writel, u32)
+RZV2H_RSPI_TX(writew, u16)
+RZV2H_RSPI_TX(writeb, u8)
+RZV2H_RSPI_RX(readl, u32)
+RZV2H_RSPI_RX(readw, u16)
+RZV2H_RSPI_RX(readl, u8)
+
+static void rzv2h_rspi_reg_rmw(const struct rzv2h_rspi_priv *rspi,
+ int reg_offs, u32 bit_mask, u32 value)
+{
+ u32 tmp;
+
+ value <<= __ffs(bit_mask);
+ tmp = (readl(rspi->base + reg_offs) & ~bit_mask) | value;
+ writel(tmp, rspi->base + reg_offs);
+}
+
+static inline void rzv2h_rspi_spe_disable(const struct rzv2h_rspi_priv *rspi)
+{
+ rzv2h_rspi_reg_rmw(rspi, RSPI_SPCR, RSPI_SPCR_SPE, 0);
+}
+
+static inline void rzv2h_rspi_spe_enable(const struct rzv2h_rspi_priv *rspi)
+{
+ rzv2h_rspi_reg_rmw(rspi, RSPI_SPCR, RSPI_SPCR_SPE, 1);
+}
+
+static inline void rzv2h_rspi_clear_fifos(const struct rzv2h_rspi_priv *rspi)
+{
+ writeb(1, rspi->base + RSPI_SPFCR);
+}
+
+static inline void rzv2h_rspi_clear_all_irqs(struct rzv2h_rspi_priv *rspi)
+{
+ writew(RSPI_SPSRC_CLEAR, rspi->base + RSPI_SPSRC);
+ rspi->status = 0;
+}
+
+static irqreturn_t rzv2h_rx_irq_handler(int irq, void *data)
+{
+ struct rzv2h_rspi_priv *rspi = data;
+
+ rspi->status = readw(rspi->base + RSPI_SPSR);
+ wake_up(&rspi->wait);
+
+ return IRQ_HANDLED;
+}
+
+static inline int rzv2h_rspi_wait_for_interrupt(struct rzv2h_rspi_priv *rspi,
+ u32 wait_mask)
+{
+ return wait_event_timeout(rspi->wait, (rspi->status & wait_mask),
+ HZ) == 0 ? -ETIMEDOUT : 0;
+}
+
+static void rzv2h_rspi_send(struct rzv2h_rspi_priv *rspi, const void *txbuf,
+ unsigned int index)
+{
+ switch (rspi->bytes_per_word) {
+ case 4:
+ rzv2h_rspi_tx_u32(rspi, txbuf, index);
+ break;
+ case 2:
+ rzv2h_rspi_tx_u16(rspi, txbuf, index);
+ break;
+ default:
+ rzv2h_rspi_tx_u8(rspi, txbuf, index);
+ }
+}
+
+static int rzv2h_rspi_receive(struct rzv2h_rspi_priv *rspi, void *rxbuf,
+ unsigned int index)
+{
+ int ret;
+
+ ret = rzv2h_rspi_wait_for_interrupt(rspi, RSPI_SPSR_SPRF);
+ if (ret)
+ return ret;
+
+ switch (rspi->bytes_per_word) {
+ case 4:
+ rzv2h_rspi_rx_u32(rspi, rxbuf, index);
+ break;
+ case 2:
+ rzv2h_rspi_rx_u16(rspi, rxbuf, index);
+ break;
+ default:
+ rzv2h_rspi_rx_u8(rspi, rxbuf, index);
+ }
+
+ return 0;
+}
+
+static int rzv2h_rspi_transfer_one(struct spi_controller *controller,
+ struct spi_device *spi,
+ struct spi_transfer *transfer)
+{
+ struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(controller);
+ unsigned int words_to_transfer, i;
+ int ret = 0;
+
+ transfer->effective_speed_hz = rspi->freq;
+ words_to_transfer = transfer->len / rspi->bytes_per_word;
+
+ for (i = 0; i < words_to_transfer; i++) {
+ rzv2h_rspi_clear_all_irqs(rspi);
+
+ rzv2h_rspi_send(rspi, transfer->tx_buf, i);
+
+ ret = rzv2h_rspi_receive(rspi, transfer->rx_buf, i);
+ if (ret)
+ break;
+ }
+
+ rzv2h_rspi_clear_all_irqs(rspi);
+
+ if (ret)
+ transfer->error = SPI_TRANS_FAIL_IO;
+
+ spi_finalize_current_transfer(controller);
+
+ return ret;
+}
+
+static inline u32 rzv2h_rspi_calc_bitrate(unsigned long tclk_rate, u8 spr,
+ u8 brdv)
+{
+ return DIV_ROUND_UP(tclk_rate, (2 * (spr + 1) * (1 << brdv)));
+}
+
+static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz)
+{
+ unsigned long tclk_rate;
+ int spr;
+ u8 brdv;
+
+ /*
+ * From the manual:
+ * Bit rate = f(RSPI_n_TCLK)/(2*(n+1)*2^(N))
+ *
+ * Where:
+ * * RSPI_n_TCLK is fixed to 200MHz on V2H
+ * * 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);
+ for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) {
+ spr = DIV_ROUND_UP(tclk_rate, hz * (1 << (brdv + 1)));
+ spr--;
+ if (spr >= RSPI_SPBR_SPR_MIN && spr <= RSPI_SPBR_SPR_MAX)
+ goto clock_found;
+ }
+
+ return 0;
+
+clock_found:
+ rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_BRDV, brdv);
+ writeb(spr, rspi->base + RSPI_SPBR);
+
+ return rzv2h_rspi_calc_bitrate(tclk_rate, spr, brdv);
+}
+
+static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr,
+ struct spi_message *message)
+{
+ struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(ctlr);
+ const struct spi_device *spi = message->spi;
+ struct spi_transfer *xfer;
+ u32 speed_hz = U32_MAX;
+ u8 bits_per_word;
+ u32 conf32;
+ u16 conf16;
+
+ /* Make sure SPCR.SPE is 0 before amending the configuration */
+ rzv2h_rspi_spe_disable(rspi);
+
+ /* Configure the device to work in "host" mode */
+ conf32 = RSPI_SPCR_MSTR;
+
+ /* Auto-stop function */
+ conf32 |= RSPI_SPCR_SCKASE;
+
+ /* SPI receive buffer full interrupt enable */
+ conf32 |= RSPI_SPCR_SPRIE;
+
+ writel(conf32, rspi->base + RSPI_SPCR);
+
+ /* Use SPCMD0 only */
+ writeb(0x0, rspi->base + RSPI_SPSCR);
+
+ /* 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_SSLKP, 1);
+ conf32 |= FIELD_PREP(RSPI_SPCMD_SSLA, spi_get_chipselect(spi, 0));
+ writel(conf32, rspi->base + RSPI_SPCMD);
+ if (spi->mode & SPI_CS_HIGH)
+ writeb(BIT(spi_get_chipselect(spi, 0)), rspi->base + RSPI_SSLP);
+ else
+ writeb(0, rspi->base + RSPI_SSLP);
+
+ /* Setup FIFO thresholds */
+ conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, RSPI_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;
+}
+
+static int rzv2h_rspi_unprepare_message(struct spi_controller *ctlr,
+ struct spi_message *message)
+{
+ struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(ctlr);
+
+ rzv2h_rspi_spe_disable(rspi);
+
+ return 0;
+}
+
+static int rzv2h_rspi_probe(struct platform_device *pdev)
+{
+ struct spi_controller *controller;
+ struct device *dev = &pdev->dev;
+ struct rzv2h_rspi_priv *rspi;
+ struct clk_bulk_data *clks;
+ unsigned long tclk_rate;
+ int irq_rx, ret, i;
+
+ controller = devm_spi_alloc_host(dev, sizeof(*rspi));
+ if (!controller)
+ return -ENOMEM;
+
+ rspi = spi_controller_get_devdata(controller);
+ platform_set_drvdata(pdev, rspi);
+
+ rspi->controller = controller;
+
+ 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)
+ 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")) {
+ rspi->tclk = clks[i].clk;
+ break;
+ }
+ }
+
+ 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);
+ if (ret)
+ return dev_err_probe(dev, ret, "cannot get resets\n");
+
+ irq_rx = platform_get_irq_byname(pdev, "rx");
+ if (irq_rx < 0)
+ return dev_err_probe(dev, irq_rx, "cannot get IRQ 'rx'\n");
+
+ ret = reset_control_bulk_deassert(RSPI_RESET_NUM, rspi->resets);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to deassert resets\n");
+
+ init_waitqueue_head(&rspi->wait);
+
+ ret = devm_request_irq(dev, irq_rx, rzv2h_rx_irq_handler, 0,
+ dev_name(dev), rspi);
+ if (ret) {
+ dev_err(dev, "cannot request `rx` IRQ\n");
+ goto quit_resets;
+ }
+
+ controller->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH |
+ SPI_LSB_FIRST;
+ 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;
+ controller->min_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate,
+ RSPI_SPBR_SPR_MAX,
+ RSPI_SPCMD_BRDV_MAX);
+ controller->max_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate,
+ RSPI_SPBR_SPR_MIN,
+ RSPI_SPCMD_BRDV_MIN);
+
+ device_set_node(&controller->dev, dev_fwnode(dev));
+
+ ret = spi_register_controller(controller);
+ if (ret) {
+ dev_err(dev, "register controller failed\n");
+ goto quit_resets;
+ }
+
+ return 0;
+
+quit_resets:
+ reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets);
+
+ return ret;
+}
+
+static void rzv2h_rspi_remove(struct platform_device *pdev)
+{
+ struct rzv2h_rspi_priv *rspi = platform_get_drvdata(pdev);
+
+ spi_unregister_controller(rspi->controller);
+
+ reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets);
+}
+
+static const struct of_device_id rzv2h_rspi_match[] = {
+ { .compatible = "renesas,r9a09g057-rspi" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzv2h_rspi_match);
+
+static struct platform_driver rzv2h_rspi_drv = {
+ .probe = rzv2h_rspi_probe,
+ .remove = rzv2h_rspi_remove,
+ .driver = {
+ .name = "rzv2h_rspi",
+ .of_match_table = rzv2h_rspi_match,
+ },
+};
+module_platform_driver(rzv2h_rspi_drv);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Fabrizio Castro <fabrizio.castro.jz@renesas.com>");
+MODULE_DESCRIPTION("Renesas RZ/V2H(P) Serial Peripheral Interface Driver");
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 9c47f5741c5f..aab36c779c06 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -1045,14 +1045,12 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
}
}
- pm_runtime_mark_last_busy(&sdd->pdev->dev);
pm_runtime_put_autosuspend(&sdd->pdev->dev);
s3c64xx_spi_set_cs(spi, false);
return 0;
setup_exit:
- pm_runtime_mark_last_busy(&sdd->pdev->dev);
pm_runtime_put_autosuspend(&sdd->pdev->dev);
/* setup() returns with device de-selected */
s3c64xx_spi_set_cs(spi, false);
@@ -1270,8 +1268,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
host = devm_spi_alloc_host(&pdev->dev, sizeof(*sdd));
if (!host)
- return dev_err_probe(&pdev->dev, -ENOMEM,
- "Unable to allocate SPI Host\n");
+ return -ENOMEM;
platform_set_drvdata(pdev, host);
@@ -1384,7 +1381,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\n",
mem_res, sdd->fifo_depth);
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
@@ -1510,16 +1506,6 @@ static const struct dev_pm_ops s3c64xx_spi_pm = {
s3c64xx_spi_runtime_resume, NULL)
};
-static const struct s3c64xx_spi_port_config s3c2443_spi_port_config = {
- /* fifo_lvl_mask is deprecated. Use {rx, tx}_fifomask instead. */
- .fifo_lvl_mask = { 0x7f },
- /* rx_lvl_offset is deprecated. Use {rx, tx}_fifomask instead. */
- .rx_lvl_offset = 13,
- .tx_st_done = 21,
- .clk_div = 2,
- .high_speed = true,
-};
-
static const struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
/* fifo_lvl_mask is deprecated. Use {rx, tx}_fifomask instead. */
.fifo_lvl_mask = { 0x7f, 0x7F },
@@ -1631,9 +1617,6 @@ static const struct s3c64xx_spi_port_config gs101_spi_port_config = {
static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
{
- .name = "s3c2443-spi",
- .driver_data = (kernel_ulong_t)&s3c2443_spi_port_config,
- }, {
.name = "s3c6410-spi",
.driver_data = (kernel_ulong_t)&s3c6410_spi_port_config,
},
@@ -1645,9 +1628,6 @@ static const struct of_device_id s3c64xx_spi_dt_match[] = {
{ .compatible = "google,gs101-spi",
.data = &gs101_spi_port_config,
},
- { .compatible = "samsung,s3c2443-spi",
- .data = &s3c2443_spi_port_config,
- },
{ .compatible = "samsung,s3c6410-spi",
.data = &s3c6410_spi_port_config,
},
diff --git a/drivers/spi/spi-sg2044-nor.c b/drivers/spi/spi-sg2044-nor.c
index a59aa3fc55d2..af48b1fcda93 100644
--- a/drivers/spi/spi-sg2044-nor.c
+++ b/drivers/spi/spi-sg2044-nor.c
@@ -84,12 +84,18 @@
#define SPIFMC_MAX_READ_SIZE 0x10000
+struct sg204x_spifmc_chip_info {
+ bool has_opt_reg;
+ u32 rd_fifo_int_trigger_level;
+};
+
struct sg2044_spifmc {
struct spi_controller *ctrl;
void __iomem *io_base;
struct device *dev;
struct mutex lock;
struct clk *clk;
+ const struct sg204x_spifmc_chip_info *chip_info;
};
static int sg2044_spifmc_wait_int(struct sg2044_spifmc *spifmc, u8 int_type)
@@ -139,7 +145,7 @@ static ssize_t sg2044_spifmc_read_64k(struct sg2044_spifmc *spifmc,
reg = sg2044_spifmc_init_reg(spifmc);
reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT;
- reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE;
+ reg |= spifmc->chip_info->rd_fifo_int_trigger_level;
reg |= SPIFMC_TRAN_CSR_WITH_CMD;
reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX;
@@ -335,7 +341,8 @@ static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc,
reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX;
reg |= SPIFMC_TRAN_CSR_TRAN_MODE_TX;
- writel(SPIFMC_OPT_DISABLE_FIFO_FLUSH, spifmc->io_base + SPIFMC_OPT);
+ if (spifmc->chip_info->has_opt_reg)
+ writel(SPIFMC_OPT_DISABLE_FIFO_FLUSH, spifmc->io_base + SPIFMC_OPT);
} else {
/*
* If write values to the Status Register,
@@ -457,6 +464,11 @@ static int sg2044_spifmc_probe(struct platform_device *pdev)
ret = devm_mutex_init(dev, &spifmc->lock);
if (ret)
return ret;
+ spifmc->chip_info = device_get_match_data(&pdev->dev);
+ if (!spifmc->chip_info) {
+ dev_err(&pdev->dev, "Failed to get specific chip info\n");
+ return -EINVAL;
+ }
sg2044_spifmc_init(spifmc);
sg2044_spifmc_init_reg(spifmc);
@@ -468,8 +480,19 @@ static int sg2044_spifmc_probe(struct platform_device *pdev)
return 0;
}
+static const struct sg204x_spifmc_chip_info sg2044_chip_info = {
+ .has_opt_reg = true,
+ .rd_fifo_int_trigger_level = SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE,
+};
+
+static const struct sg204x_spifmc_chip_info sg2042_chip_info = {
+ .has_opt_reg = false,
+ .rd_fifo_int_trigger_level = SPIFMC_TRAN_CSR_FIFO_TRG_LVL_1_BYTE,
+};
+
static const struct of_device_id sg2044_spifmc_match[] = {
- { .compatible = "sophgo,sg2044-spifmc-nor" },
+ { .compatible = "sophgo,sg2044-spifmc-nor", .data = &sg2044_chip_info },
+ { .compatible = "sophgo,sg2042-spifmc-nor", .data = &sg2042_chip_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sg2044_spifmc_match);
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 94a867967e02..b695870fae8c 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -1320,7 +1320,6 @@ static const struct platform_device_id spi_driver_ids[] = {
};
MODULE_DEVICE_TABLE(platform, spi_driver_ids);
-#ifdef CONFIG_PM_SLEEP
static int sh_msiof_spi_suspend(struct device *dev)
{
struct sh_msiof_spi_priv *p = dev_get_drvdata(dev);
@@ -1335,12 +1334,8 @@ static int sh_msiof_spi_resume(struct device *dev)
return spi_controller_resume(p->ctlr);
}
-static SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend,
- sh_msiof_spi_resume);
-#define DEV_PM_OPS (&sh_msiof_spi_pm_ops)
-#else
-#define DEV_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend,
+ sh_msiof_spi_resume);
static struct platform_driver sh_msiof_spi_drv = {
.probe = sh_msiof_spi_probe,
@@ -1348,7 +1343,7 @@ static struct platform_driver sh_msiof_spi_drv = {
.id_table = spi_driver_ids,
.driver = {
.name = "spi_sh_msiof",
- .pm = DEV_PM_OPS,
+ .pm = pm_sleep_ptr(&sh_msiof_spi_pm_ops),
.of_match_table = of_match_ptr(sh_msiof_match),
},
};
diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c
index ae794058b381..ad75f5f0f2bf 100644
--- a/drivers/spi/spi-sprd.c
+++ b/drivers/spi/spi-sprd.c
@@ -982,7 +982,6 @@ static int sprd_spi_probe(struct platform_device *pdev)
if (ret)
goto err_rpm_put;
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c
index 4cff976ab16f..c07c61dc4938 100644
--- a/drivers/spi/spi-st-ssc4.c
+++ b/drivers/spi/spi-st-ssc4.c
@@ -378,7 +378,6 @@ static void spi_st_remove(struct platform_device *pdev)
pinctrl_pm_select_sleep_state(&pdev->dev);
}
-#ifdef CONFIG_PM
static int spi_st_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -403,10 +402,8 @@ static int spi_st_runtime_resume(struct device *dev)
return ret;
}
-#endif
-#ifdef CONFIG_PM_SLEEP
-static int spi_st_suspend(struct device *dev)
+static int __maybe_unused spi_st_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
int ret;
@@ -418,7 +415,7 @@ static int spi_st_suspend(struct device *dev)
return pm_runtime_force_suspend(dev);
}
-static int spi_st_resume(struct device *dev)
+static int __maybe_unused spi_st_resume(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
int ret;
@@ -429,11 +426,10 @@ static int spi_st_resume(struct device *dev)
return pm_runtime_force_resume(dev);
}
-#endif
static const struct dev_pm_ops spi_st_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume)
- SET_RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume)
+ RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL)
};
static const struct of_device_id stm_spi_match[] = {
@@ -445,7 +441,7 @@ MODULE_DEVICE_TABLE(of, stm_spi_match);
static struct platform_driver spi_st_driver = {
.driver = {
.name = "spi-st",
- .pm = &spi_st_pm,
+ .pm = pm_ptr(&spi_st_pm),
.of_match_table = of_match_ptr(stm_spi_match),
},
.probe = spi_st_probe,
diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c
index 4ab7e86f4bd5..f36fd36da269 100644
--- a/drivers/spi/spi-stm32-ospi.c
+++ b/drivers/spi/spi-stm32-ospi.c
@@ -547,7 +547,6 @@ static int stm32_ospi_poll_status(struct spi_mem *mem,
ret = stm32_ospi_send(mem->spi, op);
mutex_unlock(&ospi->lock);
- pm_runtime_mark_last_busy(ospi->dev);
pm_runtime_put_autosuspend(ospi->dev);
return ret;
@@ -571,7 +570,6 @@ static int stm32_ospi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
ret = stm32_ospi_send(mem->spi, op);
mutex_unlock(&ospi->lock);
- pm_runtime_mark_last_busy(ospi->dev);
pm_runtime_put_autosuspend(ospi->dev);
return ret;
@@ -628,7 +626,6 @@ static ssize_t stm32_ospi_dirmap_read(struct spi_mem_dirmap_desc *desc,
ret = stm32_ospi_send(desc->mem->spi, &op);
mutex_unlock(&ospi->lock);
- pm_runtime_mark_last_busy(ospi->dev);
pm_runtime_put_autosuspend(ospi->dev);
return ret ?: len;
@@ -713,7 +710,6 @@ end_of_transfer:
msg->status = ret;
spi_finalize_current_message(ctrl);
- pm_runtime_mark_last_busy(ospi->dev);
pm_runtime_put_autosuspend(ospi->dev);
return ret;
@@ -750,7 +746,6 @@ static int stm32_ospi_setup(struct spi_device *spi)
mutex_unlock(&ospi->lock);
- pm_runtime_mark_last_busy(ospi->dev);
pm_runtime_put_autosuspend(ospi->dev);
return 0;
@@ -771,9 +766,7 @@ static int stm32_ospi_get_resources(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct stm32_ospi *ospi = platform_get_drvdata(pdev);
- struct resource *res;
- struct reserved_mem *rmem = NULL;
- struct device_node *node;
+ struct resource *res, _res;
int ret;
ospi->regs_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
@@ -825,18 +818,14 @@ static int stm32_ospi_get_resources(struct platform_device *pdev)
goto err_dma;
}
- node = of_parse_phandle(dev->of_node, "memory-region", 0);
- if (node)
- rmem = of_reserved_mem_lookup(node);
- of_node_put(node);
-
- if (rmem) {
- ospi->mm_size = rmem->size;
- ospi->mm_base = devm_ioremap(dev, rmem->base, rmem->size);
- if (!ospi->mm_base) {
- dev_err(dev, "unable to map memory region: %pa+%pa\n",
- &rmem->base, &rmem->size);
- ret = -ENOMEM;
+ res = &_res;
+ ret = of_reserved_mem_region_to_resource(dev->of_node, 0, res);
+ if (!ret) {
+ ospi->mm_size = resource_size(res);
+ ospi->mm_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ospi->mm_base)) {
+ dev_err(dev, "unable to map memory region: %pR\n", res);
+ ret = PTR_ERR(ospi->mm_base);
goto err_dma;
}
@@ -953,7 +942,6 @@ static int stm32_ospi_probe(struct platform_device *pdev)
goto err_pm_resume;
}
- pm_runtime_mark_last_busy(ospi->dev);
pm_runtime_put_autosuspend(ospi->dev);
return 0;
@@ -1032,7 +1020,6 @@ static int __maybe_unused stm32_ospi_resume(struct device *dev)
writel_relaxed(ospi->cr_reg, regs_base + OSPI_CR);
writel_relaxed(ospi->dcr_reg, regs_base + OSPI_DCR1);
- pm_runtime_mark_last_busy(ospi->dev);
pm_runtime_put_autosuspend(ospi->dev);
return 0;
diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c
index 9691197bbf5a..f2d19f1c5ab1 100644
--- a/drivers/spi/spi-stm32-qspi.c
+++ b/drivers/spi/spi-stm32-qspi.c
@@ -463,7 +463,6 @@ static int stm32_qspi_poll_status(struct spi_mem *mem, const struct spi_mem_op *
ret = stm32_qspi_send(mem->spi, op);
mutex_unlock(&qspi->lock);
- pm_runtime_mark_last_busy(qspi->dev);
pm_runtime_put_autosuspend(qspi->dev);
return ret;
@@ -487,7 +486,6 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
ret = stm32_qspi_send(mem->spi, op);
mutex_unlock(&qspi->lock);
- pm_runtime_mark_last_busy(qspi->dev);
pm_runtime_put_autosuspend(qspi->dev);
return ret;
@@ -543,7 +541,6 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc,
ret = stm32_qspi_send(desc->mem->spi, &op);
mutex_unlock(&qspi->lock);
- pm_runtime_mark_last_busy(qspi->dev);
pm_runtime_put_autosuspend(qspi->dev);
return ret ?: len;
@@ -627,7 +624,6 @@ end_of_transfer:
msg->status = ret;
spi_finalize_current_message(ctrl);
- pm_runtime_mark_last_busy(qspi->dev);
pm_runtime_put_autosuspend(qspi->dev);
return ret;
@@ -684,7 +680,6 @@ static int stm32_qspi_setup(struct spi_device *spi)
writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR);
mutex_unlock(&qspi->lock);
- pm_runtime_mark_last_busy(qspi->dev);
pm_runtime_put_autosuspend(qspi->dev);
return 0;
@@ -858,7 +853,6 @@ static int stm32_qspi_probe(struct platform_device *pdev)
if (ret)
goto err_pm_runtime_free;
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
@@ -938,7 +932,6 @@ static int __maybe_unused stm32_qspi_resume(struct device *dev)
writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR);
writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c
index da3517d7102d..2c804c1aef98 100644
--- a/drivers/spi/spi-stm32.c
+++ b/drivers/spi/spi-stm32.c
@@ -9,7 +9,9 @@
#include <linux/debugfs.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
+#include <linux/genalloc.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/module.h>
@@ -154,6 +156,9 @@
/* STM32H7_SPI_I2SCFGR bit fields */
#define STM32H7_SPI_I2SCFGR_I2SMOD BIT(0)
+/* STM32MP25_SPICFG2 bit fields */
+#define STM32MP25_SPI_CFG2_RDIOM BIT(13)
+
/* STM32MP25 SPI registers bit fields */
#define STM32MP25_SPI_HWCFGR1 0x3F0
@@ -222,6 +227,7 @@ struct stm32_spi_reg {
* @rx: SPI RX data register
* @tx: SPI TX data register
* @fullcfg: SPI full or limited feature set register
+ * @rdy_en: SPI ready feature register
*/
struct stm32_spi_regspec {
const struct stm32_spi_reg en;
@@ -235,6 +241,7 @@ struct stm32_spi_regspec {
const struct stm32_spi_reg rx;
const struct stm32_spi_reg tx;
const struct stm32_spi_reg fullcfg;
+ const struct stm32_spi_reg rdy_en;
};
struct stm32_spi;
@@ -276,7 +283,7 @@ struct stm32_spi_cfg {
int (*config)(struct stm32_spi *spi);
void (*set_bpw)(struct stm32_spi *spi);
int (*set_mode)(struct stm32_spi *spi, unsigned int comm_type);
- void (*set_data_idleness)(struct stm32_spi *spi, u32 length);
+ void (*set_data_idleness)(struct stm32_spi *spi, struct spi_transfer *xfer);
int (*set_number_of_data)(struct stm32_spi *spi, u32 length);
void (*write_tx)(struct stm32_spi *spi);
void (*read_rx)(struct stm32_spi *spi);
@@ -323,6 +330,11 @@ struct stm32_spi_cfg {
* @dma_rx: dma channel for RX transfer
* @phys_addr: SPI registers physical base address
* @device_mode: the controller is configured as SPI device
+ * @sram_pool: SRAM pool for DMA transfers
+ * @sram_rx_buf_size: size of SRAM buffer for RX transfer
+ * @sram_rx_buf: SRAM buffer for RX transfer
+ * @sram_dma_rx_buf: SRAM buffer physical address for RX transfer
+ * @mdma_rx: MDMA channel for RX transfer
*/
struct stm32_spi {
struct device *dev;
@@ -357,6 +369,12 @@ struct stm32_spi {
dma_addr_t phys_addr;
bool device_mode;
+
+ struct gen_pool *sram_pool;
+ size_t sram_rx_buf_size;
+ void *sram_rx_buf;
+ dma_addr_t sram_dma_rx_buf;
+ struct dma_chan *mdma_rx;
};
static const struct stm32_spi_regspec stm32fx_spi_regspec = {
@@ -415,6 +433,8 @@ static const struct stm32_spi_regspec stm32mp25_spi_regspec = {
.tx = { STM32H7_SPI_TXDR },
.fullcfg = { STM32MP25_SPI_HWCFGR1, STM32MP25_SPI_HWCFGR1_FULLCFG },
+
+ .rdy_en = { STM32H7_SPI_CFG2, STM32MP25_SPI_CFG2_RDIOM },
};
static inline void stm32_spi_set_bits(struct stm32_spi *spi,
@@ -878,8 +898,11 @@ static void stm32h7_spi_disable(struct stm32_spi *spi)
if (spi->cur_usedma && spi->dma_tx)
dmaengine_terminate_async(spi->dma_tx);
- if (spi->cur_usedma && spi->dma_rx)
+ if (spi->cur_usedma && spi->dma_rx) {
dmaengine_terminate_async(spi->dma_rx);
+ if (spi->mdma_rx)
+ dmaengine_terminate_async(spi->mdma_rx);
+ }
stm32_spi_clr_bits(spi, STM32H7_SPI_CR1, STM32H7_SPI_CR1_SPE);
@@ -1091,10 +1114,13 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
}
if (sr & STM32H7_SPI_SR_EOT) {
+ dev_dbg(spi->dev, "End of transfer\n");
if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0)))
stm32h7_spi_read_rxfifo(spi);
if (!spi->cur_usedma ||
- (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX))
+ (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) ||
+ (spi->mdma_rx && (spi->cur_comm == SPI_SIMPLEX_RX ||
+ spi->cur_comm == SPI_FULL_DUPLEX)))
end = true;
}
@@ -1111,6 +1137,11 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
spin_unlock_irqrestore(&spi->lock, flags);
if (end) {
+ if (spi->cur_usedma && spi->mdma_rx) {
+ dmaengine_pause(spi->dma_rx);
+ /* Wait for callback */
+ return IRQ_HANDLED;
+ }
stm32h7_spi_disable(spi);
spi_finalize_current_transfer(ctrl);
}
@@ -1172,15 +1203,21 @@ static int stm32_spi_prepare_msg(struct spi_controller *ctrl,
else
clrb |= spi->cfg->regs->cs_high.mask;
- dev_dbg(spi->dev, "cpol=%d cpha=%d lsb_first=%d cs_high=%d\n",
+ if (spi_dev->mode & SPI_READY)
+ setb |= spi->cfg->regs->rdy_en.mask;
+ else
+ clrb |= spi->cfg->regs->rdy_en.mask;
+
+ dev_dbg(spi->dev, "cpol=%d cpha=%d lsb_first=%d cs_high=%d rdy=%d\n",
!!(spi_dev->mode & SPI_CPOL),
!!(spi_dev->mode & SPI_CPHA),
!!(spi_dev->mode & SPI_LSB_FIRST),
- !!(spi_dev->mode & SPI_CS_HIGH));
+ !!(spi_dev->mode & SPI_CS_HIGH),
+ !!(spi_dev->mode & SPI_READY));
spin_lock_irqsave(&spi->lock, flags);
- /* CPOL, CPHA and LSB FIRST bits have common register */
+ /* CPOL, CPHA, LSB FIRST, CS_HIGH and RDY_EN bits have common register */
if (clrb || setb)
writel_relaxed(
(readl_relaxed(spi->base + spi->cfg->regs->cpol.reg) &
@@ -1410,6 +1447,8 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
/* Enable the interrupts */
if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX)
ier |= STM32H7_SPI_IER_EOTIE | STM32H7_SPI_IER_TXTFIE;
+ if (spi->mdma_rx && (spi->cur_comm == SPI_SIMPLEX_RX || spi->cur_comm == SPI_FULL_DUPLEX))
+ ier |= STM32H7_SPI_IER_EOTIE;
stm32_spi_set_bits(spi, STM32H7_SPI_IER, ier);
@@ -1420,6 +1459,121 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
}
/**
+ * stm32_spi_prepare_rx_dma_mdma_chaining - Prepare RX DMA and MDMA chaining
+ * @spi: pointer to the spi controller data structure
+ * @xfer: pointer to the spi transfer
+ * @rx_dma_conf: pointer to the DMA configuration for RX channel
+ * @rx_dma_desc: pointer to the RX DMA descriptor
+ * @rx_mdma_desc: pointer to the RX MDMA descriptor
+ *
+ * It must return 0 if the chaining is possible or an error code if not.
+ */
+static int stm32_spi_prepare_rx_dma_mdma_chaining(struct stm32_spi *spi,
+ struct spi_transfer *xfer,
+ struct dma_slave_config *rx_dma_conf,
+ struct dma_async_tx_descriptor **rx_dma_desc,
+ struct dma_async_tx_descriptor **rx_mdma_desc)
+{
+ struct dma_async_tx_descriptor *_mdma_desc = *rx_mdma_desc;
+ struct dma_async_tx_descriptor *_dma_desc = *rx_dma_desc;
+ struct dma_slave_config rx_mdma_conf = {0};
+ u32 sram_period, nents = 0, spi_s_len;
+ struct sg_table dma_sgt, mdma_sgt;
+ struct scatterlist *spi_s, *s;
+ dma_addr_t dma_buf;
+ int i, ret;
+
+ sram_period = spi->sram_rx_buf_size / 2;
+
+ /* Configure MDMA RX channel */
+ rx_mdma_conf.direction = rx_dma_conf->direction;
+ rx_mdma_conf.src_addr = spi->sram_dma_rx_buf;
+ rx_mdma_conf.peripheral_config = rx_dma_conf->peripheral_config;
+ rx_mdma_conf.peripheral_size = rx_dma_conf->peripheral_size;
+ dmaengine_slave_config(spi->mdma_rx, &rx_mdma_conf);
+
+ /* Count the number of entries needed */
+ for_each_sg(xfer->rx_sg.sgl, spi_s, xfer->rx_sg.nents, i)
+ if (sg_dma_len(spi_s) > sram_period)
+ nents += DIV_ROUND_UP(sg_dma_len(spi_s), sram_period);
+ else
+ nents++;
+
+ /* Prepare DMA slave_sg DBM transfer DEV_TO_MEM (RX>MEM=SRAM) */
+ ret = sg_alloc_table(&dma_sgt, nents, GFP_ATOMIC);
+ if (ret)
+ return ret;
+
+ spi_s = xfer->rx_sg.sgl;
+ spi_s_len = sg_dma_len(spi_s);
+ dma_buf = spi->sram_dma_rx_buf;
+ for_each_sg(dma_sgt.sgl, s, dma_sgt.nents, i) {
+ size_t bytes = min_t(size_t, spi_s_len, sram_period);
+
+ sg_dma_len(s) = bytes;
+ sg_dma_address(s) = dma_buf;
+ spi_s_len -= bytes;
+
+ if (!spi_s_len && sg_next(spi_s)) {
+ spi_s = sg_next(spi_s);
+ spi_s_len = sg_dma_len(spi_s);
+ dma_buf = spi->sram_dma_rx_buf;
+ } else { /* DMA configured in DBM: it will swap between the SRAM periods */
+ if (i & 1)
+ dma_buf += sram_period;
+ else
+ dma_buf = spi->sram_dma_rx_buf;
+ }
+ }
+
+ _dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, dma_sgt.sgl,
+ dma_sgt.nents, rx_dma_conf->direction,
+ DMA_PREP_INTERRUPT);
+ sg_free_table(&dma_sgt);
+
+ if (!_dma_desc)
+ return -EINVAL;
+
+ /* Prepare MDMA slave_sg transfer MEM_TO_MEM (SRAM>DDR) */
+ ret = sg_alloc_table(&mdma_sgt, nents, GFP_ATOMIC);
+ if (ret) {
+ _dma_desc = NULL;
+ return ret;
+ }
+
+ spi_s = xfer->rx_sg.sgl;
+ spi_s_len = sg_dma_len(spi_s);
+ dma_buf = sg_dma_address(spi_s);
+ for_each_sg(mdma_sgt.sgl, s, mdma_sgt.nents, i) {
+ size_t bytes = min_t(size_t, spi_s_len, sram_period);
+
+ sg_dma_len(s) = bytes;
+ sg_dma_address(s) = dma_buf;
+ spi_s_len -= bytes;
+
+ if (!spi_s_len && sg_next(spi_s)) {
+ spi_s = sg_next(spi_s);
+ spi_s_len = sg_dma_len(spi_s);
+ dma_buf = sg_dma_address(spi_s);
+ } else {
+ dma_buf += bytes;
+ }
+ }
+
+ _mdma_desc = dmaengine_prep_slave_sg(spi->mdma_rx, mdma_sgt.sgl,
+ mdma_sgt.nents, rx_mdma_conf.direction,
+ DMA_PREP_INTERRUPT);
+ sg_free_table(&mdma_sgt);
+
+ if (!_mdma_desc) {
+ _dma_desc = NULL;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
* stm32_spi_transfer_one_dma - transfer a single spi_transfer using DMA
* @spi: pointer to the spi controller data structure
* @xfer: pointer to the spi_transfer structure
@@ -1430,38 +1584,43 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
static int stm32_spi_transfer_one_dma(struct stm32_spi *spi,
struct spi_transfer *xfer)
{
+ struct dma_async_tx_descriptor *rx_mdma_desc = NULL, *rx_dma_desc = NULL;
+ struct dma_async_tx_descriptor *tx_dma_desc = NULL;
struct dma_slave_config tx_dma_conf, rx_dma_conf;
- struct dma_async_tx_descriptor *tx_dma_desc, *rx_dma_desc;
unsigned long flags;
+ int ret = 0;
spin_lock_irqsave(&spi->lock, flags);
- rx_dma_desc = NULL;
if (spi->rx_buf && spi->dma_rx) {
stm32_spi_dma_config(spi, spi->dma_rx, &rx_dma_conf, DMA_DEV_TO_MEM);
- dmaengine_slave_config(spi->dma_rx, &rx_dma_conf);
-
- /* Enable Rx DMA request */
- stm32_spi_set_bits(spi, spi->cfg->regs->dma_rx_en.reg,
- spi->cfg->regs->dma_rx_en.mask);
-
- rx_dma_desc = dmaengine_prep_slave_sg(
- spi->dma_rx, xfer->rx_sg.sgl,
- xfer->rx_sg.nents,
- rx_dma_conf.direction,
- DMA_PREP_INTERRUPT);
+ if (spi->mdma_rx) {
+ rx_dma_conf.peripheral_size = 1;
+ dmaengine_slave_config(spi->dma_rx, &rx_dma_conf);
+
+ ret = stm32_spi_prepare_rx_dma_mdma_chaining(spi, xfer, &rx_dma_conf,
+ &rx_dma_desc, &rx_mdma_desc);
+ if (ret) { /* RX DMA MDMA chaining not possible, fallback to DMA only */
+ rx_dma_conf.peripheral_config = 0;
+ rx_dma_desc = NULL;
+ }
+ }
+ if (!rx_dma_desc) {
+ dmaengine_slave_config(spi->dma_rx, &rx_dma_conf);
+ rx_dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, xfer->rx_sg.sgl,
+ xfer->rx_sg.nents,
+ rx_dma_conf.direction,
+ DMA_PREP_INTERRUPT);
+ }
}
- tx_dma_desc = NULL;
if (spi->tx_buf && spi->dma_tx) {
stm32_spi_dma_config(spi, spi->dma_tx, &tx_dma_conf, DMA_MEM_TO_DEV);
dmaengine_slave_config(spi->dma_tx, &tx_dma_conf);
-
- tx_dma_desc = dmaengine_prep_slave_sg(
- spi->dma_tx, xfer->tx_sg.sgl,
- xfer->tx_sg.nents,
- tx_dma_conf.direction,
- DMA_PREP_INTERRUPT);
+ tx_dma_desc = dmaengine_prep_slave_sg(spi->dma_tx, xfer->tx_sg.sgl,
+ xfer->tx_sg.nents,
+ tx_dma_conf.direction,
+ DMA_PREP_INTERRUPT);
}
if ((spi->tx_buf && spi->dma_tx && !tx_dma_desc) ||
@@ -1472,9 +1631,25 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi,
goto dma_desc_error;
if (rx_dma_desc) {
- rx_dma_desc->callback = spi->cfg->dma_rx_cb;
- rx_dma_desc->callback_param = spi;
+ if (rx_mdma_desc) {
+ rx_mdma_desc->callback = spi->cfg->dma_rx_cb;
+ rx_mdma_desc->callback_param = spi;
+ } else {
+ rx_dma_desc->callback = spi->cfg->dma_rx_cb;
+ rx_dma_desc->callback_param = spi;
+ }
+ /* Enable Rx DMA request */
+ stm32_spi_set_bits(spi, spi->cfg->regs->dma_rx_en.reg,
+ spi->cfg->regs->dma_rx_en.mask);
+ if (rx_mdma_desc) {
+ if (dma_submit_error(dmaengine_submit(rx_mdma_desc))) {
+ dev_err(spi->dev, "Rx MDMA submit failed\n");
+ goto dma_desc_error;
+ }
+ /* Enable Rx MDMA channel */
+ dma_async_issue_pending(spi->mdma_rx);
+ }
if (dma_submit_error(dmaengine_submit(rx_dma_desc))) {
dev_err(spi->dev, "Rx DMA submit failed\n");
goto dma_desc_error;
@@ -1509,6 +1684,8 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi,
return 1;
dma_submit_error:
+ if (spi->mdma_rx)
+ dmaengine_terminate_sync(spi->mdma_rx);
if (spi->dma_rx)
dmaengine_terminate_sync(spi->dma_rx);
@@ -1520,6 +1697,9 @@ dma_desc_error:
dev_info(spi->dev, "DMA issue: fall back to irq transfer\n");
+ if (spi->sram_rx_buf)
+ memset(spi->sram_rx_buf, 0, spi->sram_rx_buf_size);
+
spi->cur_usedma = false;
return spi->cfg->transfer_one_irq(spi);
}
@@ -1702,11 +1882,26 @@ static int stm32h7_spi_set_mode(struct stm32_spi *spi, unsigned int comm_type)
* stm32h7_spi_data_idleness - configure minimum time delay inserted between two
* consecutive data frames in host mode
* @spi: pointer to the spi controller data structure
- * @len: transfer len
+ * @xfer: pointer to spi transfer
*/
-static void stm32h7_spi_data_idleness(struct stm32_spi *spi, u32 len)
+static void stm32h7_spi_data_idleness(struct stm32_spi *spi, struct spi_transfer *xfer)
{
u32 cfg2_clrb = 0, cfg2_setb = 0;
+ u32 len = xfer->len;
+ u32 spi_delay_ns;
+
+ spi_delay_ns = spi_delay_to_ns(&xfer->word_delay, xfer);
+
+ if (spi->cur_midi != 0) {
+ dev_warn(spi->dev, "st,spi-midi-ns DT property is deprecated\n");
+ if (spi_delay_ns) {
+ dev_warn(spi->dev, "Overriding st,spi-midi-ns with word_delay_ns %d\n",
+ spi_delay_ns);
+ spi->cur_midi = spi_delay_ns;
+ }
+ } else {
+ spi->cur_midi = spi_delay_ns;
+ }
cfg2_clrb |= STM32H7_SPI_CFG2_MIDI;
if ((len > 1) && (spi->cur_midi > 0)) {
@@ -1768,6 +1963,13 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi,
spi->cur_bpw = transfer->bits_per_word;
spi->cfg->set_bpw(spi);
+ if (spi_dev->mode & SPI_READY && spi->cur_bpw < 8) {
+ writel_relaxed(readl_relaxed(spi->base + spi->cfg->regs->rdy_en.reg) &
+ ~spi->cfg->regs->rdy_en.mask,
+ spi->base + spi->cfg->regs->rdy_en.reg);
+ dev_dbg(spi->dev, "RDY logic disabled as bits per word < 8\n");
+ }
+
/* Update spi->cur_speed with real clock speed */
if (STM32_SPI_HOST_MODE(spi)) {
mbr = stm32_spi_prepare_mbr(spi, transfer->speed_hz,
@@ -1790,7 +1992,7 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi,
spi->cur_comm = comm_type;
if (STM32_SPI_HOST_MODE(spi) && spi->cfg->set_data_idleness)
- spi->cfg->set_data_idleness(spi, transfer->len);
+ spi->cfg->set_data_idleness(spi, transfer);
if (spi->cur_bpw <= 8)
nb_words = transfer->len;
@@ -1871,6 +2073,9 @@ static int stm32_spi_unprepare_msg(struct spi_controller *ctrl,
spi->cfg->disable(spi);
+ if (spi->sram_rx_buf)
+ memset(spi->sram_rx_buf, 0, spi->sram_rx_buf_size);
+
return 0;
}
@@ -2069,9 +2274,15 @@ static int stm32_spi_probe(struct platform_device *pdev)
struct resource *res;
struct reset_control *rst;
struct device_node *np = pdev->dev.of_node;
+ const struct stm32_spi_cfg *cfg;
bool device_mode;
int ret;
- const struct stm32_spi_cfg *cfg = of_device_get_match_data(&pdev->dev);
+
+ cfg = of_device_get_match_data(&pdev->dev);
+ if (!cfg) {
+ dev_err(&pdev->dev, "Failed to get match data for platform\n");
+ return -ENODEV;
+ }
device_mode = of_property_read_bool(np, "spi-slave");
if (!cfg->has_device_mode && device_mode) {
@@ -2179,7 +2390,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
ctrl->auto_runtime_pm = true;
ctrl->bus_num = pdev->id;
ctrl->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST |
- SPI_3WIRE;
+ SPI_3WIRE | SPI_READY;
ctrl->bits_per_word_mask = spi->cfg->get_bpw_mask(spi);
ctrl->max_speed_hz = spi->clk_rate / spi->cfg->baud_rate_div_min;
ctrl->min_speed_hz = spi->clk_rate / spi->cfg->baud_rate_div_max;
@@ -2219,6 +2430,33 @@ static int stm32_spi_probe(struct platform_device *pdev)
if (spi->dma_tx || spi->dma_rx)
ctrl->can_dma = stm32_spi_can_dma;
+ spi->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
+ if (spi->sram_pool) {
+ spi->sram_rx_buf_size = gen_pool_size(spi->sram_pool);
+ dev_info(&pdev->dev, "SRAM pool: %zu KiB for RX DMA/MDMA chaining\n",
+ spi->sram_rx_buf_size / 1024);
+ spi->sram_rx_buf = gen_pool_dma_zalloc(spi->sram_pool, spi->sram_rx_buf_size,
+ &spi->sram_dma_rx_buf);
+ if (!spi->sram_rx_buf) {
+ dev_err(&pdev->dev, "failed to allocate SRAM buffer\n");
+ } else {
+ spi->mdma_rx = dma_request_chan(spi->dev, "rxm2m");
+ if (IS_ERR(spi->mdma_rx)) {
+ ret = PTR_ERR(spi->mdma_rx);
+ spi->mdma_rx = NULL;
+ if (ret == -EPROBE_DEFER) {
+ goto err_pool_free;
+ } else {
+ gen_pool_free(spi->sram_pool,
+ (unsigned long)spi->sram_rx_buf,
+ spi->sram_rx_buf_size);
+ dev_warn(&pdev->dev,
+ "failed to request rx mdma channel, DMA only\n");
+ }
+ }
+ }
+ }
+
pm_runtime_set_autosuspend_delay(&pdev->dev,
STM32_SPI_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(&pdev->dev);
@@ -2233,7 +2471,6 @@ static int stm32_spi_probe(struct platform_device *pdev)
goto err_pm_disable;
}
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
dev_info(&pdev->dev, "driver initialized (%s mode)\n",
@@ -2246,6 +2483,13 @@ err_pm_disable:
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
+
+ if (spi->mdma_rx)
+ dma_release_channel(spi->mdma_rx);
+err_pool_free:
+ if (spi->sram_pool)
+ gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf,
+ spi->sram_rx_buf_size);
err_dma_release:
if (spi->dma_tx)
dma_release_channel(spi->dma_tx);
@@ -2276,6 +2520,11 @@ static void stm32_spi_remove(struct platform_device *pdev)
dma_release_channel(ctrl->dma_tx);
if (ctrl->dma_rx)
dma_release_channel(ctrl->dma_rx);
+ if (spi->mdma_rx)
+ dma_release_channel(spi->mdma_rx);
+ if (spi->sram_rx_buf)
+ gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf,
+ spi->sram_rx_buf_size);
clk_disable_unprepare(spi->clk);
@@ -2342,7 +2591,6 @@ static int __maybe_unused stm32_spi_resume(struct device *dev)
spi->cfg->config(spi);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
diff --git a/drivers/spi/spi-sunplus-sp7021.c b/drivers/spi/spi-sunplus-sp7021.c
index 7fd4cc6f74c2..256ae07db6be 100644
--- a/drivers/spi/spi-sunplus-sp7021.c
+++ b/drivers/spi/spi-sunplus-sp7021.c
@@ -103,7 +103,7 @@ static irqreturn_t sp7021_spi_target_irq(int irq, void *dev)
data_status = readl(pspim->s_base + SP7021_DATA_RDY_REG);
data_status |= SP7021_SLAVE_CLR_INT;
- writel(data_status , pspim->s_base + SP7021_DATA_RDY_REG);
+ writel(data_status, pspim->s_base + SP7021_DATA_RDY_REG);
complete(&pspim->target_isr);
return IRQ_HANDLED;
}
@@ -296,7 +296,7 @@ static void sp7021_spi_setup_clk(struct spi_controller *ctlr, struct spi_transfe
}
static int sp7021_spi_host_transfer_one(struct spi_controller *ctlr, struct spi_device *spi,
- struct spi_transfer *xfer)
+ struct spi_transfer *xfer)
{
struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(ctlr);
unsigned long timeout = msecs_to_jiffies(1000);
@@ -360,7 +360,7 @@ static int sp7021_spi_host_transfer_one(struct spi_controller *ctlr, struct spi_
}
static int sp7021_spi_target_transfer_one(struct spi_controller *ctlr, struct spi_device *spi,
- struct spi_transfer *xfer)
+ struct spi_transfer *xfer)
{
struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(ctlr);
struct device *dev = pspim->dev;
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
index a284d2794586..0b7eaccbc797 100644
--- a/drivers/spi/spi-ti-qspi.c
+++ b/drivers/spi/spi-ti-qspi.c
@@ -158,7 +158,6 @@ static int ti_qspi_setup(struct spi_device *spi)
return ret;
}
- pm_runtime_mark_last_busy(qspi->dev);
ret = pm_runtime_put_autosuspend(qspi->dev);
if (ret < 0) {
dev_err(qspi->dev, "pm_runtime_put_autosuspend() failed\n");
@@ -195,7 +194,6 @@ static void ti_qspi_setup_clk(struct ti_qspi *qspi, u32 speed_hz)
ctx_reg->clkctrl = clk_ctrl_new;
}
- pm_runtime_mark_last_busy(qspi->dev);
pm_runtime_put_autosuspend(qspi->dev);
}
diff --git a/drivers/spi/spi-virtio.c b/drivers/spi/spi-virtio.c
new file mode 100644
index 000000000000..2acb929b2c69
--- /dev/null
+++ b/drivers/spi/spi-virtio.c
@@ -0,0 +1,431 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SPI bus driver for the Virtio SPI controller
+ * Copyright (C) 2023 OpenSynergy GmbH
+ * Copyright (C) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/stddef.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ring.h>
+#include <linux/virtio_spi.h>
+
+#define VIRTIO_SPI_MODE_MASK \
+ (SPI_MODE_X_MASK | SPI_CS_HIGH | SPI_LSB_FIRST)
+
+struct virtio_spi_req {
+ struct completion completion;
+ const u8 *tx_buf;
+ u8 *rx_buf;
+ struct spi_transfer_head transfer_head ____cacheline_aligned;
+ struct spi_transfer_result result;
+};
+
+struct virtio_spi_priv {
+ /* The virtio device we're associated with */
+ struct virtio_device *vdev;
+ /* Pointer to the virtqueue */
+ struct virtqueue *vq;
+ /* Copy of config space mode_func_supported */
+ u32 mode_func_supported;
+ /* Copy of config space max_freq_hz */
+ u32 max_freq_hz;
+};
+
+static void virtio_spi_msg_done(struct virtqueue *vq)
+{
+ struct virtio_spi_req *req;
+ unsigned int len;
+
+ while ((req = virtqueue_get_buf(vq, &len)))
+ complete(&req->completion);
+}
+
+/*
+ * virtio_spi_set_delays - Set delay parameters for SPI transfer
+ *
+ * This function sets various delay parameters for SPI transfer,
+ * including delay after CS asserted, timing intervals between
+ * adjacent words within a transfer, delay before and after CS
+ * deasserted. It converts these delay parameters to nanoseconds
+ * using spi_delay_to_ns and stores the results in spi_transfer_head
+ * structure.
+ * If the conversion fails, the function logs a warning message and
+ * returns an error code.
+ * . . . . . . . . . .
+ * Delay + A + + B + + C + D + E + F + A +
+ * . . . . . . . . . .
+ * ___. . . . . . .___.___. .
+ * CS# |___.______.____.____.___.___| . |___._____________
+ * . . . . . . . . . .
+ * . . . . . . . . . .
+ * SCLK__.___.___NNN_____NNN__.___.___.___.___.___.___NNN_______
+ *
+ * NOTE: 1st transfer has two words, the delay between these two words are
+ * 'B' in the diagram.
+ *
+ * A => struct spi_device -> cs_setup
+ * B => max{struct spi_transfer -> word_delay, struct spi_device -> word_delay}
+ * Note: spi_device and spi_transfer both have word_delay, Linux
+ * choose the bigger one, refer to _spi_xfer_word_delay_update function
+ * C => struct spi_transfer -> delay
+ * D => struct spi_device -> cs_hold
+ * E => struct spi_device -> cs_inactive
+ * F => struct spi_transfer -> cs_change_delay
+ *
+ * So the corresponding relationship:
+ * A <===> cs_setup_ns (after CS asserted)
+ * B <===> word_delay_ns (delay between adjacent words within a transfer)
+ * C+D <===> cs_delay_hold_ns (before CS deasserted)
+ * E+F <===> cs_change_delay_inactive_ns (after CS deasserted, these two
+ * values are also recommended in the Linux driver to be added up)
+ */
+static int virtio_spi_set_delays(struct spi_transfer_head *th,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ int cs_setup;
+ int cs_word_delay_xfer;
+ int cs_word_delay_spi;
+ int delay;
+ int cs_hold;
+ int cs_inactive;
+ int cs_change_delay;
+
+ cs_setup = spi_delay_to_ns(&spi->cs_setup, xfer);
+ if (cs_setup < 0) {
+ dev_warn(&spi->dev, "Cannot convert cs_setup\n");
+ return cs_setup;
+ }
+ th->cs_setup_ns = cpu_to_le32(cs_setup);
+
+ cs_word_delay_xfer = spi_delay_to_ns(&xfer->word_delay, xfer);
+ if (cs_word_delay_xfer < 0) {
+ dev_warn(&spi->dev, "Cannot convert cs_word_delay_xfer\n");
+ return cs_word_delay_xfer;
+ }
+ cs_word_delay_spi = spi_delay_to_ns(&spi->word_delay, xfer);
+ if (cs_word_delay_spi < 0) {
+ dev_warn(&spi->dev, "Cannot convert cs_word_delay_spi\n");
+ return cs_word_delay_spi;
+ }
+
+ th->word_delay_ns = cpu_to_le32(max(cs_word_delay_spi, cs_word_delay_xfer));
+
+ delay = spi_delay_to_ns(&xfer->delay, xfer);
+ if (delay < 0) {
+ dev_warn(&spi->dev, "Cannot convert delay\n");
+ return delay;
+ }
+ cs_hold = spi_delay_to_ns(&spi->cs_hold, xfer);
+ if (cs_hold < 0) {
+ dev_warn(&spi->dev, "Cannot convert cs_hold\n");
+ return cs_hold;
+ }
+ th->cs_delay_hold_ns = cpu_to_le32(delay + cs_hold);
+
+ cs_inactive = spi_delay_to_ns(&spi->cs_inactive, xfer);
+ if (cs_inactive < 0) {
+ dev_warn(&spi->dev, "Cannot convert cs_inactive\n");
+ return cs_inactive;
+ }
+ cs_change_delay = spi_delay_to_ns(&xfer->cs_change_delay, xfer);
+ if (cs_change_delay < 0) {
+ dev_warn(&spi->dev, "Cannot convert cs_change_delay\n");
+ return cs_change_delay;
+ }
+ th->cs_change_delay_inactive_ns =
+ cpu_to_le32(cs_inactive + cs_change_delay);
+
+ return 0;
+}
+
+static int virtio_spi_transfer_one(struct spi_controller *ctrl,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct virtio_spi_priv *priv = spi_controller_get_devdata(ctrl);
+ struct virtio_spi_req *spi_req __free(kfree) = NULL;
+ struct spi_transfer_head *th;
+ struct scatterlist sg_out_head, sg_out_payload;
+ struct scatterlist sg_in_result, sg_in_payload;
+ struct scatterlist *sgs[4];
+ unsigned int outcnt = 0;
+ unsigned int incnt = 0;
+ int ret;
+
+ spi_req = kzalloc(sizeof(*spi_req), GFP_KERNEL);
+ if (!spi_req)
+ return -ENOMEM;
+
+ init_completion(&spi_req->completion);
+
+ th = &spi_req->transfer_head;
+
+ /* Fill struct spi_transfer_head */
+ th->chip_select_id = spi_get_chipselect(spi, 0);
+ th->bits_per_word = spi->bits_per_word;
+ th->cs_change = xfer->cs_change;
+ th->tx_nbits = xfer->tx_nbits;
+ th->rx_nbits = xfer->rx_nbits;
+ th->reserved[0] = 0;
+ th->reserved[1] = 0;
+ th->reserved[2] = 0;
+
+ static_assert(VIRTIO_SPI_CPHA == SPI_CPHA,
+ "VIRTIO_SPI_CPHA must match SPI_CPHA");
+ static_assert(VIRTIO_SPI_CPOL == SPI_CPOL,
+ "VIRTIO_SPI_CPOL must match SPI_CPOL");
+ static_assert(VIRTIO_SPI_CS_HIGH == SPI_CS_HIGH,
+ "VIRTIO_SPI_CS_HIGH must match SPI_CS_HIGH");
+ static_assert(VIRTIO_SPI_MODE_LSB_FIRST == SPI_LSB_FIRST,
+ "VIRTIO_SPI_MODE_LSB_FIRST must match SPI_LSB_FIRST");
+
+ th->mode = cpu_to_le32(spi->mode & VIRTIO_SPI_MODE_MASK);
+ if (spi->mode & SPI_LOOP)
+ th->mode |= cpu_to_le32(VIRTIO_SPI_MODE_LOOP);
+
+ th->freq = cpu_to_le32(xfer->speed_hz);
+
+ ret = virtio_spi_set_delays(th, spi, xfer);
+ if (ret)
+ goto msg_done;
+
+ /* Set buffers */
+ spi_req->tx_buf = xfer->tx_buf;
+ spi_req->rx_buf = xfer->rx_buf;
+
+ /* Prepare sending of virtio message */
+ init_completion(&spi_req->completion);
+
+ sg_init_one(&sg_out_head, th, sizeof(*th));
+ sgs[outcnt] = &sg_out_head;
+ outcnt++;
+
+ if (spi_req->tx_buf) {
+ sg_init_one(&sg_out_payload, spi_req->tx_buf, xfer->len);
+ sgs[outcnt] = &sg_out_payload;
+ outcnt++;
+ }
+
+ if (spi_req->rx_buf) {
+ sg_init_one(&sg_in_payload, spi_req->rx_buf, xfer->len);
+ sgs[outcnt] = &sg_in_payload;
+ incnt++;
+ }
+
+ sg_init_one(&sg_in_result, &spi_req->result,
+ sizeof(struct spi_transfer_result));
+ sgs[outcnt + incnt] = &sg_in_result;
+ incnt++;
+
+ ret = virtqueue_add_sgs(priv->vq, sgs, outcnt, incnt, spi_req,
+ GFP_KERNEL);
+ if (ret)
+ goto msg_done;
+
+ /* Simple implementation: There can be only one transfer in flight */
+ virtqueue_kick(priv->vq);
+
+ wait_for_completion(&spi_req->completion);
+
+ /* Read result from message and translate return code */
+ switch (spi_req->result.result) {
+ case VIRTIO_SPI_TRANS_OK:
+ break;
+ case VIRTIO_SPI_PARAM_ERR:
+ ret = -EINVAL;
+ break;
+ case VIRTIO_SPI_TRANS_ERR:
+ ret = -EIO;
+ break;
+ default:
+ ret = -EIO;
+ break;
+ }
+
+msg_done:
+ if (ret)
+ ctrl->cur_msg->status = ret;
+
+ return ret;
+}
+
+static void virtio_spi_read_config(struct virtio_device *vdev)
+{
+ struct spi_controller *ctrl = dev_get_drvdata(&vdev->dev);
+ struct virtio_spi_priv *priv = vdev->priv;
+ u8 cs_max_number;
+ u8 tx_nbits_supported;
+ u8 rx_nbits_supported;
+
+ cs_max_number = virtio_cread8(vdev, offsetof(struct virtio_spi_config,
+ cs_max_number));
+ ctrl->num_chipselect = cs_max_number;
+
+ /* Set the mode bits which are understood by this driver */
+ priv->mode_func_supported =
+ virtio_cread32(vdev, offsetof(struct virtio_spi_config,
+ mode_func_supported));
+ ctrl->mode_bits = priv->mode_func_supported &
+ (VIRTIO_SPI_CS_HIGH | VIRTIO_SPI_MODE_LSB_FIRST);
+ if (priv->mode_func_supported & VIRTIO_SPI_MF_SUPPORT_CPHA_1)
+ ctrl->mode_bits |= VIRTIO_SPI_CPHA;
+ if (priv->mode_func_supported & VIRTIO_SPI_MF_SUPPORT_CPOL_1)
+ ctrl->mode_bits |= VIRTIO_SPI_CPOL;
+ if (priv->mode_func_supported & VIRTIO_SPI_MF_SUPPORT_LSB_FIRST)
+ ctrl->mode_bits |= SPI_LSB_FIRST;
+ if (priv->mode_func_supported & VIRTIO_SPI_MF_SUPPORT_LOOPBACK)
+ ctrl->mode_bits |= SPI_LOOP;
+ tx_nbits_supported =
+ virtio_cread8(vdev, offsetof(struct virtio_spi_config,
+ tx_nbits_supported));
+ if (tx_nbits_supported & VIRTIO_SPI_RX_TX_SUPPORT_DUAL)
+ ctrl->mode_bits |= SPI_TX_DUAL;
+ if (tx_nbits_supported & VIRTIO_SPI_RX_TX_SUPPORT_QUAD)
+ ctrl->mode_bits |= SPI_TX_QUAD;
+ if (tx_nbits_supported & VIRTIO_SPI_RX_TX_SUPPORT_OCTAL)
+ ctrl->mode_bits |= SPI_TX_OCTAL;
+ rx_nbits_supported =
+ virtio_cread8(vdev, offsetof(struct virtio_spi_config,
+ rx_nbits_supported));
+ if (rx_nbits_supported & VIRTIO_SPI_RX_TX_SUPPORT_DUAL)
+ ctrl->mode_bits |= SPI_RX_DUAL;
+ if (rx_nbits_supported & VIRTIO_SPI_RX_TX_SUPPORT_QUAD)
+ ctrl->mode_bits |= SPI_RX_QUAD;
+ if (rx_nbits_supported & VIRTIO_SPI_RX_TX_SUPPORT_OCTAL)
+ ctrl->mode_bits |= SPI_RX_OCTAL;
+
+ ctrl->bits_per_word_mask =
+ virtio_cread32(vdev, offsetof(struct virtio_spi_config,
+ bits_per_word_mask));
+
+ priv->max_freq_hz =
+ virtio_cread32(vdev, offsetof(struct virtio_spi_config,
+ max_freq_hz));
+}
+
+static int virtio_spi_find_vqs(struct virtio_spi_priv *priv)
+{
+ struct virtqueue *vq;
+
+ vq = virtio_find_single_vq(priv->vdev, virtio_spi_msg_done, "spi-rq");
+ if (IS_ERR(vq))
+ return PTR_ERR(vq);
+ priv->vq = vq;
+ return 0;
+}
+
+/* Function must not be called before virtio_spi_find_vqs() has been run */
+static void virtio_spi_del_vq(void *data)
+{
+ struct virtio_device *vdev = data;
+
+ virtio_reset_device(vdev);
+ vdev->config->del_vqs(vdev);
+}
+
+static int virtio_spi_probe(struct virtio_device *vdev)
+{
+ struct virtio_spi_priv *priv;
+ struct spi_controller *ctrl;
+ int ret;
+
+ ctrl = devm_spi_alloc_host(&vdev->dev, sizeof(*priv));
+ if (!ctrl)
+ return -ENOMEM;
+
+ priv = spi_controller_get_devdata(ctrl);
+ priv->vdev = vdev;
+ vdev->priv = priv;
+
+ device_set_node(&ctrl->dev, dev_fwnode(&vdev->dev));
+
+ dev_set_drvdata(&vdev->dev, ctrl);
+
+ virtio_spi_read_config(vdev);
+
+ ctrl->transfer_one = virtio_spi_transfer_one;
+
+ ret = virtio_spi_find_vqs(priv);
+ if (ret)
+ return dev_err_probe(&vdev->dev, ret, "Cannot setup virtqueues\n");
+
+ /* Register cleanup for virtqueues using devm */
+ ret = devm_add_action_or_reset(&vdev->dev, virtio_spi_del_vq, vdev);
+ if (ret)
+ return dev_err_probe(&vdev->dev, ret, "Cannot register virtqueue cleanup\n");
+
+ /* Use devm version to register controller */
+ ret = devm_spi_register_controller(&vdev->dev, ctrl);
+ if (ret)
+ return dev_err_probe(&vdev->dev, ret, "Cannot register controller\n");
+
+ return 0;
+}
+
+static int virtio_spi_freeze(struct device *dev)
+{
+ struct spi_controller *ctrl = dev_get_drvdata(dev);
+ struct virtio_device *vdev = dev_to_virtio(dev);
+ int ret;
+
+ ret = spi_controller_suspend(ctrl);
+ if (ret) {
+ dev_warn(dev, "cannot suspend controller (%d)\n", ret);
+ return ret;
+ }
+
+ virtio_spi_del_vq(vdev);
+ return 0;
+}
+
+static int virtio_spi_restore(struct device *dev)
+{
+ struct spi_controller *ctrl = dev_get_drvdata(dev);
+ struct virtio_device *vdev = dev_to_virtio(dev);
+ int ret;
+
+ ret = virtio_spi_find_vqs(vdev->priv);
+ if (ret) {
+ dev_err(dev, "problem starting vqueue (%d)\n", ret);
+ return ret;
+ }
+
+ ret = spi_controller_resume(ctrl);
+ if (ret)
+ dev_err(dev, "problem resuming controller (%d)\n", ret);
+
+ return ret;
+}
+
+static struct virtio_device_id virtio_spi_id_table[] = {
+ { VIRTIO_ID_SPI, VIRTIO_DEV_ANY_ID },
+ {}
+};
+MODULE_DEVICE_TABLE(virtio, virtio_spi_id_table);
+
+static const struct dev_pm_ops virtio_spi_pm_ops = {
+ .freeze = pm_sleep_ptr(virtio_spi_freeze),
+ .restore = pm_sleep_ptr(virtio_spi_restore),
+};
+
+static struct virtio_driver virtio_spi_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .pm = &virtio_spi_pm_ops,
+ },
+ .id_table = virtio_spi_id_table,
+ .probe = virtio_spi_probe,
+};
+module_virtio_driver(virtio_spi_driver);
+
+MODULE_AUTHOR("OpenSynergy GmbH");
+MODULE_AUTHOR("Haixu Cui <quic_haixcui@quicinc.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Virtio SPI bus driver");
diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c
index 1a40c4866ce1..33b78c537520 100644
--- a/drivers/spi/spi-xcomm.c
+++ b/drivers/spi/spi-xcomm.c
@@ -70,7 +70,7 @@ static int spi_xcomm_gpio_add(struct spi_xcomm *spi_xcomm)
return 0;
spi_xcomm->gc.get_direction = spi_xcomm_gpio_get_direction;
- spi_xcomm->gc.set_rv = spi_xcomm_gpio_set_value;
+ spi_xcomm->gc.set = spi_xcomm_gpio_set_value;
spi_xcomm->gc.can_sleep = 1;
spi_xcomm->gc.base = -1;
spi_xcomm->gc.ngpio = 1;
diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c
index ded709b2b459..d59cc8a18484 100644
--- a/drivers/spi/spi-xilinx.c
+++ b/drivers/spi/spi-xilinx.c
@@ -89,8 +89,8 @@ struct xilinx_spi {
u8 bytes_per_word;
int buffer_size; /* buffer size in words */
u32 cs_inactive; /* Level of the CS pins when inactive*/
- unsigned int (*read_fn)(void __iomem *);
- void (*write_fn)(u32, void __iomem *);
+ unsigned int (*read_fn)(void __iomem *addr);
+ void (*write_fn)(u32 val, void __iomem *addr);
};
static void xspi_write32(u32 val, void __iomem *addr)
@@ -251,6 +251,7 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
if (xspi->irq >= 0 &&
(xspi->force_irq || remaining_words > xspi->buffer_size)) {
u32 isr;
+
use_irq = true;
/* Inhibit irq to avoid spurious irqs on tx_empty*/
cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
index 595b6dc10845..502fd5eccc83 100644
--- a/drivers/spi/spi-zynqmp-gqspi.c
+++ b/drivers/spi/spi-zynqmp-gqspi.c
@@ -1330,7 +1330,6 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
goto clk_dis_all;
}
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 1bc0fdbb1bd7..2e0647a06890 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -427,15 +427,13 @@ static int spi_probe(struct device *dev)
if (spi->irq < 0)
spi->irq = 0;
- ret = dev_pm_domain_attach(dev, true);
+ ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON |
+ PD_FLAG_DETACH_POWER_OFF);
if (ret)
return ret;
- if (sdrv->probe) {
+ if (sdrv->probe)
ret = sdrv->probe(spi);
- if (ret)
- dev_pm_domain_detach(dev, true);
- }
return ret;
}
@@ -446,8 +444,6 @@ static void spi_remove(struct device *dev)
if (sdrv->remove)
sdrv->remove(to_spi_device(dev));
-
- dev_pm_domain_detach(dev, true);
}
static void spi_shutdown(struct device *dev)
@@ -590,6 +586,7 @@ struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
spi->dev.bus = &spi_bus_type;
spi->dev.release = spidev_release;
spi->mode = ctlr->buswidth_override_bits;
+ spi->num_chipselect = 1;
device_initialize(&spi->dev);
return spi;
@@ -626,11 +623,6 @@ static void spi_dev_set_name(struct spi_device *spi)
*/
#define SPI_INVALID_CS ((s8)-1)
-static inline bool is_valid_cs(s8 chip_select)
-{
- return chip_select != SPI_INVALID_CS;
-}
-
static inline int spi_dev_check_cs(struct device *dev,
struct spi_device *spi, u8 idx,
struct spi_device *new_spi, u8 new_idx)
@@ -639,9 +631,9 @@ static inline int spi_dev_check_cs(struct device *dev,
u8 idx_new;
cs = spi_get_chipselect(spi, idx);
- for (idx_new = new_idx; idx_new < SPI_CS_CNT_MAX; idx_new++) {
+ for (idx_new = new_idx; idx_new < new_spi->num_chipselect; idx_new++) {
cs_new = spi_get_chipselect(new_spi, idx_new);
- if (is_valid_cs(cs) && is_valid_cs(cs_new) && cs == cs_new) {
+ if (cs == cs_new) {
dev_err(dev, "chipselect %u already in use\n", cs_new);
return -EBUSY;
}
@@ -656,7 +648,7 @@ static int spi_dev_check(struct device *dev, void *data)
int status, idx;
if (spi->controller == new_spi->controller) {
- for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+ for (idx = 0; idx < spi->num_chipselect; idx++) {
status = spi_dev_check_cs(dev, spi, idx, new_spi, 0);
if (status)
return status;
@@ -678,10 +670,16 @@ static int __spi_add_device(struct spi_device *spi)
int status, idx;
u8 cs;
- for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+ if (spi->num_chipselect > SPI_DEVICE_CS_CNT_MAX) {
+ dev_err(dev, "num_cs %d > max %d\n", spi->num_chipselect,
+ SPI_DEVICE_CS_CNT_MAX);
+ return -EOVERFLOW;
+ }
+
+ for (idx = 0; idx < spi->num_chipselect; idx++) {
/* Chipselects are numbered 0..max; validate. */
cs = spi_get_chipselect(spi, idx);
- if (is_valid_cs(cs) && cs >= ctlr->num_chipselect) {
+ if (cs >= ctlr->num_chipselect) {
dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, idx),
ctlr->num_chipselect);
return -EINVAL;
@@ -693,13 +691,17 @@ static int __spi_add_device(struct spi_device *spi)
* For example, spi->chip_select[0] != spi->chip_select[1] and so on.
*/
if (!spi_controller_is_target(ctlr)) {
- for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+ for (idx = 0; idx < spi->num_chipselect; idx++) {
status = spi_dev_check_cs(dev, spi, idx, spi, idx + 1);
if (status)
return status;
}
}
+ /* Initialize unused logical CS as invalid */
+ for (idx = spi->num_chipselect; idx < SPI_DEVICE_CS_CNT_MAX; idx++)
+ spi_set_chipselect(spi, idx, SPI_INVALID_CS);
+
/* Set the bus ID string */
spi_dev_set_name(spi);
@@ -721,10 +723,9 @@ static int __spi_add_device(struct spi_device *spi)
if (ctlr->cs_gpiods) {
u8 cs;
- for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+ for (idx = 0; idx < spi->num_chipselect; idx++) {
cs = spi_get_chipselect(spi, idx);
- if (is_valid_cs(cs))
- spi_set_csgpiod(spi, idx, ctlr->cs_gpiods[cs]);
+ spi_set_csgpiod(spi, idx, ctlr->cs_gpiods[cs]);
}
}
@@ -777,14 +778,6 @@ int spi_add_device(struct spi_device *spi)
}
EXPORT_SYMBOL_GPL(spi_add_device);
-static void spi_set_all_cs_unused(struct spi_device *spi)
-{
- u8 idx;
-
- for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
- spi_set_chipselect(spi, idx, SPI_INVALID_CS);
-}
-
/**
* spi_new_device - instantiate one new SPI device
* @ctlr: Controller to which device is connected
@@ -820,7 +813,6 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
/* Use provided chip-select for proxy device */
- spi_set_all_cs_unused(proxy);
spi_set_chipselect(proxy, 0, chip->chip_select);
proxy->max_speed_hz = chip->max_speed_hz;
@@ -1028,7 +1020,7 @@ static void spi_res_release(struct spi_controller *ctlr, struct spi_message *mes
/*-------------------------------------------------------------------------*/
#define spi_for_each_valid_cs(spi, idx) \
- for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) \
+ for (idx = 0; idx < spi->num_chipselect; idx++) \
if (!(spi->cs_index_mask & BIT(idx))) {} else
static inline bool spi_is_last_cs(struct spi_device *spi)
@@ -1084,8 +1076,12 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
trace_spi_set_cs(spi, activate);
spi->controller->last_cs_index_mask = spi->cs_index_mask;
- for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
- spi->controller->last_cs[idx] = enable ? spi_get_chipselect(spi, 0) : SPI_INVALID_CS;
+ for (idx = 0; idx < SPI_DEVICE_CS_CNT_MAX; idx++) {
+ if (enable && idx < spi->num_chipselect)
+ spi->controller->last_cs[idx] = spi_get_chipselect(spi, 0);
+ else
+ spi->controller->last_cs[idx] = SPI_INVALID_CS;
+ }
spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
if (spi->controller->last_cs_mode_high)
@@ -1723,7 +1719,6 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer);
static void spi_idle_runtime_pm(struct spi_controller *ctlr)
{
if (ctlr->auto_runtime_pm) {
- pm_runtime_mark_last_busy(ctlr->dev.parent);
pm_runtime_put_autosuspend(ctlr->dev.parent);
}
}
@@ -2359,7 +2354,7 @@ static void of_spi_parse_dt_cs_delay(struct device_node *nc,
static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
struct device_node *nc)
{
- u32 value, cs[SPI_CS_CNT_MAX];
+ u32 value, cs[SPI_DEVICE_CS_CNT_MAX];
int rc, idx;
/* Mode (clock phase/polarity/etc.) */
@@ -2432,31 +2427,22 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
return 0;
}
- if (ctlr->num_chipselect > SPI_CS_CNT_MAX) {
- dev_err(&ctlr->dev, "No. of CS is more than max. no. of supported CS\n");
- return -EINVAL;
- }
-
- spi_set_all_cs_unused(spi);
-
/* Device address */
rc = of_property_read_variable_u32_array(nc, "reg", &cs[0], 1,
- SPI_CS_CNT_MAX);
+ SPI_DEVICE_CS_CNT_MAX);
if (rc < 0) {
dev_err(&ctlr->dev, "%pOF has no valid 'reg' property (%d)\n",
nc, rc);
return rc;
}
- if (rc > ctlr->num_chipselect) {
- dev_err(&ctlr->dev, "%pOF has number of CS > ctlr->num_chipselect (%d)\n",
- nc, rc);
- return rc;
- }
+
if ((of_property_present(nc, "parallel-memories")) &&
(!(ctlr->flags & SPI_CONTROLLER_MULTI_CS))) {
dev_err(&ctlr->dev, "SPI controller doesn't support multi CS\n");
return -EINVAL;
}
+
+ spi->num_chipselect = rc;
for (idx = 0; idx < rc; idx++)
spi_set_chipselect(spi, idx, cs[idx]);
@@ -2581,7 +2567,6 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias));
/* Use provided chip-select for ancillary device */
- spi_set_all_cs_unused(ancillary);
spi_set_chipselect(ancillary, 0, chip_select);
/* Take over SPI mode/speed from SPI main device */
@@ -2829,7 +2814,6 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
return ERR_PTR(-ENOMEM);
}
- spi_set_all_cs_unused(spi);
spi_set_chipselect(spi, 0, lookup.chip_select);
ACPI_COMPANION_SET(&spi->dev, adev);
@@ -3329,7 +3313,7 @@ int spi_register_controller(struct spi_controller *ctlr)
}
/* Setting last_cs to SPI_INVALID_CS means no chip selected */
- for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
+ for (idx = 0; idx < SPI_DEVICE_CS_CNT_MAX; idx++)
ctlr->last_cs[idx] = SPI_INVALID_CS;
status = device_add(&ctlr->dev);
@@ -3856,7 +3840,6 @@ static int spi_set_cs_timing(struct spi_device *spi)
}
status = spi->controller->set_cs_timing(spi);
- pm_runtime_mark_last_busy(parent);
pm_runtime_put_autosuspend(parent);
} else {
status = spi->controller->set_cs_timing(spi);
@@ -3991,7 +3974,6 @@ int spi_setup(struct spi_device *spi)
status = 0;
spi_set_cs(spi, false, true);
- pm_runtime_mark_last_busy(spi->controller->dev.parent);
pm_runtime_put_autosuspend(spi->controller->dev.parent);
} else {
spi_set_cs(spi, false, true);
@@ -4138,10 +4120,13 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
xfer->tx_nbits != SPI_NBITS_OCTAL)
return -EINVAL;
if ((xfer->tx_nbits == SPI_NBITS_DUAL) &&
- !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD)))
+ !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL)))
return -EINVAL;
if ((xfer->tx_nbits == SPI_NBITS_QUAD) &&
- !(spi->mode & SPI_TX_QUAD))
+ !(spi->mode & (SPI_TX_QUAD | SPI_TX_OCTAL)))
+ return -EINVAL;
+ if ((xfer->tx_nbits == SPI_NBITS_OCTAL) &&
+ !(spi->mode & SPI_TX_OCTAL))
return -EINVAL;
}
/* Check transfer rx_nbits */
@@ -4154,10 +4139,13 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
xfer->rx_nbits != SPI_NBITS_OCTAL)
return -EINVAL;
if ((xfer->rx_nbits == SPI_NBITS_DUAL) &&
- !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD)))
+ !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL)))
return -EINVAL;
if ((xfer->rx_nbits == SPI_NBITS_QUAD) &&
- !(spi->mode & SPI_RX_QUAD))
+ !(spi->mode & (SPI_RX_QUAD | SPI_RX_OCTAL)))
+ return -EINVAL;
+ if ((xfer->rx_nbits == SPI_NBITS_OCTAL) &&
+ !(spi->mode & SPI_RX_OCTAL))
return -EINVAL;
}
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 6108959c28d9..5300c942a2a4 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -703,6 +703,7 @@ static const struct class spidev_class = {
* spidev_dt_ids array below. Both arrays are kept in the same ordering.
*/
static const struct spi_device_id spidev_spi_ids[] = {
+ { .name = /* abb */ "spi-sensor" },
{ .name = /* cisco */ "spi-petra" },
{ .name = /* dh */ "dhcom-board" },
{ .name = /* elgin */ "jg10309-01" },
@@ -735,6 +736,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 = "cisco,spi-petra", .data = &spidev_of_check },
{ .compatible = "dh,dhcom-board", .data = &spidev_of_check },
{ .compatible = "elgin,jg10309-01", .data = &spidev_of_check },