Merge branch 'spi-4.20' into spi-next
This commit is contained in:
commit
4b51c747e4
|
@ -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 = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>;
|
||||
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>;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>;
|
||||
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>;
|
||||
};
|
|
@ -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 = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
|
||||
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>;
|
||||
};
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -2,7 +2,7 @@ Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface.
|
|||
|
||||
Required properties:
|
||||
- compatible : "snps,dw-apb-ssi" or "mscc,<soc>-spi", where soc is "ocelot" or
|
||||
"jaguar2"
|
||||
"jaguar2", or "amazon,alpine-dw-apb-ssi"
|
||||
- reg : The register base for the controller. For "mscc,<soc>-spi", a second
|
||||
register set is required (named ICPU_CFG:SPI_MST)
|
||||
- interrupts : One interrupt, used by the controller.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
};
|
|
@ -3,7 +3,7 @@ Device tree configuration for Renesas RSPI/QSPI driver
|
|||
Required properties:
|
||||
- compatible : For Renesas Serial Peripheral Interface on legacy SH:
|
||||
"renesas,rspi-<soctype>", "renesas,rspi" as fallback.
|
||||
For Renesas Serial Peripheral Interface on RZ/A1H:
|
||||
For Renesas Serial Peripheral Interface on RZ/A:
|
||||
"renesas,rspi-<soctype>", "renesas,rspi-rz" as fallback.
|
||||
For Quad Serial Peripheral Interface on R-Car Gen2 and
|
||||
RZ/G1 devices:
|
||||
|
@ -11,7 +11,9 @@ Required properties:
|
|||
Examples with soctypes are:
|
||||
- "renesas,rspi-sh7757" (SH)
|
||||
- "renesas,rspi-r7s72100" (RZ/A1H)
|
||||
- "renesas,rspi-r7s9210" (RZ/A2)
|
||||
- "renesas,qspi-r8a7743" (RZ/G1M)
|
||||
- "renesas,qspi-r8a7744" (RZ/G1N)
|
||||
- "renesas,qspi-r8a7745" (RZ/G1E)
|
||||
- "renesas,qspi-r8a7790" (R-Car H2)
|
||||
- "renesas,qspi-r8a7791" (R-Car M2-W)
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
Binding for MTK SPI Slave controller
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of the following.
|
||||
- mediatek,mt2712-spi-slave: for mt2712 platforms
|
||||
- reg: Address and length of the register set for the device.
|
||||
- interrupts: Should contain spi interrupt.
|
||||
- clocks: phandles to input clocks.
|
||||
It's clock gate, and should be <&infracfg CLK_INFRA_AO_SPI1>.
|
||||
- clock-names: should be "spi" for the clock gate.
|
||||
|
||||
Optional properties:
|
||||
- assigned-clocks: it's mux clock, should be <&topckgen CLK_TOP_SPISLV_SEL>.
|
||||
- assigned-clock-parents: parent of mux clock.
|
||||
It's PLL, and should be one of the following.
|
||||
- <&topckgen CLK_TOP_UNIVPLL1_D2>: specify parent clock 312MHZ.
|
||||
It's the default one.
|
||||
- <&topckgen CLK_TOP_UNIVPLL1_D4>: specify parent clock 156MHZ.
|
||||
- <&topckgen CLK_TOP_UNIVPLL2_D4>: specify parent clock 104MHZ.
|
||||
- <&topckgen CLK_TOP_UNIVPLL1_D8>: specify parent clock 78MHZ.
|
||||
|
||||
Example:
|
||||
- SoC Specific Portion:
|
||||
spis1: spi@10013000 {
|
||||
compatible = "mediatek,mt2712-spi-slave";
|
||||
reg = <0 0x10013000 0 0x100>;
|
||||
interrupts = <GIC_SPI 283 IRQ_TYPE_LEVEL_LOW>;
|
||||
clocks = <&infracfg CLK_INFRA_AO_SPI1>;
|
||||
clock-names = "spi";
|
||||
assigned-clocks = <&topckgen CLK_TOP_SPISLV_SEL>;
|
||||
assigned-clock-parents = <&topckgen CLK_TOP_UNIVPLL1_D2>;
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
Spreadtrum SPI Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "sprd,sc9860-spi".
|
||||
- reg: Offset and length of SPI controller register space.
|
||||
- interrupts: Should contain SPI interrupt.
|
||||
- clock-names: Should contain following entries:
|
||||
"spi" for SPI clock,
|
||||
"source" for SPI source (parent) clock,
|
||||
"enable" for SPI module enable clock.
|
||||
- clocks: List of clock input name strings sorted in the same order
|
||||
as the clock-names property.
|
||||
- #address-cells: The number of cells required to define a chip select
|
||||
address on the SPI bus. Should be set to 1.
|
||||
- #size-cells: Should be set to 0.
|
||||
|
||||
Example:
|
||||
spi0: spi@70a00000{
|
||||
compatible = "sprd,sc9860-spi";
|
||||
reg = <0 0x70a00000 0 0x1000>;
|
||||
interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clock-names = "spi", "source","enable";
|
||||
clocks = <&clk_spi0>, <&ext_26m>, <&clk_ap_apb_gates 5>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
|
@ -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>;
|
||||
...
|
||||
};
|
||||
};
|
|
@ -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 / <an_integer> == @req_freq
|
||||
* - if @exact is false then @res_freq / <an_integer> <= @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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,703 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/qcom-geni-se.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* 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");
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/gcd.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio.h>
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#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)
|
||||
|
|
|
@ -0,0 +1,581 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
|
||||
#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");
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
@ -455,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;
|
||||
|
||||
|
@ -477,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);
|
||||
|
@ -509,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;
|
||||
|
@ -517,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)
|
||||
|
@ -581,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);
|
||||
|
||||
|
@ -600,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) &&
|
||||
|
@ -638,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,
|
||||
|
@ -783,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;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* SH RSPI driver
|
||||
*
|
||||
|
@ -6,15 +7,6 @@
|
|||
*
|
||||
* Based on spi-sh.c:
|
||||
* Copyright (C) 2011 Renesas Solutions Corp.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* SuperH HSPI bus driver
|
||||
*
|
||||
|
@ -7,15 +8,6 @@
|
|||
* Based on pxa2xx_spi.c:
|
||||
* Copyright (C) 2011 Renesas Solutions Corp.
|
||||
* Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
@ -316,6 +308,6 @@ static struct platform_driver hspi_driver = {
|
|||
module_platform_driver(hspi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SuperH HSPI bus driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
|
||||
MODULE_ALIAS("platform:sh-hspi");
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* SuperH MSIOF SPI Master Interface
|
||||
*
|
||||
* Copyright (c) 2009 Magnus Damm
|
||||
* Copyright (C) 2014 Renesas Electronics Corporation
|
||||
* Copyright (C) 2014-2017 Glider bvba
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* SH SPI bus driver
|
||||
*
|
||||
|
@ -5,15 +6,6 @@
|
|||
*
|
||||
* Based on pxa2xx_spi.c:
|
||||
* Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -522,6 +514,6 @@ static struct platform_driver spi_sh_driver = {
|
|||
module_platform_driver(spi_sh_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SH SPI bus driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Yoshihiro Shimoda");
|
||||
MODULE_ALIAS("platform:sh_spi");
|
||||
|
|
|
@ -0,0 +1,554 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
// Copyright (c) 2018 MediaTek Inc.
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define SPIS_IRQ_EN_REG 0x0
|
||||
#define SPIS_IRQ_CLR_REG 0x4
|
||||
#define SPIS_IRQ_ST_REG 0x8
|
||||
#define SPIS_IRQ_MASK_REG 0xc
|
||||
#define SPIS_CFG_REG 0x10
|
||||
#define SPIS_RX_DATA_REG 0x14
|
||||
#define SPIS_TX_DATA_REG 0x18
|
||||
#define SPIS_RX_DST_REG 0x1c
|
||||
#define SPIS_TX_SRC_REG 0x20
|
||||
#define SPIS_DMA_CFG_REG 0x30
|
||||
#define SPIS_SOFT_RST_REG 0x40
|
||||
|
||||
/* SPIS_IRQ_EN_REG */
|
||||
#define DMA_DONE_EN BIT(7)
|
||||
#define DATA_DONE_EN BIT(2)
|
||||
#define RSTA_DONE_EN BIT(1)
|
||||
#define CMD_INVALID_EN BIT(0)
|
||||
|
||||
/* SPIS_IRQ_ST_REG */
|
||||
#define DMA_DONE_ST BIT(7)
|
||||
#define DATA_DONE_ST BIT(2)
|
||||
#define RSTA_DONE_ST BIT(1)
|
||||
#define CMD_INVALID_ST BIT(0)
|
||||
|
||||
/* SPIS_IRQ_MASK_REG */
|
||||
#define DMA_DONE_MASK BIT(7)
|
||||
#define DATA_DONE_MASK BIT(2)
|
||||
#define RSTA_DONE_MASK BIT(1)
|
||||
#define CMD_INVALID_MASK BIT(0)
|
||||
|
||||
/* SPIS_CFG_REG */
|
||||
#define SPIS_TX_ENDIAN BIT(7)
|
||||
#define SPIS_RX_ENDIAN BIT(6)
|
||||
#define SPIS_TXMSBF BIT(5)
|
||||
#define SPIS_RXMSBF BIT(4)
|
||||
#define SPIS_CPHA BIT(3)
|
||||
#define SPIS_CPOL BIT(2)
|
||||
#define SPIS_TX_EN BIT(1)
|
||||
#define SPIS_RX_EN BIT(0)
|
||||
|
||||
/* SPIS_DMA_CFG_REG */
|
||||
#define TX_DMA_TRIG_EN BIT(31)
|
||||
#define TX_DMA_EN BIT(30)
|
||||
#define RX_DMA_EN BIT(29)
|
||||
#define TX_DMA_LEN 0xfffff
|
||||
|
||||
/* SPIS_SOFT_RST_REG */
|
||||
#define SPIS_DMA_ADDR_EN BIT(1)
|
||||
#define SPIS_SOFT_RST BIT(0)
|
||||
|
||||
#define MTK_SPI_SLAVE_MAX_FIFO_SIZE 512U
|
||||
|
||||
struct mtk_spi_slave {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct clk *spi_clk;
|
||||
struct completion xfer_done;
|
||||
struct spi_transfer *cur_transfer;
|
||||
bool slave_aborted;
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_spi_slave_of_match[] = {
|
||||
{ .compatible = "mediatek,mt2712-spi-slave", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_spi_slave_of_match);
|
||||
|
||||
static void mtk_spi_slave_disable_dma(struct mtk_spi_slave *mdata)
|
||||
{
|
||||
u32 reg_val;
|
||||
|
||||
reg_val = readl(mdata->base + SPIS_DMA_CFG_REG);
|
||||
reg_val &= ~RX_DMA_EN;
|
||||
reg_val &= ~TX_DMA_EN;
|
||||
writel(reg_val, mdata->base + SPIS_DMA_CFG_REG);
|
||||
}
|
||||
|
||||
static void mtk_spi_slave_disable_xfer(struct mtk_spi_slave *mdata)
|
||||
{
|
||||
u32 reg_val;
|
||||
|
||||
reg_val = readl(mdata->base + SPIS_CFG_REG);
|
||||
reg_val &= ~SPIS_TX_EN;
|
||||
reg_val &= ~SPIS_RX_EN;
|
||||
writel(reg_val, mdata->base + SPIS_CFG_REG);
|
||||
}
|
||||
|
||||
static int mtk_spi_slave_wait_for_completion(struct mtk_spi_slave *mdata)
|
||||
{
|
||||
if (wait_for_completion_interruptible(&mdata->xfer_done) ||
|
||||
mdata->slave_aborted) {
|
||||
dev_err(mdata->dev, "interrupted\n");
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_spi_slave_prepare_message(struct spi_controller *ctlr,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
|
||||
struct spi_device *spi = msg->spi;
|
||||
bool cpha, cpol;
|
||||
u32 reg_val;
|
||||
|
||||
cpha = spi->mode & SPI_CPHA ? 1 : 0;
|
||||
cpol = spi->mode & SPI_CPOL ? 1 : 0;
|
||||
|
||||
reg_val = readl(mdata->base + SPIS_CFG_REG);
|
||||
if (cpha)
|
||||
reg_val |= SPIS_CPHA;
|
||||
else
|
||||
reg_val &= ~SPIS_CPHA;
|
||||
if (cpol)
|
||||
reg_val |= SPIS_CPOL;
|
||||
else
|
||||
reg_val &= ~SPIS_CPOL;
|
||||
|
||||
if (spi->mode & SPI_LSB_FIRST)
|
||||
reg_val &= ~(SPIS_TXMSBF | SPIS_RXMSBF);
|
||||
else
|
||||
reg_val |= SPIS_TXMSBF | SPIS_RXMSBF;
|
||||
|
||||
reg_val &= ~SPIS_TX_ENDIAN;
|
||||
reg_val &= ~SPIS_RX_ENDIAN;
|
||||
writel(reg_val, mdata->base + SPIS_CFG_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_spi_slave_fifo_transfer(struct spi_controller *ctlr,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
|
||||
int reg_val, cnt, remainder, ret;
|
||||
|
||||
writel(SPIS_SOFT_RST, mdata->base + SPIS_SOFT_RST_REG);
|
||||
|
||||
reg_val = readl(mdata->base + SPIS_CFG_REG);
|
||||
if (xfer->rx_buf)
|
||||
reg_val |= SPIS_RX_EN;
|
||||
if (xfer->tx_buf)
|
||||
reg_val |= SPIS_TX_EN;
|
||||
writel(reg_val, mdata->base + SPIS_CFG_REG);
|
||||
|
||||
cnt = xfer->len / 4;
|
||||
if (xfer->tx_buf)
|
||||
iowrite32_rep(mdata->base + SPIS_TX_DATA_REG,
|
||||
xfer->tx_buf, cnt);
|
||||
|
||||
remainder = xfer->len % 4;
|
||||
if (xfer->tx_buf && remainder > 0) {
|
||||
reg_val = 0;
|
||||
memcpy(®_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 <leilk.liu@mediatek.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:mtk-spi-slave");
|
|
@ -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");
|
||||
|
|
|
@ -0,0 +1,745 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2018 Spreadtrum Communications Inc.
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define SPRD_SPI_TXD 0x0
|
||||
#define SPRD_SPI_CLKD 0x4
|
||||
#define SPRD_SPI_CTL0 0x8
|
||||
#define SPRD_SPI_CTL1 0xc
|
||||
#define SPRD_SPI_CTL2 0x10
|
||||
#define SPRD_SPI_CTL3 0x14
|
||||
#define SPRD_SPI_CTL4 0x18
|
||||
#define SPRD_SPI_CTL5 0x1c
|
||||
#define SPRD_SPI_INT_EN 0x20
|
||||
#define SPRD_SPI_INT_CLR 0x24
|
||||
#define SPRD_SPI_INT_RAW_STS 0x28
|
||||
#define SPRD_SPI_INT_MASK_STS 0x2c
|
||||
#define SPRD_SPI_STS1 0x30
|
||||
#define SPRD_SPI_STS2 0x34
|
||||
#define SPRD_SPI_DSP_WAIT 0x38
|
||||
#define SPRD_SPI_STS3 0x3c
|
||||
#define SPRD_SPI_CTL6 0x40
|
||||
#define SPRD_SPI_STS4 0x44
|
||||
#define SPRD_SPI_FIFO_RST 0x48
|
||||
#define SPRD_SPI_CTL7 0x4c
|
||||
#define SPRD_SPI_STS5 0x50
|
||||
#define SPRD_SPI_CTL8 0x54
|
||||
#define SPRD_SPI_CTL9 0x58
|
||||
#define SPRD_SPI_CTL10 0x5c
|
||||
#define SPRD_SPI_CTL11 0x60
|
||||
#define SPRD_SPI_CTL12 0x64
|
||||
#define SPRD_SPI_STS6 0x68
|
||||
#define SPRD_SPI_STS7 0x6c
|
||||
#define SPRD_SPI_STS8 0x70
|
||||
#define SPRD_SPI_STS9 0x74
|
||||
|
||||
/* Bits & mask definition for register CTL0 */
|
||||
#define SPRD_SPI_SCK_REV BIT(13)
|
||||
#define SPRD_SPI_NG_TX BIT(1)
|
||||
#define SPRD_SPI_NG_RX BIT(0)
|
||||
#define SPRD_SPI_CHNL_LEN_MASK GENMASK(4, 0)
|
||||
#define SPRD_SPI_CSN_MASK GENMASK(11, 8)
|
||||
#define SPRD_SPI_CS0_VALID BIT(8)
|
||||
|
||||
/* Bits & mask definition for register SPI_INT_EN */
|
||||
#define SPRD_SPI_TX_END_INT_EN BIT(8)
|
||||
#define SPRD_SPI_RX_END_INT_EN BIT(9)
|
||||
|
||||
/* Bits & mask definition for register SPI_INT_RAW_STS */
|
||||
#define SPRD_SPI_TX_END_RAW BIT(8)
|
||||
#define SPRD_SPI_RX_END_RAW BIT(9)
|
||||
|
||||
/* Bits & mask definition for register SPI_INT_CLR */
|
||||
#define SPRD_SPI_TX_END_CLR BIT(8)
|
||||
#define SPRD_SPI_RX_END_CLR BIT(9)
|
||||
|
||||
/* Bits & mask definition for register INT_MASK_STS */
|
||||
#define SPRD_SPI_MASK_RX_END BIT(9)
|
||||
#define SPRD_SPI_MASK_TX_END BIT(8)
|
||||
|
||||
/* Bits & mask definition for register STS2 */
|
||||
#define SPRD_SPI_TX_BUSY BIT(8)
|
||||
|
||||
/* Bits & mask definition for register CTL1 */
|
||||
#define SPRD_SPI_RX_MODE BIT(12)
|
||||
#define SPRD_SPI_TX_MODE BIT(13)
|
||||
#define SPRD_SPI_RTX_MD_MASK GENMASK(13, 12)
|
||||
|
||||
/* Bits & mask definition for register CTL2 */
|
||||
#define SPRD_SPI_DMA_EN BIT(6)
|
||||
|
||||
/* Bits & mask definition for register CTL4 */
|
||||
#define SPRD_SPI_START_RX BIT(9)
|
||||
#define SPRD_SPI_ONLY_RECV_MASK GENMASK(8, 0)
|
||||
|
||||
/* Bits & mask definition for register SPI_INT_CLR */
|
||||
#define SPRD_SPI_RX_END_INT_CLR BIT(9)
|
||||
#define SPRD_SPI_TX_END_INT_CLR BIT(8)
|
||||
|
||||
/* Bits & mask definition for register SPI_INT_RAW */
|
||||
#define SPRD_SPI_RX_END_IRQ BIT(9)
|
||||
#define SPRD_SPI_TX_END_IRQ BIT(8)
|
||||
|
||||
/* Bits & mask definition for register CTL12 */
|
||||
#define SPRD_SPI_SW_RX_REQ BIT(0)
|
||||
#define SPRD_SPI_SW_TX_REQ BIT(1)
|
||||
|
||||
/* Bits & mask definition for register CTL7 */
|
||||
#define SPRD_SPI_DATA_LINE2_EN BIT(15)
|
||||
#define SPRD_SPI_MODE_MASK GENMASK(5, 3)
|
||||
#define SPRD_SPI_MODE_OFFSET 3
|
||||
#define SPRD_SPI_3WIRE_MODE 4
|
||||
#define SPRD_SPI_4WIRE_MODE 0
|
||||
|
||||
/* Bits & mask definition for register CTL8 */
|
||||
#define SPRD_SPI_TX_MAX_LEN_MASK GENMASK(19, 0)
|
||||
#define SPRD_SPI_TX_LEN_H_MASK GENMASK(3, 0)
|
||||
#define SPRD_SPI_TX_LEN_H_OFFSET 16
|
||||
|
||||
/* Bits & mask definition for register CTL9 */
|
||||
#define SPRD_SPI_TX_LEN_L_MASK GENMASK(15, 0)
|
||||
|
||||
/* Bits & mask definition for register CTL10 */
|
||||
#define SPRD_SPI_RX_MAX_LEN_MASK GENMASK(19, 0)
|
||||
#define SPRD_SPI_RX_LEN_H_MASK GENMASK(3, 0)
|
||||
#define SPRD_SPI_RX_LEN_H_OFFSET 16
|
||||
|
||||
/* Bits & mask definition for register CTL11 */
|
||||
#define SPRD_SPI_RX_LEN_L_MASK GENMASK(15, 0)
|
||||
|
||||
/* Default & maximum word delay cycles */
|
||||
#define SPRD_SPI_MIN_DELAY_CYCLE 14
|
||||
#define SPRD_SPI_MAX_DELAY_CYCLE 130
|
||||
|
||||
#define SPRD_SPI_FIFO_SIZE 32
|
||||
#define SPRD_SPI_CHIP_CS_NUM 0x4
|
||||
#define SPRD_SPI_CHNL_LEN 2
|
||||
#define SPRD_SPI_DEFAULT_SOURCE 26000000
|
||||
#define SPRD_SPI_MAX_SPEED_HZ 48000000
|
||||
#define SPRD_SPI_AUTOSUSPEND_DELAY 100
|
||||
|
||||
struct sprd_spi {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
u32 src_clk;
|
||||
u32 hw_mode;
|
||||
u32 trans_len;
|
||||
u32 trans_mode;
|
||||
u32 word_delay;
|
||||
u32 hw_speed_hz;
|
||||
u32 len;
|
||||
int status;
|
||||
const void *tx_buf;
|
||||
void *rx_buf;
|
||||
int (*read_bufs)(struct sprd_spi *ss, u32 len);
|
||||
int (*write_bufs)(struct sprd_spi *ss, u32 len);
|
||||
};
|
||||
|
||||
static u32 sprd_spi_transfer_max_timeout(struct sprd_spi *ss,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
/*
|
||||
* The time spent on transmission of the full FIFO data is the maximum
|
||||
* SPI transmission time.
|
||||
*/
|
||||
u32 size = t->bits_per_word * SPRD_SPI_FIFO_SIZE;
|
||||
u32 bit_time_us = DIV_ROUND_UP(USEC_PER_SEC, ss->hw_speed_hz);
|
||||
u32 total_time_us = size * bit_time_us;
|
||||
/*
|
||||
* There is an interval between data and the data in our SPI hardware,
|
||||
* so the total transmission time need add the interval time.
|
||||
*/
|
||||
u32 interval_cycle = SPRD_SPI_FIFO_SIZE * ss->word_delay;
|
||||
u32 interval_time_us = DIV_ROUND_UP(interval_cycle * USEC_PER_SEC,
|
||||
ss->src_clk);
|
||||
|
||||
return total_time_us + interval_time_us;
|
||||
}
|
||||
|
||||
static int sprd_spi_wait_for_tx_end(struct sprd_spi *ss, struct spi_transfer *t)
|
||||
{
|
||||
u32 val, us;
|
||||
int ret;
|
||||
|
||||
us = sprd_spi_transfer_max_timeout(ss, t);
|
||||
ret = readl_relaxed_poll_timeout(ss->base + SPRD_SPI_INT_RAW_STS, val,
|
||||
val & SPRD_SPI_TX_END_IRQ, 0, us);
|
||||
if (ret) {
|
||||
dev_err(ss->dev, "SPI error, spi send timeout!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = readl_relaxed_poll_timeout(ss->base + SPRD_SPI_STS2, val,
|
||||
!(val & SPRD_SPI_TX_BUSY), 0, us);
|
||||
if (ret) {
|
||||
dev_err(ss->dev, "SPI error, spi busy timeout!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel_relaxed(SPRD_SPI_TX_END_INT_CLR, ss->base + SPRD_SPI_INT_CLR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_spi_wait_for_rx_end(struct sprd_spi *ss, struct spi_transfer *t)
|
||||
{
|
||||
u32 val, us;
|
||||
int ret;
|
||||
|
||||
us = sprd_spi_transfer_max_timeout(ss, t);
|
||||
ret = readl_relaxed_poll_timeout(ss->base + SPRD_SPI_INT_RAW_STS, val,
|
||||
val & SPRD_SPI_RX_END_IRQ, 0, us);
|
||||
if (ret) {
|
||||
dev_err(ss->dev, "SPI error, spi rx timeout!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel_relaxed(SPRD_SPI_RX_END_INT_CLR, ss->base + SPRD_SPI_INT_CLR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_spi_tx_req(struct sprd_spi *ss)
|
||||
{
|
||||
writel_relaxed(SPRD_SPI_SW_TX_REQ, ss->base + SPRD_SPI_CTL12);
|
||||
}
|
||||
|
||||
static void sprd_spi_rx_req(struct sprd_spi *ss)
|
||||
{
|
||||
writel_relaxed(SPRD_SPI_SW_RX_REQ, ss->base + SPRD_SPI_CTL12);
|
||||
}
|
||||
|
||||
static void sprd_spi_enter_idle(struct sprd_spi *ss)
|
||||
{
|
||||
u32 val = readl_relaxed(ss->base + SPRD_SPI_CTL1);
|
||||
|
||||
val &= ~SPRD_SPI_RTX_MD_MASK;
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL1);
|
||||
}
|
||||
|
||||
static void sprd_spi_set_transfer_bits(struct sprd_spi *ss, u32 bits)
|
||||
{
|
||||
u32 val = readl_relaxed(ss->base + SPRD_SPI_CTL0);
|
||||
|
||||
/* Set the valid bits for every transaction */
|
||||
val &= ~(SPRD_SPI_CHNL_LEN_MASK << SPRD_SPI_CHNL_LEN);
|
||||
val |= bits << SPRD_SPI_CHNL_LEN;
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL0);
|
||||
}
|
||||
|
||||
static void sprd_spi_set_tx_length(struct sprd_spi *ss, u32 length)
|
||||
{
|
||||
u32 val = readl_relaxed(ss->base + SPRD_SPI_CTL8);
|
||||
|
||||
length &= SPRD_SPI_TX_MAX_LEN_MASK;
|
||||
val &= ~SPRD_SPI_TX_LEN_H_MASK;
|
||||
val |= length >> SPRD_SPI_TX_LEN_H_OFFSET;
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL8);
|
||||
|
||||
val = length & SPRD_SPI_TX_LEN_L_MASK;
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL9);
|
||||
}
|
||||
|
||||
static void sprd_spi_set_rx_length(struct sprd_spi *ss, u32 length)
|
||||
{
|
||||
u32 val = readl_relaxed(ss->base + SPRD_SPI_CTL10);
|
||||
|
||||
length &= SPRD_SPI_RX_MAX_LEN_MASK;
|
||||
val &= ~SPRD_SPI_RX_LEN_H_MASK;
|
||||
val |= length >> SPRD_SPI_RX_LEN_H_OFFSET;
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL10);
|
||||
|
||||
val = length & SPRD_SPI_RX_LEN_L_MASK;
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL11);
|
||||
}
|
||||
|
||||
static void sprd_spi_chipselect(struct spi_device *sdev, bool cs)
|
||||
{
|
||||
struct spi_controller *sctlr = sdev->controller;
|
||||
struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(ss->base + SPRD_SPI_CTL0);
|
||||
/* The SPI controller will pull down CS pin if cs is 0 */
|
||||
if (!cs) {
|
||||
val &= ~SPRD_SPI_CS0_VALID;
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL0);
|
||||
} else {
|
||||
val |= SPRD_SPI_CSN_MASK;
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL0);
|
||||
}
|
||||
}
|
||||
|
||||
static int sprd_spi_write_only_receive(struct sprd_spi *ss, u32 len)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Clear the start receive bit and reset receive data number */
|
||||
val = readl_relaxed(ss->base + SPRD_SPI_CTL4);
|
||||
val &= ~(SPRD_SPI_START_RX | SPRD_SPI_ONLY_RECV_MASK);
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL4);
|
||||
|
||||
/* Set the receive data length */
|
||||
val = readl_relaxed(ss->base + SPRD_SPI_CTL4);
|
||||
val |= len & SPRD_SPI_ONLY_RECV_MASK;
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL4);
|
||||
|
||||
/* Trigger to receive data */
|
||||
val = readl_relaxed(ss->base + SPRD_SPI_CTL4);
|
||||
val |= SPRD_SPI_START_RX;
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL4);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int sprd_spi_write_bufs_u8(struct sprd_spi *ss, u32 len)
|
||||
{
|
||||
u8 *tx_p = (u8 *)ss->tx_buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
writeb_relaxed(tx_p[i], ss->base + SPRD_SPI_TXD);
|
||||
|
||||
ss->tx_buf += i;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int sprd_spi_write_bufs_u16(struct sprd_spi *ss, u32 len)
|
||||
{
|
||||
u16 *tx_p = (u16 *)ss->tx_buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
writew_relaxed(tx_p[i], ss->base + SPRD_SPI_TXD);
|
||||
|
||||
ss->tx_buf += i << 1;
|
||||
return i << 1;
|
||||
}
|
||||
|
||||
static int sprd_spi_write_bufs_u32(struct sprd_spi *ss, u32 len)
|
||||
{
|
||||
u32 *tx_p = (u32 *)ss->tx_buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
writel_relaxed(tx_p[i], ss->base + SPRD_SPI_TXD);
|
||||
|
||||
ss->tx_buf += i << 2;
|
||||
return i << 2;
|
||||
}
|
||||
|
||||
static int sprd_spi_read_bufs_u8(struct sprd_spi *ss, u32 len)
|
||||
{
|
||||
u8 *rx_p = (u8 *)ss->rx_buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
rx_p[i] = readb_relaxed(ss->base + SPRD_SPI_TXD);
|
||||
|
||||
ss->rx_buf += i;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int sprd_spi_read_bufs_u16(struct sprd_spi *ss, u32 len)
|
||||
{
|
||||
u16 *rx_p = (u16 *)ss->rx_buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
rx_p[i] = readw_relaxed(ss->base + SPRD_SPI_TXD);
|
||||
|
||||
ss->rx_buf += i << 1;
|
||||
return i << 1;
|
||||
}
|
||||
|
||||
static int sprd_spi_read_bufs_u32(struct sprd_spi *ss, u32 len)
|
||||
{
|
||||
u32 *rx_p = (u32 *)ss->rx_buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
rx_p[i] = readl_relaxed(ss->base + SPRD_SPI_TXD);
|
||||
|
||||
ss->rx_buf += i << 2;
|
||||
return i << 2;
|
||||
}
|
||||
|
||||
static int sprd_spi_txrx_bufs(struct spi_device *sdev, struct spi_transfer *t)
|
||||
{
|
||||
struct sprd_spi *ss = spi_controller_get_devdata(sdev->controller);
|
||||
u32 trans_len = ss->trans_len, len;
|
||||
int ret, write_size = 0;
|
||||
|
||||
while (trans_len) {
|
||||
len = trans_len > SPRD_SPI_FIFO_SIZE ? SPRD_SPI_FIFO_SIZE :
|
||||
trans_len;
|
||||
if (ss->trans_mode & SPRD_SPI_TX_MODE) {
|
||||
sprd_spi_set_tx_length(ss, len);
|
||||
write_size += ss->write_bufs(ss, len);
|
||||
|
||||
/*
|
||||
* For our 3 wires mode or dual TX line mode, we need
|
||||
* to request the controller to transfer.
|
||||
*/
|
||||
if (ss->hw_mode & SPI_3WIRE || ss->hw_mode & SPI_TX_DUAL)
|
||||
sprd_spi_tx_req(ss);
|
||||
|
||||
ret = sprd_spi_wait_for_tx_end(ss, t);
|
||||
} else {
|
||||
sprd_spi_set_rx_length(ss, len);
|
||||
|
||||
/*
|
||||
* For our 3 wires mode or dual TX line mode, we need
|
||||
* to request the controller to read.
|
||||
*/
|
||||
if (ss->hw_mode & SPI_3WIRE || ss->hw_mode & SPI_TX_DUAL)
|
||||
sprd_spi_rx_req(ss);
|
||||
else
|
||||
write_size += ss->write_bufs(ss, len);
|
||||
|
||||
ret = sprd_spi_wait_for_rx_end(ss, t);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto complete;
|
||||
|
||||
if (ss->trans_mode & SPRD_SPI_RX_MODE)
|
||||
ss->read_bufs(ss, len);
|
||||
|
||||
trans_len -= len;
|
||||
}
|
||||
|
||||
ret = write_size;
|
||||
|
||||
complete:
|
||||
sprd_spi_enter_idle(ss);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sprd_spi_set_speed(struct sprd_spi *ss, u32 speed_hz)
|
||||
{
|
||||
/*
|
||||
* From SPI datasheet, the prescale calculation formula:
|
||||
* prescale = SPI source clock / (2 * SPI_freq) - 1;
|
||||
*/
|
||||
u32 clk_div = DIV_ROUND_UP(ss->src_clk, speed_hz << 1) - 1;
|
||||
|
||||
/* Save the real hardware speed */
|
||||
ss->hw_speed_hz = (ss->src_clk >> 1) / (clk_div + 1);
|
||||
writel_relaxed(clk_div, ss->base + SPRD_SPI_CLKD);
|
||||
}
|
||||
|
||||
static void sprd_spi_init_hw(struct sprd_spi *ss, struct spi_transfer *t)
|
||||
{
|
||||
u16 word_delay, interval;
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(ss->base + SPRD_SPI_CTL7);
|
||||
val &= ~(SPRD_SPI_SCK_REV | SPRD_SPI_NG_TX | SPRD_SPI_NG_RX);
|
||||
/* Set default chip selection, clock phase and clock polarity */
|
||||
val |= ss->hw_mode & SPI_CPHA ? SPRD_SPI_NG_RX : SPRD_SPI_NG_TX;
|
||||
val |= ss->hw_mode & SPI_CPOL ? SPRD_SPI_SCK_REV : 0;
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL0);
|
||||
|
||||
/*
|
||||
* Set the intervals of two SPI frames, and the inteval calculation
|
||||
* formula as below per datasheet:
|
||||
* interval time (source clock cycles) = interval * 4 + 10.
|
||||
*/
|
||||
word_delay = clamp_t(u16, t->word_delay, SPRD_SPI_MIN_DELAY_CYCLE,
|
||||
SPRD_SPI_MAX_DELAY_CYCLE);
|
||||
interval = DIV_ROUND_UP(word_delay - 10, 4);
|
||||
ss->word_delay = interval * 4 + 10;
|
||||
writel_relaxed(interval, ss->base + SPRD_SPI_CTL5);
|
||||
|
||||
/* Reset SPI fifo */
|
||||
writel_relaxed(1, ss->base + SPRD_SPI_FIFO_RST);
|
||||
writel_relaxed(0, ss->base + SPRD_SPI_FIFO_RST);
|
||||
|
||||
/* Set SPI work mode */
|
||||
val = readl_relaxed(ss->base + SPRD_SPI_CTL7);
|
||||
val &= ~SPRD_SPI_MODE_MASK;
|
||||
|
||||
if (ss->hw_mode & SPI_3WIRE)
|
||||
val |= SPRD_SPI_3WIRE_MODE << SPRD_SPI_MODE_OFFSET;
|
||||
else
|
||||
val |= SPRD_SPI_4WIRE_MODE << SPRD_SPI_MODE_OFFSET;
|
||||
|
||||
if (ss->hw_mode & SPI_TX_DUAL)
|
||||
val |= SPRD_SPI_DATA_LINE2_EN;
|
||||
else
|
||||
val &= ~SPRD_SPI_DATA_LINE2_EN;
|
||||
|
||||
writel_relaxed(val, ss->base + SPRD_SPI_CTL7);
|
||||
}
|
||||
|
||||
static int sprd_spi_setup_transfer(struct spi_device *sdev,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct sprd_spi *ss = spi_controller_get_devdata(sdev->controller);
|
||||
u8 bits_per_word = t->bits_per_word;
|
||||
u32 val, mode = 0;
|
||||
|
||||
ss->len = t->len;
|
||||
ss->tx_buf = t->tx_buf;
|
||||
ss->rx_buf = t->rx_buf;
|
||||
|
||||
ss->hw_mode = sdev->mode;
|
||||
sprd_spi_init_hw(ss, t);
|
||||
|
||||
/* Set tansfer speed and valid bits */
|
||||
sprd_spi_set_speed(ss, t->speed_hz);
|
||||
sprd_spi_set_transfer_bits(ss, bits_per_word);
|
||||
|
||||
if (bits_per_word > 16)
|
||||
bits_per_word = round_up(bits_per_word, 16);
|
||||
else
|
||||
bits_per_word = round_up(bits_per_word, 8);
|
||||
|
||||
switch (bits_per_word) {
|
||||
case 8:
|
||||
ss->trans_len = t->len;
|
||||
ss->read_bufs = sprd_spi_read_bufs_u8;
|
||||
ss->write_bufs = sprd_spi_write_bufs_u8;
|
||||
break;
|
||||
case 16:
|
||||
ss->trans_len = t->len >> 1;
|
||||
ss->read_bufs = sprd_spi_read_bufs_u16;
|
||||
ss->write_bufs = sprd_spi_write_bufs_u16;
|
||||
break;
|
||||
case 32:
|
||||
ss->trans_len = t->len >> 2;
|
||||
ss->read_bufs = sprd_spi_read_bufs_u32;
|
||||
ss->write_bufs = sprd_spi_write_bufs_u32;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set transfer read or write mode */
|
||||
val = readl_relaxed(ss->base + SPRD_SPI_CTL1);
|
||||
val &= ~SPRD_SPI_RTX_MD_MASK;
|
||||
if (t->tx_buf)
|
||||
mode |= SPRD_SPI_TX_MODE;
|
||||
if (t->rx_buf)
|
||||
mode |= SPRD_SPI_RX_MODE;
|
||||
|
||||
writel_relaxed(val | mode, ss->base + SPRD_SPI_CTL1);
|
||||
|
||||
ss->trans_mode = mode;
|
||||
|
||||
/*
|
||||
* If in only receive mode, we need to trigger the SPI controller to
|
||||
* receive data automatically.
|
||||
*/
|
||||
if (ss->trans_mode == SPRD_SPI_RX_MODE)
|
||||
ss->write_bufs = sprd_spi_write_only_receive;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_spi_transfer_one(struct spi_controller *sctlr,
|
||||
struct spi_device *sdev,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sprd_spi_setup_transfer(sdev, t);
|
||||
if (ret)
|
||||
goto setup_err;
|
||||
|
||||
ret = sprd_spi_txrx_bufs(sdev, t);
|
||||
if (ret == t->len)
|
||||
ret = 0;
|
||||
else if (ret >= 0)
|
||||
ret = -EREMOTEIO;
|
||||
|
||||
setup_err:
|
||||
spi_finalize_current_transfer(sctlr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sprd_spi_clk_init(struct platform_device *pdev, struct sprd_spi *ss)
|
||||
{
|
||||
struct clk *clk_spi, *clk_parent;
|
||||
|
||||
clk_spi = devm_clk_get(&pdev->dev, "spi");
|
||||
if (IS_ERR(clk_spi)) {
|
||||
dev_warn(&pdev->dev, "can't get the spi clock\n");
|
||||
clk_spi = NULL;
|
||||
}
|
||||
|
||||
clk_parent = devm_clk_get(&pdev->dev, "source");
|
||||
if (IS_ERR(clk_parent)) {
|
||||
dev_warn(&pdev->dev, "can't get the source clock\n");
|
||||
clk_parent = NULL;
|
||||
}
|
||||
|
||||
ss->clk = devm_clk_get(&pdev->dev, "enable");
|
||||
if (IS_ERR(ss->clk)) {
|
||||
dev_err(&pdev->dev, "can't get the enable clock\n");
|
||||
return PTR_ERR(ss->clk);
|
||||
}
|
||||
|
||||
if (!clk_set_parent(clk_spi, clk_parent))
|
||||
ss->src_clk = clk_get_rate(clk_spi);
|
||||
else
|
||||
ss->src_clk = SPRD_SPI_DEFAULT_SOURCE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *sctlr;
|
||||
struct resource *res;
|
||||
struct sprd_spi *ss;
|
||||
int ret;
|
||||
|
||||
pdev->id = of_alias_get_id(pdev->dev.of_node, "spi");
|
||||
sctlr = spi_alloc_master(&pdev->dev, sizeof(*ss));
|
||||
if (!sctlr)
|
||||
return -ENOMEM;
|
||||
|
||||
ss = spi_controller_get_devdata(sctlr);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ss->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ss->base)) {
|
||||
ret = PTR_ERR(ss->base);
|
||||
goto free_controller;
|
||||
}
|
||||
|
||||
ss->dev = &pdev->dev;
|
||||
sctlr->dev.of_node = pdev->dev.of_node;
|
||||
sctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_3WIRE | SPI_TX_DUAL;
|
||||
sctlr->bus_num = pdev->id;
|
||||
sctlr->set_cs = sprd_spi_chipselect;
|
||||
sctlr->transfer_one = sprd_spi_transfer_one;
|
||||
sctlr->auto_runtime_pm = true;
|
||||
sctlr->max_speed_hz = min_t(u32, ss->src_clk >> 1,
|
||||
SPRD_SPI_MAX_SPEED_HZ);
|
||||
|
||||
platform_set_drvdata(pdev, sctlr);
|
||||
ret = sprd_spi_clk_init(pdev, ss);
|
||||
if (ret)
|
||||
goto free_controller;
|
||||
|
||||
ret = clk_prepare_enable(ss->clk);
|
||||
if (ret)
|
||||
goto free_controller;
|
||||
|
||||
ret = pm_runtime_set_active(&pdev->dev);
|
||||
if (ret < 0)
|
||||
goto disable_clk;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev,
|
||||
SPRD_SPI_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to resume SPI controller\n");
|
||||
goto err_rpm_put;
|
||||
}
|
||||
|
||||
ret = devm_spi_register_controller(&pdev->dev, sctlr);
|
||||
if (ret)
|
||||
goto err_rpm_put;
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_rpm_put:
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
disable_clk:
|
||||
clk_disable_unprepare(ss->clk);
|
||||
free_controller:
|
||||
spi_controller_put(sctlr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sprd_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *sctlr = platform_get_drvdata(pdev);
|
||||
struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(ss->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(ss->dev, "failed to resume SPI controller\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(ss->clk);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused sprd_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *sctlr = dev_get_drvdata(dev);
|
||||
struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
|
||||
|
||||
clk_disable_unprepare(ss->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused sprd_spi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *sctlr = dev_get_drvdata(dev);
|
||||
struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(ss->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sprd_spi_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(sprd_spi_runtime_suspend,
|
||||
sprd_spi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id sprd_spi_of_match[] = {
|
||||
{ .compatible = "sprd,sc9860-spi", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver sprd_spi_driver = {
|
||||
.driver = {
|
||||
.name = "sprd-spi",
|
||||
.of_match_table = sprd_spi_of_match,
|
||||
.pm = &sprd_spi_pm_ops,
|
||||
},
|
||||
.probe = sprd_spi_probe,
|
||||
.remove = sprd_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(sprd_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Spreadtrum SPI Controller driver");
|
||||
MODULE_AUTHOR("Lanqing Liu <lanqing.liu@spreadtrum.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,512 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
||||
* Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics.
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
#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 <ludovic.barre@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 quad spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1,18 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* SPI init/core code
|
||||
*
|
||||
* Copyright (C) 2005 David Brownell
|
||||
* Copyright (C) 2008 Secret Lab Technologies Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
@ -60,6 +51,7 @@ static void spidev_release(struct device *dev)
|
|||
spi->controller->cleanup(spi);
|
||||
|
||||
spi_controller_put(spi->controller);
|
||||
kfree(spi->driver_override);
|
||||
kfree(spi);
|
||||
}
|
||||
|
||||
|
@ -77,6 +69,51 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf)
|
|||
}
|
||||
static DEVICE_ATTR_RO(modalias);
|
||||
|
||||
static ssize_t driver_override_store(struct device *dev,
|
||||
struct device_attribute *a,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
const char *end = memchr(buf, '\n', count);
|
||||
const size_t len = end ? end - buf : count;
|
||||
const char *driver_override, *old;
|
||||
|
||||
/* We need to keep extra room for a newline when displaying value */
|
||||
if (len >= (PAGE_SIZE - 1))
|
||||
return -EINVAL;
|
||||
|
||||
driver_override = kstrndup(buf, len, GFP_KERNEL);
|
||||
if (!driver_override)
|
||||
return -ENOMEM;
|
||||
|
||||
device_lock(dev);
|
||||
old = spi->driver_override;
|
||||
if (len) {
|
||||
spi->driver_override = driver_override;
|
||||
} else {
|
||||
/* Emptry string, disable driver override */
|
||||
spi->driver_override = NULL;
|
||||
kfree(driver_override);
|
||||
}
|
||||
device_unlock(dev);
|
||||
kfree(old);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t driver_override_show(struct device *dev,
|
||||
struct device_attribute *a, char *buf)
|
||||
{
|
||||
const struct spi_device *spi = to_spi_device(dev);
|
||||
ssize_t len;
|
||||
|
||||
device_lock(dev);
|
||||
len = snprintf(buf, PAGE_SIZE, "%s\n", spi->driver_override ? : "");
|
||||
device_unlock(dev);
|
||||
return len;
|
||||
}
|
||||
static DEVICE_ATTR_RW(driver_override);
|
||||
|
||||
#define SPI_STATISTICS_ATTRS(field, file) \
|
||||
static ssize_t spi_controller_##field##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
|
@ -158,6 +195,7 @@ SPI_STATISTICS_SHOW(transfers_split_maxsize, "%lu");
|
|||
|
||||
static struct attribute *spi_dev_attrs[] = {
|
||||
&dev_attr_modalias.attr,
|
||||
&dev_attr_driver_override.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -305,6 +343,10 @@ static int spi_match_device(struct device *dev, struct device_driver *drv)
|
|||
const struct spi_device *spi = to_spi_device(dev);
|
||||
const struct spi_driver *sdrv = to_spi_driver(drv);
|
||||
|
||||
/* Check override first, and if set, only use the named driver */
|
||||
if (spi->driver_override)
|
||||
return strcmp(spi->driver_override, drv->name) == 0;
|
||||
|
||||
/* Attempt an OF style match */
|
||||
if (of_driver_match_device(dev, drv))
|
||||
return 1;
|
||||
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue