Commit 0051db82 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'spi-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi

Pull spi updates from Mark Brown:
 "The main thing this release has been a lot of work on the integration
  with SPI NOR flashes, there's been some specific support for a while
  for controller features designed to make them perform better but it's
  not worked out as well as hoped so the interface has been redesigned
  in a way that will hopefully do better - it's already been adopted by
  a number of additional controllers so things are looking good.

  Otherwise most of the work has been driver specific:

   - Support for better integration with NOR flashes from Boris
     Brezillon and Yogesh Narayan Gaur plus usage of it in several
     drivers.

   - A big cleanup of the Rockchip driver from Emil Renner Berthing.

   - Lots of performance improvements for bcm2835 from Lukas Wunner.

   - Slave mode support for pxa2xx from Lubomir Rintel.

   - Support for Macronix MXIC, Mediatek MT7629 and MT8183, NPCM PSPI,
     and Renesas r8a77470"

* tag 'spi-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (90 commits)
  spi: sh-msiof: Reduce the number of times write to and perform the transmission from FIFO
  spi: sh-msiof: Add r8a774c0 support
  doc: lpspi: Document DT bindings for LPSPI slave mode
  spi: lpspi: Let watermark change with send data length
  spi: lpspi: Add slave mode support
  spi: lpspi: Replace all "master" with "controller"
  spi: imx: drop useless member speed_hz from driver data struct
  spi: imx: rename config callback and add useful parameters
  spi: imx: style fixes
  spi: imx: mx51-ecspi: Move some initialisation to prepare_message hook.
  spi: imx: add a device specific prepare_message callback
  mtd: atmel-quadspi: disallow building on ebsa110
  spi: Update NPCM PSPI controller documentation
  spi: npcm: Modify pspi send function
  spi: Use of_node_name_eq for node name comparisons
  spi: dw-mmio: add ACPI support
  spi: bcm2835: Synchronize with callback on DMA termination
  spi: bcm2835: Speed up FIFO access if fill level is known
  spi: bcm2835: Polish transfer of DMA prologue
  spi: spi-mem: add support for octal mode I/O data transfer
  ...
parents 79f20778 74ff666b
Nuvoton NPCM Peripheral Serial Peripheral Interface(PSPI) controller driver
Nuvoton NPCM7xx SOC support two PSPI channels.
Required properties:
- compatible : "nuvoton,npcm750-pspi" for NPCM7XX BMC
- #address-cells : should be 1. see spi-bus.txt
- #size-cells : should be 0. see spi-bus.txt
- specifies physical base address and size of the register.
- interrupts : contain PSPI interrupt.
- clocks : phandle of PSPI reference clock.
- clock-names: Should be "clk_apb5".
- pinctrl-names : a pinctrl state named "default" must be defined.
- pinctrl-0 : phandle referencing pin configuration of the device.
- cs-gpios: Specifies the gpio pins to be used for chipselects.
See: Documentation/devicetree/bindings/spi/spi-bus.txt
Optional properties:
- clock-frequency : Input clock frequency to the PSPI block in Hz.
Default is 25000000 Hz.
Aliases:
- All the SPI controller nodes should be represented in the aliases node using
the following format 'spi{n}' withe the correct numbered in "aliases" node.
Example:
aliases {
spi0 = &spi0;
};
spi0: spi@f0200000 {
compatible = "nuvoton,npcm750-pspi";
reg = <0xf0200000 0x1000>;
pinctrl-names = "default";
pinctrl-0 = <&pspi1_pins>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk NPCM7XX_CLK_APB5>;
clock-names = "clk_apb5";
cs-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
};
...@@ -2,6 +2,7 @@ OMAP2+ McSPI device ...@@ -2,6 +2,7 @@ OMAP2+ McSPI device
Required properties: Required properties:
- compatible : - compatible :
- "ti,am654-mcspi" for AM654.
- "ti,omap2-mcspi" for OMAP2 & OMAP3. - "ti,omap2-mcspi" for OMAP2 & OMAP3.
- "ti,omap4-mcspi" for OMAP4+. - "ti,omap4-mcspi" for OMAP4+.
- ti,spi-num-cs : Number of chipselect supported by the instance. - ti,spi-num-cs : Number of chipselect supported by the instance.
......
...@@ -5,6 +5,7 @@ Required properties: ...@@ -5,6 +5,7 @@ Required properties:
"renesas,msiof-r8a7744" (RZ/G1N) "renesas,msiof-r8a7744" (RZ/G1N)
"renesas,msiof-r8a7745" (RZ/G1E) "renesas,msiof-r8a7745" (RZ/G1E)
"renesas,msiof-r8a774a1" (RZ/G2M) "renesas,msiof-r8a774a1" (RZ/G2M)
"renesas,msiof-r8a774c0" (RZ/G2E)
"renesas,msiof-r8a7790" (R-Car H2) "renesas,msiof-r8a7790" (R-Car H2)
"renesas,msiof-r8a7791" (R-Car M2-W) "renesas,msiof-r8a7791" (R-Car M2-W)
"renesas,msiof-r8a7792" (R-Car V2H) "renesas,msiof-r8a7792" (R-Car V2H)
......
...@@ -5,8 +5,11 @@ Required properties: ...@@ -5,8 +5,11 @@ Required properties:
- "fsl,imx7ulp-spi" for LPSPI compatible with the one integrated on i.MX7ULP soc - "fsl,imx7ulp-spi" for LPSPI compatible with the one integrated on i.MX7ULP soc
- "fsl,imx8qxp-spi" for LPSPI compatible with the one integrated on i.MX8QXP soc - "fsl,imx8qxp-spi" for LPSPI compatible with the one integrated on i.MX8QXP soc
- reg : address and length of the lpspi master registers - reg : address and length of the lpspi master registers
- interrupt-parent : core interrupt controller
- interrupts : lpspi interrupt - interrupts : lpspi interrupt
- clocks : lpspi clock specifier - clocks : lpspi clock specifier
- spi-slave : spi slave mode support. In slave mode, add this attribute without
value. In master mode, remove it.
Examples: Examples:
...@@ -16,4 +19,5 @@ lpspi2: lpspi@40290000 { ...@@ -16,4 +19,5 @@ lpspi2: lpspi@40290000 {
interrupt-parent = <&intc>; interrupt-parent = <&intc>;
interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX7ULP_CLK_LPSPI2>; clocks = <&clks IMX7ULP_CLK_LPSPI2>;
spi-slave;
}; };
...@@ -6,8 +6,10 @@ Required properties: ...@@ -6,8 +6,10 @@ Required properties:
- mediatek,mt2712-spi: for mt2712 platforms - mediatek,mt2712-spi: for mt2712 platforms
- mediatek,mt6589-spi: for mt6589 platforms - mediatek,mt6589-spi: for mt6589 platforms
- mediatek,mt7622-spi: for mt7622 platforms - mediatek,mt7622-spi: for mt7622 platforms
- "mediatek,mt7629-spi", "mediatek,mt7622-spi": for mt7629 platforms
- mediatek,mt8135-spi: for mt8135 platforms - mediatek,mt8135-spi: for mt8135 platforms
- mediatek,mt8173-spi: for mt8173 platforms - mediatek,mt8173-spi: for mt8173 platforms
- mediatek,mt8183-spi: for mt8183 platforms
- #address-cells: should be 1. - #address-cells: should be 1.
......
Macronix SPI controller Device Tree Bindings
--------------------------------------------
Required properties:
- compatible: should be "mxicy,mx25f0a-spi"
- #address-cells: should be 1
- #size-cells: should be 0
- reg: should contain 2 entries, one for the registers and one for the direct
mapping area
- reg-names: should contain "regs" and "dirmap"
- interrupts: interrupt line connected to the SPI controller
- clock-names: should contain "ps_clk", "send_clk" and "send_dly_clk"
- clocks: should contain 3 entries for the "ps_clk", "send_clk" and
"send_dly_clk" clocks
Example:
spi@43c30000 {
compatible = "mxicy,mx25f0a-spi";
reg = <0x43c30000 0x10000>, <0xa0000000 0x20000000>;
reg-names = "regs", "dirmap";
clocks = <&clkwizard 0>, <&clkwizard 1>, <&clkc 18>;
clock-names = "send_clk", "send_dly_clk", "ps_clk";
#address-cells = <1>;
#size-cells = <0>;
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <25000000>;
spi-tx-bus-width = <4>;
spi-rx-bus-width = <4>;
};
};
...@@ -11,6 +11,9 @@ Required properties: ...@@ -11,6 +11,9 @@ Required properties:
Optional properties: Optional properties:
- cs-gpios: list of GPIO chip selects. See the SPI bus bindings, - cs-gpios: list of GPIO chip selects. See the SPI bus bindings,
Documentation/devicetree/bindings/spi/spi-bus.txt Documentation/devicetree/bindings/spi/spi-bus.txt
- spi-slave: Empty property indicating the SPI controller is used in slave mode.
- ready-gpios: GPIO used to signal a SPI master that the FIFO is filled
and we're ready to service a transfer. Only useful in slave mode.
Child nodes represent devices on the SPI bus Child nodes represent devices on the SPI bus
See ../spi/spi-bus.txt See ../spi/spi-bus.txt
......
...@@ -15,6 +15,7 @@ Required properties: ...@@ -15,6 +15,7 @@ Required properties:
- "renesas,qspi-r8a7743" (RZ/G1M) - "renesas,qspi-r8a7743" (RZ/G1M)
- "renesas,qspi-r8a7744" (RZ/G1N) - "renesas,qspi-r8a7744" (RZ/G1N)
- "renesas,qspi-r8a7745" (RZ/G1E) - "renesas,qspi-r8a7745" (RZ/G1E)
- "renesas,qspi-r8a77470" (RZ/G1C)
- "renesas,qspi-r8a7790" (R-Car H2) - "renesas,qspi-r8a7790" (R-Car H2)
- "renesas,qspi-r8a7791" (R-Car M2-W) - "renesas,qspi-r8a7791" (R-Car M2-W)
- "renesas,qspi-r8a7792" (R-Car V2H) - "renesas,qspi-r8a7792" (R-Car V2H)
......
...@@ -5,6 +5,8 @@ UniPhier SoCs have SCSSI which supports SPI single channel. ...@@ -5,6 +5,8 @@ UniPhier SoCs have SCSSI which supports SPI single channel.
Required properties: Required properties:
- compatible: should be "socionext,uniphier-scssi" - compatible: should be "socionext,uniphier-scssi"
- reg: address and length of the spi master registers - reg: address and length of the spi master registers
- #address-cells: must be <1>, see spi-bus.txt
- #size-cells: must be <0>, see spi-bus.txt
- interrupts: a single interrupt specifier - interrupts: a single interrupt specifier
- pinctrl-names: should be "default" - pinctrl-names: should be "default"
- pinctrl-0: pin control state for the default mode - pinctrl-0: pin control state for the default mode
...@@ -16,6 +18,8 @@ Example: ...@@ -16,6 +18,8 @@ Example:
spi0: spi@54006000 { spi0: spi@54006000 {
compatible = "socionext,uniphier-scssi"; compatible = "socionext,uniphier-scssi";
reg = <0x54006000 0x100>; reg = <0x54006000 0x100>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <0 39 4>; interrupts = <0 39 4>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi0>; pinctrl-0 = <&pinctrl_spi0>;
......
...@@ -39,15 +39,6 @@ config SPI_ASPEED_SMC ...@@ -39,15 +39,6 @@ config SPI_ASPEED_SMC
and support for the SPI flash memory controller (SPI) for and support for the SPI flash memory controller (SPI) for
the host firmware. The implementation only supports SPI NOR. the host firmware. The implementation only supports SPI NOR.
config SPI_ATMEL_QUADSPI
tristate "Atmel Quad SPI Controller"
depends on ARCH_AT91 || (ARM && COMPILE_TEST)
depends on OF && HAS_IOMEM
help
This enables support for the Quad SPI controller in master mode.
This driver does not support generic SPI. The implementation only
supports SPI NOR.
config SPI_CADENCE_QUADSPI config SPI_CADENCE_QUADSPI
tristate "Cadence Quad SPI controller" tristate "Cadence Quad SPI controller"
depends on OF && (ARM || ARM64 || COMPILE_TEST) depends on OF && (ARM || ARM64 || COMPILE_TEST)
......
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
......
...@@ -91,6 +91,15 @@ config SPI_AT91_USART ...@@ -91,6 +91,15 @@ config SPI_AT91_USART
This selects a driver for the AT91 USART Controller as SPI Master, This selects a driver for the AT91 USART Controller as SPI Master,
present on AT91 and SAMA5 SoC series. present on AT91 and SAMA5 SoC series.
config SPI_ATMEL_QUADSPI
tristate "Atmel Quad SPI Controller"
depends on ARCH_AT91 || (ARM && COMPILE_TEST && !ARCH_EBSA110)
depends on OF && HAS_IOMEM
help
This enables support for the Quad SPI controller in master mode.
This driver does not support generic SPI. The implementation only
supports spi-mem interface.
config SPI_AU1550 config SPI_AU1550
tristate "Au1550/Au1200/Au1300 SPI Controller" tristate "Au1550/Au1200/Au1300 SPI Controller"
depends on MIPS_ALCHEMY depends on MIPS_ALCHEMY
...@@ -397,6 +406,13 @@ config SPI_MT65XX ...@@ -397,6 +406,13 @@ config SPI_MT65XX
say Y or M here.If you are not sure, say N. say Y or M here.If you are not sure, say N.
SPI drivers for Mediatek MT65XX and MT81XX series ARM SoCs. SPI drivers for Mediatek MT65XX and MT81XX series ARM SoCs.
config SPI_NPCM_PSPI
tristate "Nuvoton NPCM PSPI Controller"
depends on ARCH_NPCM || COMPILE_TEST
help
This driver provides support for Nuvoton NPCM BMC
Peripheral SPI controller in master mode.
config SPI_NUC900 config SPI_NUC900
tristate "Nuvoton NUC900 series SPI" tristate "Nuvoton NUC900 series SPI"
depends on ARCH_W90X900 depends on ARCH_W90X900
...@@ -435,7 +451,7 @@ config SPI_OMAP_UWIRE ...@@ -435,7 +451,7 @@ config SPI_OMAP_UWIRE
config SPI_OMAP24XX config SPI_OMAP24XX
tristate "McSPI driver for OMAP" tristate "McSPI driver for OMAP"
depends on ARCH_OMAP2PLUS || COMPILE_TEST depends on ARCH_OMAP2PLUS || ARCH_K3 || COMPILE_TEST
select SG_SPLIT select SG_SPLIT
help help
SPI master controller for OMAP24XX and later Multichannel SPI SPI master controller for OMAP24XX and later Multichannel SPI
...@@ -684,6 +700,12 @@ config SPI_SUN6I ...@@ -684,6 +700,12 @@ config SPI_SUN6I
help help
This enables using the SPI controller on the Allwinner A31 SoCs. This enables using the SPI controller on the Allwinner A31 SoCs.
config SPI_MXIC
tristate "Macronix MX25F0A SPI controller"
depends on SPI_MASTER
help
This selects the Macronix MX25F0A SPI controller driver.
config SPI_MXS config SPI_MXS
tristate "Freescale MXS SPI controller" tristate "Freescale MXS SPI controller"
depends on ARCH_MXS depends on ARCH_MXS
......
...@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o ...@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
obj-$(CONFIG_SPI_AT91_USART) += spi-at91-usart.o obj-$(CONFIG_SPI_AT91_USART) += spi-at91-usart.o
obj-$(CONFIG_SPI_ATH79) += spi-ath79.o obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
obj-$(CONFIG_SPI_AU1550) += spi-au1550.o obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
...@@ -58,7 +59,9 @@ obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o ...@@ -58,7 +59,9 @@ obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o
obj-$(CONFIG_SPI_MXIC) += spi-mxic.o
obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o
obj-$(CONFIG_SPI_NPCM_PSPI) += spi-npcm-pspi.o
obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o
obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
spi-octeon-objs := spi-cavium.o spi-cavium-octeon.o spi-octeon-objs := spi-cavium.o spi-cavium-octeon.o
......
...@@ -12,7 +12,9 @@ ...@@ -12,7 +12,9 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
...@@ -399,6 +401,59 @@ static int at91_usart_spi_probe(struct platform_device *pdev) ...@@ -399,6 +401,59 @@ static int at91_usart_spi_probe(struct platform_device *pdev)
return ret; return ret;
} }
__maybe_unused static int at91_usart_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
clk_disable_unprepare(aus->clk);
pinctrl_pm_select_sleep_state(dev);
return 0;
}
__maybe_unused static int at91_usart_spi_runtime_resume(struct device *dev)
{
struct spi_controller *ctrl = dev_get_drvdata(dev);
struct at91_usart_spi *aus = spi_master_get_devdata(ctrl);
pinctrl_pm_select_default_state(dev);
return clk_prepare_enable(aus->clk);
}
__maybe_unused static int at91_usart_spi_suspend(struct device *dev)
{
struct spi_controller *ctrl = dev_get_drvdata(dev);
int ret;
ret = spi_controller_suspend(ctrl);
if (ret)
return ret;
if (!pm_runtime_suspended(dev))
at91_usart_spi_runtime_suspend(dev);
return 0;
}
__maybe_unused static int at91_usart_spi_resume(struct device *dev)
{
struct spi_controller *ctrl = dev_get_drvdata(dev);
struct at91_usart_spi *aus = spi_master_get_devdata(ctrl);
int ret;
if (!pm_runtime_suspended(dev)) {
ret = at91_usart_spi_runtime_resume(dev);
if (ret)
return ret;
}
at91_usart_spi_init(aus);
return spi_controller_resume(ctrl);
}
static int at91_usart_spi_remove(struct platform_device *pdev) static int at91_usart_spi_remove(struct platform_device *pdev)
{ {
struct spi_controller *ctlr = platform_get_drvdata(pdev); struct spi_controller *ctlr = platform_get_drvdata(pdev);
...@@ -409,6 +464,12 @@ static int at91_usart_spi_remove(struct platform_device *pdev) ...@@ -409,6 +464,12 @@ static int at91_usart_spi_remove(struct platform_device *pdev)
return 0; return 0;
} }
static const struct dev_pm_ops at91_usart_spi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(at91_usart_spi_suspend, at91_usart_spi_resume)
SET_RUNTIME_PM_OPS(at91_usart_spi_runtime_suspend,
at91_usart_spi_runtime_resume, NULL)
};
static const struct of_device_id at91_usart_spi_dt_ids[] = { static const struct of_device_id at91_usart_spi_dt_ids[] = {
{ .compatible = "microchip,at91sam9g45-usart-spi"}, { .compatible = "microchip,at91sam9g45-usart-spi"},
{ /* sentinel */} { /* sentinel */}
...@@ -419,6 +480,7 @@ MODULE_DEVICE_TABLE(of, at91_usart_spi_dt_ids); ...@@ -419,6 +480,7 @@ MODULE_DEVICE_TABLE(of, at91_usart_spi_dt_ids);
static struct platform_driver at91_usart_spi_driver = { static struct platform_driver at91_usart_spi_driver = {
.driver = { .driver = {
.name = "at91_usart_spi", .name = "at91_usart_spi",
.pm = &at91_usart_spi_pm_ops,
}, },
.probe = at91_usart_spi_probe, .probe = at91_usart_spi_probe,
.remove = at91_usart_spi_remove, .remove = at91_usart_spi_remove,
......
This diff is collapsed.
...@@ -542,4 +542,4 @@ module_platform_driver(bcm2835aux_spi_driver); ...@@ -542,4 +542,4 @@ module_platform_driver(bcm2835aux_spi_driver);
MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835 aux"); MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835 aux");
MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>"); MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL");
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/acpi.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/regmap.h> #include <linux/regmap.h>
...@@ -243,12 +244,19 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { ...@@ -243,12 +244,19 @@ static const struct of_device_id dw_spi_mmio_of_match[] = {
}; };
MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
static const struct acpi_device_id dw_spi_mmio_acpi_match[] = {
{"HISI0173", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match);
static struct platform_driver dw_spi_mmio_driver = { static struct platform_driver dw_spi_mmio_driver = {
.probe = dw_spi_mmio_probe, .probe = dw_spi_mmio_probe,
.remove = dw_spi_mmio_remove, .remove = dw_spi_mmio_remove,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.of_match_table = dw_spi_mmio_of_match, .of_match_table = dw_spi_mmio_of_match,
.acpi_match_table = ACPI_PTR(dw_spi_mmio_acpi_match),
}, },
}; };
module_platform_driver(dw_spi_mmio_driver); module_platform_driver(dw_spi_mmio_driver);
......
...@@ -507,6 +507,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) ...@@ -507,6 +507,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
master->handle_err = dw_spi_handle_err; master->handle_err = dw_spi_handle_err;
master->max_speed_hz = dws->max_freq; master->max_speed_hz = dws->max_freq;
master->dev.of_node = dev->of_node; master->dev.of_node = dev->of_node;
master->dev.fwnode = dev->fwnode;
master->flags = SPI_MASTER_GPIO_SS; master->flags = SPI_MASTER_GPIO_SS;
if (dws->set_cs) if (dws->set_cs)
......
...@@ -1090,8 +1090,8 @@ static int dspi_probe(struct platform_device *pdev) ...@@ -1090,8 +1090,8 @@ static int dspi_probe(struct platform_device *pdev)
goto out_clk_put; goto out_clk_put;
} }
ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, 0, ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt,
pdev->name, dspi); IRQF_SHARED, pdev->name, dspi);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n"); dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n");
goto out_clk_put; goto out_clk_put;
......
This diff is collapsed.
...@@ -64,15 +64,13 @@ ...@@ -64,15 +64,13 @@
#define TIMESTAMP_AFTER BIT(3) #define TIMESTAMP_AFTER BIT(3)
#define POST_CMD_DELAY BIT(4) #define POST_CMD_DELAY BIT(4)
/* SPI M_COMMAND OPCODE */ enum spi_m_cmd_opcode {
enum spi_mcmd_code {
CMD_NONE, CMD_NONE,
CMD_XFER, CMD_XFER,
CMD_CS, CMD_CS,
CMD_CANCEL, CMD_CANCEL,
}; };
struct spi_geni_master { struct spi_geni_master {
struct geni_se se; struct geni_se se;
struct device *dev; struct device *dev;
...@@ -87,7 +85,7 @@ struct spi_geni_master { ...@@ -87,7 +85,7 @@ struct spi_geni_master {
struct completion xfer_done; struct completion xfer_done;
unsigned int oversampling; unsigned int oversampling;
spinlock_t lock; spinlock_t lock;
unsigned int cur_mcmd; enum spi_m_cmd_opcode cur_mcmd;
int irq; int irq;
}; };
...@@ -129,7 +127,7 @@ static void spi_geni_set_cs(struct spi_device *slv, bool set_flag) ...@@ -129,7 +127,7 @@ static void spi_geni_set_cs(struct spi_device *slv, bool set_flag)
struct spi_geni_master *mas = spi_master_get_devdata(slv->master); struct spi_geni_master *mas = spi_master_get_devdata(slv->master);
struct spi_master *spi = dev_get_drvdata(mas->dev); struct spi_master *spi = dev_get_drvdata(mas->dev);
struct geni_se *se = &mas->se; struct geni_se *se = &mas->se;
unsigned long timeout; unsigned long time_left;
reinit_completion(&mas->xfer_done); reinit_completion(&mas->xfer_done);
pm_runtime_get_sync(mas->dev); pm_runtime_get_sync(mas->dev);
...@@ -142,8 +140,8 @@ static void spi_geni_set_cs(struct spi_device *slv, bool set_flag) ...@@ -142,8 +140,8 @@ static void spi_geni_set_cs(struct spi_device *slv, bool set_flag)
else else
geni_se_setup_m_cmd(se, SPI_CS_DEASSERT, 0); geni_se_setup_m_cmd(se, SPI_CS_DEASSERT, 0);
timeout = wait_for_completion_timeout(&mas->xfer_done, HZ); time_left = wait_for_completion_timeout(&mas->xfer_done, HZ);
if (!timeout) if (!time_left)
handle_fifo_timeout(spi, NULL); handle_fifo_timeout(spi, NULL);
pm_runtime_put(mas->dev); pm_runtime_put(mas->dev);
...@@ -485,7 +483,6 @@ static irqreturn_t geni_spi_isr(int irq, void *data) ...@@ -485,7 +483,6 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
struct geni_se *se = &mas->se; struct geni_se *se = &mas->se;
u32 m_irq; u32 m_irq;
unsigned long flags; unsigned long flags;
irqreturn_t ret = IRQ_HANDLED;
if (mas->cur_mcmd == CMD_NONE) if (mas->cur_mcmd == CMD_NONE)
return IRQ_NONE; return IRQ_NONE;
...@@ -533,16 +530,35 @@ static irqreturn_t geni_spi_isr(int irq, void *data) ...@@ -533,16 +530,35 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
writel(m_irq, se->base + SE_GENI_M_IRQ_CLEAR); writel(m_irq, se->base + SE_GENI_M_IRQ_CLEAR);
spin_unlock_irqrestore(&mas->lock, flags); spin_unlock_irqrestore(&mas->lock, flags);
return ret; return IRQ_HANDLED;
} }
static int spi_geni_probe(struct platform_device *pdev) static int spi_geni_probe(struct platform_device *pdev)
{ {
int ret; int ret, irq;
struct spi_master *spi; struct spi_master *spi;
struct spi_geni_master *mas; struct spi_geni_master *mas;
struct resource *res; struct resource *res;
struct geni_se *se; void __iomem *base;
struct clk *clk;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Err getting IRQ %d\n", irq);
return irq;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
clk = devm_clk_get(&pdev->dev, "se");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Err getting SE Core clk %ld\n",
PTR_ERR(clk));
return PTR_ERR(clk);
}
spi = spi_alloc_master(&pdev->dev, sizeof(*mas)); spi = spi_alloc_master(&pdev->dev, sizeof(*mas));
if (!spi) if (!spi)
...@@ -550,27 +566,15 @@ static int spi_geni_probe(struct platform_device *pdev) ...@@ -550,27 +566,15 @@ static int spi_geni_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, spi); platform_set_drvdata(pdev, spi);
mas = spi_master_get_devdata(spi); mas = spi_master_get_devdata(spi);
mas->irq = irq;
mas->dev = &pdev->dev; mas->dev = &pdev->dev;
mas->se.dev = &pdev->dev; mas->se.dev = &pdev->dev;
mas->se.wrapper = dev_get_drvdata(pdev->dev.parent); mas->se.wrapper = dev_get_drvdata(pdev->dev.parent);
se = &mas->se; mas->se.base = base;
mas->se.clk = clk;
spi->bus_num = -1; spi->bus_num = -1;
spi->dev.of_node = pdev->dev.of_node; spi->dev.of_node = pdev->dev.of_node;
mas->se.clk = devm_clk_get(&pdev->dev, "se");
if (IS_ERR(mas->se.clk)) {
ret = PTR_ERR(mas->se.clk);
dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
goto spi_geni_probe_err;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
se->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(se->base)) {
ret = PTR_ERR(se->base);
goto spi_geni_probe_err;
}
spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH; spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
spi->num_chipselect = 4; spi->num_chipselect = 4;
...@@ -589,13 +593,6 @@ static int spi_geni_probe(struct platform_device *pdev) ...@@ -589,13 +593,6 @@ static int spi_geni_probe(struct platform_device *pdev)
if (ret) if (ret)
goto spi_geni_probe_runtime_disable; goto spi_geni_probe_runtime_disable;
mas->irq = platform_get_irq(pdev, 0);
if (mas->irq < 0) {
ret = mas->irq;
dev_err(&pdev->dev, "Err getting IRQ %d\n", ret);
goto spi_geni_probe_runtime_disable;
}
ret = request_irq(mas->irq, geni_spi_isr, ret = request_irq(mas->irq, geni_spi_isr,
IRQF_TRIGGER_HIGH, "spi_geni", spi); IRQF_TRIGGER_HIGH, "spi_geni", spi);
if (ret) if (ret)
...@@ -610,7 +607,6 @@ static int spi_geni_probe(struct platform_device *pdev) ...@@ -610,7 +607,6 @@ static int spi_geni_probe(struct platform_device *pdev)
free_irq(mas->irq, spi); free_irq(mas->irq, spi);
spi_geni_probe_runtime_disable: spi_geni_probe_runtime_disable:
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
spi_geni_probe_err:
spi_master_put(spi); spi_master_put(spi);
return ret; return ret;
} }
......
...@@ -256,11 +256,29 @@ static int spi_gpio_setup(struct spi_device *spi) ...@@ -256,11 +256,29 @@ static int spi_gpio_setup(struct spi_device *spi)
static int spi_gpio_set_direction(struct spi_device *spi, bool output) static int spi_gpio_set_direction(struct spi_device *spi, bool output)
{ {
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
int ret;
if (output) if (output)
return gpiod_direction_output(spi_gpio->mosi, 1); return gpiod_direction_output(spi_gpio->mosi, 1);
else
return gpiod_direction_input(spi_gpio->mosi); ret = gpiod_direction_input(spi_gpio->mosi);
if (ret)
return ret;
/*
* Send a turnaround high impedance cycle when switching
* from output to input. Theoretically there should be
* a clock delay here, but as has been noted above, the
* nsec delay function for bit-banged GPIO is simply
* {} because bit-banging just doesn't get fast enough
* anyway.
*/
if (spi->mode & SPI_3WIRE_HIZ) {
gpiod_set_value_cansleep(spi_gpio->sck,
!(spi->mode & SPI_CPOL));
gpiod_set_value_cansleep(spi_gpio->sck,
!!(spi->mode & SPI_CPOL));
}
return 0;
} }
static void spi_gpio_cleanup(struct spi_device *spi) static void spi_gpio_cleanup(struct spi_device *spi)
...@@ -410,7 +428,7 @@ static int spi_gpio_probe(struct platform_device *pdev) ...@@ -410,7 +428,7 @@ static int spi_gpio_probe(struct platform_device *pdev)
return status; return status;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
master->mode_bits = SPI_3WIRE | SPI_CPHA | SPI_CPOL; master->mode_bits = SPI_3WIRE | SPI_3WIRE_HIZ | SPI_CPHA | SPI_CPOL;
master->flags = master_flags; master->flags = master_flags;
master->bus_num = pdev->id; master->bus_num = pdev->id;
/* The master needs to think there is a chipselect even if not connected */ /* The master needs to think there is a chipselect even if not connected */
......
This diff is collapsed.
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include "internals.h" #include "internals.h"
#define SPI_MEM_MAX_BUSWIDTH 4 #define SPI_MEM_MAX_BUSWIDTH 8
/** /**
* spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a
...@@ -121,6 +121,13 @@ static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx) ...@@ -121,6 +121,13 @@ static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
break; break;
case 8:
if ((tx && (mode & SPI_TX_OCTAL)) ||
(!tx && (mode & SPI_RX_OCTAL)))
return 0;
break;
default: default:
break; break;
} }
...@@ -142,7 +149,7 @@ static bool spi_mem_default_supports_op(struct spi_mem *mem, ...@@ -142,7 +149,7 @@ static bool spi_mem_default_supports_op(struct spi_mem *mem,
spi_check_buswidth_req(mem, op->dummy.buswidth, true)) spi_check_buswidth_req(mem, op->dummy.buswidth, true))
return false; return false;
if (op->data.nbytes && if (op->data.dir != SPI_MEM_NO_DATA &&
spi_check_buswidth_req(mem, op->data.buswidth, spi_check_buswidth_req(mem, op->data.buswidth,
op->data.dir == SPI_MEM_DATA_OUT)) op->data.dir == SPI_MEM_DATA_OUT))
return false; return false;
...@@ -213,6 +220,44 @@ bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) ...@@ -213,6 +220,44 @@ bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
} }
EXPORT_SYMBOL_GPL(spi_mem_supports_op); EXPORT_SYMBOL_GPL(spi_mem_supports_op);
static int spi_mem_access_start(struct spi_mem *mem)
{
struct spi_controller *ctlr = mem->spi->controller;
/*
* Flush the message queue before executing our SPI memory
* operation to prevent preemption of regular SPI transfers.
*/
spi_flush_queue(ctlr);
if (ctlr->auto_runtime_pm) {
int ret;
ret = pm_runtime_get_sync(ctlr->dev.parent);
if (ret < 0) {
dev_err(&ctlr->dev, "Failed to power device: %d\n",
ret);
return ret;
}
}
mutex_lock(&ctlr->bus_lock_mutex);
mutex_lock(&ctlr->io_mutex);
return 0;
}
static void spi_mem_access_end(struct spi_mem *mem)
{
struct spi_controller *ctlr = mem->spi->controller;
mutex_unlock(&ctlr->io_mutex);
mutex_unlock(&ctlr->bus_lock_mutex);
if (ctlr->auto_runtime_pm)
pm_runtime_put(ctlr->dev.parent);
}
/** /**
* spi_mem_exec_op() - Execute a memory operation * spi_mem_exec_op() - Execute a memory operation
* @mem: the SPI memory * @mem: the SPI memory
...@@ -242,30 +287,13 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) ...@@ -242,30 +287,13 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
return -ENOTSUPP; return -ENOTSUPP;
if (ctlr->mem_ops) { if (ctlr->mem_ops) {
/* ret = spi_mem_access_start(mem);
* Flush the message queue before executing our SPI memory if (ret)
* operation to prevent preemption of regular SPI transfers. return ret;
*/
spi_flush_queue(ctlr);
if (ctlr->auto_runtime_pm) {
ret = pm_runtime_get_sync(ctlr->dev.parent);
if (ret < 0) {
dev_err(&ctlr->dev,
"Failed to power device: %d\n",
ret);
return ret;
}
}
mutex_lock(&ctlr->bus_lock_mutex);
mutex_lock(&ctlr->io_mutex);
ret = ctlr->mem_ops->exec_op(mem, op); ret = ctlr->mem_ops->exec_op(mem, op);
mutex_unlock(&ctlr->io_mutex);
mutex_unlock(&ctlr->bus_lock_mutex);
if (ctlr->auto_runtime_pm) spi_mem_access_end(mem);
pm_runtime_put(ctlr->dev.parent);
/* /*
* Some controllers only optimize specific paths (typically the * Some controllers only optimize specific paths (typically the
...@@ -411,6 +439,210 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) ...@@ -411,6 +439,210 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
} }
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size); EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
struct spi_mem_op op = desc->info.op_tmpl;
int ret;
op.addr.val = desc->info.offset + offs;
op.data.buf.in = buf;
op.data.nbytes = len;
ret = spi_mem_adjust_op_size(desc->mem, &op);
if (ret)
return ret;
ret = spi_mem_exec_op(desc->mem, &op);
if (ret)
return ret;
return op.data.nbytes;
}
static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf)
{
struct spi_mem_op op = desc->info.op_tmpl;
int ret;
op.addr.val = desc->info.offset + offs;
op.data.buf.out = buf;
op.data.nbytes = len;
ret = spi_mem_adjust_op_size(desc->mem, &op);
if (ret)
return ret;
ret = spi_mem_exec_op(desc->mem, &op);
if (ret)
return ret;
return op.data.nbytes;
}
/**
* spi_mem_dirmap_create() - Create a direct mapping descriptor
* @mem: SPI mem device this direct mapping should be created for
* @info: direct mapping information
*
* This function is creating a direct mapping descriptor which can then be used
* to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write().
* If the SPI controller driver does not support direct mapping, this function
* fallback to an implementation using spi_mem_exec_op(), so that the caller
* doesn't have to bother implementing a fallback on his own.
*
* Return: a valid pointer in case of success, and ERR_PTR() otherwise.
*/
struct spi_mem_dirmap_desc *
spi_mem_dirmap_create(struct spi_mem *mem,
const struct spi_mem_dirmap_info *info)
{
struct spi_controller *ctlr = mem->spi->controller;
struct spi_mem_dirmap_desc *desc;
int ret = -ENOTSUPP;
/* Make sure the number of address cycles is between 1 and 8 bytes. */
if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8)
return ERR_PTR(-EINVAL);
/* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA)
return ERR_PTR(-EINVAL);
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (!desc)
return ERR_PTR(-ENOMEM);
desc->mem = mem;
desc->info = *info;
if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create)
ret = ctlr->mem_ops->dirmap_create(desc);
if (ret) {
desc->nodirmap = true;
if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
ret = -ENOTSUPP;
else
ret = 0;
}
if (ret) {
kfree(desc);
return ERR_PTR(ret);
}
return desc;
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_create);
/**
* spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor
* @desc: the direct mapping descriptor to destroy
* @info: direct mapping information
*
* This function destroys a direct mapping descriptor previously created by
* spi_mem_dirmap_create().
*/
void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc)
{
struct spi_controller *ctlr = desc->mem->spi->controller;
if (!desc->nodirmap && ctlr->mem_ops && ctlr->mem_ops->dirmap_destroy)
ctlr->mem_ops->dirmap_destroy(desc);
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_destroy);
/**
* spi_mem_dirmap_dirmap_read() - Read data through a direct mapping
* @desc: direct mapping descriptor
* @offs: offset to start reading from. Note that this is not an absolute
* offset, but the offset within the direct mapping which already has
* its own offset
* @len: length in bytes
* @buf: destination buffer. This buffer must be DMA-able
*
* This function reads data from a memory device using a direct mapping
* previously instantiated with spi_mem_dirmap_create().
*
* Return: the amount of data read from the memory device or a negative error
* code. Note that the returned size might be smaller than @len, and the caller
* is responsible for calling spi_mem_dirmap_read() again when that happens.
*/
ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
struct spi_controller *ctlr = desc->mem->spi->controller;
ssize_t ret;
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
return -EINVAL;
if (!len)
return 0;
if (desc->nodirmap) {
ret = spi_mem_no_dirmap_read(desc, offs, len, buf);
} else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_read) {
ret = spi_mem_access_start(desc->mem);
if (ret)
return ret;
ret = ctlr->mem_ops->dirmap_read(desc, offs, len, buf);
spi_mem_access_end(desc->mem);
} else {
ret = -ENOTSUPP;
}
return ret;
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_read);
/**
* spi_mem_dirmap_dirmap_write() - Write data through a direct mapping
* @desc: direct mapping descriptor
* @offs: offset to start writing from. Note that this is not an absolute
* offset, but the offset within the direct mapping which already has
* its own offset
* @len: length in bytes
* @buf: source buffer. This buffer must be DMA-able
*
* This function writes data to a memory device using a direct mapping
* previously instantiated with spi_mem_dirmap_create().
*
* Return: the amount of data written to the memory device or a negative error
* code. Note that the returned size might be smaller than @len, and the caller
* is responsible for calling spi_mem_dirmap_write() again when that happens.
*/
ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf)
{
struct spi_controller *ctlr = desc->mem->spi->controller;
ssize_t ret;
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT)
return -EINVAL;
if (!len)
return 0;
if (desc->nodirmap) {
ret = spi_mem_no_dirmap_write(desc, offs, len, buf);
} else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_write) {
ret = spi_mem_access_start(desc->mem);
if (ret)
return ret;
ret = ctlr->mem_ops->dirmap_write(desc, offs, len, buf);
spi_mem_access_end(desc->mem);
} else {
ret = -ENOTSUPP;
}
return ret;
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_write);
static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
{ {
return container_of(drv, struct spi_mem_driver, spidrv.driver); return container_of(drv, struct spi_mem_driver, spidrv.driver);
......
...@@ -120,6 +120,12 @@ static const struct mtk_spi_compatible mt8173_compat = { ...@@ -120,6 +120,12 @@ static const struct mtk_spi_compatible mt8173_compat = {
.must_tx = true, .must_tx = true,
}; };
static const struct mtk_spi_compatible mt8183_compat = {
.need_pad_sel = true,
.must_tx = true,
.enhance_timing = true,
};
/* /*
* A piece of default chip info unless the platform * A piece of default chip info unless the platform
* supplies it. * supplies it.
...@@ -144,12 +150,18 @@ static const struct of_device_id mtk_spi_of_match[] = { ...@@ -144,12 +150,18 @@ static const struct of_device_id mtk_spi_of_match[] = {
{ .compatible = "mediatek,mt7622-spi", { .compatible = "mediatek,mt7622-spi",
.data = (void *)&mt7622_compat, .data = (void *)&mt7622_compat,
}, },
{ .compatible = "mediatek,mt7629-spi",
.data = (void *)&mt7622_compat,
},
{ .compatible = "mediatek,mt8135-spi", { .compatible = "mediatek,mt8135-spi",
.data = (void *)&mtk_common_compat, .data = (void *)&mtk_common_compat,
}, },
{ .compatible = "mediatek,mt8173-spi", { .compatible = "mediatek,mt8173-spi",
.data = (void *)&mt8173_compat, .data = (void *)&mt8173_compat,
}, },
{ .compatible = "mediatek,mt8183-spi",
.data = (void *)&mt8183_compat,
},
{} {}
}; };
MODULE_DEVICE_TABLE(of, mtk_spi_of_match); MODULE_DEVICE_TABLE(of, mtk_spi_of_match);
......
This diff is collapsed.
This diff is collapsed.
...@@ -861,11 +861,10 @@ static void dma_callback(void *data) ...@@ -861,11 +861,10 @@ static void dma_callback(void *data)
/* Update total bytes transferred */ /* Update total bytes transferred */
msg->actual_length += pl022->cur_transfer->len; msg->actual_length += pl022->cur_transfer->len;
if (pl022->cur_transfer->cs_change)
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
/* Move to next transfer */ /* Move to next transfer */
msg->state = next_transfer(pl022); msg->state = next_transfer(pl022);
if (msg->state != STATE_DONE && pl022->cur_transfer->cs_change)
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
tasklet_schedule(&pl022->pump_transfers); tasklet_schedule(&pl022->pump_transfers);
} }
...@@ -1333,10 +1332,10 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id) ...@@ -1333,10 +1332,10 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
} }
/* Update total bytes transferred */ /* Update total bytes transferred */
msg->actual_length += pl022->cur_transfer->len; msg->actual_length += pl022->cur_transfer->len;
if (pl022->cur_transfer->cs_change)
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
/* Move to next transfer */ /* Move to next transfer */
msg->state = next_transfer(pl022); msg->state = next_transfer(pl022);
if (msg->state != STATE_DONE && pl022->cur_transfer->cs_change)
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
tasklet_schedule(&pl022->pump_transfers); tasklet_schedule(&pl022->pump_transfers);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1544,10 +1543,11 @@ static void do_polling_transfer(struct pl022 *pl022) ...@@ -1544,10 +1543,11 @@ static void do_polling_transfer(struct pl022 *pl022)
/* Update total byte transferred */ /* Update total byte transferred */
message->actual_length += pl022->cur_transfer->len; message->actual_length += pl022->cur_transfer->len;
if (pl022->cur_transfer->cs_change)
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
/* Move to next transfer */ /* Move to next transfer */
message->state = next_transfer(pl022); message->state = next_transfer(pl022);
if (message->state != STATE_DONE
&& pl022->cur_transfer->cs_change)
pl022_cs_control(pl022, SSP_CHIP_DESELECT);
} }
out: out:
/* Handle end of message */ /* Handle end of message */
......
...@@ -626,6 +626,11 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) ...@@ -626,6 +626,11 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
if (irq_status & SSSR_TUR) {
int_error_stop(drv_data, "interrupt_transfer: fifo underrun");
return IRQ_HANDLED;
}
if (irq_status & SSSR_TINT) { if (irq_status & SSSR_TINT) {
pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT); pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT);
if (drv_data->read(drv_data)) { if (drv_data->read(drv_data)) {
...@@ -1073,6 +1078,30 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *master, ...@@ -1073,6 +1078,30 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *master,
pxa2xx_spi_write(drv_data, SSTO, chip->timeout); pxa2xx_spi_write(drv_data, SSTO, chip->timeout);
} }
if (drv_data->ssp_type == MMP2_SSP) {
u8 tx_level = (pxa2xx_spi_read(drv_data, SSSR)
& SSSR_TFL_MASK) >> 8;
if (tx_level) {
/* On MMP2, flipping SSE doesn't to empty TXFIFO. */
dev_warn(&spi->dev, "%d bytes of garbage in TXFIFO!\n",
tx_level);
if (tx_level > transfer->len)
tx_level = transfer->len;
drv_data->tx += tx_level;
}
}
if (spi_controller_is_slave(master)) {
while (drv_data->write(drv_data))
;
if (drv_data->gpiod_ready) {
gpiod_set_value(drv_data->gpiod_ready, 1);
udelay(1);
gpiod_set_value(drv_data->gpiod_ready, 0);
}
}
/* /*
* Release the data by enabling service requests and interrupts, * Release the data by enabling service requests and interrupts,
* without changing any mode bits * without changing any mode bits
...@@ -1082,6 +1111,27 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *master, ...@@ -1082,6 +1111,27 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *master,
return 1; return 1;
} }
static int pxa2xx_spi_slave_abort(struct spi_master *master)
{
struct driver_data *drv_data = spi_controller_get_devdata(master);
/* Stop and reset SSP */
write_SSSR_CS(drv_data, drv_data->clear_sr);
reset_sccr1(drv_data);
if (!pxa25x_ssp_comp(drv_data))
pxa2xx_spi_write(drv_data, SSTO, 0);
pxa2xx_spi_flush(drv_data);
pxa2xx_spi_write(drv_data, SSCR0,
pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
dev_dbg(&drv_data->pdev->dev, "transfer aborted\n");
drv_data->master->cur_msg->status = -EINTR;
spi_finalize_current_transfer(drv_data->master);
return 0;
}
static void pxa2xx_spi_handle_err(struct spi_controller *master, static void pxa2xx_spi_handle_err(struct spi_controller *master,
struct spi_message *msg) struct spi_message *msg)
{ {
...@@ -1209,9 +1259,14 @@ static int setup(struct spi_device *spi) ...@@ -1209,9 +1259,14 @@ static int setup(struct spi_device *spi)
rx_thres = config->rx_threshold; rx_thres = config->rx_threshold;
break; break;
default: default:
tx_thres = TX_THRESH_DFLT;
tx_hi_thres = 0; tx_hi_thres = 0;
rx_thres = RX_THRESH_DFLT; if (spi_controller_is_slave(drv_data->master)) {
tx_thres = 1;
rx_thres = 2;
} else {
tx_thres = TX_THRESH_DFLT;
rx_thres = RX_THRESH_DFLT;
}
break; break;
} }
...@@ -1255,6 +1310,12 @@ static int setup(struct spi_device *spi) ...@@ -1255,6 +1310,12 @@ static int setup(struct spi_device *spi)
if (chip_info->enable_loopback) if (chip_info->enable_loopback)
chip->cr1 = SSCR1_LBM; chip->cr1 = SSCR1_LBM;
} }
if (spi_controller_is_slave(drv_data->master)) {
chip->cr1 |= SSCR1_SCFR;
chip->cr1 |= SSCR1_SCLKDIR;
chip->cr1 |= SSCR1_SFRMDIR;
chip->cr1 |= SSCR1_SPH;
}
chip->lpss_rx_threshold = SSIRF_RxThresh(rx_thres); chip->lpss_rx_threshold = SSIRF_RxThresh(rx_thres);
chip->lpss_tx_threshold = SSITF_TxLoThresh(tx_thres) chip->lpss_tx_threshold = SSITF_TxLoThresh(tx_thres)
...@@ -1500,6 +1561,7 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) ...@@ -1500,6 +1561,7 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev)
ssp->pdev = pdev; ssp->pdev = pdev;
ssp->port_id = pxa2xx_spi_get_port_id(adev); ssp->port_id = pxa2xx_spi_get_port_id(adev);
pdata->is_slave = of_property_read_bool(pdev->dev.of_node, "spi-slave");
pdata->num_chipselect = 1; pdata->num_chipselect = 1;
pdata->enable_dma = true; pdata->enable_dma = true;
...@@ -1559,7 +1621,11 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) ...@@ -1559,7 +1621,11 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
master = spi_alloc_master(dev, sizeof(struct driver_data)); if (platform_info->is_slave)
master = spi_alloc_slave(dev, sizeof(struct driver_data));
else
master = spi_alloc_master(dev, sizeof(struct driver_data));
if (!master) { if (!master) {
dev_err(&pdev->dev, "cannot alloc spi_master\n"); dev_err(&pdev->dev, "cannot alloc spi_master\n");
pxa_ssp_free(ssp); pxa_ssp_free(ssp);
...@@ -1581,6 +1647,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) ...@@ -1581,6 +1647,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
master->setup = setup; master->setup = setup;
master->set_cs = pxa2xx_spi_set_cs; master->set_cs = pxa2xx_spi_set_cs;
master->transfer_one = pxa2xx_spi_transfer_one; master->transfer_one = pxa2xx_spi_transfer_one;
master->slave_abort = pxa2xx_spi_slave_abort;
master->handle_err = pxa2xx_spi_handle_err; master->handle_err = pxa2xx_spi_handle_err;
master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer; master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer;
master->fw_translate_cs = pxa2xx_spi_fw_translate_cs; master->fw_translate_cs = pxa2xx_spi_fw_translate_cs;
...@@ -1610,7 +1677,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) ...@@ -1610,7 +1677,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE; drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE;
drv_data->dma_cr1 = DEFAULT_DMA_CR1; drv_data->dma_cr1 = DEFAULT_DMA_CR1;
drv_data->clear_sr = SSSR_ROR | SSSR_TINT; drv_data->clear_sr = SSSR_ROR | SSSR_TINT;
drv_data->mask_sr = SSSR_TINT | SSSR_RFS | SSSR_TFS | SSSR_ROR; drv_data->mask_sr = SSSR_TINT | SSSR_RFS | SSSR_TFS
| SSSR_ROR | SSSR_TUR;
} }
status = request_irq(ssp->irq, ssp_int, IRQF_SHARED, dev_name(dev), status = request_irq(ssp->irq, ssp_int, IRQF_SHARED, dev_name(dev),
...@@ -1658,10 +1726,22 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) ...@@ -1658,10 +1726,22 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
pxa2xx_spi_write(drv_data, SSCR0, tmp); pxa2xx_spi_write(drv_data, SSCR0, tmp);
break; break;
default: default:
tmp = SSCR1_RxTresh(RX_THRESH_DFLT) |
SSCR1_TxTresh(TX_THRESH_DFLT); if (spi_controller_is_slave(master)) {
tmp = SSCR1_SCFR |
SSCR1_SCLKDIR |
SSCR1_SFRMDIR |
SSCR1_RxTresh(2) |
SSCR1_TxTresh(1) |
SSCR1_SPH;
} else {
tmp = SSCR1_RxTresh(RX_THRESH_DFLT) |
SSCR1_TxTresh(TX_THRESH_DFLT);
}
pxa2xx_spi_write(drv_data, SSCR1, tmp); pxa2xx_spi_write(drv_data, SSCR1, tmp);
tmp = SSCR0_SCR(2) | SSCR0_Motorola | SSCR0_DataSize(8); tmp = SSCR0_Motorola | SSCR0_DataSize(8);
if (!spi_controller_is_slave(master))
tmp |= SSCR0_SCR(2);
pxa2xx_spi_write(drv_data, SSCR0, tmp); pxa2xx_spi_write(drv_data, SSCR0, tmp);
break; break;
} }
...@@ -1711,7 +1791,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) ...@@ -1711,7 +1791,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
if (PTR_ERR(gpiod) == -ENOENT) if (PTR_ERR(gpiod) == -ENOENT)
continue; continue;
status = (int)PTR_ERR(gpiod); status = PTR_ERR(gpiod);
goto out_error_clock_enabled; goto out_error_clock_enabled;
} else { } else {
drv_data->cs_gpiods[i] = gpiod; drv_data->cs_gpiods[i] = gpiod;
...@@ -1719,6 +1799,15 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) ...@@ -1719,6 +1799,15 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
} }
} }
if (platform_info->is_slave) {
drv_data->gpiod_ready = devm_gpiod_get_optional(dev,
"ready", GPIOD_OUT_LOW);
if (IS_ERR(drv_data->gpiod_ready)) {
status = PTR_ERR(drv_data->gpiod_ready);
goto out_error_clock_enabled;
}
}
pm_runtime_set_autosuspend_delay(&pdev->dev, 50); pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_active(&pdev->dev); pm_runtime_set_active(&pdev->dev);
...@@ -1811,10 +1900,6 @@ static int pxa2xx_spi_resume(struct device *dev) ...@@ -1811,10 +1900,6 @@ static int pxa2xx_spi_resume(struct device *dev)
return status; return status;
} }
/* Restore LPSS private register bits */
if (is_lpss_ssp(drv_data))
lpss_ssp_setup(drv_data);
/* Start the queue running */ /* Start the queue running */
return spi_controller_resume(drv_data->master); return spi_controller_resume(drv_data->master);
} }
......
...@@ -64,6 +64,9 @@ struct driver_data { ...@@ -64,6 +64,9 @@ struct driver_data {
/* GPIOs for chip selects */ /* GPIOs for chip selects */
struct gpio_desc **cs_gpiods; struct gpio_desc **cs_gpiods;
/* Optional slave FIFO ready signal */
struct gpio_desc *gpiod_ready;
}; };
struct chip_data { struct chip_data {
......
This diff is collapsed.
...@@ -1347,16 +1347,14 @@ MODULE_DEVICE_TABLE(platform, spi_driver_ids); ...@@ -1347,16 +1347,14 @@ MODULE_DEVICE_TABLE(platform, spi_driver_ids);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int rspi_suspend(struct device *dev) static int rspi_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct rspi_data *rspi = dev_get_drvdata(dev);
struct rspi_data *rspi = platform_get_drvdata(pdev);
return spi_master_suspend(rspi->master); return spi_master_suspend(rspi->master);
} }
static int rspi_resume(struct device *dev) static int rspi_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct rspi_data *rspi = dev_get_drvdata(dev);
struct rspi_data *rspi = platform_get_drvdata(pdev);
return spi_master_resume(rspi->master); return spi_master_resume(rspi->master);
} }
......
...@@ -977,7 +977,7 @@ static int sh_msiof_transfer_one(struct spi_master *master, ...@@ -977,7 +977,7 @@ static int sh_msiof_transfer_one(struct spi_master *master,
return 0; return 0;
} }
if (bits <= 8 && len > 15 && !(len & 3)) { if (bits <= 8 && len > 15) {
bits = 32; bits = 32;
swab = true; swab = true;
} else { } else {
...@@ -1038,6 +1038,14 @@ static int sh_msiof_transfer_one(struct spi_master *master, ...@@ -1038,6 +1038,14 @@ static int sh_msiof_transfer_one(struct spi_master *master,
if (rx_buf) if (rx_buf)
rx_buf += n * bytes_per_word; rx_buf += n * bytes_per_word;
words -= n; words -= n;
if (words == 0 && (len % bytes_per_word)) {
words = len % bytes_per_word;
bits = t->bits_per_word;
bytes_per_word = 1;
tx_fifo = sh_msiof_spi_write_fifo_8;
rx_fifo = sh_msiof_spi_read_fifo_8;
}
} }
return 0; return 0;
...@@ -1426,16 +1434,14 @@ MODULE_DEVICE_TABLE(platform, spi_driver_ids); ...@@ -1426,16 +1434,14 @@ MODULE_DEVICE_TABLE(platform, spi_driver_ids);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int sh_msiof_spi_suspend(struct device *dev) static int sh_msiof_spi_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct sh_msiof_spi_priv *p = dev_get_drvdata(dev);
struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev);
return spi_master_suspend(p->master); return spi_master_suspend(p->master);
} }
static int sh_msiof_spi_resume(struct device *dev) static int sh_msiof_spi_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct sh_msiof_spi_priv *p = dev_get_drvdata(dev);
struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev);
return spi_master_resume(p->master); return spi_master_resume(p->master);
} }
......
...@@ -960,8 +960,7 @@ static int __maybe_unused zynqmp_qspi_resume(struct device *dev) ...@@ -960,8 +960,7 @@ static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
*/ */
static int __maybe_unused zynqmp_runtime_suspend(struct device *dev) static int __maybe_unused zynqmp_runtime_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct spi_master *master = dev_get_drvdata(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
clk_disable(xqspi->refclk); clk_disable(xqspi->refclk);
...@@ -980,8 +979,7 @@ static int __maybe_unused zynqmp_runtime_suspend(struct device *dev) ...@@ -980,8 +979,7 @@ static int __maybe_unused zynqmp_runtime_suspend(struct device *dev)
*/ */
static int __maybe_unused zynqmp_runtime_resume(struct device *dev) static int __maybe_unused zynqmp_runtime_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct spi_master *master = dev_get_drvdata(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
int ret; int ret;
......
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
/* // SPI init/core code
* SPI init/core code //
* // Copyright (C) 2005 David Brownell
* Copyright (C) 2005 David Brownell // Copyright (C) 2008 Secret Lab Technologies Ltd.
* Copyright (C) 2008 Secret Lab Technologies Ltd.
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -1037,6 +1035,42 @@ static int spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) ...@@ -1037,6 +1035,42 @@ static int spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
return __spi_map_msg(ctlr, msg); return __spi_map_msg(ctlr, msg);
} }
static int spi_transfer_wait(struct spi_controller *ctlr,
struct spi_message *msg,
struct spi_transfer *xfer)
{
struct spi_statistics *statm = &ctlr->statistics;
struct spi_statistics *stats = &msg->spi->statistics;
unsigned long long ms = 1;
if (spi_controller_is_slave(ctlr)) {
if (wait_for_completion_interruptible(&ctlr->xfer_completion)) {
dev_dbg(&msg->spi->dev, "SPI transfer interrupted\n");
return -EINTR;
}
} else {
ms = 8LL * 1000LL * xfer->len;
do_div(ms, xfer->speed_hz);
ms += ms + 200; /* some tolerance */
if (ms > UINT_MAX)
ms = UINT_MAX;
ms = wait_for_completion_timeout(&ctlr->xfer_completion,
msecs_to_jiffies(ms));
if (ms == 0) {
SPI_STATISTICS_INCREMENT_FIELD(statm, timedout);
SPI_STATISTICS_INCREMENT_FIELD(stats, timedout);
dev_err(&msg->spi->dev,
"SPI transfer timed out\n");
return -ETIMEDOUT;
}
}
return 0;
}
/* /*
* spi_transfer_one_message - Default implementation of transfer_one_message() * spi_transfer_one_message - Default implementation of transfer_one_message()
* *
...@@ -1050,7 +1084,6 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, ...@@ -1050,7 +1084,6 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
struct spi_transfer *xfer; struct spi_transfer *xfer;
bool keep_cs = false; bool keep_cs = false;
int ret = 0; int ret = 0;
unsigned long long ms = 1;
struct spi_statistics *statm = &ctlr->statistics; struct spi_statistics *statm = &ctlr->statistics;
struct spi_statistics *stats = &msg->spi->statistics; struct spi_statistics *stats = &msg->spi->statistics;
...@@ -1080,26 +1113,9 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, ...@@ -1080,26 +1113,9 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
} }
if (ret > 0) { if (ret > 0) {
ret = 0; ret = spi_transfer_wait(ctlr, msg, xfer);
ms = 8LL * 1000LL * xfer->len; if (ret < 0)
do_div(ms, xfer->speed_hz); msg->status = ret;
ms += ms + 200; /* some tolerance */
if (ms > UINT_MAX)
ms = UINT_MAX;
ms = wait_for_completion_timeout(&ctlr->xfer_completion,
msecs_to_jiffies(ms));
}
if (ms == 0) {
SPI_STATISTICS_INCREMENT_FIELD(statm,
timedout);
SPI_STATISTICS_INCREMENT_FIELD(stats,
timedout);
dev_err(&msg->spi->dev,
"SPI transfer timed out\n");
msg->status = -ETIMEDOUT;
} }
} else { } else {
if (xfer->len) if (xfer->len)
...@@ -1617,6 +1633,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, ...@@ -1617,6 +1633,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
case 4: case 4:
spi->mode |= SPI_TX_QUAD; spi->mode |= SPI_TX_QUAD;
break; break;
case 8:
spi->mode |= SPI_TX_OCTAL;
break;
default: default:
dev_warn(&ctlr->dev, dev_warn(&ctlr->dev,
"spi-tx-bus-width %d not supported\n", "spi-tx-bus-width %d not supported\n",
...@@ -1635,6 +1654,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, ...@@ -1635,6 +1654,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
case 4: case 4:
spi->mode |= SPI_RX_QUAD; spi->mode |= SPI_RX_QUAD;
break; break;
case 8:
spi->mode |= SPI_RX_OCTAL;
break;
default: default:
dev_warn(&ctlr->dev, dev_warn(&ctlr->dev,
"spi-rx-bus-width %d not supported\n", "spi-rx-bus-width %d not supported\n",
...@@ -1644,7 +1666,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, ...@@ -1644,7 +1666,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
} }
if (spi_controller_is_slave(ctlr)) { if (spi_controller_is_slave(ctlr)) {
if (strcmp(nc->name, "slave")) { if (!of_node_name_eq(nc, "slave")) {
dev_err(&ctlr->dev, "%pOF is not called 'slave'\n", dev_err(&ctlr->dev, "%pOF is not called 'slave'\n",
nc); nc);
return -EINVAL; return -EINVAL;
...@@ -2823,7 +2845,8 @@ int spi_setup(struct spi_device *spi) ...@@ -2823,7 +2845,8 @@ int spi_setup(struct spi_device *spi)
/* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden /* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden
*/ */
if ((spi->mode & SPI_3WIRE) && (spi->mode & if ((spi->mode & SPI_3WIRE) && (spi->mode &
(SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD))) (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL |
SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL)))
return -EINVAL; return -EINVAL;
/* help drivers fail *cleanly* when they need options /* help drivers fail *cleanly* when they need options
* that aren't supported with their current controller * that aren't supported with their current controller
...@@ -2832,7 +2855,8 @@ int spi_setup(struct spi_device *spi) ...@@ -2832,7 +2855,8 @@ int spi_setup(struct spi_device *spi)
*/ */
bad_bits = spi->mode & ~(spi->controller->mode_bits | SPI_CS_WORD); bad_bits = spi->mode & ~(spi->controller->mode_bits | SPI_CS_WORD);
ugly_bits = bad_bits & ugly_bits = bad_bits &
(SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD); (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL |
SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL);
if (ugly_bits) { if (ugly_bits) {
dev_warn(&spi->dev, dev_warn(&spi->dev,
"setup: ignoring unsupported mode bits %x\n", "setup: ignoring unsupported mode bits %x\n",
......
...@@ -25,6 +25,7 @@ struct dma_chan; ...@@ -25,6 +25,7 @@ struct dma_chan;
struct pxa2xx_spi_master { struct pxa2xx_spi_master {
u16 num_chipselect; u16 num_chipselect;
u8 enable_dma; u8 enable_dma;
bool is_slave;
/* DMA engine specific config */ /* DMA engine specific config */
bool (*dma_filter)(struct dma_chan *chan, void *param); bool (*dma_filter)(struct dma_chan *chan, void *param);
......
...@@ -57,10 +57,12 @@ ...@@ -57,10 +57,12 @@
/** /**
* enum spi_mem_data_dir - describes the direction of a SPI memory data * enum spi_mem_data_dir - describes the direction of a SPI memory data
* transfer from the controller perspective * transfer from the controller perspective
* @SPI_MEM_NO_DATA: no data transferred
* @SPI_MEM_DATA_IN: data coming from the SPI memory * @SPI_MEM_DATA_IN: data coming from the SPI memory
* @SPI_MEM_DATA_OUT: data sent the SPI memory * @SPI_MEM_DATA_OUT: data sent to the SPI memory
*/ */
enum spi_mem_data_dir { enum spi_mem_data_dir {
SPI_MEM_NO_DATA,
SPI_MEM_DATA_IN, SPI_MEM_DATA_IN,
SPI_MEM_DATA_OUT, SPI_MEM_DATA_OUT,
}; };
...@@ -122,6 +124,49 @@ struct spi_mem_op { ...@@ -122,6 +124,49 @@ struct spi_mem_op {
.data = __data, \ .data = __data, \
} }
/**
* struct spi_mem_dirmap_info - Direct mapping information
* @op_tmpl: operation template that should be used by the direct mapping when
* the memory device is accessed
* @offset: absolute offset this direct mapping is pointing to
* @length: length in byte of this direct mapping
*
* These information are used by the controller specific implementation to know
* the portion of memory that is directly mapped and the spi_mem_op that should
* be used to access the device.
* A direct mapping is only valid for one direction (read or write) and this
* direction is directly encoded in the ->op_tmpl.data.dir field.
*/
struct spi_mem_dirmap_info {
struct spi_mem_op op_tmpl;
u64 offset;
u64 length;
};
/**
* struct spi_mem_dirmap_desc - Direct mapping descriptor
* @mem: the SPI memory device this direct mapping is attached to
* @info: information passed at direct mapping creation time
* @nodirmap: set to 1 if the SPI controller does not implement
* ->mem_ops->dirmap_create() or when this function returned an
* error. If @nodirmap is true, all spi_mem_dirmap_{read,write}()
* calls will use spi_mem_exec_op() to access the memory. This is a
* degraded mode that allows spi_mem drivers to use the same code
* no matter whether the controller supports direct mapping or not
* @priv: field pointing to controller specific data
*
* Common part of a direct mapping descriptor. This object is created by
* spi_mem_dirmap_create() and controller implementation of ->create_dirmap()
* can create/attach direct mapping resources to the descriptor in the ->priv
* field.
*/
struct spi_mem_dirmap_desc {
struct spi_mem *mem;
struct spi_mem_dirmap_info info;
unsigned int nodirmap;
void *priv;
};
/** /**
* struct spi_mem - describes a SPI memory device * struct spi_mem - describes a SPI memory device
* @spi: the underlying SPI device * @spi: the underlying SPI device
...@@ -177,10 +222,32 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem) ...@@ -177,10 +222,32 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
* Note that if the implementation of this function allocates memory * Note that if the implementation of this function allocates memory
* dynamically, then it should do so with devm_xxx(), as we don't * dynamically, then it should do so with devm_xxx(), as we don't
* have a ->free_name() function. * have a ->free_name() function.
* @dirmap_create: create a direct mapping descriptor that can later be used to
* access the memory device. This method is optional
* @dirmap_destroy: destroy a memory descriptor previous created by
* ->dirmap_create()
* @dirmap_read: read data from the memory device using the direct mapping
* created by ->dirmap_create(). The function can return less
* data than requested (for example when the request is crossing
* the currently mapped area), and the caller of
* spi_mem_dirmap_read() is responsible for calling it again in
* this case.
* @dirmap_write: write data to the memory device using the direct mapping
* created by ->dirmap_create(). The function can return less
* data than requested (for example when the request is crossing
* the currently mapped area), and the caller of
* spi_mem_dirmap_write() is responsible for calling it again in
* this case.
* *
* This interface should be implemented by SPI controllers providing an * This interface should be implemented by SPI controllers providing an
* high-level interface to execute SPI memory operation, which is usually the * high-level interface to execute SPI memory operation, which is usually the
* case for QSPI controllers. * case for QSPI controllers.
*
* Note on ->dirmap_{read,write}(): drivers should avoid accessing the direct
* mapping from the CPU because doing that can stall the CPU waiting for the
* SPI mem transaction to finish, and this will make real-time maintainers
* unhappy and might make your system less reactive. Instead, drivers should
* use DMA to access this direct mapping.
*/ */
struct spi_controller_mem_ops { struct spi_controller_mem_ops {
int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op); int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
...@@ -189,6 +256,12 @@ struct spi_controller_mem_ops { ...@@ -189,6 +256,12 @@ struct spi_controller_mem_ops {
int (*exec_op)(struct spi_mem *mem, int (*exec_op)(struct spi_mem *mem,
const struct spi_mem_op *op); const struct spi_mem_op *op);
const char *(*get_name)(struct spi_mem *mem); const char *(*get_name)(struct spi_mem *mem);
int (*dirmap_create)(struct spi_mem_dirmap_desc *desc);
void (*dirmap_destroy)(struct spi_mem_dirmap_desc *desc);
ssize_t (*dirmap_read)(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf);
ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf);
}; };
/** /**
...@@ -249,6 +322,15 @@ int spi_mem_exec_op(struct spi_mem *mem, ...@@ -249,6 +322,15 @@ int spi_mem_exec_op(struct spi_mem *mem,
const char *spi_mem_get_name(struct spi_mem *mem); const char *spi_mem_get_name(struct spi_mem *mem);
struct spi_mem_dirmap_desc *
spi_mem_dirmap_create(struct spi_mem *mem,
const struct spi_mem_dirmap_info *info);
void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc);
ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf);
ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf);
int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv, int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
struct module *owner); struct module *owner);
......
...@@ -154,7 +154,10 @@ struct spi_device { ...@@ -154,7 +154,10 @@ struct spi_device {
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */ #define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */ #define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */ #define SPI_RX_QUAD 0x800 /* receive with 4 wires */
#define SPI_CS_WORD 0x1000 /* toggle cs after each word */ #define SPI_CS_WORD 0x1000 /* toggle cs after each word */
#define SPI_TX_OCTAL 0x2000 /* transmit with 8 wires */
#define SPI_RX_OCTAL 0x4000 /* receive with 8 wires */
#define SPI_3WIRE_HIZ 0x8000 /* high impedance turnaround */
int irq; int irq;
void *controller_state; void *controller_state;
void *controller_data; void *controller_data;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment