aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/spi/sh-msiof.txt6
-rw-r--r--Documentation/devicetree/bindings/spi/spi-rspi.txt4
-rw-r--r--Documentation/devicetree/bindings/spi/spi-slave-mt27xx.txt32
-rw-r--r--Documentation/devicetree/bindings/spi/spi-sprd.txt26
-rw-r--r--drivers/spi/Kconfig16
-rw-r--r--drivers/spi/Makefile2
-rw-r--r--drivers/spi/spi-atmel.c10
-rw-r--r--drivers/spi/spi-bcm63xx-hsspi.c20
-rw-r--r--drivers/spi/spi-davinci.c19
-rw-r--r--drivers/spi/spi-dw-mmio.c29
-rw-r--r--drivers/spi/spi-dw.c22
-rw-r--r--drivers/spi/spi-ep93xx.c36
-rw-r--r--drivers/spi/spi-fsl-espi.c4
-rw-r--r--drivers/spi/spi-fsl-lpspi.c2
-rw-r--r--drivers/spi/spi-gpio.c12
-rw-r--r--drivers/spi/spi-mem.c69
-rw-r--r--drivers/spi/spi-mt65xx.c37
-rw-r--r--drivers/spi/spi-orion.c22
-rw-r--r--drivers/spi/spi-pic32-sqi.c2
-rw-r--r--drivers/spi/spi-pic32.c6
-rw-r--r--drivers/spi/spi-pl022.c10
-rw-r--r--drivers/spi/spi-pxa2xx.c21
-rw-r--r--drivers/spi/spi-rb4xx.c2
-rw-r--r--drivers/spi/spi-rspi.c10
-rw-r--r--drivers/spi/spi-sh-hspi.c12
-rw-r--r--drivers/spi/spi-sh-msiof.c6
-rw-r--r--drivers/spi/spi-sh.c12
-rw-r--r--drivers/spi/spi-slave-mt27xx.c554
-rw-r--r--drivers/spi/spi-slave-system-control.c1
-rw-r--r--drivers/spi/spi-sprd.c745
-rw-r--r--drivers/spi/spi.c95
-rw-r--r--drivers/spi/spidev.c9
-rw-r--r--include/linux/platform_data/spi-davinci.h4
-rw-r--r--include/linux/spi/spi.h19
-rw-r--r--tools/spi/spidev_test.c6
35 files changed, 1679 insertions, 203 deletions
diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt
index bfbc2035fb6b..49fd806b3689 100644
--- a/Documentation/devicetree/bindings/spi/sh-msiof.txt
+++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt
@@ -2,7 +2,9 @@ Renesas MSIOF spi controller
Required properties:
- compatible : "renesas,msiof-r8a7743" (RZ/G1M)
+ "renesas,msiof-r8a7744" (RZ/G1N)
"renesas,msiof-r8a7745" (RZ/G1E)
+ "renesas,msiof-r8a774a1" (RZ/G2M)
"renesas,msiof-r8a7790" (R-Car H2)
"renesas,msiof-r8a7791" (R-Car M2-W)
"renesas,msiof-r8a7792" (R-Car V2H)
@@ -11,10 +13,12 @@ Required properties:
"renesas,msiof-r8a7795" (R-Car H3)
"renesas,msiof-r8a7796" (R-Car M3-W)
"renesas,msiof-r8a77965" (R-Car M3-N)
+ "renesas,msiof-r8a77990" (R-Car E3)
+ "renesas,msiof-r8a77995" (R-Car D3)
"renesas,msiof-sh73a0" (SH-Mobile AG5)
"renesas,sh-mobile-msiof" (generic SH-Mobile compatibile device)
"renesas,rcar-gen2-msiof" (generic R-Car Gen2 and RZ/G1 compatible device)
- "renesas,rcar-gen3-msiof" (generic R-Car Gen3 compatible device)
+ "renesas,rcar-gen3-msiof" (generic R-Car Gen3 and RZ/G2 compatible device)
"renesas,sh-msiof" (deprecated)
When compatible with the generic version, nodes
diff --git a/Documentation/devicetree/bindings/spi/spi-rspi.txt b/Documentation/devicetree/bindings/spi/spi-rspi.txt
index 96fd58548f69..fc97ad64fbf2 100644
--- a/Documentation/devicetree/bindings/spi/spi-rspi.txt
+++ b/Documentation/devicetree/bindings/spi/spi-rspi.txt
@@ -3,7 +3,7 @@ Device tree configuration for Renesas RSPI/QSPI driver
Required properties:
- compatible : For Renesas Serial Peripheral Interface on legacy SH:
"renesas,rspi-<soctype>", "renesas,rspi" as fallback.
- For Renesas Serial Peripheral Interface on RZ/A1H:
+ For Renesas Serial Peripheral Interface on RZ/A:
"renesas,rspi-<soctype>", "renesas,rspi-rz" as fallback.
For Quad Serial Peripheral Interface on R-Car Gen2 and
RZ/G1 devices:
@@ -11,7 +11,9 @@ Required properties:
Examples with soctypes are:
- "renesas,rspi-sh7757" (SH)
- "renesas,rspi-r7s72100" (RZ/A1H)
+ - "renesas,rspi-r7s9210" (RZ/A2)
- "renesas,qspi-r8a7743" (RZ/G1M)
+ - "renesas,qspi-r8a7744" (RZ/G1N)
- "renesas,qspi-r8a7745" (RZ/G1E)
- "renesas,qspi-r8a7790" (R-Car H2)
- "renesas,qspi-r8a7791" (R-Car M2-W)
diff --git a/Documentation/devicetree/bindings/spi/spi-slave-mt27xx.txt b/Documentation/devicetree/bindings/spi/spi-slave-mt27xx.txt
new file mode 100644
index 000000000000..c37e5a179b21
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi-slave-mt27xx.txt
@@ -0,0 +1,32 @@
+Binding for MTK SPI Slave controller
+
+Required properties:
+- compatible: should be one of the following.
+ - mediatek,mt2712-spi-slave: for mt2712 platforms
+- reg: Address and length of the register set for the device.
+- interrupts: Should contain spi interrupt.
+- clocks: phandles to input clocks.
+ It's clock gate, and should be <&infracfg CLK_INFRA_AO_SPI1>.
+- clock-names: should be "spi" for the clock gate.
+
+Optional properties:
+- assigned-clocks: it's mux clock, should be <&topckgen CLK_TOP_SPISLV_SEL>.
+- assigned-clock-parents: parent of mux clock.
+ It's PLL, and should be one of the following.
+ - <&topckgen CLK_TOP_UNIVPLL1_D2>: specify parent clock 312MHZ.
+ It's the default one.
+ - <&topckgen CLK_TOP_UNIVPLL1_D4>: specify parent clock 156MHZ.
+ - <&topckgen CLK_TOP_UNIVPLL2_D4>: specify parent clock 104MHZ.
+ - <&topckgen CLK_TOP_UNIVPLL1_D8>: specify parent clock 78MHZ.
+
+Example:
+- SoC Specific Portion:
+spis1: spi@10013000 {
+ compatible = "mediatek,mt2712-spi-slave";
+ reg = <0 0x10013000 0 0x100>;
+ interrupts = <GIC_SPI 283 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&infracfg CLK_INFRA_AO_SPI1>;
+ clock-names = "spi";
+ assigned-clocks = <&topckgen CLK_TOP_SPISLV_SEL>;
+ assigned-clock-parents = <&topckgen CLK_TOP_UNIVPLL1_D2>;
+};
diff --git a/Documentation/devicetree/bindings/spi/spi-sprd.txt b/Documentation/devicetree/bindings/spi/spi-sprd.txt
new file mode 100644
index 000000000000..bad211a19da4
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi-sprd.txt
@@ -0,0 +1,26 @@
+Spreadtrum SPI Controller
+
+Required properties:
+- compatible: Should be "sprd,sc9860-spi".
+- reg: Offset and length of SPI controller register space.
+- interrupts: Should contain SPI interrupt.
+- clock-names: Should contain following entries:
+ "spi" for SPI clock,
+ "source" for SPI source (parent) clock,
+ "enable" for SPI module enable clock.
+- clocks: List of clock input name strings sorted in the same order
+ as the clock-names property.
+- #address-cells: The number of cells required to define a chip select
+ address on the SPI bus. Should be set to 1.
+- #size-cells: Should be set to 0.
+
+Example:
+spi0: spi@70a00000{
+ compatible = "sprd,sc9860-spi";
+ reg = <0 0x70a00000 0 0x1000>;
+ interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+ clock-names = "spi", "source","enable";
+ clocks = <&clk_spi0>, <&ext_26m>, <&clk_ap_apb_gates 5>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 671d078349cc..e6b430890f7a 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -596,6 +596,22 @@ config SPI_SIRF
help
SPI driver for CSR SiRFprimaII SoCs
+config SPI_SLAVE_MT27XX
+ tristate "MediaTek SPI slave device"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on SPI_SLAVE
+ help
+ This selects the MediaTek(R) SPI slave device driver.
+ If you want to use MediaTek(R) SPI slave interface,
+ say Y or M here.If you are not sure, say N.
+ SPI slave drivers for Mediatek MT27XX series ARM SoCs.
+
+config SPI_SPRD
+ tristate "Spreadtrum SPI controller"
+ depends on ARCH_SPRD || COMPILE_TEST
+ help
+ SPI driver for Spreadtrum SoCs.
+
config SPI_SPRD_ADI
tristate "Spreadtrum ADI controller"
depends on ARCH_SPRD || COMPILE_TEST
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index a90d55970036..644b22ecb4b1 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -88,6 +88,8 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
obj-$(CONFIG_SPI_SIRF) += spi-sirf.o
+obj-$(CONFIG_SPI_SLAVE_MT27XX) += spi-slave-mt27xx.o
+obj-$(CONFIG_SPI_SPRD) += spi-sprd.o
obj-$(CONFIG_SPI_SPRD_ADI) += spi-sprd-adi.o
obj-$(CONFIG_SPI_STM32) += spi-stm32.o
obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 3f890d162934..74fddcd3282b 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -1767,10 +1767,8 @@ static int atmel_spi_suspend(struct device *dev)
/* Stop the queue running */
ret = spi_master_suspend(master);
- if (ret) {
- dev_warn(dev, "cannot suspend master\n");
+ if (ret)
return ret;
- }
if (!pm_runtime_suspended(dev))
atmel_spi_runtime_suspend(dev);
@@ -1799,11 +1797,7 @@ static int atmel_spi_resume(struct device *dev)
}
/* Start the queue running */
- ret = spi_master_resume(master);
- if (ret)
- dev_err(dev, "problem starting queue (%d)\n", ret);
-
- return ret;
+ return spi_master_resume(master);
}
#endif
diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index c23849f7aa7b..9a06ffdb73b8 100644
--- a/drivers/spi/spi-bcm63xx-hsspi.c
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -101,6 +101,7 @@ struct bcm63xx_hsspi {
struct platform_device *pdev;
struct clk *clk;
+ struct clk *pll_clk;
void __iomem *regs;
u8 __iomem *fifo;
@@ -332,7 +333,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
struct resource *res_mem;
void __iomem *regs;
struct device *dev = &pdev->dev;
- struct clk *clk;
+ struct clk *clk, *pll_clk = NULL;
int irq, ret;
u32 reg, rate, num_cs = HSSPI_SPI_MAX_CS;
@@ -358,7 +359,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
rate = clk_get_rate(clk);
if (!rate) {
- struct clk *pll_clk = devm_clk_get(dev, "pll");
+ pll_clk = devm_clk_get(dev, "pll");
if (IS_ERR(pll_clk)) {
ret = PTR_ERR(pll_clk);
@@ -373,19 +374,20 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
clk_disable_unprepare(pll_clk);
if (!rate) {
ret = -EINVAL;
- goto out_disable_clk;
+ goto out_disable_pll_clk;
}
}
master = spi_alloc_master(&pdev->dev, sizeof(*bs));
if (!master) {
ret = -ENOMEM;
- goto out_disable_clk;
+ goto out_disable_pll_clk;
}
bs = spi_master_get_devdata(master);
bs->pdev = pdev;
bs->clk = clk;
+ bs->pll_clk = pll_clk;
bs->regs = regs;
bs->speed_hz = rate;
bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0));
@@ -440,6 +442,8 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
out_put_master:
spi_master_put(master);
+out_disable_pll_clk:
+ clk_disable_unprepare(pll_clk);
out_disable_clk:
clk_disable_unprepare(clk);
return ret;
@@ -453,6 +457,7 @@ static int bcm63xx_hsspi_remove(struct platform_device *pdev)
/* reset the hardware and block queue progress */
__raw_writel(0, bs->regs + HSSPI_INT_MASK_REG);
+ clk_disable_unprepare(bs->pll_clk);
clk_disable_unprepare(bs->clk);
return 0;
@@ -465,6 +470,7 @@ static int bcm63xx_hsspi_suspend(struct device *dev)
struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
spi_master_suspend(master);
+ clk_disable_unprepare(bs->pll_clk);
clk_disable_unprepare(bs->clk);
return 0;
@@ -480,6 +486,12 @@ static int bcm63xx_hsspi_resume(struct device *dev)
if (ret)
return ret;
+ if (bs->pll_clk) {
+ ret = clk_prepare_enable(bs->pll_clk);
+ if (ret)
+ return ret;
+ }
+
spi_master_resume(master);
return 0;
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index a02099c90c5c..56adec83f8fc 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -208,13 +208,11 @@ static inline void clear_io_bits(void __iomem *addr, u32 bits)
static void davinci_spi_chipselect(struct spi_device *spi, int value)
{
struct davinci_spi *dspi;
- struct davinci_spi_platform_data *pdata;
struct davinci_spi_config *spicfg = spi->controller_data;
u8 chip_sel = spi->chip_select;
u16 spidat1 = CS_DEFAULT;
dspi = spi_master_get_devdata(spi->master);
- pdata = &dspi->pdata;
/* program delay transfers if tx_delay is non zero */
if (spicfg && spicfg->wdelay)
@@ -232,7 +230,8 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value)
!(spi->mode & SPI_CS_HIGH));
} else {
if (value == BITBANG_CS_ACTIVE) {
- spidat1 |= SPIDAT1_CSHOLD_MASK;
+ if (!(spi->mode & SPI_CS_WORD))
+ spidat1 |= SPIDAT1_CSHOLD_MASK;
spidat1 &= ~(0x1 << chip_sel);
}
}
@@ -421,26 +420,17 @@ static int davinci_spi_setup(struct spi_device *spi)
{
int retval = 0;
struct davinci_spi *dspi;
- struct davinci_spi_platform_data *pdata;
struct spi_master *master = spi->master;
struct device_node *np = spi->dev.of_node;
bool internal_cs = true;
dspi = spi_master_get_devdata(spi->master);
- pdata = &dspi->pdata;
if (!(spi->mode & SPI_NO_CS)) {
if (np && (master->cs_gpios != NULL) && (spi->cs_gpio >= 0)) {
retval = gpio_direction_output(
spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
internal_cs = false;
- } else if (pdata->chip_sel &&
- spi->chip_select < pdata->num_chipselect &&
- pdata->chip_sel[spi->chip_select] != SPI_INTERN_CS) {
- spi->cs_gpio = pdata->chip_sel[spi->chip_select];
- retval = gpio_direction_output(
- spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
- internal_cs = false;
}
if (retval) {
@@ -449,8 +439,9 @@ static int davinci_spi_setup(struct spi_device *spi)
return retval;
}
- if (internal_cs)
+ if (internal_cs) {
set_io_bits(dspi->base + SPIPC0, 1 << spi->chip_select);
+ }
}
if (spi->mode & SPI_READY)
@@ -985,7 +976,7 @@ static int davinci_spi_probe(struct platform_device *pdev)
dspi->prescaler_limit = pdata->prescaler_limit;
dspi->version = pdata->version;
- dspi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP;
+ dspi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_WORD;
if (dspi->version == SPI_VERSION_2)
dspi->bitbang.flags |= SPI_READY;
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index e80f60ed6fdf..a768461614a0 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -34,8 +34,9 @@ struct dw_spi_mmio {
};
#define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24
-#define OCELOT_IF_SI_OWNER_MASK GENMASK(5, 4)
#define OCELOT_IF_SI_OWNER_OFFSET 4
+#define JAGUAR2_IF_SI_OWNER_OFFSET 6
+#define MSCC_IF_SI_OWNER_MASK GENMASK(1, 0)
#define MSCC_IF_SI_OWNER_SISL 0
#define MSCC_IF_SI_OWNER_SIBM 1
#define MSCC_IF_SI_OWNER_SIMC 2
@@ -76,7 +77,8 @@ static void dw_spi_mscc_set_cs(struct spi_device *spi, bool enable)
}
static int dw_spi_mscc_init(struct platform_device *pdev,
- struct dw_spi_mmio *dwsmmio)
+ struct dw_spi_mmio *dwsmmio,
+ const char *cpu_syscon, u32 if_si_owner_offset)
{
struct dw_spi_mscc *dwsmscc;
struct resource *res;
@@ -92,7 +94,7 @@ static int dw_spi_mscc_init(struct platform_device *pdev,
return PTR_ERR(dwsmscc->spi_mst);
}
- dwsmscc->syscon = syscon_regmap_lookup_by_compatible("mscc,ocelot-cpu-syscon");
+ dwsmscc->syscon = syscon_regmap_lookup_by_compatible(cpu_syscon);
if (IS_ERR(dwsmscc->syscon))
return PTR_ERR(dwsmscc->syscon);
@@ -101,8 +103,8 @@ static int dw_spi_mscc_init(struct platform_device *pdev,
/* Select the owner of the SI interface */
regmap_update_bits(dwsmscc->syscon, MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL,
- OCELOT_IF_SI_OWNER_MASK,
- MSCC_IF_SI_OWNER_SIMC << OCELOT_IF_SI_OWNER_OFFSET);
+ MSCC_IF_SI_OWNER_MASK << if_si_owner_offset,
+ MSCC_IF_SI_OWNER_SIMC << if_si_owner_offset);
dwsmmio->dws.set_cs = dw_spi_mscc_set_cs;
dwsmmio->priv = dwsmscc;
@@ -110,6 +112,20 @@ static int dw_spi_mscc_init(struct platform_device *pdev,
return 0;
}
+static int dw_spi_mscc_ocelot_init(struct platform_device *pdev,
+ struct dw_spi_mmio *dwsmmio)
+{
+ return dw_spi_mscc_init(pdev, dwsmmio, "mscc,ocelot-cpu-syscon",
+ OCELOT_IF_SI_OWNER_OFFSET);
+}
+
+static int dw_spi_mscc_jaguar2_init(struct platform_device *pdev,
+ struct dw_spi_mmio *dwsmmio)
+{
+ return dw_spi_mscc_init(pdev, dwsmmio, "mscc,jaguar2-cpu-syscon",
+ JAGUAR2_IF_SI_OWNER_OFFSET);
+}
+
static int dw_spi_mmio_probe(struct platform_device *pdev)
{
int (*init_func)(struct platform_device *pdev,
@@ -212,7 +228,8 @@ static int dw_spi_mmio_remove(struct platform_device *pdev)
static const struct of_device_id dw_spi_mmio_of_match[] = {
{ .compatible = "snps,dw-apb-ssi", },
- { .compatible = "mscc,ocelot-spi", .data = dw_spi_mscc_init},
+ { .compatible = "mscc,ocelot-spi", .data = dw_spi_mscc_ocelot_init},
+ { .compatible = "mscc,jaguar2-spi", .data = dw_spi_mscc_jaguar2_init},
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index ac2eb89ef7a5..3e205ab60cd4 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -308,15 +308,10 @@ static int dw_spi_transfer_one(struct spi_controller *master,
dws->current_freq = transfer->speed_hz;
spi_set_clk(dws, chip->clk_div);
}
- if (transfer->bits_per_word == 8) {
- dws->n_bytes = 1;
- dws->dma_width = 1;
- } else if (transfer->bits_per_word == 16) {
- dws->n_bytes = 2;
- dws->dma_width = 2;
- } else {
- return -EINVAL;
- }
+
+ dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE);
+ dws->dma_width = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE);
+
/* Default SPI mode is SCPOL = 0, SCPH = 0 */
cr0 = (transfer->bits_per_word - 1)
| (chip->type << SPI_FRF_OFFSET)
@@ -496,7 +491,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
}
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
- master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
+ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
master->bus_num = dws->bus_num;
master->num_chipselect = dws->num_cs;
master->setup = dw_spi_setup;
@@ -572,13 +567,8 @@ EXPORT_SYMBOL_GPL(dw_spi_suspend_host);
int dw_spi_resume_host(struct dw_spi *dws)
{
- int ret;
-
spi_hw_init(&dws->master->dev, dws);
- ret = spi_controller_resume(dws->master);
- if (ret)
- dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret);
- return ret;
+ return spi_controller_resume(dws->master);
}
EXPORT_SYMBOL_GPL(dw_spi_resume_host);
diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c
index f1526757aaf6..79fc3940245a 100644
--- a/drivers/spi/spi-ep93xx.c
+++ b/drivers/spi/spi-ep93xx.c
@@ -246,6 +246,19 @@ static int ep93xx_spi_read_write(struct spi_master *master)
return -EINPROGRESS;
}
+static enum dma_transfer_direction
+ep93xx_dma_data_to_trans_dir(enum dma_data_direction dir)
+{
+ switch (dir) {
+ case DMA_TO_DEVICE:
+ return DMA_MEM_TO_DEV;
+ case DMA_FROM_DEVICE:
+ return DMA_DEV_TO_MEM;
+ default:
+ return DMA_TRANS_NONE;
+ }
+}
+
/**
* ep93xx_spi_dma_prepare() - prepares a DMA transfer
* @master: SPI master
@@ -257,7 +270,7 @@ static int ep93xx_spi_read_write(struct spi_master *master)
*/
static struct dma_async_tx_descriptor *
ep93xx_spi_dma_prepare(struct spi_master *master,
- enum dma_transfer_direction dir)
+ enum dma_data_direction dir)
{
struct ep93xx_spi *espi = spi_master_get_devdata(master);
struct spi_transfer *xfer = master->cur_msg->state;
@@ -277,9 +290,9 @@ ep93xx_spi_dma_prepare(struct spi_master *master,
buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
memset(&conf, 0, sizeof(conf));
- conf.direction = dir;
+ conf.direction = ep93xx_dma_data_to_trans_dir(dir);
- if (dir == DMA_DEV_TO_MEM) {
+ if (dir == DMA_FROM_DEVICE) {
chan = espi->dma_rx;
buf = xfer->rx_buf;
sgt = &espi->rx_sgt;
@@ -343,7 +356,8 @@ ep93xx_spi_dma_prepare(struct spi_master *master,
if (!nents)
return ERR_PTR(-ENOMEM);
- txd = dmaengine_prep_slave_sg(chan, sgt->sgl, nents, dir, DMA_CTRL_ACK);
+ txd = dmaengine_prep_slave_sg(chan, sgt->sgl, nents, conf.direction,
+ DMA_CTRL_ACK);
if (!txd) {
dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
return ERR_PTR(-ENOMEM);
@@ -360,13 +374,13 @@ ep93xx_spi_dma_prepare(struct spi_master *master,
* unmapped.
*/
static void ep93xx_spi_dma_finish(struct spi_master *master,
- enum dma_transfer_direction dir)
+ enum dma_data_direction dir)
{
struct ep93xx_spi *espi = spi_master_get_devdata(master);
struct dma_chan *chan;
struct sg_table *sgt;
- if (dir == DMA_DEV_TO_MEM) {
+ if (dir == DMA_FROM_DEVICE) {
chan = espi->dma_rx;
sgt = &espi->rx_sgt;
} else {
@@ -381,8 +395,8 @@ static void ep93xx_spi_dma_callback(void *callback_param)
{
struct spi_master *master = callback_param;
- ep93xx_spi_dma_finish(master, DMA_MEM_TO_DEV);
- ep93xx_spi_dma_finish(master, DMA_DEV_TO_MEM);
+ ep93xx_spi_dma_finish(master, DMA_TO_DEVICE);
+ ep93xx_spi_dma_finish(master, DMA_FROM_DEVICE);
spi_finalize_current_transfer(master);
}
@@ -392,15 +406,15 @@ static int ep93xx_spi_dma_transfer(struct spi_master *master)
struct ep93xx_spi *espi = spi_master_get_devdata(master);
struct dma_async_tx_descriptor *rxd, *txd;
- rxd = ep93xx_spi_dma_prepare(master, DMA_DEV_TO_MEM);
+ rxd = ep93xx_spi_dma_prepare(master, DMA_FROM_DEVICE);
if (IS_ERR(rxd)) {
dev_err(&master->dev, "DMA RX failed: %ld\n", PTR_ERR(rxd));
return PTR_ERR(rxd);
}
- txd = ep93xx_spi_dma_prepare(master, DMA_MEM_TO_DEV);
+ txd = ep93xx_spi_dma_prepare(master, DMA_TO_DEVICE);
if (IS_ERR(txd)) {
- ep93xx_spi_dma_finish(master, DMA_DEV_TO_MEM);
+ ep93xx_spi_dma_finish(master, DMA_FROM_DEVICE);
dev_err(&master->dev, "DMA TX failed: %ld\n", PTR_ERR(txd));
return PTR_ERR(txd);
}
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 1e8ff6256079..cf2118dc91f4 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -798,10 +798,8 @@ static int of_fsl_espi_suspend(struct device *dev)
int ret;
ret = spi_master_suspend(master);
- if (ret) {
- dev_warn(dev, "cannot suspend master\n");
+ if (ret)
return ret;
- }
return pm_runtime_force_suspend(dev);
}
diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index e6d5cc6ab108..51670976faa3 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -276,7 +276,7 @@ static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi)
fsl_lpspi_set_watermark(fsl_lpspi);
- temp = CFGR1_PCSCFG | CFGR1_MASTER | CFGR1_NOSTALL;
+ temp = CFGR1_PCSCFG | CFGR1_MASTER;
if (fsl_lpspi->config.mode & SPI_CS_HIGH)
temp |= CFGR1_PCSPOL;
writel(temp, fsl_lpspi->base + IMX7ULP_CFGR1);
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index 0626e6e3ea0c..a2b08b464857 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -295,9 +295,11 @@ static int spi_gpio_request(struct device *dev,
spi_gpio->miso = devm_gpiod_get_optional(dev, "miso", GPIOD_IN);
if (IS_ERR(spi_gpio->miso))
return PTR_ERR(spi_gpio->miso);
- if (!spi_gpio->miso)
- /* HW configuration without MISO pin */
- *mflags |= SPI_MASTER_NO_RX;
+ /*
+ * No setting SPI_MASTER_NO_RX here - if there is only a MOSI
+ * pin connected the host can still do RX by changing the
+ * direction of the line.
+ */
spi_gpio->sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW);
if (IS_ERR(spi_gpio->mosi))
@@ -423,7 +425,7 @@ static int spi_gpio_probe(struct platform_device *pdev)
spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
spi_gpio->bitbang.set_line_direction = spi_gpio_set_direction;
- if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) {
+ if ((master_flags & SPI_MASTER_NO_TX) == 0) {
spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;
spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;
@@ -447,10 +449,8 @@ static int spi_gpio_probe(struct platform_device *pdev)
static int spi_gpio_remove(struct platform_device *pdev)
{
struct spi_gpio *spi_gpio;
- struct spi_gpio_platform_data *pdata;
spi_gpio = platform_get_drvdata(pdev);
- pdata = dev_get_platdata(&pdev->dev);
/* stop() unregisters child devices too */
spi_bitbang_stop(&spi_gpio->bitbang);
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index e43842c7a31a..62a7b80801d2 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -12,6 +12,8 @@
#include "internals.h"
+#define SPI_MEM_MAX_BUSWIDTH 4
+
/**
* spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a
* memory operation
@@ -149,6 +151,44 @@ static bool spi_mem_default_supports_op(struct spi_mem *mem,
}
EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
+static bool spi_mem_buswidth_is_valid(u8 buswidth)
+{
+ if (hweight8(buswidth) > 1 || buswidth > SPI_MEM_MAX_BUSWIDTH)
+ return false;
+
+ return true;
+}
+
+static int spi_mem_check_op(const struct spi_mem_op *op)
+{
+ if (!op->cmd.buswidth)
+ return -EINVAL;
+
+ if ((op->addr.nbytes && !op->addr.buswidth) ||
+ (op->dummy.nbytes && !op->dummy.buswidth) ||
+ (op->data.nbytes && !op->data.buswidth))
+ return -EINVAL;
+
+ if (!spi_mem_buswidth_is_valid(op->cmd.buswidth) ||
+ !spi_mem_buswidth_is_valid(op->addr.buswidth) ||
+ !spi_mem_buswidth_is_valid(op->dummy.buswidth) ||
+ !spi_mem_buswidth_is_valid(op->data.buswidth))
+ return -EINVAL;
+
+ return 0;
+}
+
+static bool spi_mem_internal_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct spi_controller *ctlr = mem->spi->controller;
+
+ if (ctlr->mem_ops && ctlr->mem_ops->supports_op)
+ return ctlr->mem_ops->supports_op(mem, op);
+
+ return spi_mem_default_supports_op(mem, op);
+}
+
/**
* spi_mem_supports_op() - Check if a memory device and the controller it is
* connected to support a specific memory operation
@@ -166,12 +206,10 @@ EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
*/
bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
- struct spi_controller *ctlr = mem->spi->controller;
-
- if (ctlr->mem_ops && ctlr->mem_ops->supports_op)
- return ctlr->mem_ops->supports_op(mem, op);
+ if (spi_mem_check_op(op))
+ return false;
- return spi_mem_default_supports_op(mem, op);
+ return spi_mem_internal_supports_op(mem, op);
}
EXPORT_SYMBOL_GPL(spi_mem_supports_op);
@@ -196,7 +234,11 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
u8 *tmpbuf;
int ret;
- if (!spi_mem_supports_op(mem, op))
+ ret = spi_mem_check_op(op);
+ if (ret)
+ return ret;
+
+ if (!spi_mem_internal_supports_op(mem, op))
return -ENOTSUPP;
if (ctlr->mem_ops) {
@@ -346,10 +388,25 @@ EXPORT_SYMBOL_GPL(spi_mem_get_name);
int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
struct spi_controller *ctlr = mem->spi->controller;
+ size_t len;
+
+ len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
return ctlr->mem_ops->adjust_op_size(mem, op);
+ if (!ctlr->mem_ops || !ctlr->mem_ops->exec_op) {
+ if (len > spi_max_transfer_size(mem->spi))
+ return -EINVAL;
+
+ op->data.nbytes = min3((size_t)op->data.nbytes,
+ spi_max_transfer_size(mem->spi),
+ spi_max_message_size(mem->spi) -
+ len);
+ if (!op->data.nbytes)
+ return -EINVAL;
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
index 86bf45667a04..3dc31627c655 100644
--- a/drivers/spi/spi-mt65xx.c
+++ b/drivers/spi/spi-mt65xx.c
@@ -98,6 +98,7 @@ struct mtk_spi {
struct clk *parent_clk, *sel_clk, *spi_clk;
struct spi_transfer *cur_transfer;
u32 xfer_len;
+ u32 num_xfered;
struct scatterlist *tx_sgl, *rx_sgl;
u32 tx_sgl_len, rx_sgl_len;
const struct mtk_spi_compatible *dev_comp;
@@ -385,6 +386,7 @@ static int mtk_spi_fifo_transfer(struct spi_master *master,
mdata->cur_transfer = xfer;
mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len);
+ mdata->num_xfered = 0;
mtk_spi_prepare_transfer(master, xfer);
mtk_spi_setup_packet(master);
@@ -415,6 +417,7 @@ static int mtk_spi_dma_transfer(struct spi_master *master,
mdata->tx_sgl_len = 0;
mdata->rx_sgl_len = 0;
mdata->cur_transfer = xfer;
+ mdata->num_xfered = 0;
mtk_spi_prepare_transfer(master, xfer);
@@ -482,7 +485,7 @@ static int mtk_spi_setup(struct spi_device *spi)
static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
{
- u32 cmd, reg_val, cnt, remainder;
+ u32 cmd, reg_val, cnt, remainder, len;
struct spi_master *master = dev_id;
struct mtk_spi *mdata = spi_master_get_devdata(master);
struct spi_transfer *trans = mdata->cur_transfer;
@@ -497,36 +500,38 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
if (trans->rx_buf) {
cnt = mdata->xfer_len / 4;
ioread32_rep(mdata->base + SPI_RX_DATA_REG,
- trans->rx_buf, cnt);
+ trans->rx_buf + mdata->num_xfered, cnt);
remainder = mdata->xfer_len % 4;
if (remainder > 0) {
reg_val = readl(mdata->base + SPI_RX_DATA_REG);
- memcpy(trans->rx_buf + (cnt * 4),
- &reg_val, remainder);
+ memcpy(trans->rx_buf +
+ mdata->num_xfered +
+ (cnt * 4),
+ &reg_val,
+ remainder);
}
}
- trans->len -= mdata->xfer_len;
- if (!trans->len) {
+ mdata->num_xfered += mdata->xfer_len;
+ if (mdata->num_xfered == trans->len) {
spi_finalize_current_transfer(master);
return IRQ_HANDLED;
}
- if (trans->tx_buf)
- trans->tx_buf += mdata->xfer_len;
- if (trans->rx_buf)
- trans->rx_buf += mdata->xfer_len;
-
- mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, trans->len);
+ len = trans->len - mdata->num_xfered;
+ mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, len);
mtk_spi_setup_packet(master);
- cnt = trans->len / 4;
- iowrite32_rep(mdata->base + SPI_TX_DATA_REG, trans->tx_buf, cnt);
+ cnt = len / 4;
+ iowrite32_rep(mdata->base + SPI_TX_DATA_REG,
+ trans->tx_buf + mdata->num_xfered, cnt);
- remainder = trans->len % 4;
+ remainder = len % 4;
if (remainder > 0) {
reg_val = 0;
- memcpy(&reg_val, trans->tx_buf + (cnt * 4), remainder);
+ memcpy(&reg_val,
+ trans->tx_buf + (cnt * 4) + mdata->num_xfered,
+ remainder);
writel(reg_val, mdata->base + SPI_TX_DATA_REG);
}
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index 47ef6b1a2e76..7f280567093e 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -431,6 +431,7 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
int word_len;
struct orion_spi *orion_spi;
int cs = spi->chip_select;
+ void __iomem *vaddr;
word_len = spi->bits_per_word;
count = xfer->len;
@@ -441,8 +442,9 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
* Use SPI direct write mode if base address is available. Otherwise
* fall back to PIO mode for this transfer.
*/
- if ((orion_spi->child[cs].direct_access.vaddr) && (xfer->tx_buf) &&
- (word_len == 8)) {
+ vaddr = orion_spi->child[cs].direct_access.vaddr;
+
+ if (vaddr && xfer->tx_buf && word_len == 8) {
unsigned int cnt = count / 4;
unsigned int rem = count % 4;
@@ -450,13 +452,11 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
* Send the TX-data to the SPI device via the direct
* mapped address window
*/
- iowrite32_rep(orion_spi->child[cs].direct_access.vaddr,
- xfer->tx_buf, cnt);
+ iowrite32_rep(vaddr, xfer->tx_buf, cnt);
if (rem) {
u32 *buf = (u32 *)xfer->tx_buf;
- iowrite8_rep(orion_spi->child[cs].direct_access.vaddr,
- &buf[cnt], rem);
+ iowrite8_rep(vaddr, &buf[cnt], rem);
}
return count;
@@ -683,6 +683,7 @@ static int orion_spi_probe(struct platform_device *pdev)
}
for_each_available_child_of_node(pdev->dev.of_node, np) {
+ struct orion_direct_acc *dir_acc;
u32 cs;
int cs_gpio;
@@ -750,14 +751,13 @@ static int orion_spi_probe(struct platform_device *pdev)
* This needs to get extended for the direct SPI-NOR / SPI-NAND
* support, once this gets implemented.
*/
- spi->child[cs].direct_access.vaddr = devm_ioremap(&pdev->dev,
- r->start,
- PAGE_SIZE);
- if (!spi->child[cs].direct_access.vaddr) {
+ dir_acc = &spi->child[cs].direct_access;
+ dir_acc->vaddr = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
+ if (!dir_acc->vaddr) {
status = -ENOMEM;
goto out_rel_axi_clk;
}
- spi->child[cs].direct_access.size = PAGE_SIZE;
+ dir_acc->size = PAGE_SIZE;
dev_info(&pdev->dev, "CS%d configured for direct access\n", cs);
}
diff --git a/drivers/spi/spi-pic32-sqi.c b/drivers/spi/spi-pic32-sqi.c
index bd1c6b53283f..62e6bf1f50b1 100644
--- a/drivers/spi/spi-pic32-sqi.c
+++ b/drivers/spi/spi-pic32-sqi.c
@@ -656,7 +656,7 @@ static int pic32_sqi_probe(struct platform_device *pdev)
master->max_speed_hz = clk_get_rate(sqi->base_clk);
master->dma_alignment = 32;
master->max_dma_len = PESQI_BD_BUF_LEN_MAX;
- master->dev.of_node = of_node_get(pdev->dev.of_node);
+ master->dev.of_node = pdev->dev.of_node;
master->mode_bits = SPI_MODE_3 | SPI_MODE_0 | SPI_TX_DUAL |
SPI_RX_DUAL | SPI_TX_QUAD | SPI_RX_QUAD;
master->flags = SPI_MASTER_HALF_DUPLEX;
diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c
index f8a45af1fa9f..131849adc570 100644
--- a/drivers/spi/spi-pic32.c
+++ b/drivers/spi/spi-pic32.c
@@ -320,7 +320,7 @@ static int pic32_spi_dma_transfer(struct pic32_spi *pic32s,
desc_rx = dmaengine_prep_slave_sg(master->dma_rx,
xfer->rx_sg.sgl,
xfer->rx_sg.nents,
- DMA_FROM_DEVICE,
+ DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_rx) {
ret = -EINVAL;
@@ -330,7 +330,7 @@ static int pic32_spi_dma_transfer(struct pic32_spi *pic32s,
desc_tx = dmaengine_prep_slave_sg(master->dma_tx,
xfer->tx_sg.sgl,
xfer->tx_sg.nents,
- DMA_TO_DEVICE,
+ DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc_tx) {
ret = -EINVAL;
@@ -774,7 +774,7 @@ static int pic32_spi_probe(struct platform_device *pdev)
if (ret)
goto err_master;
- master->dev.of_node = of_node_get(pdev->dev.of_node);
+ master->dev.of_node = pdev->dev.of_node;
master->mode_bits = SPI_MODE_3 | SPI_MODE_0 | SPI_CS_HIGH;
master->num_chipselect = 1; /* single chip-select */
master->max_speed_hz = clk_get_rate(pic32s->clk);
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 1af8c96b940e..6120e6abcd96 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -1490,10 +1490,8 @@ static void do_polling_transfer(struct pl022 *pl022)
struct spi_message *message = NULL;
struct spi_transfer *transfer = NULL;
struct spi_transfer *previous = NULL;
- struct chip_data *chip;
unsigned long time, timeout;
- chip = pl022->cur_chip;
message = pl022->cur_msg;
while (message->state != STATE_DONE) {
@@ -2325,10 +2323,8 @@ static int pl022_suspend(struct device *dev)
int ret;
ret = spi_master_suspend(pl022->master);
- if (ret) {
- dev_warn(dev, "cannot suspend master\n");
+ if (ret)
return ret;
- }
ret = pm_runtime_force_suspend(dev);
if (ret) {
@@ -2353,9 +2349,7 @@ static int pl022_resume(struct device *dev)
/* Start the queue running */
ret = spi_master_resume(pl022->master);
- if (ret)
- dev_err(dev, "problem starting queue (%d)\n", ret);
- else
+ if (!ret)
dev_dbg(dev, "resumed\n");
return ret;
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index 14f4ea59caff..d68f511a9ef1 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -665,9 +665,11 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
bytes_left = drv_data->rx_end - drv_data->rx;
switch (drv_data->n_bytes) {
case 4:
- bytes_left >>= 1;
+ bytes_left >>= 2;
+ break;
case 2:
bytes_left >>= 1;
+ break;
}
rx_thre = pxa2xx_spi_get_rx_default_thre(drv_data);
@@ -1764,14 +1766,6 @@ static int pxa2xx_spi_remove(struct platform_device *pdev)
return 0;
}
-static void pxa2xx_spi_shutdown(struct platform_device *pdev)
-{
- int status = 0;
-
- if ((status = pxa2xx_spi_remove(pdev)) != 0)
- dev_err(&pdev->dev, "shutdown failed with %d\n", status);
-}
-
#ifdef CONFIG_PM_SLEEP
static int pxa2xx_spi_suspend(struct device *dev)
{
@@ -1808,13 +1802,7 @@ static int pxa2xx_spi_resume(struct device *dev)
lpss_ssp_setup(drv_data);
/* Start the queue running */
- status = spi_controller_resume(drv_data->master);
- if (status != 0) {
- dev_err(dev, "problem starting queue (%d)\n", status);
- return status;
- }
-
- return 0;
+ return spi_controller_resume(drv_data->master);
}
#endif
@@ -1851,7 +1839,6 @@ static struct platform_driver driver = {
},
.probe = pxa2xx_spi_probe,
.remove = pxa2xx_spi_remove,
- .shutdown = pxa2xx_spi_shutdown,
};
static int __init pxa2xx_spi_init(void)
diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c
index 3641d0e20135..fbbf9a188247 100644
--- a/drivers/spi/spi-rb4xx.c
+++ b/drivers/spi/spi-rb4xx.c
@@ -159,7 +159,7 @@ static int rb4xx_spi_probe(struct platform_device *pdev)
master->bus_num = 0;
master->num_chipselect = 3;
master->mode_bits = SPI_TX_DUAL;
- master->bits_per_word_mask = BIT(7);
+ master->bits_per_word_mask = SPI_BPW_MASK(8);
master->flags = SPI_MASTER_MUST_TX;
master->transfer_one = rb4xx_transfer_one;
master->set_cs = rb4xx_set_cs;
diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c
index 95dc4d78618d..018dec2fac4b 100644
--- a/drivers/spi/spi-rspi.c
+++ b/drivers/spi/spi-rspi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* SH RSPI driver
*
@@ -6,15 +7,6 @@
*
* Based on spi-sh.c:
* Copyright (C) 2011 Renesas Solutions Corp.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c
index 20e800e70442..dc0926e43665 100644
--- a/drivers/spi/spi-sh-hspi.c
+++ b/drivers/spi/spi-sh-hspi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* SuperH HSPI bus driver
*
@@ -7,15 +8,6 @@
* Based on pxa2xx_spi.c:
* Copyright (C) 2011 Renesas Solutions Corp.
* Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/clk.h>
@@ -316,6 +308,6 @@ static struct platform_driver hspi_driver = {
module_platform_driver(hspi_driver);
MODULE_DESCRIPTION("SuperH HSPI bus driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Kuninori Morimoto <[email protected]>");
MODULE_ALIAS("platform:sh-hspi");
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 539d6d1a277a..3eb7bcd33f22 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* SuperH MSIOF SPI Master Interface
*
* Copyright (c) 2009 Magnus Damm
* Copyright (C) 2014 Renesas Electronics Corporation
* Copyright (C) 2014-2017 Glider bvba
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
#include <linux/bitmap.h>
diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c
index 50e0ea9acf8b..f1ee58208216 100644
--- a/drivers/spi/spi-sh.c
+++ b/drivers/spi/spi-sh.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* SH SPI bus driver
*
@@ -5,15 +6,6 @@
*
* Based on pxa2xx_spi.c:
* Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -522,6 +514,6 @@ static struct platform_driver spi_sh_driver = {
module_platform_driver(spi_sh_driver);
MODULE_DESCRIPTION("SH SPI bus driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Yoshihiro Shimoda");
MODULE_ALIAS("platform:sh_spi");
diff --git a/drivers/spi/spi-slave-mt27xx.c b/drivers/spi/spi-slave-mt27xx.c
new file mode 100644
index 000000000000..d1075433f6a6
--- /dev/null
+++ b/drivers/spi/spi-slave-mt27xx.c
@@ -0,0 +1,554 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (c) 2018 MediaTek Inc.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/spi/spi.h>
+
+#define SPIS_IRQ_EN_REG 0x0
+#define SPIS_IRQ_CLR_REG 0x4
+#define SPIS_IRQ_ST_REG 0x8
+#define SPIS_IRQ_MASK_REG 0xc
+#define SPIS_CFG_REG 0x10
+#define SPIS_RX_DATA_REG 0x14
+#define SPIS_TX_DATA_REG 0x18
+#define SPIS_RX_DST_REG 0x1c
+#define SPIS_TX_SRC_REG 0x20
+#define SPIS_DMA_CFG_REG 0x30
+#define SPIS_SOFT_RST_REG 0x40
+
+/* SPIS_IRQ_EN_REG */
+#define DMA_DONE_EN BIT(7)
+#define DATA_DONE_EN BIT(2)
+#define RSTA_DONE_EN BIT(1)
+#define CMD_INVALID_EN BIT(0)
+
+/* SPIS_IRQ_ST_REG */
+#define DMA_DONE_ST BIT(7)
+#define DATA_DONE_ST BIT(2)
+#define RSTA_DONE_ST BIT(1)
+#define CMD_INVALID_ST BIT(0)
+
+/* SPIS_IRQ_MASK_REG */
+#define DMA_DONE_MASK BIT(7)
+#define DATA_DONE_MASK BIT(2)
+#define RSTA_DONE_MASK BIT(1)
+#define CMD_INVALID_MASK BIT(0)
+
+/* SPIS_CFG_REG */
+#define SPIS_TX_ENDIAN BIT(7)
+#define SPIS_RX_ENDIAN BIT(6)
+#define SPIS_TXMSBF BIT(5)
+#define SPIS_RXMSBF BIT(4)
+#define SPIS_CPHA BIT(3)
+#define SPIS_CPOL BIT(2)
+#define SPIS_TX_EN BIT(1)
+#define SPIS_RX_EN BIT(0)
+
+/* SPIS_DMA_CFG_REG */
+#define TX_DMA_TRIG_EN BIT(31)
+#define TX_DMA_EN BIT(30)
+#define RX_DMA_EN BIT(29)
+#define TX_DMA_LEN 0xfffff
+
+/* SPIS_SOFT_RST_REG */
+#define SPIS_DMA_ADDR_EN BIT(1)
+#define SPIS_SOFT_RST BIT(0)
+
+#define MTK_SPI_SLAVE_MAX_FIFO_SIZE 512U
+
+struct mtk_spi_slave {
+ struct device *dev;
+ void __iomem *base;
+ struct clk *spi_clk;
+ struct completion xfer_done;
+ struct spi_transfer *cur_transfer;
+ bool slave_aborted;
+};
+
+static const struct of_device_id mtk_spi_slave_of_match[] = {
+ { .compatible = "mediatek,mt2712-spi-slave", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mtk_spi_slave_of_match);
+
+static void mtk_spi_slave_disable_dma(struct mtk_spi_slave *mdata)
+{
+ u32 reg_val;
+
+ reg_val = readl(mdata->base + SPIS_DMA_CFG_REG);
+ reg_val &= ~RX_DMA_EN;
+ reg_val &= ~TX_DMA_EN;
+ writel(reg_val, mdata->base + SPIS_DMA_CFG_REG);
+}
+
+static void mtk_spi_slave_disable_xfer(struct mtk_spi_slave *mdata)
+{
+ u32 reg_val;
+
+ reg_val = readl(mdata->base + SPIS_CFG_REG);
+ reg_val &= ~SPIS_TX_EN;
+ reg_val &= ~SPIS_RX_EN;
+ writel(reg_val, mdata->base + SPIS_CFG_REG);
+}
+
+static int mtk_spi_slave_wait_for_completion(struct mtk_spi_slave *mdata)
+{
+ if (wait_for_completion_interruptible(&mdata->xfer_done) ||
+ mdata->slave_aborted) {
+ dev_err(mdata->dev, "interrupted\n");
+ return -EINTR;
+ }
+
+ return 0;
+}
+
+static int mtk_spi_slave_prepare_message(struct spi_controller *ctlr,
+ struct spi_message *msg)
+{
+ struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
+ struct spi_device *spi = msg->spi;
+ bool cpha, cpol;
+ u32 reg_val;
+
+ cpha = spi->mode & SPI_CPHA ? 1 : 0;
+ cpol = spi->mode & SPI_CPOL ? 1 : 0;
+
+ reg_val = readl(mdata->base + SPIS_CFG_REG);
+ if (cpha)
+ reg_val |= SPIS_CPHA;
+ else
+ reg_val &= ~SPIS_CPHA;
+ if (cpol)
+ reg_val |= SPIS_CPOL;
+ else
+ reg_val &= ~SPIS_CPOL;
+
+ if (spi->mode & SPI_LSB_FIRST)
+ reg_val &= ~(SPIS_TXMSBF | SPIS_RXMSBF);
+ else
+ reg_val |= SPIS_TXMSBF | SPIS_RXMSBF;
+
+ reg_val &= ~SPIS_TX_ENDIAN;
+ reg_val &= ~SPIS_RX_ENDIAN;
+ writel(reg_val, mdata->base + SPIS_CFG_REG);
+
+ return 0;
+}
+
+static int mtk_spi_slave_fifo_transfer(struct spi_controller *ctlr,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
+ int reg_val, cnt, remainder, ret;
+
+ writel(SPIS_SOFT_RST, mdata->base + SPIS_SOFT_RST_REG);
+
+ reg_val = readl(mdata->base + SPIS_CFG_REG);
+ if (xfer->rx_buf)
+ reg_val |= SPIS_RX_EN;
+ if (xfer->tx_buf)
+ reg_val |= SPIS_TX_EN;
+ writel(reg_val, mdata->base + SPIS_CFG_REG);
+
+ cnt = xfer->len / 4;
+ if (xfer->tx_buf)
+ iowrite32_rep(mdata->base + SPIS_TX_DATA_REG,
+ xfer->tx_buf, cnt);
+
+ remainder = xfer->len % 4;
+ if (xfer->tx_buf && remainder > 0) {
+ reg_val = 0;
+ memcpy(&reg_val, xfer->tx_buf + cnt * 4, remainder);
+ writel(reg_val, mdata->base + SPIS_TX_DATA_REG);
+ }
+
+ ret = mtk_spi_slave_wait_for_completion(mdata);
+ if (ret) {
+ mtk_spi_slave_disable_xfer(mdata);
+ writel(SPIS_SOFT_RST, mdata->base + SPIS_SOFT_RST_REG);
+ }
+
+ return ret;
+}
+
+static int mtk_spi_slave_dma_transfer(struct spi_controller *ctlr,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
+ struct device *dev = mdata->dev;
+ int reg_val, ret;
+
+ writel(SPIS_SOFT_RST, mdata->base + SPIS_SOFT_RST_REG);
+
+ if (xfer->tx_buf) {
+ /* tx_buf is a const void* where we need a void * for
+ * the dma mapping
+ */
+ void *nonconst_tx = (void *)xfer->tx_buf;
+
+ xfer->tx_dma = dma_map_single(dev, nonconst_tx,
+ xfer->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, xfer->tx_dma)) {
+ ret = -ENOMEM;
+ goto disable_transfer;
+ }
+ }
+
+ if (xfer->rx_buf) {
+ xfer->rx_dma = dma_map_single(dev, xfer->rx_buf,
+ xfer->len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, xfer->rx_dma)) {
+ ret = -ENOMEM;
+ goto unmap_txdma;
+ }
+ }
+
+ writel(xfer->tx_dma, mdata->base + SPIS_TX_SRC_REG);
+ writel(xfer->rx_dma, mdata->base + SPIS_RX_DST_REG);
+
+ writel(SPIS_DMA_ADDR_EN, mdata->base + SPIS_SOFT_RST_REG);
+
+ /* enable config reg tx rx_enable */
+ reg_val = readl(mdata->base + SPIS_CFG_REG);
+ if (xfer->tx_buf)
+ reg_val |= SPIS_TX_EN;
+ if (xfer->rx_buf)
+ reg_val |= SPIS_RX_EN;
+ writel(reg_val, mdata->base + SPIS_CFG_REG);
+
+ /* config dma */
+ reg_val = 0;
+ reg_val |= (xfer->len - 1) & TX_DMA_LEN;
+ writel(reg_val, mdata->base + SPIS_DMA_CFG_REG);
+
+ reg_val = readl(mdata->base + SPIS_DMA_CFG_REG);
+ if (xfer->tx_buf)
+ reg_val |= TX_DMA_EN;
+ if (xfer->rx_buf)
+ reg_val |= RX_DMA_EN;
+ reg_val |= TX_DMA_TRIG_EN;
+ writel(reg_val, mdata->base + SPIS_DMA_CFG_REG);
+
+ ret = mtk_spi_slave_wait_for_completion(mdata);
+ if (ret)
+ goto unmap_rxdma;
+
+ return 0;
+
+unmap_rxdma:
+ if (xfer->rx_buf)
+ dma_unmap_single(dev, xfer->rx_dma,
+ xfer->len, DMA_FROM_DEVICE);
+
+unmap_txdma:
+ if (xfer->tx_buf)
+ dma_unmap_single(dev, xfer->tx_dma,
+ xfer->len, DMA_TO_DEVICE);
+
+disable_transfer:
+ mtk_spi_slave_disable_dma(mdata);
+ mtk_spi_slave_disable_xfer(mdata);
+ writel(SPIS_SOFT_RST, mdata->base + SPIS_SOFT_RST_REG);
+
+ return ret;
+}
+
+static int mtk_spi_slave_transfer_one(struct spi_controller *ctlr,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
+
+ reinit_completion(&mdata->xfer_done);
+ mdata->slave_aborted = false;
+ mdata->cur_transfer = xfer;
+
+ if (xfer->len > MTK_SPI_SLAVE_MAX_FIFO_SIZE)
+ return mtk_spi_slave_dma_transfer(ctlr, spi, xfer);
+ else
+ return mtk_spi_slave_fifo_transfer(ctlr, spi, xfer);
+}
+
+static int mtk_spi_slave_setup(struct spi_device *spi)
+{
+ struct mtk_spi_slave *mdata = spi_controller_get_devdata(spi->master);
+ u32 reg_val;
+
+ reg_val = DMA_DONE_EN | DATA_DONE_EN |
+ RSTA_DONE_EN | CMD_INVALID_EN;
+ writel(reg_val, mdata->base + SPIS_IRQ_EN_REG);
+
+ reg_val = DMA_DONE_MASK | DATA_DONE_MASK |
+ RSTA_DONE_MASK | CMD_INVALID_MASK;
+ writel(reg_val, mdata->base + SPIS_IRQ_MASK_REG);
+
+ mtk_spi_slave_disable_dma(mdata);
+ mtk_spi_slave_disable_xfer(mdata);
+
+ return 0;
+}
+
+static int mtk_slave_abort(struct spi_controller *ctlr)
+{
+ struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
+
+ mdata->slave_aborted = true;
+ complete(&mdata->xfer_done);
+
+ return 0;
+}
+
+static irqreturn_t mtk_spi_slave_interrupt(int irq, void *dev_id)
+{
+ struct spi_controller *ctlr = dev_id;
+ struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
+ struct spi_transfer *trans = mdata->cur_transfer;
+ u32 int_status, reg_val, cnt, remainder;
+
+ int_status = readl(mdata->base + SPIS_IRQ_ST_REG);
+ writel(int_status, mdata->base + SPIS_IRQ_CLR_REG);
+
+ if (!trans)
+ return IRQ_NONE;
+
+ if ((int_status & DMA_DONE_ST) &&
+ ((int_status & DATA_DONE_ST) ||
+ (int_status & RSTA_DONE_ST))) {
+ writel(SPIS_SOFT_RST, mdata->base + SPIS_SOFT_RST_REG);
+
+ if (trans->tx_buf)
+ dma_unmap_single(mdata->dev, trans->tx_dma,
+ trans->len, DMA_TO_DEVICE);
+ if (trans->rx_buf)
+ dma_unmap_single(mdata->dev, trans->rx_dma,
+ trans->len, DMA_FROM_DEVICE);
+
+ mtk_spi_slave_disable_dma(mdata);
+ mtk_spi_slave_disable_xfer(mdata);
+ }
+
+ if ((!(int_status & DMA_DONE_ST)) &&
+ ((int_status & DATA_DONE_ST) ||
+ (int_status & RSTA_DONE_ST))) {
+ cnt = trans->len / 4;
+ if (trans->rx_buf)
+ ioread32_rep(mdata->base + SPIS_RX_DATA_REG,
+ trans->rx_buf, cnt);
+ remainder = trans->len % 4;
+ if (trans->rx_buf && remainder > 0) {
+ reg_val = readl(mdata->base + SPIS_RX_DATA_REG);
+ memcpy(trans->rx_buf + (cnt * 4),
+ &reg_val, remainder);
+ }
+
+ mtk_spi_slave_disable_xfer(mdata);
+ }
+
+ if (int_status & CMD_INVALID_ST) {
+ dev_warn(&ctlr->dev, "cmd invalid\n");
+ return IRQ_NONE;
+ }
+
+ mdata->cur_transfer = NULL;
+ complete(&mdata->xfer_done);
+
+ return IRQ_HANDLED;
+}
+
+static int mtk_spi_slave_probe(struct platform_device *pdev)
+{
+ struct spi_controller *ctlr;
+ struct mtk_spi_slave *mdata;
+ struct resource *res;
+ int irq, ret;
+
+ ctlr = spi_alloc_slave(&pdev->dev, sizeof(*mdata));
+ if (!ctlr) {
+ dev_err(&pdev->dev, "failed to alloc spi slave\n");
+ return -ENOMEM;
+ }
+
+ ctlr->auto_runtime_pm = true;
+ ctlr->dev.of_node = pdev->dev.of_node;
+ ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
+ ctlr->mode_bits |= SPI_LSB_FIRST;
+
+ ctlr->prepare_message = mtk_spi_slave_prepare_message;
+ ctlr->transfer_one = mtk_spi_slave_transfer_one;
+ ctlr->setup = mtk_spi_slave_setup;
+ ctlr->slave_abort = mtk_slave_abort;
+
+ mdata = spi_controller_get_devdata(ctlr);
+
+ platform_set_drvdata(pdev, ctlr);
+
+ init_completion(&mdata->xfer_done);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "failed to determine base address\n");
+ goto err_put_ctlr;
+ }
+
+ mdata->dev = &pdev->dev;
+
+ mdata->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mdata->base)) {
+ ret = PTR_ERR(mdata->base);
+ goto err_put_ctlr;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq (%d)\n", irq);
+ ret = irq;
+ goto err_put_ctlr;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, mtk_spi_slave_interrupt,
+ IRQF_TRIGGER_NONE, dev_name(&pdev->dev), ctlr);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register irq (%d)\n", ret);
+ goto err_put_ctlr;
+ }
+
+ mdata->spi_clk = devm_clk_get(&pdev->dev, "spi");
+ if (IS_ERR(mdata->spi_clk)) {
+ ret = PTR_ERR(mdata->spi_clk);
+ dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret);
+ goto err_put_ctlr;
+ }
+
+ ret = clk_prepare_enable(mdata->spi_clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret);
+ goto err_put_ctlr;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_spi_register_controller(&pdev->dev, ctlr);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to register slave controller(%d)\n", ret);
+ clk_disable_unprepare(mdata->spi_clk);
+ goto err_disable_runtime_pm;
+ }
+
+ clk_disable_unprepare(mdata->spi_clk);
+
+ return 0;
+
+err_disable_runtime_pm:
+ pm_runtime_disable(&pdev->dev);
+err_put_ctlr:
+ spi_controller_put(ctlr);
+
+ return ret;
+}
+
+static int mtk_spi_slave_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_spi_slave_suspend(struct device *dev)
+{
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
+ int ret;
+
+ ret = spi_controller_suspend(ctlr);
+ if (ret)
+ return ret;
+
+ if (!pm_runtime_suspended(dev))
+ clk_disable_unprepare(mdata->spi_clk);
+
+ return ret;
+}
+
+static int mtk_spi_slave_resume(struct device *dev)
+{
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
+ int ret;
+
+ if (!pm_runtime_suspended(dev)) {
+ ret = clk_prepare_enable(mdata->spi_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable spi_clk (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ ret = spi_controller_resume(ctlr);
+ if (ret < 0)
+ clk_disable_unprepare(mdata->spi_clk);
+
+ return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM
+static int mtk_spi_slave_runtime_suspend(struct device *dev)
+{
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
+
+ clk_disable_unprepare(mdata->spi_clk);
+
+ return 0;
+}
+
+static int mtk_spi_slave_runtime_resume(struct device *dev)
+{
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+ struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
+ int ret;
+
+ ret = clk_prepare_enable(mdata->spi_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable spi_clk (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops mtk_spi_slave_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(mtk_spi_slave_suspend, mtk_spi_slave_resume)
+ SET_RUNTIME_PM_OPS(mtk_spi_slave_runtime_suspend,
+ mtk_spi_slave_runtime_resume, NULL)
+};
+
+static struct platform_driver mtk_spi_slave_driver = {
+ .driver = {
+ .name = "mtk-spi-slave",
+ .pm = &mtk_spi_slave_pm,
+ .of_match_table = mtk_spi_slave_of_match,
+ },
+ .probe = mtk_spi_slave_probe,
+ .remove = mtk_spi_slave_remove,
+};
+
+module_platform_driver(mtk_spi_slave_driver);
+
+MODULE_DESCRIPTION("MTK SPI Slave Controller driver");
+MODULE_AUTHOR("Leilk Liu <[email protected]>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mtk-spi-slave");
diff --git a/drivers/spi/spi-slave-system-control.c b/drivers/spi/spi-slave-system-control.c
index c0257e937995..169f3d595f60 100644
--- a/drivers/spi/spi-slave-system-control.c
+++ b/drivers/spi/spi-slave-system-control.c
@@ -60,6 +60,7 @@ static void spi_slave_system_control_complete(void *arg)
case CMD_REBOOT:
dev_info(&priv->spi->dev, "Rebooting system...\n");
kernel_restart(NULL);
+ break;
case CMD_POWEROFF:
dev_info(&priv->spi->dev, "Powering off system...\n");
diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c
new file mode 100644
index 000000000000..8daa24eec624
--- /dev/null
+++ b/drivers/spi/spi-sprd.c
@@ -0,0 +1,745 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Spreadtrum Communications Inc.
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/spi/spi.h>
+
+#define SPRD_SPI_TXD 0x0
+#define SPRD_SPI_CLKD 0x4
+#define SPRD_SPI_CTL0 0x8
+#define SPRD_SPI_CTL1 0xc
+#define SPRD_SPI_CTL2 0x10
+#define SPRD_SPI_CTL3 0x14
+#define SPRD_SPI_CTL4 0x18
+#define SPRD_SPI_CTL5 0x1c
+#define SPRD_SPI_INT_EN 0x20
+#define SPRD_SPI_INT_CLR 0x24
+#define SPRD_SPI_INT_RAW_STS 0x28
+#define SPRD_SPI_INT_MASK_STS 0x2c
+#define SPRD_SPI_STS1 0x30
+#define SPRD_SPI_STS2 0x34
+#define SPRD_SPI_DSP_WAIT 0x38
+#define SPRD_SPI_STS3 0x3c
+#define SPRD_SPI_CTL6 0x40
+#define SPRD_SPI_STS4 0x44
+#define SPRD_SPI_FIFO_RST 0x48
+#define SPRD_SPI_CTL7 0x4c
+#define SPRD_SPI_STS5 0x50
+#define SPRD_SPI_CTL8 0x54
+#define SPRD_SPI_CTL9 0x58
+#define SPRD_SPI_CTL10 0x5c
+#define SPRD_SPI_CTL11 0x60
+#define SPRD_SPI_CTL12 0x64
+#define SPRD_SPI_STS6 0x68
+#define SPRD_SPI_STS7 0x6c
+#define SPRD_SPI_STS8 0x70
+#define SPRD_SPI_STS9 0x74
+
+/* Bits & mask definition for register CTL0 */
+#define SPRD_SPI_SCK_REV BIT(13)
+#define SPRD_SPI_NG_TX BIT(1)
+#define SPRD_SPI_NG_RX BIT(0)
+#define SPRD_SPI_CHNL_LEN_MASK GENMASK(4, 0)
+#define SPRD_SPI_CSN_MASK GENMASK(11, 8)
+#define SPRD_SPI_CS0_VALID BIT(8)
+
+/* Bits & mask definition for register SPI_INT_EN */
+#define SPRD_SPI_TX_END_INT_EN BIT(8)
+#define SPRD_SPI_RX_END_INT_EN BIT(9)
+
+/* Bits & mask definition for register SPI_INT_RAW_STS */
+#define SPRD_SPI_TX_END_RAW BIT(8)
+#define SPRD_SPI_RX_END_RAW BIT(9)
+
+/* Bits & mask definition for register SPI_INT_CLR */
+#define SPRD_SPI_TX_END_CLR BIT(8)
+#define SPRD_SPI_RX_END_CLR BIT(9)
+
+/* Bits & mask definition for register INT_MASK_STS */
+#define SPRD_SPI_MASK_RX_END BIT(9)
+#define SPRD_SPI_MASK_TX_END BIT(8)
+
+/* Bits & mask definition for register STS2 */
+#define SPRD_SPI_TX_BUSY BIT(8)
+
+/* Bits & mask definition for register CTL1 */
+#define SPRD_SPI_RX_MODE BIT(12)
+#define SPRD_SPI_TX_MODE BIT(13)
+#define SPRD_SPI_RTX_MD_MASK GENMASK(13, 12)
+
+/* Bits & mask definition for register CTL2 */
+#define SPRD_SPI_DMA_EN BIT(6)
+
+/* Bits & mask definition for register CTL4 */
+#define SPRD_SPI_START_RX BIT(9)
+#define SPRD_SPI_ONLY_RECV_MASK GENMASK(8, 0)
+
+/* Bits & mask definition for register SPI_INT_CLR */
+#define SPRD_SPI_RX_END_INT_CLR BIT(9)
+#define SPRD_SPI_TX_END_INT_CLR BIT(8)
+
+/* Bits & mask definition for register SPI_INT_RAW */
+#define SPRD_SPI_RX_END_IRQ BIT(9)
+#define SPRD_SPI_TX_END_IRQ BIT(8)
+
+/* Bits & mask definition for register CTL12 */
+#define SPRD_SPI_SW_RX_REQ BIT(0)
+#define SPRD_SPI_SW_TX_REQ BIT(1)
+
+/* Bits & mask definition for register CTL7 */
+#define SPRD_SPI_DATA_LINE2_EN BIT(15)
+#define SPRD_SPI_MODE_MASK GENMASK(5, 3)
+#define SPRD_SPI_MODE_OFFSET 3
+#define SPRD_SPI_3WIRE_MODE 4
+#define SPRD_SPI_4WIRE_MODE 0
+
+/* Bits & mask definition for register CTL8 */
+#define SPRD_SPI_TX_MAX_LEN_MASK GENMASK(19, 0)
+#define SPRD_SPI_TX_LEN_H_MASK GENMASK(3, 0)
+#define SPRD_SPI_TX_LEN_H_OFFSET 16
+
+/* Bits & mask definition for register CTL9 */
+#define SPRD_SPI_TX_LEN_L_MASK GENMASK(15, 0)
+
+/* Bits & mask definition for register CTL10 */
+#define SPRD_SPI_RX_MAX_LEN_MASK GENMASK(19, 0)
+#define SPRD_SPI_RX_LEN_H_MASK GENMASK(3, 0)
+#define SPRD_SPI_RX_LEN_H_OFFSET 16
+
+/* Bits & mask definition for register CTL11 */
+#define SPRD_SPI_RX_LEN_L_MASK GENMASK(15, 0)
+
+/* Default & maximum word delay cycles */
+#define SPRD_SPI_MIN_DELAY_CYCLE 14
+#define SPRD_SPI_MAX_DELAY_CYCLE 130
+
+#define SPRD_SPI_FIFO_SIZE 32
+#define SPRD_SPI_CHIP_CS_NUM 0x4
+#define SPRD_SPI_CHNL_LEN 2
+#define SPRD_SPI_DEFAULT_SOURCE 26000000
+#define SPRD_SPI_MAX_SPEED_HZ 48000000
+#define SPRD_SPI_AUTOSUSPEND_DELAY 100
+
+struct sprd_spi {
+ void __iomem *base;
+ struct device *dev;
+ struct clk *clk;
+ u32 src_clk;
+ u32 hw_mode;
+ u32 trans_len;
+ u32 trans_mode;
+ u32 word_delay;
+ u32 hw_speed_hz;
+ u32 len;
+ int status;
+ const void *tx_buf;
+ void *rx_buf;
+ int (*read_bufs)(struct sprd_spi *ss, u32 len);
+ int (*write_bufs)(struct sprd_spi *ss, u32 len);
+};
+
+static u32 sprd_spi_transfer_max_timeout(struct sprd_spi *ss,
+ struct spi_transfer *t)
+{
+ /*
+ * The time spent on transmission of the full FIFO data is the maximum
+ * SPI transmission time.
+ */
+ u32 size = t->bits_per_word * SPRD_SPI_FIFO_SIZE;
+ u32 bit_time_us = DIV_ROUND_UP(USEC_PER_SEC, ss->hw_speed_hz);
+ u32 total_time_us = size * bit_time_us;
+ /*
+ * There is an interval between data and the data in our SPI hardware,
+ * so the total transmission time need add the interval time.
+ */
+ u32 interval_cycle = SPRD_SPI_FIFO_SIZE * ss->word_delay;
+ u32 interval_time_us = DIV_ROUND_UP(interval_cycle * USEC_PER_SEC,
+ ss->src_clk);
+
+ return total_time_us + interval_time_us;
+}
+
+static int sprd_spi_wait_for_tx_end(struct sprd_spi *ss, struct spi_transfer *t)
+{
+ u32 val, us;
+ int ret;
+
+ us = sprd_spi_transfer_max_timeout(ss, t);
+ ret = readl_relaxed_poll_timeout(ss->base + SPRD_SPI_INT_RAW_STS, val,
+ val & SPRD_SPI_TX_END_IRQ, 0, us);
+ if (ret) {
+ dev_err(ss->dev, "SPI error, spi send timeout!\n");
+ return ret;
+ }
+
+ ret = readl_relaxed_poll_timeout(ss->base + SPRD_SPI_STS2, val,
+ !(val & SPRD_SPI_TX_BUSY), 0, us);
+ if (ret) {
+ dev_err(ss->dev, "SPI error, spi busy timeout!\n");
+ return ret;
+ }
+
+ writel_relaxed(SPRD_SPI_TX_END_INT_CLR, ss->base + SPRD_SPI_INT_CLR);
+
+ return 0;
+}
+
+static int sprd_spi_wait_for_rx_end(struct sprd_spi *ss, struct spi_transfer *t)
+{
+ u32 val, us;
+ int ret;
+
+ us = sprd_spi_transfer_max_timeout(ss, t);
+ ret = readl_relaxed_poll_timeout(ss->base + SPRD_SPI_INT_RAW_STS, val,
+ val & SPRD_SPI_RX_END_IRQ, 0, us);
+ if (ret) {
+ dev_err(ss->dev, "SPI error, spi rx timeout!\n");
+ return ret;
+ }
+
+ writel_relaxed(SPRD_SPI_RX_END_INT_CLR, ss->base + SPRD_SPI_INT_CLR);
+
+ return 0;
+}
+
+static void sprd_spi_tx_req(struct sprd_spi *ss)
+{
+ writel_relaxed(SPRD_SPI_SW_TX_REQ, ss->base + SPRD_SPI_CTL12);
+}
+
+static void sprd_spi_rx_req(struct sprd_spi *ss)
+{
+ writel_relaxed(SPRD_SPI_SW_RX_REQ, ss->base + SPRD_SPI_CTL12);
+}
+
+static void sprd_spi_enter_idle(struct sprd_spi *ss)
+{
+ u32 val = readl_relaxed(ss->base + SPRD_SPI_CTL1);
+
+ val &= ~SPRD_SPI_RTX_MD_MASK;
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL1);
+}
+
+static void sprd_spi_set_transfer_bits(struct sprd_spi *ss, u32 bits)
+{
+ u32 val = readl_relaxed(ss->base + SPRD_SPI_CTL0);
+
+ /* Set the valid bits for every transaction */
+ val &= ~(SPRD_SPI_CHNL_LEN_MASK << SPRD_SPI_CHNL_LEN);
+ val |= bits << SPRD_SPI_CHNL_LEN;
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL0);
+}
+
+static void sprd_spi_set_tx_length(struct sprd_spi *ss, u32 length)
+{
+ u32 val = readl_relaxed(ss->base + SPRD_SPI_CTL8);
+
+ length &= SPRD_SPI_TX_MAX_LEN_MASK;
+ val &= ~SPRD_SPI_TX_LEN_H_MASK;
+ val |= length >> SPRD_SPI_TX_LEN_H_OFFSET;
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL8);
+
+ val = length & SPRD_SPI_TX_LEN_L_MASK;
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL9);
+}
+
+static void sprd_spi_set_rx_length(struct sprd_spi *ss, u32 length)
+{
+ u32 val = readl_relaxed(ss->base + SPRD_SPI_CTL10);
+
+ length &= SPRD_SPI_RX_MAX_LEN_MASK;
+ val &= ~SPRD_SPI_RX_LEN_H_MASK;
+ val |= length >> SPRD_SPI_RX_LEN_H_OFFSET;
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL10);
+
+ val = length & SPRD_SPI_RX_LEN_L_MASK;
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL11);
+}
+
+static void sprd_spi_chipselect(struct spi_device *sdev, bool cs)
+{
+ struct spi_controller *sctlr = sdev->controller;
+ struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
+ u32 val;
+
+ val = readl_relaxed(ss->base + SPRD_SPI_CTL0);
+ /* The SPI controller will pull down CS pin if cs is 0 */
+ if (!cs) {
+ val &= ~SPRD_SPI_CS0_VALID;
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL0);
+ } else {
+ val |= SPRD_SPI_CSN_MASK;
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL0);
+ }
+}
+
+static int sprd_spi_write_only_receive(struct sprd_spi *ss, u32 len)
+{
+ u32 val;
+
+ /* Clear the start receive bit and reset receive data number */
+ val = readl_relaxed(ss->base + SPRD_SPI_CTL4);
+ val &= ~(SPRD_SPI_START_RX | SPRD_SPI_ONLY_RECV_MASK);
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL4);
+
+ /* Set the receive data length */
+ val = readl_relaxed(ss->base + SPRD_SPI_CTL4);
+ val |= len & SPRD_SPI_ONLY_RECV_MASK;
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL4);
+
+ /* Trigger to receive data */
+ val = readl_relaxed(ss->base + SPRD_SPI_CTL4);
+ val |= SPRD_SPI_START_RX;
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL4);
+
+ return len;
+}
+
+static int sprd_spi_write_bufs_u8(struct sprd_spi *ss, u32 len)
+{
+ u8 *tx_p = (u8 *)ss->tx_buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ writeb_relaxed(tx_p[i], ss->base + SPRD_SPI_TXD);
+
+ ss->tx_buf += i;
+ return i;
+}
+
+static int sprd_spi_write_bufs_u16(struct sprd_spi *ss, u32 len)
+{
+ u16 *tx_p = (u16 *)ss->tx_buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ writew_relaxed(tx_p[i], ss->base + SPRD_SPI_TXD);
+
+ ss->tx_buf += i << 1;
+ return i << 1;
+}
+
+static int sprd_spi_write_bufs_u32(struct sprd_spi *ss, u32 len)
+{
+ u32 *tx_p = (u32 *)ss->tx_buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ writel_relaxed(tx_p[i], ss->base + SPRD_SPI_TXD);
+
+ ss->tx_buf += i << 2;
+ return i << 2;
+}
+
+static int sprd_spi_read_bufs_u8(struct sprd_spi *ss, u32 len)
+{
+ u8 *rx_p = (u8 *)ss->rx_buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ rx_p[i] = readb_relaxed(ss->base + SPRD_SPI_TXD);
+
+ ss->rx_buf += i;
+ return i;
+}
+
+static int sprd_spi_read_bufs_u16(struct sprd_spi *ss, u32 len)
+{
+ u16 *rx_p = (u16 *)ss->rx_buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ rx_p[i] = readw_relaxed(ss->base + SPRD_SPI_TXD);
+
+ ss->rx_buf += i << 1;
+ return i << 1;
+}
+
+static int sprd_spi_read_bufs_u32(struct sprd_spi *ss, u32 len)
+{
+ u32 *rx_p = (u32 *)ss->rx_buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ rx_p[i] = readl_relaxed(ss->base + SPRD_SPI_TXD);
+
+ ss->rx_buf += i << 2;
+ return i << 2;
+}
+
+static int sprd_spi_txrx_bufs(struct spi_device *sdev, struct spi_transfer *t)
+{
+ struct sprd_spi *ss = spi_controller_get_devdata(sdev->controller);
+ u32 trans_len = ss->trans_len, len;
+ int ret, write_size = 0;
+
+ while (trans_len) {
+ len = trans_len > SPRD_SPI_FIFO_SIZE ? SPRD_SPI_FIFO_SIZE :
+ trans_len;
+ if (ss->trans_mode & SPRD_SPI_TX_MODE) {
+ sprd_spi_set_tx_length(ss, len);
+ write_size += ss->write_bufs(ss, len);
+
+ /*
+ * For our 3 wires mode or dual TX line mode, we need
+ * to request the controller to transfer.
+ */
+ if (ss->hw_mode & SPI_3WIRE || ss->hw_mode & SPI_TX_DUAL)
+ sprd_spi_tx_req(ss);
+
+ ret = sprd_spi_wait_for_tx_end(ss, t);
+ } else {
+ sprd_spi_set_rx_length(ss, len);
+
+ /*
+ * For our 3 wires mode or dual TX line mode, we need
+ * to request the controller to read.
+ */
+ if (ss->hw_mode & SPI_3WIRE || ss->hw_mode & SPI_TX_DUAL)
+ sprd_spi_rx_req(ss);
+ else
+ write_size += ss->write_bufs(ss, len);
+
+ ret = sprd_spi_wait_for_rx_end(ss, t);
+ }
+
+ if (ret)
+ goto complete;
+
+ if (ss->trans_mode & SPRD_SPI_RX_MODE)
+ ss->read_bufs(ss, len);
+
+ trans_len -= len;
+ }
+
+ ret = write_size;
+
+complete:
+ sprd_spi_enter_idle(ss);
+
+ return ret;
+}
+
+static void sprd_spi_set_speed(struct sprd_spi *ss, u32 speed_hz)
+{
+ /*
+ * From SPI datasheet, the prescale calculation formula:
+ * prescale = SPI source clock / (2 * SPI_freq) - 1;
+ */
+ u32 clk_div = DIV_ROUND_UP(ss->src_clk, speed_hz << 1) - 1;
+
+ /* Save the real hardware speed */
+ ss->hw_speed_hz = (ss->src_clk >> 1) / (clk_div + 1);
+ writel_relaxed(clk_div, ss->base + SPRD_SPI_CLKD);
+}
+
+static void sprd_spi_init_hw(struct sprd_spi *ss, struct spi_transfer *t)
+{
+ u16 word_delay, interval;
+ u32 val;
+
+ val = readl_relaxed(ss->base + SPRD_SPI_CTL7);
+ val &= ~(SPRD_SPI_SCK_REV | SPRD_SPI_NG_TX | SPRD_SPI_NG_RX);
+ /* Set default chip selection, clock phase and clock polarity */
+ val |= ss->hw_mode & SPI_CPHA ? SPRD_SPI_NG_RX : SPRD_SPI_NG_TX;
+ val |= ss->hw_mode & SPI_CPOL ? SPRD_SPI_SCK_REV : 0;
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL0);
+
+ /*
+ * Set the intervals of two SPI frames, and the inteval calculation
+ * formula as below per datasheet:
+ * interval time (source clock cycles) = interval * 4 + 10.
+ */
+ word_delay = clamp_t(u16, t->word_delay, SPRD_SPI_MIN_DELAY_CYCLE,
+ SPRD_SPI_MAX_DELAY_CYCLE);
+ interval = DIV_ROUND_UP(word_delay - 10, 4);
+ ss->word_delay = interval * 4 + 10;
+ writel_relaxed(interval, ss->base + SPRD_SPI_CTL5);
+
+ /* Reset SPI fifo */
+ writel_relaxed(1, ss->base + SPRD_SPI_FIFO_RST);
+ writel_relaxed(0, ss->base + SPRD_SPI_FIFO_RST);
+
+ /* Set SPI work mode */
+ val = readl_relaxed(ss->base + SPRD_SPI_CTL7);
+ val &= ~SPRD_SPI_MODE_MASK;
+
+ if (ss->hw_mode & SPI_3WIRE)
+ val |= SPRD_SPI_3WIRE_MODE << SPRD_SPI_MODE_OFFSET;
+ else
+ val |= SPRD_SPI_4WIRE_MODE << SPRD_SPI_MODE_OFFSET;
+
+ if (ss->hw_mode & SPI_TX_DUAL)
+ val |= SPRD_SPI_DATA_LINE2_EN;
+ else
+ val &= ~SPRD_SPI_DATA_LINE2_EN;
+
+ writel_relaxed(val, ss->base + SPRD_SPI_CTL7);
+}
+
+static int sprd_spi_setup_transfer(struct spi_device *sdev,
+ struct spi_transfer *t)
+{
+ struct sprd_spi *ss = spi_controller_get_devdata(sdev->controller);
+ u8 bits_per_word = t->bits_per_word;
+ u32 val, mode = 0;
+
+ ss->len = t->len;
+ ss->tx_buf = t->tx_buf;
+ ss->rx_buf = t->rx_buf;
+
+ ss->hw_mode = sdev->mode;
+ sprd_spi_init_hw(ss, t);
+
+ /* Set tansfer speed and valid bits */
+ sprd_spi_set_speed(ss, t->speed_hz);
+ sprd_spi_set_transfer_bits(ss, bits_per_word);
+
+ if (bits_per_word > 16)
+ bits_per_word = round_up(bits_per_word, 16);
+ else
+ bits_per_word = round_up(bits_per_word, 8);
+
+ switch (bits_per_word) {
+ case 8:
+ ss->trans_len = t->len;
+ ss->read_bufs = sprd_spi_read_bufs_u8;
+ ss->write_bufs = sprd_spi_write_bufs_u8;
+ break;
+ case 16:
+ ss->trans_len = t->len >> 1;
+ ss->read_bufs = sprd_spi_read_bufs_u16;
+ ss->write_bufs = sprd_spi_write_bufs_u16;
+ break;
+ case 32:
+ ss->trans_len = t->len >> 2;
+ ss->read_bufs = sprd_spi_read_bufs_u32;
+ ss->write_bufs = sprd_spi_write_bufs_u32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set transfer read or write mode */
+ val = readl_relaxed(ss->base + SPRD_SPI_CTL1);
+ val &= ~SPRD_SPI_RTX_MD_MASK;
+ if (t->tx_buf)
+ mode |= SPRD_SPI_TX_MODE;
+ if (t->rx_buf)
+ mode |= SPRD_SPI_RX_MODE;
+
+ writel_relaxed(val | mode, ss->base + SPRD_SPI_CTL1);
+
+ ss->trans_mode = mode;
+
+ /*
+ * If in only receive mode, we need to trigger the SPI controller to
+ * receive data automatically.
+ */
+ if (ss->trans_mode == SPRD_SPI_RX_MODE)
+ ss->write_bufs = sprd_spi_write_only_receive;
+
+ return 0;
+}
+
+static int sprd_spi_transfer_one(struct spi_controller *sctlr,
+ struct spi_device *sdev,
+ struct spi_transfer *t)
+{
+ int ret;
+
+ ret = sprd_spi_setup_transfer(sdev, t);
+ if (ret)
+ goto setup_err;
+
+ ret = sprd_spi_txrx_bufs(sdev, t);
+ if (ret == t->len)
+ ret = 0;
+ else if (ret >= 0)
+ ret = -EREMOTEIO;
+
+setup_err:
+ spi_finalize_current_transfer(sctlr);
+
+ return ret;
+}
+
+static int sprd_spi_clk_init(struct platform_device *pdev, struct sprd_spi *ss)
+{
+ struct clk *clk_spi, *clk_parent;
+
+ clk_spi = devm_clk_get(&pdev->dev, "spi");
+ if (IS_ERR(clk_spi)) {
+ dev_warn(&pdev->dev, "can't get the spi clock\n");
+ clk_spi = NULL;
+ }
+
+ clk_parent = devm_clk_get(&pdev->dev, "source");
+ if (IS_ERR(clk_parent)) {
+ dev_warn(&pdev->dev, "can't get the source clock\n");
+ clk_parent = NULL;
+ }
+
+ ss->clk = devm_clk_get(&pdev->dev, "enable");
+ if (IS_ERR(ss->clk)) {
+ dev_err(&pdev->dev, "can't get the enable clock\n");
+ return PTR_ERR(ss->clk);
+ }
+
+ if (!clk_set_parent(clk_spi, clk_parent))
+ ss->src_clk = clk_get_rate(clk_spi);
+ else
+ ss->src_clk = SPRD_SPI_DEFAULT_SOURCE;
+
+ return 0;
+}
+
+static int sprd_spi_probe(struct platform_device *pdev)
+{
+ struct spi_controller *sctlr;
+ struct resource *res;
+ struct sprd_spi *ss;
+ int ret;
+
+ pdev->id = of_alias_get_id(pdev->dev.of_node, "spi");
+ sctlr = spi_alloc_master(&pdev->dev, sizeof(*ss));
+ if (!sctlr)
+ return -ENOMEM;
+
+ ss = spi_controller_get_devdata(sctlr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ss->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ss->base)) {
+ ret = PTR_ERR(ss->base);
+ goto free_controller;
+ }
+
+ ss->dev = &pdev->dev;
+ sctlr->dev.of_node = pdev->dev.of_node;
+ sctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_3WIRE | SPI_TX_DUAL;
+ sctlr->bus_num = pdev->id;
+ sctlr->set_cs = sprd_spi_chipselect;
+ sctlr->transfer_one = sprd_spi_transfer_one;
+ sctlr->auto_runtime_pm = true;
+ sctlr->max_speed_hz = min_t(u32, ss->src_clk >> 1,
+ SPRD_SPI_MAX_SPEED_HZ);
+
+ platform_set_drvdata(pdev, sctlr);
+ ret = sprd_spi_clk_init(pdev, ss);
+ if (ret)
+ goto free_controller;
+
+ ret = clk_prepare_enable(ss->clk);
+ if (ret)
+ goto free_controller;
+
+ ret = pm_runtime_set_active(&pdev->dev);
+ if (ret < 0)
+ goto disable_clk;
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev,
+ SPRD_SPI_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to resume SPI controller\n");
+ goto err_rpm_put;
+ }
+
+ ret = devm_spi_register_controller(&pdev->dev, sctlr);
+ if (ret)
+ goto err_rpm_put;
+
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+disable_clk:
+ clk_disable_unprepare(ss->clk);
+free_controller:
+ spi_controller_put(sctlr);
+
+ return ret;
+}
+
+static int sprd_spi_remove(struct platform_device *pdev)
+{
+ struct spi_controller *sctlr = platform_get_drvdata(pdev);
+ struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
+ int ret;
+
+ ret = pm_runtime_get_sync(ss->dev);
+ if (ret < 0) {
+ dev_err(ss->dev, "failed to resume SPI controller\n");
+ return ret;
+ }
+
+ clk_disable_unprepare(ss->clk);
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static int __maybe_unused sprd_spi_runtime_suspend(struct device *dev)
+{
+ struct spi_controller *sctlr = dev_get_drvdata(dev);
+ struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
+
+ clk_disable_unprepare(ss->clk);
+
+ return 0;
+}
+
+static int __maybe_unused sprd_spi_runtime_resume(struct device *dev)
+{
+ struct spi_controller *sctlr = dev_get_drvdata(dev);
+ struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
+ int ret;
+
+ ret = clk_prepare_enable(ss->clk);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct dev_pm_ops sprd_spi_pm_ops = {
+ SET_RUNTIME_PM_OPS(sprd_spi_runtime_suspend,
+ sprd_spi_runtime_resume, NULL)
+};
+
+static const struct of_device_id sprd_spi_of_match[] = {
+ { .compatible = "sprd,sc9860-spi", },
+ { /* sentinel */ }
+};
+
+static struct platform_driver sprd_spi_driver = {
+ .driver = {
+ .name = "sprd-spi",
+ .of_match_table = sprd_spi_of_match,
+ .pm = &sprd_spi_pm_ops,
+ },
+ .probe = sprd_spi_probe,
+ .remove = sprd_spi_remove,
+};
+
+module_platform_driver(sprd_spi_driver);
+
+MODULE_DESCRIPTION("Spreadtrum SPI Controller driver");
+MODULE_AUTHOR("Lanqing Liu <[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index ec395a6baf9c..fcbd4cfd2818 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* SPI init/core code
*
* Copyright (C) 2005 David Brownell
* Copyright (C) 2008 Secret Lab Technologies Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
@@ -60,6 +51,7 @@ static void spidev_release(struct device *dev)
spi->controller->cleanup(spi);
spi_controller_put(spi->controller);
+ kfree(spi->driver_override);
kfree(spi);
}
@@ -77,6 +69,51 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf)
}
static DEVICE_ATTR_RO(modalias);
+static ssize_t driver_override_store(struct device *dev,
+ struct device_attribute *a,
+ const char *buf, size_t count)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ const char *end = memchr(buf, '\n', count);
+ const size_t len = end ? end - buf : count;
+ const char *driver_override, *old;
+
+ /* We need to keep extra room for a newline when displaying value */
+ if (len >= (PAGE_SIZE - 1))
+ return -EINVAL;
+
+ driver_override = kstrndup(buf, len, GFP_KERNEL);
+ if (!driver_override)
+ return -ENOMEM;
+
+ device_lock(dev);
+ old = spi->driver_override;
+ if (len) {
+ spi->driver_override = driver_override;
+ } else {
+ /* Emptry string, disable driver override */
+ spi->driver_override = NULL;
+ kfree(driver_override);
+ }
+ device_unlock(dev);
+ kfree(old);
+
+ return count;
+}
+
+static ssize_t driver_override_show(struct device *dev,
+ struct device_attribute *a, char *buf)
+{
+ const struct spi_device *spi = to_spi_device(dev);
+ ssize_t len;
+
+ device_lock(dev);
+ len = snprintf(buf, PAGE_SIZE, "%s\n", spi->driver_override ? : "");
+ device_unlock(dev);
+ return len;
+}
+static DEVICE_ATTR_RW(driver_override);
+
#define SPI_STATISTICS_ATTRS(field, file) \
static ssize_t spi_controller_##field##_show(struct device *dev, \
struct device_attribute *attr, \
@@ -158,6 +195,7 @@ SPI_STATISTICS_SHOW(transfers_split_maxsize, "%lu");
static struct attribute *spi_dev_attrs[] = {
&dev_attr_modalias.attr,
+ &dev_attr_driver_override.attr,
NULL,
};
@@ -305,6 +343,10 @@ static int spi_match_device(struct device *dev, struct device_driver *drv)
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
+ /* Check override first, and if set, only use the named driver */
+ if (spi->driver_override)
+ return strcmp(spi->driver_override, drv->name) == 0;
+
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
@@ -2774,8 +2816,10 @@ int spi_setup(struct spi_device *spi)
return -EINVAL;
/* help drivers fail *cleanly* when they need options
* that aren't supported with their current controller
+ * SPI_CS_WORD has a fallback software implementation,
+ * so it is ignored here.
*/
- bad_bits = spi->mode & ~spi->controller->mode_bits;
+ bad_bits = spi->mode & ~(spi->controller->mode_bits | SPI_CS_WORD);
ugly_bits = bad_bits &
(SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD);
if (ugly_bits) {
@@ -2829,6 +2873,35 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
if (list_empty(&message->transfers))
return -EINVAL;
+ /* If an SPI controller does not support toggling the CS line on each
+ * transfer (indicated by the SPI_CS_WORD flag) or we are using a GPIO
+ * for the CS line, we can emulate the CS-per-word hardware function by
+ * splitting transfers into one-word transfers and ensuring that
+ * cs_change is set for each transfer.
+ */
+ if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) ||
+ gpio_is_valid(spi->cs_gpio))) {
+ size_t maxsize;
+ int ret;
+
+ maxsize = (spi->bits_per_word + 7) / 8;
+
+ /* spi_split_transfers_maxsize() requires message->spi */
+ message->spi = spi;
+
+ ret = spi_split_transfers_maxsize(ctlr, message, maxsize,
+ GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(xfer, &message->transfers, transfer_list) {
+ /* don't change cs_change on the last entry in the list */
+ if (list_is_last(&xfer->transfer_list, &message->transfers))
+ break;
+ xfer->cs_change = 1;
+ }
+ }
+
/* Half-duplex links include original MicroWire, and ones with
* only one data pin like SPI_3WIRE (switches direction) or where
* either MOSI or MISO is missing. They can also be caused by
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index cda10719d1d1..b0c76e2626ce 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -669,6 +669,7 @@ static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "lineartechnology,ltc2488" },
{ .compatible = "ge,achc" },
{ .compatible = "semtech,sx1301" },
+ { .compatible = "lwn,bk4" },
{},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
@@ -724,11 +725,9 @@ static int spidev_probe(struct spi_device *spi)
* compatible string, it is a Linux implementation thing
* rather than a description of the hardware.
*/
- if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {
- dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n");
- WARN_ON(spi->dev.of_node &&
- !of_match_device(spidev_dt_ids, &spi->dev));
- }
+ WARN(spi->dev.of_node &&
+ of_device_is_compatible(spi->dev.of_node, "spidev"),
+ "%pOF: buggy DT: spidev listed directly in DT\n", spi->dev.of_node);
spidev_probe_acpi(spi);
diff --git a/include/linux/platform_data/spi-davinci.h b/include/linux/platform_data/spi-davinci.h
index f4edcb03c40c..0638fb6353bc 100644
--- a/include/linux/platform_data/spi-davinci.h
+++ b/include/linux/platform_data/spi-davinci.h
@@ -36,9 +36,6 @@ enum {
* @num_chipselect: number of chipselects supported by this SPI master
* @intr_line: interrupt line used to connect the SPI IP to the ARM interrupt
* controller withn the SoC. Possible values are 0 and 1.
- * @chip_sel: list of GPIOs which can act as chip-selects for the SPI.
- * SPI_INTERN_CS denotes internal SPI chip-select. Not necessary
- * to populate if all chip-selects are internal.
* @cshold_bug: set this to true if the SPI controller on your chip requires
* a write to CSHOLD bit in between transfers (like in DM355).
* @dma_event_q: DMA event queue to use if SPI_IO_TYPE_DMA is used for any
@@ -48,7 +45,6 @@ struct davinci_spi_platform_data {
u8 version;
u8 num_chipselect;
u8 intr_line;
- u8 *chip_sel;
u8 prescaler_limit;
bool cshold_bug;
enum dma_event_q dma_event_q;
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index a64235e05321..3de9958ca28b 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -1,15 +1,6 @@
-/*
- * Copyright (C) 2005 David Brownell
+/* SPDX-License-Identifier: GPL-2.0-or-later
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2005 David Brownell
*/
#ifndef __LINUX_SPI_H
@@ -163,10 +154,12 @@ struct spi_device {
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
+#define SPI_CS_WORD 0x1000 /* toggle cs after each word */
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
+ const char *driver_override;
int cs_gpio; /* chip select gpio */
/* the statistics */
@@ -177,7 +170,6 @@ struct spi_device {
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
- * - drop chipselect after each word
* - chipselect delays
* - ...
*/
@@ -711,6 +703,8 @@ extern void spi_res_release(struct spi_controller *ctlr,
* @delay_usecs: microseconds to delay after this transfer before
* (optionally) changing the chipselect status, then starting
* the next transfer or completing this @spi_message.
+ * @word_delay: clock cycles to inter word delay after each word size
+ * (set by bits_per_word) transmission.
* @transfer_list: transfers are sequenced through @spi_message.transfers
* @tx_sg: Scatterlist for transmit, currently not for client use
* @rx_sg: Scatterlist for receive, currently not for client use
@@ -793,6 +787,7 @@ struct spi_transfer {
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
+ u16 word_delay;
struct list_head transfer_list;
};
diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c
index 8c590cd1171a..4c12e6aea5d5 100644
--- a/tools/spi/spidev_test.c
+++ b/tools/spi/spidev_test.c
@@ -73,12 +73,12 @@ static void hex_dump(const void *src, size_t length, size_t line_size,
while (i++ % line_size)
printf("__ ");
}
- printf(" | "); /* right close */
+ printf(" |");
while (line < address) {
c = *line++;
- printf("%c", (c < 33 || c == 255) ? 0x2E : c);
+ printf("%c", (c < 32 || c > 126) ? '.' : c);
}
- printf("\n");
+ printf("|\n");
if (length > 0)
printf("%s | ", prefix);
}