diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt index ff92e5a41bed..dab7ca9f250c 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt @@ -53,20 +53,8 @@ Required properties: - clocks: Serial engine core clock needed by the device. Qualcomm Technologies Inc. GENI Serial Engine based SPI Controller - -Required properties: -- compatible: Must contain "qcom,geni-spi". -- reg: Must contain SPI register location and length. -- interrupts: Must contain SPI controller interrupts. -- clock-names: Must contain "se". -- clocks: Serial engine core clock needed by the device. -- spi-max-frequency: Specifies maximum SPI clock frequency, units - Hz. -- #address-cells: Must be <1> to define a chip select address on - the SPI bus. -- #size-cells: Must be <0>. - -SPI slave nodes must be children of the SPI master node and conform to SPI bus -binding as described in Documentation/devicetree/bindings/spi/spi-bus.txt. +node binding is described in +Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt. Example: geniqup@8c0000 { @@ -103,17 +91,4 @@ Example: pinctrl-1 = <&qup_1_uart_3_sleep>; }; - spi0: spi@a84000 { - compatible = "qcom,geni-spi"; - reg = <0xa84000 0x4000>; - interrupts = ; - clock-names = "se"; - clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>; - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&qup_1_spi_2_active>; - pinctrl-1 = <&qup_1_spi_2_sleep>; - spi-max-frequency = <19200000>; - #address-cells = <1>; - #size-cells = <0>; - }; } diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt new file mode 100644 index 000000000000..790311a42bf1 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt @@ -0,0 +1,39 @@ +GENI based Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI) + +The QUP v3 core is a GENI based AHB slave that provides a common data path +(an output FIFO and an input FIFO) for serial peripheral interface (SPI) +mini-core. + +SPI in master mode supports up to 50MHz, up to four chip selects, programmable +data path from 4 bits to 32 bits and numerous protocol variants. + +Required properties: +- compatible: Must contain "qcom,geni-spi". +- reg: Must contain SPI register location and length. +- interrupts: Must contain SPI controller interrupts. +- clock-names: Must contain "se". +- clocks: Serial engine core clock needed by the device. +- #address-cells: Must be <1> to define a chip select address on + the SPI bus. +- #size-cells: Must be <0>. + +SPI Controller nodes must be child of GENI based Qualcomm Universal +Peripharal. Please refer GENI based QUP wrapper controller node bindings +described in Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt. + +SPI slave nodes must be children of the SPI master node and conform to SPI bus +binding as described in Documentation/devicetree/bindings/spi/spi-bus.txt. + +Example: + spi0: spi@a84000 { + compatible = "qcom,geni-spi"; + reg = <0xa84000 0x4000>; + interrupts = ; + clock-names = "se"; + clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qup_1_spi_2_active>; + pinctrl-1 = <&qup_1_spi_2_sleep>; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.txt new file mode 100644 index 000000000000..1d64b61f5171 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.txt @@ -0,0 +1,36 @@ +Qualcomm Quad Serial Peripheral Interface (QSPI) + +The QSPI controller allows SPI protocol communication in single, dual, or quad +wire transmission modes for read/write access to slaves such as NOR flash. + +Required properties: +- compatible: An SoC specific identifier followed by "qcom,qspi-v1", such as + "qcom,sdm845-qspi", "qcom,qspi-v1" +- reg: Should contain the base register location and length. +- interrupts: Interrupt number used by the controller. +- clocks: Should contain the core and AHB clock. +- clock-names: Should be "core" for core clock and "iface" for AHB clock. + +SPI slave nodes must be children of the SPI master node and can contain +properties described in Documentation/devicetree/bindings/spi/spi-bus.txt + +Example: + + qspi: spi@88df000 { + compatible = "qcom,sdm845-qspi", "qcom,qspi-v1"; + reg = <0x88df000 0x600>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = ; + clock-names = "iface", "core"; + clocks = <&gcc GCC_QSPI_CNOC_PERIPH_AHB_CLK>, + <&gcc GCC_QSPI_CORE_CLK>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <25000000>; + spi-tx-bus-width = <2>; + spi-rx-bus-width = <2>; + }; + }; diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt index bfbc2035fb6b..4b836ad17b19 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,14 @@ Required properties: "renesas,msiof-r8a7795" (R-Car H3) "renesas,msiof-r8a7796" (R-Car M3-W) "renesas,msiof-r8a77965" (R-Car M3-N) + "renesas,msiof-r8a77970" (R-Car V3M) + "renesas,msiof-r8a77980" (R-Car V3H) + "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/snps,dw-apb-ssi.txt b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt index 642d3fb1ef85..2864bc6b659c 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt @@ -2,7 +2,7 @@ Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface. Required properties: - compatible : "snps,dw-apb-ssi" or "mscc,-spi", where soc is "ocelot" or - "jaguar2" + "jaguar2", or "amazon,alpine-dw-apb-ssi" - reg : The register base for the controller. For "mscc,-spi", a second register set is required (named ICPU_CFG:SPI_MST) - interrupts : One interrupt, used by the controller. diff --git a/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt index 4af132606b37..8d178a4503cf 100644 --- a/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt +++ b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt @@ -3,6 +3,7 @@ Required properties: - compatible : - "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 - reg : address and length of the lpspi master registers - interrupts : lpspi interrupt - clocks : lpspi clock specifier diff --git a/Documentation/devicetree/bindings/spi/spi-pxa2xx.txt b/Documentation/devicetree/bindings/spi/spi-pxa2xx.txt new file mode 100644 index 000000000000..0335a9bd2e8a --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-pxa2xx.txt @@ -0,0 +1,24 @@ +PXA2xx SSP SPI Controller + +Required properties: +- compatible: Must be "marvell,mmp2-ssp". +- reg: Offset and length of the device's register set. +- interrupts: Should be the interrupt number. +- clocks: Should contain a single entry describing the clock input. +- #address-cells: Number of cells required to define a chip select address. +- #size-cells: Should be zero. + +Optional properties: +- cs-gpios: list of GPIO chip selects. See the SPI bus bindings, + Documentation/devicetree/bindings/spi/spi-bus.txt + +Child nodes represent devices on the SPI bus + See ../spi/spi-bus.txt + +Example: + ssp1: spi@d4035000 { + compatible = "marvell,mmp2-ssp"; + reg = <0xd4035000 0x1000>; + clocks = <&soc_clocks MMP2_CLK_SSP0>; + interrupts = <0>; + }; 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-", "renesas,rspi" as fallback. - For Renesas Serial Peripheral Interface on RZ/A1H: + For Renesas Serial Peripheral Interface on RZ/A: "renesas,rspi-", "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 = ; + 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 = ; + clock-names = "spi", "source","enable"; + clocks = <&clk_spi0>, <&ext_26m>, <&clk_ap_apb_gates 5>; + #address-cells = <1>; + #size-cells = <0>; +}; diff --git a/Documentation/devicetree/bindings/spi/spi-stm32-qspi.txt b/Documentation/devicetree/bindings/spi/spi-stm32-qspi.txt new file mode 100644 index 000000000000..adeeb63e84b9 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-stm32-qspi.txt @@ -0,0 +1,44 @@ +* STMicroelectronics Quad Serial Peripheral Interface(QSPI) + +Required properties: +- compatible: should be "st,stm32f469-qspi" +- reg: the first contains the register location and length. + the second contains the memory mapping address and length +- reg-names: should contain the reg names "qspi" "qspi_mm" +- interrupts: should contain the interrupt for the device +- clocks: the phandle of the clock needed by the QSPI controller +- A pinctrl must be defined to set pins in mode of operation for QSPI transfer + +Optional properties: +- resets: must contain the phandle to the reset controller. + +A spi flash (NOR/NAND) must be a child of spi node and could have some +properties. Also see jedec,spi-nor.txt. + +Required properties: +- reg: chip-Select number (QSPI controller may connect 2 flashes) +- spi-max-frequency: max frequency of spi bus + +Optional property: +- spi-rx-bus-width: see ./spi-bus.txt for the description + +Example: + +qspi: spi@a0001000 { + compatible = "st,stm32f469-qspi"; + reg = <0xa0001000 0x1000>, <0x90000000 0x10000000>; + reg-names = "qspi", "qspi_mm"; + interrupts = <91>; + resets = <&rcc STM32F4_AHB3_RESET(QSPI)>; + clocks = <&rcc 0 STM32F4_AHB3_CLOCK(QSPI)>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi0>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-rx-bus-width = <4>; + spi-max-frequency = <108000000>; + ... + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index c742c517f95c..40f5ec061896 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3121,6 +3121,15 @@ S: Maintained F: Documentation/devicetree/bindings/memory-controllers/brcm,dpfe-cpu.txt F: drivers/memory/brcmstb_dpfe.c +BROADCOM SPI DRIVER +M: Kamal Dasu +M: bcm-kernel-feedback-list@broadcom.com +S: Maintained +F: Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt +F: drivers/spi/spi-bcm-qspi.* +F: drivers/spi/spi-brcmstb-qspi.c +F: drivers/spi/spi-iproc-qspi.c + BROADCOM SYSTEMPORT ETHERNET DRIVER M: Florian Fainelli L: netdev@vger.kernel.org diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index feed3db21c10..ee89ffb6dde8 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -513,7 +513,7 @@ EXPORT_SYMBOL(geni_se_resources_on); */ int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl) { - unsigned long freq = 0; + long freq = 0; int i; if (se->clk_perf_tbl) { @@ -529,7 +529,7 @@ int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl) for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) { freq = clk_round_rate(se->clk, freq + 1); - if (!freq || freq == se->clk_perf_tbl[i - 1]) + if (freq <= 0 || freq == se->clk_perf_tbl[i - 1]) break; se->clk_perf_tbl[i] = freq; } @@ -544,16 +544,17 @@ EXPORT_SYMBOL(geni_se_clk_tbl_get); * @se: Pointer to the concerned serial engine. * @req_freq: Requested clock frequency. * @index: Index of the resultant frequency in the table. - * @res_freq: Resultant frequency which matches or is closer to the - * requested frequency. + * @res_freq: Resultant frequency of the source clock. * @exact: Flag to indicate exact multiple requirement of the requested * frequency. * - * This function is called by the protocol drivers to determine the matching - * or exact multiple of the requested frequency, as provided by the serial - * engine clock in order to meet the performance requirements. If there is - * no matching or exact multiple of the requested frequency found, then it - * selects the closest floor frequency, if exact flag is not set. + * This function is called by the protocol drivers to determine the best match + * of the requested frequency as provided by the serial engine clock in order + * to meet the performance requirements. + * + * If we return success: + * - if @exact is true then @res_freq / == @req_freq + * - if @exact is false then @res_freq / <= @req_freq * * Return: 0 on success, standard Linux error codes on failure. */ @@ -564,6 +565,9 @@ int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq, unsigned long *tbl; int num_clk_levels; int i; + unsigned long best_delta; + unsigned long new_delta; + unsigned int divider; num_clk_levels = geni_se_clk_tbl_get(se, &tbl); if (num_clk_levels < 0) @@ -572,18 +576,21 @@ int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq, if (num_clk_levels == 0) return -EINVAL; - *res_freq = 0; + best_delta = ULONG_MAX; for (i = 0; i < num_clk_levels; i++) { - if (!(tbl[i] % req_freq)) { + divider = DIV_ROUND_UP(tbl[i], req_freq); + new_delta = req_freq - tbl[i] / divider; + if (new_delta < best_delta) { + /* We have a new best! */ *index = i; *res_freq = tbl[i]; - return 0; - } - if (!(*res_freq) || ((tbl[i] > *res_freq) && - (tbl[i] < req_freq))) { - *index = i; - *res_freq = tbl[i]; + /* If the new best is exact then we're done */ + if (new_delta == 0) + return 0; + + /* Record how close we got */ + best_delta = new_delta; } } diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 671d078349cc..f756450a8914 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -129,7 +129,7 @@ config SPI_BCM63XX config SPI_BCM63XX_HSSPI tristate "Broadcom BCM63XX HS SPI controller driver" - depends on BCM63XX || COMPILE_TEST + depends on BCM63XX || ARCH_BCM_63XX || COMPILE_TEST help This enables support for the High Speed SPI controller present on newer Broadcom BCM63XX SoCs. @@ -520,6 +520,12 @@ config SPI_RSPI help SPI driver for Renesas RSPI and QSPI blocks. +config SPI_QCOM_QSPI + tristate "QTI QSPI controller" + depends on ARCH_QCOM + help + QSPI(Quad SPI) driver for Qualcomm QSPI controller. + config SPI_QUP tristate "Qualcomm SPI controller with QUP interface" depends on ARCH_QCOM || (ARM && COMPILE_TEST) @@ -533,6 +539,18 @@ config SPI_QUP This driver can also be built as a module. If so, the module will be called spi_qup. +config SPI_QCOM_GENI + tristate "Qualcomm GENI based SPI controller" + depends on QCOM_GENI_SE + help + This driver supports GENI serial engine based SPI controller in + master mode on the Qualcomm Technologies Inc.'s SoCs. If you say + yes to this option, support will be included for the built-in SPI + interface on the Qualcomm Technologies Inc.'s SoCs. + + This driver can also be built as a module. If so, the module + will be called spi-geni-qcom. + config SPI_S3C24XX tristate "Samsung S3C24XX series SPI" depends on ARCH_S3C24XX @@ -596,6 +614,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 @@ -613,6 +647,15 @@ config SPI_STM32 is not available, the driver automatically falls back to PIO mode. +config SPI_STM32_QSPI + tristate "STMicroelectronics STM32 QUAD SPI controller" + depends on ARCH_STM32 || COMPILE_TEST + depends on OF + 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_ST_SSC4 tristate "STMicroelectronics SPI SSC-based driver" depends on ARCH_STI || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index a90d55970036..df04dfbe7d70 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -74,6 +74,8 @@ obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o spi-pxa2xx-platform-objs := spi-pxa2xx.o spi-pxa2xx-dma.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o +obj-$(CONFIG_SPI_QCOM_GENI) += spi-geni-qcom.o +obj-$(CONFIG_SPI_QCOM_QSPI) += spi-qcom-qspi.o obj-$(CONFIG_SPI_QUP) += spi-qup.o obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o @@ -88,8 +90,11 @@ 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_STM32_QSPI) += spi-stm32-qspi.o obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.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-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 8612525fa4e3..584bcb018a62 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -89,7 +89,7 @@ #define BSPI_BPP_MODE_SELECT_MASK BIT(8) #define BSPI_BPP_ADDR_SELECT_MASK BIT(16) -#define BSPI_READ_LENGTH 512 +#define BSPI_READ_LENGTH 256 /* MSPI register offsets */ #define MSPI_SPCR0_LSB 0x000 @@ -355,7 +355,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int bpc = 0, bpp = 0; u8 command = op->cmd.opcode; int width = op->cmd.buswidth ? op->cmd.buswidth : SPI_NBITS_SINGLE; - int addrlen = op->addr.nbytes * 8; + int addrlen = op->addr.nbytes; int flex_mode = 1; dev_dbg(&qspi->pdev->dev, "set flex mode w %x addrlen %x hp %d\n", 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..3ffb6a40fe0c 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,28 @@ 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_alpine_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + dwsmmio->dws.cs_override = 1; + + return 0; +} + static int dw_spi_mmio_probe(struct platform_device *pdev) { int (*init_func)(struct platform_device *pdev, @@ -212,7 +236,9 @@ 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}, + { .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_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..b705f2bdb8b9 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -144,6 +144,8 @@ void dw_spi_set_cs(struct spi_device *spi, bool enable) if (!enable) dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); + else if (dws->cs_override) + dw_writel(dws, DW_SPI_SER, 0); } EXPORT_SYMBOL_GPL(dw_spi_set_cs); @@ -308,15 +310,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) @@ -468,6 +465,10 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws) dws->fifo_len = (fifo == 1) ? 0 : fifo; dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); } + + /* enable HW fixup for explicit CS deselect for Amazon's alpine chip */ + if (dws->cs_override) + dw_writel(dws, DW_SPI_CS_OVERRIDE, 0xF); } int dw_spi_add_host(struct device *dev, struct dw_spi *dws) @@ -496,7 +497,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 +573,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-dw.h b/drivers/spi/spi-dw.h index 0168b08364d5..c9c15881e982 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -32,6 +32,7 @@ #define DW_SPI_IDR 0x58 #define DW_SPI_VERSION 0x5c #define DW_SPI_DR 0x60 +#define DW_SPI_CS_OVERRIDE 0xf4 /* Bit fields in CTRLR0 */ #define SPI_DFS_OFFSET 0 @@ -109,6 +110,7 @@ struct dw_spi { u32 fifo_len; /* depth of the FIFO buffer */ u32 max_freq; /* max bus freq supported */ + int cs_override; u32 reg_io_width; /* DR I/O width in bytes */ u16 bus_num; u16 num_cs; /* supported slave numbers */ 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-geni-qcom.c b/drivers/spi/spi-geni-qcom.c new file mode 100644 index 000000000000..6432ecc4e2ca --- /dev/null +++ b/drivers/spi/spi-geni-qcom.c @@ -0,0 +1,703 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017-2018, The Linux foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SPI SE specific registers and respective register fields */ +#define SE_SPI_CPHA 0x224 +#define CPHA BIT(0) + +#define SE_SPI_LOOPBACK 0x22c +#define LOOPBACK_ENABLE 0x1 +#define NORMAL_MODE 0x0 +#define LOOPBACK_MSK GENMASK(1, 0) + +#define SE_SPI_CPOL 0x230 +#define CPOL BIT(2) + +#define SE_SPI_DEMUX_OUTPUT_INV 0x24c +#define CS_DEMUX_OUTPUT_INV_MSK GENMASK(3, 0) + +#define SE_SPI_DEMUX_SEL 0x250 +#define CS_DEMUX_OUTPUT_SEL GENMASK(3, 0) + +#define SE_SPI_TRANS_CFG 0x25c +#define CS_TOGGLE BIT(0) + +#define SE_SPI_WORD_LEN 0x268 +#define WORD_LEN_MSK GENMASK(9, 0) +#define MIN_WORD_LEN 4 + +#define SE_SPI_TX_TRANS_LEN 0x26c +#define SE_SPI_RX_TRANS_LEN 0x270 +#define TRANS_LEN_MSK GENMASK(23, 0) + +#define SE_SPI_PRE_POST_CMD_DLY 0x274 + +#define SE_SPI_DELAY_COUNTERS 0x278 +#define SPI_INTER_WORDS_DELAY_MSK GENMASK(9, 0) +#define SPI_CS_CLK_DELAY_MSK GENMASK(19, 10) +#define SPI_CS_CLK_DELAY_SHFT 10 + +/* M_CMD OP codes for SPI */ +#define SPI_TX_ONLY 1 +#define SPI_RX_ONLY 2 +#define SPI_FULL_DUPLEX 3 +#define SPI_TX_RX 7 +#define SPI_CS_ASSERT 8 +#define SPI_CS_DEASSERT 9 +#define SPI_SCK_ONLY 10 +/* M_CMD params for SPI */ +#define SPI_PRE_CMD_DELAY BIT(0) +#define TIMESTAMP_BEFORE BIT(1) +#define FRAGMENTATION BIT(2) +#define TIMESTAMP_AFTER BIT(3) +#define POST_CMD_DELAY BIT(4) + +/* SPI M_COMMAND OPCODE */ +enum spi_mcmd_code { + CMD_NONE, + CMD_XFER, + CMD_CS, + CMD_CANCEL, +}; + + +struct spi_geni_master { + struct geni_se se; + struct device *dev; + u32 tx_fifo_depth; + u32 fifo_width_bits; + u32 tx_wm; + unsigned long cur_speed_hz; + unsigned int cur_bits_per_word; + unsigned int tx_rem_bytes; + unsigned int rx_rem_bytes; + const struct spi_transfer *cur_xfer; + struct completion xfer_done; + unsigned int oversampling; + spinlock_t lock; + unsigned int cur_mcmd; + int irq; +}; + +static void handle_fifo_timeout(struct spi_master *spi, + struct spi_message *msg); + +static int get_spi_clk_cfg(unsigned int speed_hz, + struct spi_geni_master *mas, + unsigned int *clk_idx, + unsigned int *clk_div) +{ + unsigned long sclk_freq; + unsigned int actual_hz; + struct geni_se *se = &mas->se; + int ret; + + ret = geni_se_clk_freq_match(&mas->se, + speed_hz * mas->oversampling, + clk_idx, &sclk_freq, false); + if (ret) { + dev_err(mas->dev, "Failed(%d) to find src clk for %dHz\n", + ret, speed_hz); + return ret; + } + + *clk_div = DIV_ROUND_UP(sclk_freq, mas->oversampling * speed_hz); + actual_hz = sclk_freq / (mas->oversampling * *clk_div); + + dev_dbg(mas->dev, "req %u=>%u sclk %lu, idx %d, div %d\n", speed_hz, + actual_hz, sclk_freq, *clk_idx, *clk_div); + ret = clk_set_rate(se->clk, sclk_freq); + if (ret) + dev_err(mas->dev, "clk_set_rate failed %d\n", ret); + return ret; +} + +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_master *spi = dev_get_drvdata(mas->dev); + struct geni_se *se = &mas->se; + unsigned long timeout; + + reinit_completion(&mas->xfer_done); + pm_runtime_get_sync(mas->dev); + if (!(slv->mode & SPI_CS_HIGH)) + set_flag = !set_flag; + + mas->cur_mcmd = CMD_CS; + if (set_flag) + geni_se_setup_m_cmd(se, SPI_CS_ASSERT, 0); + else + geni_se_setup_m_cmd(se, SPI_CS_DEASSERT, 0); + + timeout = wait_for_completion_timeout(&mas->xfer_done, HZ); + if (!timeout) + handle_fifo_timeout(spi, NULL); + + pm_runtime_put(mas->dev); +} + +static void spi_setup_word_len(struct spi_geni_master *mas, u16 mode, + unsigned int bits_per_word) +{ + unsigned int pack_words; + bool msb_first = (mode & SPI_LSB_FIRST) ? false : true; + struct geni_se *se = &mas->se; + u32 word_len; + + word_len = readl(se->base + SE_SPI_WORD_LEN); + + /* + * If bits_per_word isn't a byte aligned value, set the packing to be + * 1 SPI word per FIFO word. + */ + if (!(mas->fifo_width_bits % bits_per_word)) + pack_words = mas->fifo_width_bits / bits_per_word; + else + pack_words = 1; + word_len &= ~WORD_LEN_MSK; + word_len |= ((bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK); + geni_se_config_packing(&mas->se, bits_per_word, pack_words, msb_first, + true, true); + writel(word_len, se->base + SE_SPI_WORD_LEN); +} + +static int setup_fifo_params(struct spi_device *spi_slv, + struct spi_master *spi) +{ + struct spi_geni_master *mas = spi_master_get_devdata(spi); + struct geni_se *se = &mas->se; + u32 loopback_cfg, cpol, cpha, demux_output_inv; + u32 demux_sel, clk_sel, m_clk_cfg, idx, div; + int ret; + + loopback_cfg = readl(se->base + SE_SPI_LOOPBACK); + cpol = readl(se->base + SE_SPI_CPOL); + cpha = readl(se->base + SE_SPI_CPHA); + demux_output_inv = 0; + loopback_cfg &= ~LOOPBACK_MSK; + cpol &= ~CPOL; + cpha &= ~CPHA; + + if (spi_slv->mode & SPI_LOOP) + loopback_cfg |= LOOPBACK_ENABLE; + + if (spi_slv->mode & SPI_CPOL) + cpol |= CPOL; + + if (spi_slv->mode & SPI_CPHA) + cpha |= CPHA; + + if (spi_slv->mode & SPI_CS_HIGH) + demux_output_inv = BIT(spi_slv->chip_select); + + demux_sel = spi_slv->chip_select; + mas->cur_speed_hz = spi_slv->max_speed_hz; + mas->cur_bits_per_word = spi_slv->bits_per_word; + + ret = get_spi_clk_cfg(mas->cur_speed_hz, mas, &idx, &div); + if (ret) { + dev_err(mas->dev, "Err setting clks ret(%d) for %ld\n", + ret, mas->cur_speed_hz); + return ret; + } + + clk_sel = idx & CLK_SEL_MSK; + m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN; + spi_setup_word_len(mas, spi_slv->mode, spi_slv->bits_per_word); + writel(loopback_cfg, se->base + SE_SPI_LOOPBACK); + writel(demux_sel, se->base + SE_SPI_DEMUX_SEL); + writel(cpha, se->base + SE_SPI_CPHA); + writel(cpol, se->base + SE_SPI_CPOL); + writel(demux_output_inv, se->base + SE_SPI_DEMUX_OUTPUT_INV); + writel(clk_sel, se->base + SE_GENI_CLK_SEL); + writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG); + return 0; +} + +static int spi_geni_prepare_message(struct spi_master *spi, + struct spi_message *spi_msg) +{ + int ret; + struct spi_geni_master *mas = spi_master_get_devdata(spi); + struct geni_se *se = &mas->se; + + geni_se_select_mode(se, GENI_SE_FIFO); + reinit_completion(&mas->xfer_done); + ret = setup_fifo_params(spi_msg->spi, spi); + if (ret) + dev_err(mas->dev, "Couldn't select mode %d\n", ret); + return ret; +} + +static int spi_geni_init(struct spi_geni_master *mas) +{ + struct geni_se *se = &mas->se; + unsigned int proto, major, minor, ver; + + pm_runtime_get_sync(mas->dev); + + proto = geni_se_read_proto(se); + if (proto != GENI_SE_SPI) { + dev_err(mas->dev, "Invalid proto %d\n", proto); + pm_runtime_put(mas->dev); + return -ENXIO; + } + mas->tx_fifo_depth = geni_se_get_tx_fifo_depth(se); + + /* Width of Tx and Rx FIFO is same */ + mas->fifo_width_bits = geni_se_get_tx_fifo_width(se); + + /* + * Hardware programming guide suggests to configure + * RX FIFO RFR level to fifo_depth-2. + */ + geni_se_init(se, 0x0, mas->tx_fifo_depth - 2); + /* Transmit an entire FIFO worth of data per IRQ */ + mas->tx_wm = 1; + ver = geni_se_get_qup_hw_version(se); + major = GENI_SE_VERSION_MAJOR(ver); + minor = GENI_SE_VERSION_MINOR(ver); + + if (major == 1 && minor == 0) + mas->oversampling = 2; + else + mas->oversampling = 1; + + pm_runtime_put(mas->dev); + return 0; +} + +static void setup_fifo_xfer(struct spi_transfer *xfer, + struct spi_geni_master *mas, + u16 mode, struct spi_master *spi) +{ + u32 m_cmd = 0; + u32 spi_tx_cfg, len; + struct geni_se *se = &mas->se; + + spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG); + if (xfer->bits_per_word != mas->cur_bits_per_word) { + spi_setup_word_len(mas, mode, xfer->bits_per_word); + mas->cur_bits_per_word = xfer->bits_per_word; + } + + /* Speed and bits per word can be overridden per transfer */ + if (xfer->speed_hz != mas->cur_speed_hz) { + int ret; + u32 clk_sel, m_clk_cfg; + unsigned int idx, div; + + ret = get_spi_clk_cfg(xfer->speed_hz, mas, &idx, &div); + if (ret) { + dev_err(mas->dev, "Err setting clks:%d\n", ret); + return; + } + /* + * SPI core clock gets configured with the requested frequency + * or the frequency closer to the requested frequency. + * For that reason requested frequency is stored in the + * cur_speed_hz and referred in the consecutive transfer instead + * of calling clk_get_rate() API. + */ + mas->cur_speed_hz = xfer->speed_hz; + clk_sel = idx & CLK_SEL_MSK; + m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN; + writel(clk_sel, se->base + SE_GENI_CLK_SEL); + writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG); + } + + mas->tx_rem_bytes = 0; + mas->rx_rem_bytes = 0; + if (xfer->tx_buf && xfer->rx_buf) + m_cmd = SPI_FULL_DUPLEX; + else if (xfer->tx_buf) + m_cmd = SPI_TX_ONLY; + else if (xfer->rx_buf) + m_cmd = SPI_RX_ONLY; + + spi_tx_cfg &= ~CS_TOGGLE; + + if (!(mas->cur_bits_per_word % MIN_WORD_LEN)) + len = xfer->len * BITS_PER_BYTE / mas->cur_bits_per_word; + else + len = xfer->len / (mas->cur_bits_per_word / BITS_PER_BYTE + 1); + len &= TRANS_LEN_MSK; + + mas->cur_xfer = xfer; + if (m_cmd & SPI_TX_ONLY) { + mas->tx_rem_bytes = xfer->len; + writel(len, se->base + SE_SPI_TX_TRANS_LEN); + } + + if (m_cmd & SPI_RX_ONLY) { + writel(len, se->base + SE_SPI_RX_TRANS_LEN); + mas->rx_rem_bytes = xfer->len; + } + writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG); + mas->cur_mcmd = CMD_XFER; + geni_se_setup_m_cmd(se, m_cmd, FRAGMENTATION); + + /* + * TX_WATERMARK_REG should be set after SPI configuration and + * setting up GENI SE engine, as driver starts data transfer + * for the watermark interrupt. + */ + if (m_cmd & SPI_TX_ONLY) + writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG); +} + +static void handle_fifo_timeout(struct spi_master *spi, + struct spi_message *msg) +{ + struct spi_geni_master *mas = spi_master_get_devdata(spi); + unsigned long time_left, flags; + struct geni_se *se = &mas->se; + + spin_lock_irqsave(&mas->lock, flags); + reinit_completion(&mas->xfer_done); + mas->cur_mcmd = CMD_CANCEL; + geni_se_cancel_m_cmd(se); + writel(0, se->base + SE_GENI_TX_WATERMARK_REG); + spin_unlock_irqrestore(&mas->lock, flags); + time_left = wait_for_completion_timeout(&mas->xfer_done, HZ); + if (time_left) + return; + + spin_lock_irqsave(&mas->lock, flags); + reinit_completion(&mas->xfer_done); + geni_se_abort_m_cmd(se); + spin_unlock_irqrestore(&mas->lock, flags); + time_left = wait_for_completion_timeout(&mas->xfer_done, HZ); + if (!time_left) + dev_err(mas->dev, "Failed to cancel/abort m_cmd\n"); +} + +static int spi_geni_transfer_one(struct spi_master *spi, + struct spi_device *slv, + struct spi_transfer *xfer) +{ + struct spi_geni_master *mas = spi_master_get_devdata(spi); + + /* Terminate and return success for 0 byte length transfer */ + if (!xfer->len) + return 0; + + setup_fifo_xfer(xfer, mas, slv->mode, spi); + return 1; +} + +static unsigned int geni_byte_per_fifo_word(struct spi_geni_master *mas) +{ + /* + * Calculate how many bytes we'll put in each FIFO word. If the + * transfer words don't pack cleanly into a FIFO word we'll just put + * one transfer word in each FIFO word. If they do pack we'll pack 'em. + */ + if (mas->fifo_width_bits % mas->cur_bits_per_word) + return roundup_pow_of_two(DIV_ROUND_UP(mas->cur_bits_per_word, + BITS_PER_BYTE)); + + return mas->fifo_width_bits / BITS_PER_BYTE; +} + +static void geni_spi_handle_tx(struct spi_geni_master *mas) +{ + struct geni_se *se = &mas->se; + unsigned int max_bytes; + const u8 *tx_buf; + unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas); + unsigned int i = 0; + + max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * bytes_per_fifo_word; + if (mas->tx_rem_bytes < max_bytes) + max_bytes = mas->tx_rem_bytes; + + tx_buf = mas->cur_xfer->tx_buf + mas->cur_xfer->len - mas->tx_rem_bytes; + while (i < max_bytes) { + unsigned int j; + unsigned int bytes_to_write; + u32 fifo_word = 0; + u8 *fifo_byte = (u8 *)&fifo_word; + + bytes_to_write = min(bytes_per_fifo_word, max_bytes - i); + for (j = 0; j < bytes_to_write; j++) + fifo_byte[j] = tx_buf[i++]; + iowrite32_rep(se->base + SE_GENI_TX_FIFOn, &fifo_word, 1); + } + mas->tx_rem_bytes -= max_bytes; + if (!mas->tx_rem_bytes) + writel(0, se->base + SE_GENI_TX_WATERMARK_REG); +} + +static void geni_spi_handle_rx(struct spi_geni_master *mas) +{ + struct geni_se *se = &mas->se; + u32 rx_fifo_status; + unsigned int rx_bytes; + unsigned int rx_last_byte_valid; + u8 *rx_buf; + unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas); + unsigned int i = 0; + + rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS); + rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * bytes_per_fifo_word; + if (rx_fifo_status & RX_LAST) { + rx_last_byte_valid = rx_fifo_status & RX_LAST_BYTE_VALID_MSK; + rx_last_byte_valid >>= RX_LAST_BYTE_VALID_SHFT; + if (rx_last_byte_valid && rx_last_byte_valid < 4) + rx_bytes -= bytes_per_fifo_word - rx_last_byte_valid; + } + if (mas->rx_rem_bytes < rx_bytes) + rx_bytes = mas->rx_rem_bytes; + + rx_buf = mas->cur_xfer->rx_buf + mas->cur_xfer->len - mas->rx_rem_bytes; + while (i < rx_bytes) { + u32 fifo_word = 0; + u8 *fifo_byte = (u8 *)&fifo_word; + unsigned int bytes_to_read; + unsigned int j; + + bytes_to_read = min(bytes_per_fifo_word, rx_bytes - i); + ioread32_rep(se->base + SE_GENI_RX_FIFOn, &fifo_word, 1); + for (j = 0; j < bytes_to_read; j++) + rx_buf[i++] = fifo_byte[j]; + } + mas->rx_rem_bytes -= rx_bytes; +} + +static irqreturn_t geni_spi_isr(int irq, void *data) +{ + struct spi_master *spi = data; + struct spi_geni_master *mas = spi_master_get_devdata(spi); + struct geni_se *se = &mas->se; + u32 m_irq; + unsigned long flags; + irqreturn_t ret = IRQ_HANDLED; + + if (mas->cur_mcmd == CMD_NONE) + return IRQ_NONE; + + spin_lock_irqsave(&mas->lock, flags); + m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS); + + if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN)) + geni_spi_handle_rx(mas); + + if (m_irq & M_TX_FIFO_WATERMARK_EN) + geni_spi_handle_tx(mas); + + if (m_irq & M_CMD_DONE_EN) { + if (mas->cur_mcmd == CMD_XFER) + spi_finalize_current_transfer(spi); + else if (mas->cur_mcmd == CMD_CS) + complete(&mas->xfer_done); + mas->cur_mcmd = CMD_NONE; + /* + * If this happens, then a CMD_DONE came before all the Tx + * buffer bytes were sent out. This is unusual, log this + * condition and disable the WM interrupt to prevent the + * system from stalling due an interrupt storm. + * If this happens when all Rx bytes haven't been received, log + * the condition. + * The only known time this can happen is if bits_per_word != 8 + * and some registers that expect xfer lengths in num spi_words + * weren't written correctly. + */ + if (mas->tx_rem_bytes) { + writel(0, se->base + SE_GENI_TX_WATERMARK_REG); + dev_err(mas->dev, "Premature done. tx_rem = %d bpw%d\n", + mas->tx_rem_bytes, mas->cur_bits_per_word); + } + if (mas->rx_rem_bytes) + dev_err(mas->dev, "Premature done. rx_rem = %d bpw%d\n", + mas->rx_rem_bytes, mas->cur_bits_per_word); + } + + if ((m_irq & M_CMD_CANCEL_EN) || (m_irq & M_CMD_ABORT_EN)) { + mas->cur_mcmd = CMD_NONE; + complete(&mas->xfer_done); + } + + writel(m_irq, se->base + SE_GENI_M_IRQ_CLEAR); + spin_unlock_irqrestore(&mas->lock, flags); + return ret; +} + +static int spi_geni_probe(struct platform_device *pdev) +{ + int ret; + struct spi_master *spi; + struct spi_geni_master *mas; + struct resource *res; + struct geni_se *se; + + spi = spi_alloc_master(&pdev->dev, sizeof(*mas)); + if (!spi) + return -ENOMEM; + + platform_set_drvdata(pdev, spi); + mas = spi_master_get_devdata(spi); + mas->dev = &pdev->dev; + mas->se.dev = &pdev->dev; + mas->se.wrapper = dev_get_drvdata(pdev->dev.parent); + se = &mas->se; + + spi->bus_num = -1; + 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->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + spi->num_chipselect = 4; + spi->max_speed_hz = 50000000; + spi->prepare_message = spi_geni_prepare_message; + spi->transfer_one = spi_geni_transfer_one; + spi->auto_runtime_pm = true; + spi->handle_err = handle_fifo_timeout; + spi->set_cs = spi_geni_set_cs; + + init_completion(&mas->xfer_done); + spin_lock_init(&mas->lock); + pm_runtime_enable(&pdev->dev); + + ret = spi_geni_init(mas); + if (ret) + 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, + IRQF_TRIGGER_HIGH, "spi_geni", spi); + if (ret) + goto spi_geni_probe_runtime_disable; + + ret = spi_register_master(spi); + if (ret) + goto spi_geni_probe_free_irq; + + return 0; +spi_geni_probe_free_irq: + free_irq(mas->irq, spi); +spi_geni_probe_runtime_disable: + pm_runtime_disable(&pdev->dev); +spi_geni_probe_err: + spi_master_put(spi); + return ret; +} + +static int spi_geni_remove(struct platform_device *pdev) +{ + struct spi_master *spi = platform_get_drvdata(pdev); + struct spi_geni_master *mas = spi_master_get_devdata(spi); + + /* Unregister _before_ disabling pm_runtime() so we stop transfers */ + spi_unregister_master(spi); + + free_irq(mas->irq, spi); + pm_runtime_disable(&pdev->dev); + return 0; +} + +static int __maybe_unused spi_geni_runtime_suspend(struct device *dev) +{ + struct spi_master *spi = dev_get_drvdata(dev); + struct spi_geni_master *mas = spi_master_get_devdata(spi); + + return geni_se_resources_off(&mas->se); +} + +static int __maybe_unused spi_geni_runtime_resume(struct device *dev) +{ + struct spi_master *spi = dev_get_drvdata(dev); + struct spi_geni_master *mas = spi_master_get_devdata(spi); + + return geni_se_resources_on(&mas->se); +} + +static int __maybe_unused spi_geni_suspend(struct device *dev) +{ + struct spi_master *spi = dev_get_drvdata(dev); + int ret; + + ret = spi_master_suspend(spi); + if (ret) + return ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + spi_master_resume(spi); + + return ret; +} + +static int __maybe_unused spi_geni_resume(struct device *dev) +{ + struct spi_master *spi = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + ret = spi_master_resume(spi); + if (ret) + pm_runtime_force_suspend(dev); + + return ret; +} + +static const struct dev_pm_ops spi_geni_pm_ops = { + SET_RUNTIME_PM_OPS(spi_geni_runtime_suspend, + spi_geni_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(spi_geni_suspend, spi_geni_resume) +}; + +static const struct of_device_id spi_geni_dt_match[] = { + { .compatible = "qcom,geni-spi" }, + {} +}; +MODULE_DEVICE_TABLE(of, spi_geni_dt_match); + +static struct platform_driver spi_geni_driver = { + .probe = spi_geni_probe, + .remove = spi_geni_remove, + .driver = { + .name = "geni_spi", + .pm = &spi_geni_pm_ops, + .of_match_table = spi_geni_dt_match, + }, +}; +module_platform_driver(spi_geni_driver); + +MODULE_DESCRIPTION("SPI driver for GENI based QUP cores"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 421bfc7dda67..45973ee3ae11 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->sck)) @@ -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-imx.c b/drivers/spi/spi-imx.c index 08dd3a31a3e5..dd1ce12aa386 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -63,6 +63,7 @@ struct spi_imx_devtype_data { void (*trigger)(struct spi_imx_data *); int (*rx_available)(struct spi_imx_data *); void (*reset)(struct spi_imx_data *); + void (*setup_wml)(struct spi_imx_data *); void (*disable)(struct spi_imx_data *); bool has_dmamode; bool has_slavemode; @@ -216,7 +217,6 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *transfer) { struct spi_imx_data *spi_imx = spi_master_get_devdata(master); - unsigned int bytes_per_word, i; if (!master->dma_rx) return false; @@ -224,14 +224,9 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, if (spi_imx->slave_mode) return false; - bytes_per_word = spi_imx_bytes_per_word(transfer->bits_per_word); + if (transfer->len < spi_imx->devtype_data->fifo_size) + return false; - for (i = spi_imx->devtype_data->fifo_size / 2; i > 0; i--) { - if (!(transfer->len % (i * bytes_per_word))) - break; - } - - spi_imx->wml = i; spi_imx->dynamic_burst = 0; return true; @@ -583,18 +578,21 @@ static int mx51_ecspi_config(struct spi_device *spi) else /* SCLK is _very_ slow */ usleep_range(delay, delay + 10); + return 0; +} + +static void mx51_setup_wml(struct spi_imx_data *spi_imx) +{ /* * Configure the DMA register: setup the watermark * and enable DMA request. */ - writel(MX51_ECSPI_DMA_RX_WML(spi_imx->wml) | + writel(MX51_ECSPI_DMA_RX_WML(spi_imx->wml - 1) | MX51_ECSPI_DMA_TX_WML(spi_imx->wml) | MX51_ECSPI_DMA_RXT_WML(spi_imx->wml) | MX51_ECSPI_DMA_TEDEN | MX51_ECSPI_DMA_RXDEN | MX51_ECSPI_DMA_RXTDEN, spi_imx->base + MX51_ECSPI_DMA); - - return 0; } static int mx51_ecspi_rx_available(struct spi_imx_data *spi_imx) @@ -931,6 +929,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = { .trigger = mx51_ecspi_trigger, .rx_available = mx51_ecspi_rx_available, .reset = mx51_ecspi_reset, + .setup_wml = mx51_setup_wml, .fifo_size = 64, .has_dmamode = true, .dynamic_burst = true, @@ -1138,7 +1137,6 @@ static int spi_imx_setupxfer(struct spi_device *spi, struct spi_transfer *t) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); - int ret; if (!t) return 0; @@ -1179,12 +1177,6 @@ static int spi_imx_setupxfer(struct spi_device *spi, else spi_imx->usedma = 0; - if (spi_imx->usedma) { - ret = spi_imx_dma_configure(spi->master); - if (ret) - return ret; - } - if (is_imx53_ecspi(spi_imx) && spi_imx->slave_mode) { spi_imx->rx = mx53_ecspi_rx_slave; spi_imx->tx = mx53_ecspi_tx_slave; @@ -1289,6 +1281,31 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, unsigned long timeout; struct spi_master *master = spi_imx->bitbang.master; struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg; + struct scatterlist *last_sg = sg_last(rx->sgl, rx->nents); + unsigned int bytes_per_word, i; + int ret; + + /* Get the right burst length from the last sg to ensure no tail data */ + bytes_per_word = spi_imx_bytes_per_word(transfer->bits_per_word); + for (i = spi_imx->devtype_data->fifo_size / 2; i > 0; i--) { + if (!(sg_dma_len(last_sg) % (i * bytes_per_word))) + break; + } + /* Use 1 as wml in case no available burst length got */ + if (i == 0) + i = 1; + + spi_imx->wml = i; + + ret = spi_imx_dma_configure(master); + if (ret) + return ret; + + if (!spi_imx->devtype_data->setup_wml) { + dev_err(spi_imx->dev, "No setup_wml()?\n"); + return -EINVAL; + } + spi_imx->devtype_data->setup_wml(spi_imx); /* * The TX DMA setup starts the transfer, so make sure RX is configured 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 (spi_mem_check_op(op)) + return false; - 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); + 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), - ®_val, remainder); + memcpy(trans->rx_buf + + mdata->num_xfered + + (cnt * 4), + ®_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(®_val, trans->tx_buf + (cnt * 4), remainder); + memcpy(®_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-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 508c61c669e7..f024c3fc3679 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -126,6 +127,7 @@ struct omap2_mcspi_regs { }; struct omap2_mcspi { + struct completion txdone; struct spi_master *master; /* Virtual base address of the controller */ void __iomem *base; @@ -135,6 +137,7 @@ struct omap2_mcspi { struct device *dev; struct omap2_mcspi_regs ctx; int fifo_depth; + bool slave_aborted; unsigned int pin_dir:1; }; @@ -274,19 +277,23 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable) } } -static void omap2_mcspi_set_master_mode(struct spi_master *master) +static void omap2_mcspi_set_mode(struct spi_master *master) { struct omap2_mcspi *mcspi = spi_master_get_devdata(master); struct omap2_mcspi_regs *ctx = &mcspi->ctx; u32 l; /* - * Setup when switching from (reset default) slave mode - * to single-channel master mode + * Choose master or slave mode */ l = mcspi_read_reg(master, OMAP2_MCSPI_MODULCTRL); - l &= ~(OMAP2_MCSPI_MODULCTRL_STEST | OMAP2_MCSPI_MODULCTRL_MS); - l |= OMAP2_MCSPI_MODULCTRL_SINGLE; + l &= ~(OMAP2_MCSPI_MODULCTRL_STEST); + if (spi_controller_is_slave(master)) { + l |= (OMAP2_MCSPI_MODULCTRL_MS); + } else { + l &= ~(OMAP2_MCSPI_MODULCTRL_MS); + l |= OMAP2_MCSPI_MODULCTRL_SINGLE; + } mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l); ctx->modulctrl = l; @@ -299,7 +306,7 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi, struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi *mcspi; unsigned int wcnt; - int max_fifo_depth, fifo_depth, bytes_per_word; + int max_fifo_depth, bytes_per_word; u32 chconf, xferlevel; mcspi = spi_master_get_devdata(master); @@ -315,10 +322,6 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi, else max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH; - fifo_depth = gcd(t->len, max_fifo_depth); - if (fifo_depth < 2 || fifo_depth % bytes_per_word != 0) - goto disable_fifo; - wcnt = t->len / bytes_per_word; if (wcnt > OMAP2_MCSPI_MAX_FIFOWCNT) goto disable_fifo; @@ -326,16 +329,17 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi, xferlevel = wcnt << 16; if (t->rx_buf != NULL) { chconf |= OMAP2_MCSPI_CHCONF_FFER; - xferlevel |= (fifo_depth - 1) << 8; + xferlevel |= (bytes_per_word - 1) << 8; } + if (t->tx_buf != NULL) { chconf |= OMAP2_MCSPI_CHCONF_FFET; - xferlevel |= fifo_depth - 1; + xferlevel |= bytes_per_word - 1; } mcspi_write_reg(master, OMAP2_MCSPI_XFERLEVEL, xferlevel); mcspi_write_chconf0(spi, chconf); - mcspi->fifo_depth = fifo_depth; + mcspi->fifo_depth = max_fifo_depth; return; } @@ -353,18 +357,22 @@ disable_fifo: static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) { - unsigned long timeout; + u32 val; - timeout = jiffies + msecs_to_jiffies(1000); - while (!(readl_relaxed(reg) & bit)) { - if (time_after(jiffies, timeout)) { - if (!(readl_relaxed(reg) & bit)) - return -ETIMEDOUT; - else - return 0; - } - cpu_relax(); + return readl_poll_timeout(reg, val, val & bit, 1, MSEC_PER_SEC); +} + +static int mcspi_wait_for_completion(struct omap2_mcspi *mcspi, + struct completion *x) +{ + if (spi_controller_is_slave(mcspi->master)) { + if (wait_for_completion_interruptible(x) || + mcspi->slave_aborted) + return -EINTR; + } else { + wait_for_completion(x); } + return 0; } @@ -517,7 +525,12 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, dma_async_issue_pending(mcspi_dma->dma_rx); omap2_mcspi_set_dma_req(spi, 1, 1); - wait_for_completion(&mcspi_dma->dma_rx_completion); + ret = mcspi_wait_for_completion(mcspi, &mcspi_dma->dma_rx_completion); + if (ret || mcspi->slave_aborted) { + dmaengine_terminate_sync(mcspi_dma->dma_rx); + omap2_mcspi_set_dma_req(spi, 1, 0); + return 0; + } for (x = 0; x < nb_sizes; x++) kfree(sg_out[x]); @@ -585,7 +598,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) struct dma_slave_config cfg; enum dma_slave_buswidth width; unsigned es; - u32 burst; void __iomem *chstat_reg; void __iomem *irqstat_reg; int wait_res; @@ -605,34 +617,49 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) } count = xfer->len; - burst = 1; - - if (mcspi->fifo_depth > 0) { - if (count > mcspi->fifo_depth) - burst = mcspi->fifo_depth / es; - else - burst = count / es; - } memset(&cfg, 0, sizeof(cfg)); cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0; cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0; cfg.src_addr_width = width; cfg.dst_addr_width = width; - cfg.src_maxburst = burst; - cfg.dst_maxburst = burst; + cfg.src_maxburst = es; + cfg.dst_maxburst = es; rx = xfer->rx_buf; tx = xfer->tx_buf; - if (tx != NULL) + mcspi->slave_aborted = false; + reinit_completion(&mcspi_dma->dma_tx_completion); + reinit_completion(&mcspi_dma->dma_rx_completion); + reinit_completion(&mcspi->txdone); + if (tx) { + /* Enable EOW IRQ to know end of tx in slave mode */ + if (spi_controller_is_slave(spi->master)) + mcspi_write_reg(spi->master, + OMAP2_MCSPI_IRQENABLE, + OMAP2_MCSPI_IRQSTATUS_EOW); omap2_mcspi_tx_dma(spi, xfer, cfg); + } if (rx != NULL) count = omap2_mcspi_rx_dma(spi, xfer, cfg, es); if (tx != NULL) { - wait_for_completion(&mcspi_dma->dma_tx_completion); + int ret; + + ret = mcspi_wait_for_completion(mcspi, &mcspi_dma->dma_tx_completion); + if (ret || mcspi->slave_aborted) { + dmaengine_terminate_sync(mcspi_dma->dma_tx); + omap2_mcspi_set_dma_req(spi, 0, 0); + return 0; + } + + if (spi_controller_is_slave(mcspi->master)) { + ret = mcspi_wait_for_completion(mcspi, &mcspi->txdone); + if (ret || mcspi->slave_aborted) + return 0; + } if (mcspi->fifo_depth > 0) { irqstat_reg = mcspi->base + OMAP2_MCSPI_IRQSTATUS; @@ -1089,6 +1116,36 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) gpio_free(spi->cs_gpio); } +static irqreturn_t omap2_mcspi_irq_handler(int irq, void *data) +{ + struct omap2_mcspi *mcspi = data; + u32 irqstat; + + irqstat = mcspi_read_reg(mcspi->master, OMAP2_MCSPI_IRQSTATUS); + if (!irqstat) + return IRQ_NONE; + + /* Disable IRQ and wakeup slave xfer task */ + mcspi_write_reg(mcspi->master, OMAP2_MCSPI_IRQENABLE, 0); + if (irqstat & OMAP2_MCSPI_IRQSTATUS_EOW) + complete(&mcspi->txdone); + + return IRQ_HANDLED; +} + +static int omap2_mcspi_slave_abort(struct spi_master *master) +{ + struct omap2_mcspi *mcspi = spi_master_get_devdata(master); + struct omap2_mcspi_dma *mcspi_dma = mcspi->dma_channels; + + mcspi->slave_aborted = true; + complete(&mcspi_dma->dma_rx_completion); + complete(&mcspi_dma->dma_tx_completion); + complete(&mcspi->txdone); + + return 0; +} + static int omap2_mcspi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *t) @@ -1255,10 +1312,20 @@ static bool omap2_mcspi_can_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { + struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); + struct omap2_mcspi_dma *mcspi_dma = + &mcspi->dma_channels[spi->chip_select]; + + if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) + return false; + + if (spi_controller_is_slave(master)) + return true; + return (xfer->len >= DMA_MIN_BYTES); } -static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) +static int omap2_mcspi_controller_setup(struct omap2_mcspi *mcspi) { struct spi_master *master = mcspi->master; struct omap2_mcspi_regs *ctx = &mcspi->ctx; @@ -1275,7 +1342,7 @@ static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) OMAP2_MCSPI_WAKEUPENABLE_WKEN); ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN; - omap2_mcspi_set_master_mode(master); + omap2_mcspi_set_mode(master); pm_runtime_mark_last_busy(mcspi->dev); pm_runtime_put_autosuspend(mcspi->dev); return 0; @@ -1350,11 +1417,12 @@ static int omap2_mcspi_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; const struct of_device_id *match; - master = spi_alloc_master(&pdev->dev, sizeof *mcspi); - if (master == NULL) { - dev_dbg(&pdev->dev, "master allocation failed\n"); + if (of_property_read_bool(node, "spi-slave")) + master = spi_alloc_slave(&pdev->dev, sizeof(*mcspi)); + else + master = spi_alloc_master(&pdev->dev, sizeof(*mcspi)); + if (!master) return -ENOMEM; - } /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; @@ -1366,6 +1434,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->transfer_one = omap2_mcspi_transfer_one; master->set_cs = omap2_mcspi_set_cs; master->cleanup = omap2_mcspi_cleanup; + master->slave_abort = omap2_mcspi_slave_abort; master->dev.of_node = node; master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ; master->min_speed_hz = OMAP2_MCSPI_MAX_FREQ >> 15; @@ -1417,15 +1486,31 @@ static int omap2_mcspi_probe(struct platform_device *pdev) sprintf(mcspi->dma_channels[i].dma_tx_ch_name, "tx%d", i); } + status = platform_get_irq(pdev, 0); + if (status == -EPROBE_DEFER) + goto free_master; + if (status < 0) { + dev_err(&pdev->dev, "no irq resource found\n"); + goto free_master; + } + init_completion(&mcspi->txdone); + status = devm_request_irq(&pdev->dev, status, + omap2_mcspi_irq_handler, 0, pdev->name, + mcspi); + if (status) { + dev_err(&pdev->dev, "Cannot request IRQ"); + goto free_master; + } + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); pm_runtime_enable(&pdev->dev); - status = omap2_mcspi_master_setup(mcspi); + status = omap2_mcspi_controller_setup(mcspi); if (status < 0) goto disable_pm; - status = devm_spi_register_master(&pdev->dev, master); + status = devm_spi_register_controller(&pdev->dev, master); if (status < 0) goto disable_pm; 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..d7e4e18ec3df 100644 --- a/drivers/spi/spi-pic32-sqi.c +++ b/drivers/spi/spi-pic32-sqi.c @@ -468,7 +468,7 @@ static int ring_desc_ring_alloc(struct pic32_sqi *sqi) /* allocate coherent DMAable memory for hardware buffer descriptors. */ sqi->bd = dma_zalloc_coherent(&sqi->master->dev, sizeof(*bd) * PESQI_BD_COUNT, - &sqi->bd_dma, GFP_DMA32); + &sqi->bd_dma, GFP_KERNEL); if (!sqi->bd) { dev_err(&sqi->master->dev, "failed allocating dma buffer\n"); return -ENOMEM; @@ -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..612cc49db28f 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "spi-pxa2xx.h" @@ -665,9 +666,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); @@ -1333,9 +1336,6 @@ static void cleanup(struct spi_device *spi) kfree(chip); } -#ifdef CONFIG_PCI -#ifdef CONFIG_ACPI - static const struct acpi_device_id pxa2xx_spi_acpi_match[] = { { "INT33C0", LPSS_LPT_SSP }, { "INT33C1", LPSS_LPT_SSP }, @@ -1347,23 +1347,6 @@ static const struct acpi_device_id pxa2xx_spi_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); -static int pxa2xx_spi_get_port_id(struct acpi_device *adev) -{ - unsigned int devid; - int port_id = -1; - - if (adev && adev->pnp.unique_id && - !kstrtouint(adev->pnp.unique_id, 0, &devid)) - port_id = devid; - return port_id; -} -#else /* !CONFIG_ACPI */ -static int pxa2xx_spi_get_port_id(struct acpi_device *adev) -{ - return -1; -} -#endif - /* * PCI IDs of compound devices that integrate both host controller and private * integrated DMA engine. Please note these are not used in module @@ -1410,6 +1393,37 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { { }, }; +static const struct of_device_id pxa2xx_spi_of_match[] = { + { .compatible = "marvell,mmp2-ssp", .data = (void *)MMP2_SSP }, + {}, +}; +MODULE_DEVICE_TABLE(of, pxa2xx_spi_of_match); + +#ifdef CONFIG_ACPI + +static int pxa2xx_spi_get_port_id(struct acpi_device *adev) +{ + unsigned int devid; + int port_id = -1; + + if (adev && adev->pnp.unique_id && + !kstrtouint(adev->pnp.unique_id, 0, &devid)) + port_id = devid; + return port_id; +} + +#else /* !CONFIG_ACPI */ + +static int pxa2xx_spi_get_port_id(struct acpi_device *adev) +{ + return -1; +} + +#endif /* CONFIG_ACPI */ + + +#ifdef CONFIG_PCI + static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param) { struct device *dev = param; @@ -1420,6 +1434,8 @@ static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param) return true; } +#endif /* CONFIG_PCI */ + static struct pxa2xx_spi_master * pxa2xx_spi_init_pdata(struct platform_device *pdev) { @@ -1429,11 +1445,15 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) struct resource *res; const struct acpi_device_id *adev_id = NULL; const struct pci_device_id *pcidev_id = NULL; - int type; + const struct of_device_id *of_id = NULL; + enum pxa_ssp_type type; adev = ACPI_COMPANION(&pdev->dev); - if (dev_is_pci(pdev->dev.parent)) + if (pdev->dev.of_node) + of_id = of_match_device(pdev->dev.driver->of_match_table, + &pdev->dev); + else if (dev_is_pci(pdev->dev.parent)) pcidev_id = pci_match_id(pxa2xx_spi_pci_compound_match, to_pci_dev(pdev->dev.parent)); else if (adev) @@ -1443,9 +1463,11 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) return NULL; if (adev_id) - type = (int)adev_id->driver_data; + type = (enum pxa_ssp_type)adev_id->driver_data; else if (pcidev_id) - type = (int)pcidev_id->driver_data; + type = (enum pxa_ssp_type)pcidev_id->driver_data; + else if (of_id) + type = (enum pxa_ssp_type)of_id->data; else return NULL; @@ -1464,11 +1486,13 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) if (IS_ERR(ssp->mmio_base)) return NULL; +#ifdef CONFIG_PCI if (pcidev_id) { pdata->tx_param = pdev->dev.parent; pdata->rx_param = pdev->dev.parent; pdata->dma_filter = pxa2xx_spi_idma_filter; } +#endif ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->irq = platform_get_irq(pdev, 0); @@ -1482,14 +1506,6 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) return pdata; } -#else /* !CONFIG_PCI */ -static inline struct pxa2xx_spi_master * -pxa2xx_spi_init_pdata(struct platform_device *pdev) -{ - return NULL; -} -#endif - static int pxa2xx_spi_fw_translate_cs(struct spi_controller *master, unsigned int cs) { @@ -1764,14 +1780,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 +1816,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 @@ -1848,10 +1850,10 @@ static struct platform_driver driver = { .name = "pxa2xx-spi", .pm = &pxa2xx_spi_pm_ops, .acpi_match_table = ACPI_PTR(pxa2xx_spi_acpi_match), + .of_match_table = of_match_ptr(pxa2xx_spi_of_match), }, .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-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c new file mode 100644 index 000000000000..b8163b40bb92 --- /dev/null +++ b/drivers/spi/spi-qcom-qspi.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017-2018, The Linux foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define QSPI_NUM_CS 2 +#define QSPI_BYTES_PER_WORD 4 + +#define MSTR_CONFIG 0x0000 +#define FULL_CYCLE_MODE BIT(3) +#define FB_CLK_EN BIT(4) +#define PIN_HOLDN BIT(6) +#define PIN_WPN BIT(7) +#define DMA_ENABLE BIT(8) +#define BIG_ENDIAN_MODE BIT(9) +#define SPI_MODE_MSK 0xc00 +#define SPI_MODE_SHFT 10 +#define CHIP_SELECT_NUM BIT(12) +#define SBL_EN BIT(13) +#define LPA_BASE_MSK 0x3c000 +#define LPA_BASE_SHFT 14 +#define TX_DATA_DELAY_MSK 0xc0000 +#define TX_DATA_DELAY_SHFT 18 +#define TX_CLK_DELAY_MSK 0x300000 +#define TX_CLK_DELAY_SHFT 20 +#define TX_CS_N_DELAY_MSK 0xc00000 +#define TX_CS_N_DELAY_SHFT 22 +#define TX_DATA_OE_DELAY_MSK 0x3000000 +#define TX_DATA_OE_DELAY_SHFT 24 + +#define AHB_MASTER_CFG 0x0004 +#define HMEM_TYPE_START_MID_TRANS_MSK 0x7 +#define HMEM_TYPE_START_MID_TRANS_SHFT 0 +#define HMEM_TYPE_LAST_TRANS_MSK 0x38 +#define HMEM_TYPE_LAST_TRANS_SHFT 3 +#define USE_HMEMTYPE_LAST_ON_DESC_OR_CHAIN_MSK 0xc0 +#define USE_HMEMTYPE_LAST_ON_DESC_OR_CHAIN_SHFT 6 +#define HMEMTYPE_READ_TRANS_MSK 0x700 +#define HMEMTYPE_READ_TRANS_SHFT 8 +#define HSHARED BIT(11) +#define HINNERSHARED BIT(12) + +#define MSTR_INT_EN 0x000C +#define MSTR_INT_STATUS 0x0010 +#define RESP_FIFO_UNDERRUN BIT(0) +#define RESP_FIFO_NOT_EMPTY BIT(1) +#define RESP_FIFO_RDY BIT(2) +#define HRESP_FROM_NOC_ERR BIT(3) +#define WR_FIFO_EMPTY BIT(9) +#define WR_FIFO_FULL BIT(10) +#define WR_FIFO_OVERRUN BIT(11) +#define TRANSACTION_DONE BIT(16) +#define QSPI_ERR_IRQS (RESP_FIFO_UNDERRUN | HRESP_FROM_NOC_ERR | \ + WR_FIFO_OVERRUN) +#define QSPI_ALL_IRQS (QSPI_ERR_IRQS | RESP_FIFO_RDY | \ + WR_FIFO_EMPTY | WR_FIFO_FULL | \ + TRANSACTION_DONE) + +#define PIO_XFER_CTRL 0x0014 +#define REQUEST_COUNT_MSK 0xffff + +#define PIO_XFER_CFG 0x0018 +#define TRANSFER_DIRECTION BIT(0) +#define MULTI_IO_MODE_MSK 0xe +#define MULTI_IO_MODE_SHFT 1 +#define TRANSFER_FRAGMENT BIT(8) +#define SDR_1BIT 1 +#define SDR_2BIT 2 +#define SDR_4BIT 3 +#define DDR_1BIT 5 +#define DDR_2BIT 6 +#define DDR_4BIT 7 +#define DMA_DESC_SINGLE_SPI 1 +#define DMA_DESC_DUAL_SPI 2 +#define DMA_DESC_QUAD_SPI 3 + +#define PIO_XFER_STATUS 0x001c +#define WR_FIFO_BYTES_MSK 0xffff0000 +#define WR_FIFO_BYTES_SHFT 16 + +#define PIO_DATAOUT_1B 0x0020 +#define PIO_DATAOUT_4B 0x0024 + +#define RD_FIFO_STATUS 0x002c +#define FIFO_EMPTY BIT(11) +#define WR_CNTS_MSK 0x7f0 +#define WR_CNTS_SHFT 4 +#define RDY_64BYTE BIT(3) +#define RDY_32BYTE BIT(2) +#define RDY_16BYTE BIT(1) +#define FIFO_RDY BIT(0) + +#define RD_FIFO_CFG 0x0028 +#define CONTINUOUS_MODE BIT(0) + +#define RD_FIFO_RESET 0x0030 +#define RESET_FIFO BIT(0) + +#define CUR_MEM_ADDR 0x0048 +#define HW_VERSION 0x004c +#define RD_FIFO 0x0050 +#define SAMPLING_CLK_CFG 0x0090 +#define SAMPLING_CLK_STATUS 0x0094 + + +enum qspi_dir { + QSPI_READ, + QSPI_WRITE, +}; + +struct qspi_xfer { + union { + const void *tx_buf; + void *rx_buf; + }; + unsigned int rem_bytes; + unsigned int buswidth; + enum qspi_dir dir; + bool is_last; +}; + +enum qspi_clocks { + QSPI_CLK_CORE, + QSPI_CLK_IFACE, + QSPI_NUM_CLKS +}; + +struct qcom_qspi { + void __iomem *base; + struct device *dev; + struct clk_bulk_data clks[QSPI_NUM_CLKS]; + struct qspi_xfer xfer; + /* Lock to protect data accessed by IRQs */ + spinlock_t lock; +}; + +static u32 qspi_buswidth_to_iomode(struct qcom_qspi *ctrl, + unsigned int buswidth) +{ + switch (buswidth) { + case 1: + return SDR_1BIT << MULTI_IO_MODE_SHFT; + case 2: + return SDR_2BIT << MULTI_IO_MODE_SHFT; + case 4: + return SDR_4BIT << MULTI_IO_MODE_SHFT; + default: + dev_warn_once(ctrl->dev, + "Unexpected bus width: %u\n", buswidth); + return SDR_1BIT << MULTI_IO_MODE_SHFT; + } +} + +static void qcom_qspi_pio_xfer_cfg(struct qcom_qspi *ctrl) +{ + u32 pio_xfer_cfg; + const struct qspi_xfer *xfer; + + xfer = &ctrl->xfer; + pio_xfer_cfg = readl(ctrl->base + PIO_XFER_CFG); + pio_xfer_cfg &= ~TRANSFER_DIRECTION; + pio_xfer_cfg |= xfer->dir; + if (xfer->is_last) + pio_xfer_cfg &= ~TRANSFER_FRAGMENT; + else + pio_xfer_cfg |= TRANSFER_FRAGMENT; + pio_xfer_cfg &= ~MULTI_IO_MODE_MSK; + pio_xfer_cfg |= qspi_buswidth_to_iomode(ctrl, xfer->buswidth); + + writel(pio_xfer_cfg, ctrl->base + PIO_XFER_CFG); +} + +static void qcom_qspi_pio_xfer_ctrl(struct qcom_qspi *ctrl) +{ + u32 pio_xfer_ctrl; + + pio_xfer_ctrl = readl(ctrl->base + PIO_XFER_CTRL); + pio_xfer_ctrl &= ~REQUEST_COUNT_MSK; + pio_xfer_ctrl |= ctrl->xfer.rem_bytes; + writel(pio_xfer_ctrl, ctrl->base + PIO_XFER_CTRL); +} + +static void qcom_qspi_pio_xfer(struct qcom_qspi *ctrl) +{ + u32 ints; + + qcom_qspi_pio_xfer_cfg(ctrl); + + /* Ack any previous interrupts that might be hanging around */ + writel(QSPI_ALL_IRQS, ctrl->base + MSTR_INT_STATUS); + + /* Setup new interrupts */ + if (ctrl->xfer.dir == QSPI_WRITE) + ints = QSPI_ERR_IRQS | WR_FIFO_EMPTY; + else + ints = QSPI_ERR_IRQS | RESP_FIFO_RDY; + writel(ints, ctrl->base + MSTR_INT_EN); + + /* Kick off the transfer */ + qcom_qspi_pio_xfer_ctrl(ctrl); +} + +static void qcom_qspi_handle_err(struct spi_master *master, + struct spi_message *msg) +{ + struct qcom_qspi *ctrl = spi_master_get_devdata(master); + unsigned long flags; + + spin_lock_irqsave(&ctrl->lock, flags); + writel(0, ctrl->base + MSTR_INT_EN); + ctrl->xfer.rem_bytes = 0; + spin_unlock_irqrestore(&ctrl->lock, flags); +} + +static int qcom_qspi_transfer_one(struct spi_master *master, + struct spi_device *slv, + struct spi_transfer *xfer) +{ + struct qcom_qspi *ctrl = spi_master_get_devdata(master); + int ret; + unsigned long speed_hz; + unsigned long flags; + + speed_hz = slv->max_speed_hz; + if (xfer->speed_hz) + speed_hz = xfer->speed_hz; + + /* In regular operation (SBL_EN=1) core must be 4x transfer clock */ + ret = clk_set_rate(ctrl->clks[QSPI_CLK_CORE].clk, speed_hz * 4); + if (ret) { + dev_err(ctrl->dev, "Failed to set core clk %d\n", ret); + return ret; + } + + spin_lock_irqsave(&ctrl->lock, flags); + + /* We are half duplex, so either rx or tx will be set */ + if (xfer->rx_buf) { + ctrl->xfer.dir = QSPI_READ; + ctrl->xfer.buswidth = xfer->rx_nbits; + ctrl->xfer.rx_buf = xfer->rx_buf; + } else { + ctrl->xfer.dir = QSPI_WRITE; + ctrl->xfer.buswidth = xfer->tx_nbits; + ctrl->xfer.tx_buf = xfer->tx_buf; + } + ctrl->xfer.is_last = list_is_last(&xfer->transfer_list, + &master->cur_msg->transfers); + ctrl->xfer.rem_bytes = xfer->len; + qcom_qspi_pio_xfer(ctrl); + + spin_unlock_irqrestore(&ctrl->lock, flags); + + /* We'll call spi_finalize_current_transfer() when done */ + return 1; +} + +static int qcom_qspi_prepare_message(struct spi_master *master, + struct spi_message *message) +{ + u32 mstr_cfg; + struct qcom_qspi *ctrl; + int tx_data_oe_delay = 1; + int tx_data_delay = 1; + unsigned long flags; + + ctrl = spi_master_get_devdata(master); + spin_lock_irqsave(&ctrl->lock, flags); + + mstr_cfg = readl(ctrl->base + MSTR_CONFIG); + mstr_cfg &= ~CHIP_SELECT_NUM; + if (message->spi->chip_select) + mstr_cfg |= CHIP_SELECT_NUM; + + mstr_cfg |= FB_CLK_EN | PIN_WPN | PIN_HOLDN | SBL_EN | FULL_CYCLE_MODE; + mstr_cfg &= ~(SPI_MODE_MSK | TX_DATA_OE_DELAY_MSK | TX_DATA_DELAY_MSK); + mstr_cfg |= message->spi->mode << SPI_MODE_SHFT; + mstr_cfg |= tx_data_oe_delay << TX_DATA_OE_DELAY_SHFT; + mstr_cfg |= tx_data_delay << TX_DATA_DELAY_SHFT; + mstr_cfg &= ~DMA_ENABLE; + + writel(mstr_cfg, ctrl->base + MSTR_CONFIG); + spin_unlock_irqrestore(&ctrl->lock, flags); + + return 0; +} + +static irqreturn_t pio_read(struct qcom_qspi *ctrl) +{ + u32 rd_fifo_status; + u32 rd_fifo; + unsigned int wr_cnts; + unsigned int bytes_to_read; + unsigned int words_to_read; + u32 *word_buf; + u8 *byte_buf; + int i; + + rd_fifo_status = readl(ctrl->base + RD_FIFO_STATUS); + + if (!(rd_fifo_status & FIFO_RDY)) { + dev_dbg(ctrl->dev, "Spurious IRQ %#x\n", rd_fifo_status); + return IRQ_NONE; + } + + wr_cnts = (rd_fifo_status & WR_CNTS_MSK) >> WR_CNTS_SHFT; + wr_cnts = min(wr_cnts, ctrl->xfer.rem_bytes); + + words_to_read = wr_cnts / QSPI_BYTES_PER_WORD; + bytes_to_read = wr_cnts % QSPI_BYTES_PER_WORD; + + if (words_to_read) { + word_buf = ctrl->xfer.rx_buf; + ctrl->xfer.rem_bytes -= words_to_read * QSPI_BYTES_PER_WORD; + ioread32_rep(ctrl->base + RD_FIFO, word_buf, words_to_read); + ctrl->xfer.rx_buf = word_buf + words_to_read; + } + + if (bytes_to_read) { + byte_buf = ctrl->xfer.rx_buf; + rd_fifo = readl(ctrl->base + RD_FIFO); + ctrl->xfer.rem_bytes -= bytes_to_read; + for (i = 0; i < bytes_to_read; i++) + *byte_buf++ = rd_fifo >> (i * BITS_PER_BYTE); + ctrl->xfer.rx_buf = byte_buf; + } + + return IRQ_HANDLED; +} + +static irqreturn_t pio_write(struct qcom_qspi *ctrl) +{ + const void *xfer_buf = ctrl->xfer.tx_buf; + const int *word_buf; + const char *byte_buf; + unsigned int wr_fifo_bytes; + unsigned int wr_fifo_words; + unsigned int wr_size; + unsigned int rem_words; + + wr_fifo_bytes = readl(ctrl->base + PIO_XFER_STATUS); + wr_fifo_bytes >>= WR_FIFO_BYTES_SHFT; + + if (ctrl->xfer.rem_bytes < QSPI_BYTES_PER_WORD) { + /* Process the last 1-3 bytes */ + wr_size = min(wr_fifo_bytes, ctrl->xfer.rem_bytes); + ctrl->xfer.rem_bytes -= wr_size; + + byte_buf = xfer_buf; + while (wr_size--) + writel(*byte_buf++, + ctrl->base + PIO_DATAOUT_1B); + ctrl->xfer.tx_buf = byte_buf; + } else { + /* + * Process all the whole words; to keep things simple we'll + * just wait for the next interrupt to handle the last 1-3 + * bytes if we don't have an even number of words. + */ + rem_words = ctrl->xfer.rem_bytes / QSPI_BYTES_PER_WORD; + wr_fifo_words = wr_fifo_bytes / QSPI_BYTES_PER_WORD; + + wr_size = min(rem_words, wr_fifo_words); + ctrl->xfer.rem_bytes -= wr_size * QSPI_BYTES_PER_WORD; + + word_buf = xfer_buf; + iowrite32_rep(ctrl->base + PIO_DATAOUT_4B, word_buf, wr_size); + ctrl->xfer.tx_buf = word_buf + wr_size; + + } + + return IRQ_HANDLED; +} + +static irqreturn_t qcom_qspi_irq(int irq, void *dev_id) +{ + u32 int_status; + struct qcom_qspi *ctrl = dev_id; + irqreturn_t ret = IRQ_NONE; + unsigned long flags; + + spin_lock_irqsave(&ctrl->lock, flags); + + int_status = readl(ctrl->base + MSTR_INT_STATUS); + writel(int_status, ctrl->base + MSTR_INT_STATUS); + + if (ctrl->xfer.dir == QSPI_WRITE) { + if (int_status & WR_FIFO_EMPTY) + ret = pio_write(ctrl); + } else { + if (int_status & RESP_FIFO_RDY) + ret = pio_read(ctrl); + } + + if (int_status & QSPI_ERR_IRQS) { + if (int_status & RESP_FIFO_UNDERRUN) + dev_err(ctrl->dev, "IRQ error: FIFO underrun\n"); + if (int_status & WR_FIFO_OVERRUN) + dev_err(ctrl->dev, "IRQ error: FIFO overrun\n"); + if (int_status & HRESP_FROM_NOC_ERR) + dev_err(ctrl->dev, "IRQ error: NOC response error\n"); + ret = IRQ_HANDLED; + } + + if (!ctrl->xfer.rem_bytes) { + writel(0, ctrl->base + MSTR_INT_EN); + spi_finalize_current_transfer(dev_get_drvdata(ctrl->dev)); + } + + spin_unlock_irqrestore(&ctrl->lock, flags); + return ret; +} + +static int qcom_qspi_probe(struct platform_device *pdev) +{ + int ret; + struct device *dev; + struct resource *res; + struct spi_master *master; + struct qcom_qspi *ctrl; + + dev = &pdev->dev; + + master = spi_alloc_master(dev, sizeof(*ctrl)); + if (!master) + return -ENOMEM; + + platform_set_drvdata(pdev, master); + + ctrl = spi_master_get_devdata(master); + + spin_lock_init(&ctrl->lock); + ctrl->dev = dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctrl->base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->base)) { + ret = PTR_ERR(ctrl->base); + goto exit_probe_master_put; + } + + ctrl->clks[QSPI_CLK_CORE].id = "core"; + ctrl->clks[QSPI_CLK_IFACE].id = "iface"; + ret = devm_clk_bulk_get(dev, QSPI_NUM_CLKS, ctrl->clks); + if (ret) + goto exit_probe_master_put; + + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "Failed to get irq %d\n", ret); + goto exit_probe_master_put; + } + ret = devm_request_irq(dev, ret, qcom_qspi_irq, + IRQF_TRIGGER_HIGH, dev_name(dev), ctrl); + if (ret) { + dev_err(dev, "Failed to request irq %d\n", ret); + goto exit_probe_master_put; + } + + master->max_speed_hz = 300000000; + master->num_chipselect = QSPI_NUM_CS; + master->bus_num = -1; + master->dev.of_node = pdev->dev.of_node; + master->mode_bits = SPI_MODE_0 | + SPI_TX_DUAL | SPI_RX_DUAL | + SPI_TX_QUAD | SPI_RX_QUAD; + master->flags = SPI_MASTER_HALF_DUPLEX; + master->prepare_message = qcom_qspi_prepare_message; + master->transfer_one = qcom_qspi_transfer_one; + master->handle_err = qcom_qspi_handle_err; + master->auto_runtime_pm = true; + + pm_runtime_enable(dev); + + ret = spi_register_master(master); + if (!ret) + return 0; + + pm_runtime_disable(dev); + +exit_probe_master_put: + spi_master_put(master); + + return ret; +} + +static int qcom_qspi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + + /* Unregister _before_ disabling pm_runtime() so we stop transfers */ + spi_unregister_master(master); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct qcom_qspi *ctrl = spi_master_get_devdata(master); + + clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks); + + return 0; +} + +static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct qcom_qspi *ctrl = spi_master_get_devdata(master); + + return clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks); +} + +static int __maybe_unused qcom_qspi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + int ret; + + ret = spi_master_suspend(master); + if (ret) + return ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + spi_master_resume(master); + + return ret; +} + +static int __maybe_unused qcom_qspi_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + ret = spi_master_resume(master); + if (ret) + pm_runtime_force_suspend(dev); + + return ret; +} + +static const struct dev_pm_ops qcom_qspi_dev_pm_ops = { + SET_RUNTIME_PM_OPS(qcom_qspi_runtime_suspend, + qcom_qspi_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(qcom_qspi_suspend, qcom_qspi_resume) +}; + +static const struct of_device_id qcom_qspi_dt_match[] = { + { .compatible = "qcom,qspi-v1", }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_qspi_dt_match); + +static struct platform_driver qcom_qspi_driver = { + .driver = { + .name = "qcom_qspi", + .pm = &qcom_qspi_dev_pm_ops, + .of_match_table = qcom_qspi_dt_match, + }, + .probe = qcom_qspi_probe, + .remove = qcom_qspi_remove, +}; +module_platform_driver(qcom_qspi_driver); + +MODULE_DESCRIPTION("SPI driver for QSPI cores"); +MODULE_LICENSE("GPL v2"); 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-rockchip.c b/drivers/spi/spi-rockchip.c index fdcf3076681b..51ef632bca52 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -164,7 +164,6 @@ enum rockchip_ssi_type { struct rockchip_spi_dma_data { struct dma_chan *ch; - enum dma_transfer_direction direction; dma_addr_t addr; }; @@ -202,12 +201,11 @@ struct rockchip_spi { bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; - u32 use_dma; + bool use_dma; struct sg_table tx_sg; struct sg_table rx_sg; struct rockchip_spi_dma_data dma_rx; struct rockchip_spi_dma_data dma_tx; - struct dma_slave_caps dma_caps; }; static inline void spi_enable_chip(struct rockchip_spi *rs, int enable) @@ -381,6 +379,8 @@ static int rockchip_spi_pio_transfer(struct rockchip_spi *rs) { int remain = 0; + spi_enable_chip(rs, 1); + do { if (rs->tx) { remain = rs->tx_end - rs->tx; @@ -445,6 +445,9 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) struct dma_slave_config rxconf, txconf; struct dma_async_tx_descriptor *rxdesc, *txdesc; + memset(&rxconf, 0, sizeof(rxconf)); + memset(&txconf, 0, sizeof(txconf)); + spin_lock_irqsave(&rs->lock, flags); rs->state &= ~RXBUSY; rs->state &= ~TXBUSY; @@ -452,19 +455,16 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) rxdesc = NULL; if (rs->rx) { - rxconf.direction = rs->dma_rx.direction; + rxconf.direction = DMA_DEV_TO_MEM; rxconf.src_addr = rs->dma_rx.addr; rxconf.src_addr_width = rs->n_bytes; - if (rs->dma_caps.max_burst > 4) - rxconf.src_maxburst = 4; - else - rxconf.src_maxburst = 1; + rxconf.src_maxburst = 1; dmaengine_slave_config(rs->dma_rx.ch, &rxconf); rxdesc = dmaengine_prep_slave_sg( rs->dma_rx.ch, rs->rx_sg.sgl, rs->rx_sg.nents, - rs->dma_rx.direction, DMA_PREP_INTERRUPT); + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!rxdesc) return -EINVAL; @@ -474,19 +474,16 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) txdesc = NULL; if (rs->tx) { - txconf.direction = rs->dma_tx.direction; + txconf.direction = DMA_MEM_TO_DEV; txconf.dst_addr = rs->dma_tx.addr; txconf.dst_addr_width = rs->n_bytes; - if (rs->dma_caps.max_burst > 4) - txconf.dst_maxburst = 4; - else - txconf.dst_maxburst = 1; + txconf.dst_maxburst = rs->fifo_len / 2; dmaengine_slave_config(rs->dma_tx.ch, &txconf); txdesc = dmaengine_prep_slave_sg( rs->dma_tx.ch, rs->tx_sg.sgl, rs->tx_sg.nents, - rs->dma_tx.direction, DMA_PREP_INTERRUPT); + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!txdesc) { if (rxdesc) dmaengine_terminate_sync(rs->dma_rx.ch); @@ -506,6 +503,8 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) dma_async_issue_pending(rs->dma_rx.ch); } + spi_enable_chip(rs, 1); + if (txdesc) { spin_lock_irqsave(&rs->lock, flags); rs->state |= TXBUSY; @@ -514,7 +513,8 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) dma_async_issue_pending(rs->dma_tx.ch); } - return 0; + /* 1 means the transfer is in progress */ + return 1; } static void rockchip_spi_config(struct rockchip_spi *rs) @@ -578,7 +578,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs) writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_TXFTLR); writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_RXFTLR); - writel_relaxed(0, rs->regs + ROCKCHIP_SPI_DMATDLR); + writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_DMATDLR); writel_relaxed(0, rs->regs + ROCKCHIP_SPI_DMARDLR); writel_relaxed(dmacr, rs->regs + ROCKCHIP_SPI_DMACR); @@ -597,7 +597,6 @@ static int rockchip_spi_transfer_one( struct spi_device *spi, struct spi_transfer *xfer) { - int ret = 0; struct rockchip_spi *rs = spi_master_get_devdata(master); WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) && @@ -635,30 +634,16 @@ static int rockchip_spi_transfer_one( /* we need prepare dma before spi was enabled */ if (master->can_dma && master->can_dma(master, spi, xfer)) - rs->use_dma = 1; + rs->use_dma = true; else - rs->use_dma = 0; + rs->use_dma = false; rockchip_spi_config(rs); - if (rs->use_dma) { - if (rs->tmode == CR0_XFM_RO) { - /* rx: dma must be prepared first */ - ret = rockchip_spi_prepare_dma(rs); - spi_enable_chip(rs, 1); - } else { - /* tx or tr: spi must be enabled first */ - spi_enable_chip(rs, 1); - ret = rockchip_spi_prepare_dma(rs); - } - /* successful DMA prepare means the transfer is in progress */ - ret = ret ? ret : 1; - } else { - spi_enable_chip(rs, 1); - ret = rockchip_spi_pio_transfer(rs); - } + if (rs->use_dma) + return rockchip_spi_prepare_dma(rs); - return ret; + return rockchip_spi_pio_transfer(rs); } static bool rockchip_spi_can_dma(struct spi_master *master, @@ -780,11 +765,8 @@ static int rockchip_spi_probe(struct platform_device *pdev) } if (rs->dma_tx.ch && rs->dma_rx.ch) { - dma_get_slave_caps(rs->dma_rx.ch, &(rs->dma_caps)); rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR); rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR); - rs->dma_tx.direction = DMA_MEM_TO_DEV; - rs->dma_rx.direction = DMA_DEV_TO_MEM; master->can_dma = rockchip_spi_can_dma; master->dma_tx = rs->dma_tx.ch; diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index b37de1d991d6..55f8e55327b3 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 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 @@ -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 "); MODULE_ALIAS("platform:sh-hspi"); diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 101cd6aae2ea..adf384323934 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 @@ -1343,8 +1339,8 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) i = platform_get_irq(pdev, 0); if (i < 0) { - dev_err(&pdev->dev, "cannot get platform IRQ\n"); - ret = -ENOENT; + dev_err(&pdev->dev, "cannot get IRQ\n"); + ret = i; goto err1; } 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 @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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(®_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), + ®_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 "); +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c new file mode 100644 index 000000000000..3b2a9a6b990d --- /dev/null +++ b/drivers/spi/spi-stm32-qspi.c @@ -0,0 +1,512 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved + * Author: Ludovic Barre for STMicroelectronics. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QSPI_CR 0x00 +#define CR_EN BIT(0) +#define CR_ABORT BIT(1) +#define CR_DMAEN BIT(2) +#define CR_TCEN BIT(3) +#define CR_SSHIFT BIT(4) +#define CR_DFM BIT(6) +#define CR_FSEL BIT(7) +#define CR_FTHRES_MASK GENMASK(12, 8) +#define CR_TEIE BIT(16) +#define CR_TCIE BIT(17) +#define CR_FTIE BIT(18) +#define CR_SMIE BIT(19) +#define CR_TOIE BIT(20) +#define CR_PRESC_MASK GENMASK(31, 24) + +#define QSPI_DCR 0x04 +#define DCR_FSIZE_MASK GENMASK(20, 16) + +#define QSPI_SR 0x08 +#define SR_TEF BIT(0) +#define SR_TCF BIT(1) +#define SR_FTF BIT(2) +#define SR_SMF BIT(3) +#define SR_TOF BIT(4) +#define SR_BUSY BIT(5) +#define SR_FLEVEL_MASK GENMASK(13, 8) + +#define QSPI_FCR 0x0c +#define FCR_CTEF BIT(0) +#define FCR_CTCF BIT(1) + +#define QSPI_DLR 0x10 + +#define QSPI_CCR 0x14 +#define CCR_INST_MASK GENMASK(7, 0) +#define CCR_IMODE_MASK GENMASK(9, 8) +#define CCR_ADMODE_MASK GENMASK(11, 10) +#define CCR_ADSIZE_MASK GENMASK(13, 12) +#define CCR_DCYC_MASK GENMASK(22, 18) +#define CCR_DMODE_MASK GENMASK(25, 24) +#define CCR_FMODE_MASK GENMASK(27, 26) +#define CCR_FMODE_INDW (0U << 26) +#define CCR_FMODE_INDR (1U << 26) +#define CCR_FMODE_APM (2U << 26) +#define CCR_FMODE_MM (3U << 26) +#define CCR_BUSWIDTH_0 0x0 +#define CCR_BUSWIDTH_1 0x1 +#define CCR_BUSWIDTH_2 0x2 +#define CCR_BUSWIDTH_4 0x3 + +#define QSPI_AR 0x18 +#define QSPI_ABR 0x1c +#define QSPI_DR 0x20 +#define QSPI_PSMKR 0x24 +#define QSPI_PSMAR 0x28 +#define QSPI_PIR 0x2c +#define QSPI_LPTR 0x30 +#define LPTR_DFT_TIMEOUT 0x10 + +#define STM32_QSPI_MAX_MMAP_SZ SZ_256M +#define STM32_QSPI_MAX_NORCHIP 2 + +#define STM32_FIFO_TIMEOUT_US 30000 +#define STM32_BUSY_TIMEOUT_US 100000 +#define STM32_ABT_TIMEOUT_US 100000 + +struct stm32_qspi_flash { + struct stm32_qspi *qspi; + u32 cs; + u32 presc; +}; + +struct stm32_qspi { + struct device *dev; + void __iomem *io_base; + void __iomem *mm_base; + resource_size_t mm_size; + struct clk *clk; + u32 clk_rate; + struct stm32_qspi_flash flash[STM32_QSPI_MAX_NORCHIP]; + struct completion data_completion; + u32 fmode; + + /* + * to protect device configuration, could be different between + * 2 flash access (bk1, bk2) + */ + struct mutex lock; +}; + +static irqreturn_t stm32_qspi_irq(int irq, void *dev_id) +{ + struct stm32_qspi *qspi = (struct stm32_qspi *)dev_id; + u32 cr, sr; + + sr = readl_relaxed(qspi->io_base + QSPI_SR); + + if (sr & (SR_TEF | SR_TCF)) { + /* disable irq */ + cr = readl_relaxed(qspi->io_base + QSPI_CR); + cr &= ~CR_TCIE & ~CR_TEIE; + writel_relaxed(cr, qspi->io_base + QSPI_CR); + complete(&qspi->data_completion); + } + + return IRQ_HANDLED; +} + +static void stm32_qspi_read_fifo(u8 *val, void __iomem *addr) +{ + *val = readb_relaxed(addr); +} + +static void stm32_qspi_write_fifo(u8 *val, void __iomem *addr) +{ + writeb_relaxed(*val, addr); +} + +static int stm32_qspi_tx_poll(struct stm32_qspi *qspi, + const struct spi_mem_op *op) +{ + void (*tx_fifo)(u8 *val, void __iomem *addr); + u32 len = op->data.nbytes, sr; + u8 *buf; + int ret; + + if (op->data.dir == SPI_MEM_DATA_IN) { + tx_fifo = stm32_qspi_read_fifo; + buf = op->data.buf.in; + + } else { + tx_fifo = stm32_qspi_write_fifo; + buf = (u8 *)op->data.buf.out; + } + + while (len--) { + ret = readl_relaxed_poll_timeout_atomic(qspi->io_base + QSPI_SR, + sr, (sr & SR_FTF), 1, + STM32_FIFO_TIMEOUT_US); + if (ret) { + dev_err(qspi->dev, "fifo timeout (len:%d stat:%#x)\n", + len, sr); + return ret; + } + tx_fifo(buf++, qspi->io_base + QSPI_DR); + } + + return 0; +} + +static int stm32_qspi_tx_mm(struct stm32_qspi *qspi, + const struct spi_mem_op *op) +{ + memcpy_fromio(op->data.buf.in, qspi->mm_base + op->addr.val, + op->data.nbytes); + return 0; +} + +static int stm32_qspi_tx(struct stm32_qspi *qspi, const struct spi_mem_op *op) +{ + if (!op->data.nbytes) + return 0; + + if (qspi->fmode == CCR_FMODE_MM) + return stm32_qspi_tx_mm(qspi, op); + + return stm32_qspi_tx_poll(qspi, op); +} + +static int stm32_qspi_wait_nobusy(struct stm32_qspi *qspi) +{ + u32 sr; + + return readl_relaxed_poll_timeout_atomic(qspi->io_base + QSPI_SR, sr, + !(sr & SR_BUSY), 1, + STM32_BUSY_TIMEOUT_US); +} + +static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi, + const struct spi_mem_op *op) +{ + u32 cr, sr; + int err = 0; + + if (!op->data.nbytes) + return stm32_qspi_wait_nobusy(qspi); + + if (readl_relaxed(qspi->io_base + QSPI_SR) & SR_TCF) + goto out; + + reinit_completion(&qspi->data_completion); + cr = readl_relaxed(qspi->io_base + QSPI_CR); + writel_relaxed(cr | CR_TCIE | CR_TEIE, qspi->io_base + QSPI_CR); + + if (!wait_for_completion_interruptible_timeout(&qspi->data_completion, + msecs_to_jiffies(1000))) { + err = -ETIMEDOUT; + } else { + sr = readl_relaxed(qspi->io_base + QSPI_SR); + if (sr & SR_TEF) + err = -EIO; + } + +out: + /* clear flags */ + writel_relaxed(FCR_CTCF | FCR_CTEF, qspi->io_base + QSPI_FCR); + + return err; +} + +static int stm32_qspi_get_mode(struct stm32_qspi *qspi, u8 buswidth) +{ + if (buswidth == 4) + return CCR_BUSWIDTH_4; + + return buswidth; +} + +static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master); + struct stm32_qspi_flash *flash = &qspi->flash[mem->spi->chip_select]; + u32 ccr, cr, addr_max; + int timeout, err = 0; + + dev_dbg(qspi->dev, "cmd:%#x mode:%d.%d.%d.%d addr:%#llx len:%#x\n", + op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + op->dummy.buswidth, op->data.buswidth, + op->addr.val, op->data.nbytes); + + err = stm32_qspi_wait_nobusy(qspi); + if (err) + goto abort; + + addr_max = op->addr.val + op->data.nbytes + 1; + + if (op->data.dir == SPI_MEM_DATA_IN) { + if (addr_max < qspi->mm_size && + op->addr.buswidth) + qspi->fmode = CCR_FMODE_MM; + else + qspi->fmode = CCR_FMODE_INDR; + } else { + qspi->fmode = CCR_FMODE_INDW; + } + + cr = readl_relaxed(qspi->io_base + QSPI_CR); + cr &= ~CR_PRESC_MASK & ~CR_FSEL; + cr |= FIELD_PREP(CR_PRESC_MASK, flash->presc); + cr |= FIELD_PREP(CR_FSEL, flash->cs); + writel_relaxed(cr, qspi->io_base + QSPI_CR); + + if (op->data.nbytes) + writel_relaxed(op->data.nbytes - 1, + qspi->io_base + QSPI_DLR); + else + qspi->fmode = CCR_FMODE_INDW; + + ccr = qspi->fmode; + ccr |= FIELD_PREP(CCR_INST_MASK, op->cmd.opcode); + ccr |= FIELD_PREP(CCR_IMODE_MASK, + stm32_qspi_get_mode(qspi, op->cmd.buswidth)); + + if (op->addr.nbytes) { + ccr |= FIELD_PREP(CCR_ADMODE_MASK, + stm32_qspi_get_mode(qspi, op->addr.buswidth)); + ccr |= FIELD_PREP(CCR_ADSIZE_MASK, op->addr.nbytes - 1); + } + + if (op->dummy.buswidth && op->dummy.nbytes) + ccr |= FIELD_PREP(CCR_DCYC_MASK, + op->dummy.nbytes * 8 / op->dummy.buswidth); + + if (op->data.nbytes) { + ccr |= FIELD_PREP(CCR_DMODE_MASK, + stm32_qspi_get_mode(qspi, op->data.buswidth)); + } + + writel_relaxed(ccr, qspi->io_base + QSPI_CCR); + + if (op->addr.nbytes && qspi->fmode != CCR_FMODE_MM) + writel_relaxed(op->addr.val, qspi->io_base + QSPI_AR); + + err = stm32_qspi_tx(qspi, op); + + /* + * Abort in: + * -error case + * -read memory map: prefetching must be stopped if we read the last + * byte of device (device size - fifo size). like device size is not + * knows, the prefetching is always stop. + */ + if (err || qspi->fmode == CCR_FMODE_MM) + goto abort; + + /* wait end of tx in indirect mode */ + err = stm32_qspi_wait_cmd(qspi, op); + if (err) + goto abort; + + return 0; + +abort: + cr = readl_relaxed(qspi->io_base + QSPI_CR) | CR_ABORT; + writel_relaxed(cr, qspi->io_base + QSPI_CR); + + /* wait clear of abort bit by hw */ + timeout = readl_relaxed_poll_timeout_atomic(qspi->io_base + QSPI_CR, + cr, !(cr & CR_ABORT), 1, + STM32_ABT_TIMEOUT_US); + + writel_relaxed(FCR_CTCF, qspi->io_base + QSPI_FCR); + + if (err || timeout) + dev_err(qspi->dev, "%s err:%d abort timeout:%d\n", + __func__, err, timeout); + + return err; +} + +static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master); + int ret; + + mutex_lock(&qspi->lock); + ret = stm32_qspi_send(mem, op); + mutex_unlock(&qspi->lock); + + return ret; +} + +static int stm32_qspi_setup(struct spi_device *spi) +{ + struct spi_controller *ctrl = spi->master; + struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl); + struct stm32_qspi_flash *flash; + u32 cr, presc; + + if (ctrl->busy) + return -EBUSY; + + if (!spi->max_speed_hz) + return -EINVAL; + + presc = DIV_ROUND_UP(qspi->clk_rate, spi->max_speed_hz) - 1; + + flash = &qspi->flash[spi->chip_select]; + flash->qspi = qspi; + flash->cs = spi->chip_select; + flash->presc = presc; + + mutex_lock(&qspi->lock); + writel_relaxed(LPTR_DFT_TIMEOUT, qspi->io_base + QSPI_LPTR); + cr = FIELD_PREP(CR_FTHRES_MASK, 3) | CR_TCEN | CR_SSHIFT | CR_EN; + writel_relaxed(cr, qspi->io_base + QSPI_CR); + + /* set dcr fsize to max address */ + writel_relaxed(DCR_FSIZE_MASK, qspi->io_base + QSPI_DCR); + mutex_unlock(&qspi->lock); + + return 0; +} + +/* + * no special host constraint, so use default spi_mem_default_supports_op + * to check supported mode. + */ +static const struct spi_controller_mem_ops stm32_qspi_mem_ops = { + .exec_op = stm32_qspi_exec_op, +}; + +static void stm32_qspi_release(struct stm32_qspi *qspi) +{ + /* disable qspi */ + writel_relaxed(0, qspi->io_base + QSPI_CR); + mutex_destroy(&qspi->lock); + clk_disable_unprepare(qspi->clk); +} + +static int stm32_qspi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_controller *ctrl; + struct reset_control *rstc; + struct stm32_qspi *qspi; + struct resource *res; + int ret, irq; + + ctrl = spi_alloc_master(dev, sizeof(*qspi)); + if (!ctrl) + return -ENOMEM; + + qspi = spi_controller_get_devdata(ctrl); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi"); + qspi->io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->io_base)) + return PTR_ERR(qspi->io_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mm"); + qspi->mm_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->mm_base)) + return PTR_ERR(qspi->mm_base); + + qspi->mm_size = resource_size(res); + if (qspi->mm_size > STM32_QSPI_MAX_MMAP_SZ) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(dev, irq, stm32_qspi_irq, 0, + dev_name(dev), qspi); + if (ret) { + dev_err(dev, "failed to request irq\n"); + return ret; + } + + init_completion(&qspi->data_completion); + + qspi->clk = devm_clk_get(dev, NULL); + if (IS_ERR(qspi->clk)) + return PTR_ERR(qspi->clk); + + qspi->clk_rate = clk_get_rate(qspi->clk); + if (!qspi->clk_rate) + return -EINVAL; + + ret = clk_prepare_enable(qspi->clk); + if (ret) { + dev_err(dev, "can not enable the clock\n"); + return ret; + } + + rstc = devm_reset_control_get_exclusive(dev, NULL); + if (!IS_ERR(rstc)) { + reset_control_assert(rstc); + udelay(2); + reset_control_deassert(rstc); + } + + qspi->dev = dev; + platform_set_drvdata(pdev, qspi); + mutex_init(&qspi->lock); + + ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD + | SPI_TX_DUAL | SPI_TX_QUAD; + ctrl->setup = stm32_qspi_setup; + ctrl->bus_num = -1; + ctrl->mem_ops = &stm32_qspi_mem_ops; + ctrl->num_chipselect = STM32_QSPI_MAX_NORCHIP; + ctrl->dev.of_node = dev->of_node; + + ret = devm_spi_register_master(dev, ctrl); + if (ret) + goto err_spi_register; + + return 0; + +err_spi_register: + stm32_qspi_release(qspi); + + return ret; +} + +static int stm32_qspi_remove(struct platform_device *pdev) +{ + struct stm32_qspi *qspi = platform_get_drvdata(pdev); + + stm32_qspi_release(qspi); + return 0; +} + +static const struct of_device_id stm32_qspi_match[] = { + {.compatible = "st,stm32f469-qspi"}, + {} +}; +MODULE_DEVICE_TABLE(of, stm32_qspi_match); + +static struct platform_driver stm32_qspi_driver = { + .probe = stm32_qspi_probe, + .remove = stm32_qspi_remove, + .driver = { + .name = "stm32-qspi", + .of_match_table = stm32_qspi_match, + }, +}; +module_platform_driver(stm32_qspi_driver); + +MODULE_AUTHOR("Ludovic Barre "); +MODULE_DESCRIPTION("STMicroelectronics STM32 quad spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 9da0bc5a036c..6ca59406b0b7 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 @@ -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; @@ -733,7 +775,9 @@ static void spi_set_cs(struct spi_device *spi, bool enable) enable = !enable; if (gpio_is_valid(spi->cs_gpio)) { - gpio_set_value(spi->cs_gpio, !enable); + /* Honour the SPI_NO_CS flag */ + if (!(spi->mode & SPI_NO_CS)) + gpio_set_value(spi->cs_gpio, !enable); /* Some SPI masters need both GPIO CS & slave_select */ if ((spi->controller->flags & SPI_MASTER_GPIO_SS) && spi->controller->set_cs) @@ -2783,8 +2827,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) { @@ -2838,6 +2884,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 @@ -3323,20 +3398,23 @@ EXPORT_SYMBOL_GPL(spi_write_then_read); /*-------------------------------------------------------------------------*/ -#if IS_ENABLED(CONFIG_OF_DYNAMIC) +#if IS_ENABLED(CONFIG_OF) static int __spi_of_device_match(struct device *dev, void *data) { return dev->of_node == data; } /* must call put_device() when done with returned spi_device device */ -static struct spi_device *of_find_spi_device_by_node(struct device_node *node) +struct spi_device *of_find_spi_device_by_node(struct device_node *node) { struct device *dev = bus_find_device(&spi_bus_type, NULL, node, __spi_of_device_match); return dev ? to_spi_device(dev) : NULL; } +EXPORT_SYMBOL_GPL(of_find_spi_device_by_node); +#endif /* IS_ENABLED(CONFIG_OF) */ +#if IS_ENABLED(CONFIG_OF_DYNAMIC) static int __spi_of_controller_match(struct device *dev, const void *data) { return dev->of_node == data; 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/pci.h b/include/linux/pci.h index 6925828f9f25..2c4755032475 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1705,6 +1705,10 @@ static inline int pci_irqd_intx_xlate(struct irq_domain *d, unsigned long *out_hwirq, unsigned int *out_type) { return -EINVAL; } + +static inline const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, + struct pci_dev *dev) +{ return NULL; } #endif /* CONFIG_PCI */ /* Include architecture-dependent settings and functions */ 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/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 13b4244d44c1..979087e021f3 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -196,6 +196,7 @@ enum pxa_ssp_type { PXA27x_SSP, PXA3xx_SSP, PXA168_SSP, + MMP2_SSP, PXA910_SSP, CE4100_SSP, QUARK_X1000_SSP, @@ -217,7 +218,7 @@ struct ssp_device { const char *label; int port_id; - int type; + enum pxa_ssp_type type; int use_count; int irq; diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h index 5d6144977828..3bcd67fd5548 100644 --- a/include/linux/qcom-geni-se.h +++ b/include/linux/qcom-geni-se.h @@ -225,19 +225,14 @@ struct geni_se { #define HW_VER_MINOR_SHFT 16 #define HW_VER_STEP_MASK GENMASK(15, 0) +#define GENI_SE_VERSION_MAJOR(ver) ((ver & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT) +#define GENI_SE_VERSION_MINOR(ver) ((ver & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT) +#define GENI_SE_VERSION_STEP(ver) (ver & HW_VER_STEP_MASK) + #if IS_ENABLED(CONFIG_QCOM_GENI_SE) u32 geni_se_get_qup_hw_version(struct geni_se *se); -#define geni_se_get_wrapper_version(se, major, minor, step) do { \ - u32 ver; \ -\ - ver = geni_se_get_qup_hw_version(se); \ - major = (ver & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT; \ - minor = (ver & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT; \ - step = version & HW_VER_STEP_MASK; \ -} while (0) - /** * geni_se_read_proto() - Read the protocol configured for a serial engine * @se: Pointer to the concerned serial engine. diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index a64235e05321..6be77fa5ab90 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -1,15 +1,6 @@ -/* +/* SPDX-License-Identifier: GPL-2.0-or-later + * * Copyright (C) 2005 David Brownell - * - * 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. */ #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; }; @@ -1277,7 +1272,6 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) { return 0; } #endif - /* If you're hotplugging an adapter with devices (parport, usb, etc) * use spi_new_device() to describe each device. You can also call * spi_unregister_device() to start making that device vanish, but @@ -1309,6 +1303,22 @@ spi_transfer_is_last(struct spi_controller *ctlr, struct spi_transfer *xfer) return list_is_last(&xfer->transfer_list, &ctlr->cur_msg->transfers); } +/* OF support code */ +#if IS_ENABLED(CONFIG_OF) + +/* must call put_device() when done with returned spi_device device */ +extern struct spi_device * +of_find_spi_device_by_node(struct device_node *node); + +#else + +static inline struct spi_device * +of_find_spi_device_by_node(struct device_node *node) +{ + return NULL; +} + +#endif /* IS_ENABLED(CONFIG_OF) */ /* Compatibility layer */ #define spi_master spi_controller 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); }