spi: Updates for v3.15
A busy release for both cleanups and new drivers this time along with further factoring out of replicated code into the core: - Provide support in the core for DMA mapping transfers - essentially all drivers weren't implementing this properly, now there's no excuse. - Dual and quad mode support for spidev. - Fix handling of cs_change in the generic implementation. - Remove the S3C_DMA code from the s3c64xx driver now that all the platforms using it have been converted to dmaengine. - Lots of improvements to the Renesas SPI controllers. - Drivers for Allwinner A10 and A31, Qualcomm QUP and Xylinx xtfpga. - Removal of the bitrotted ti-ssp driver. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTOUttAAoJELSic+t+oim9NqMQAI3zR6K5e7o/GJhxWMOBDkdo hN0ZSt6NEYGHQqXZ0kR0Azj7I3PO/Bj0US2gYx1myfggV/++rZwgGI97fsF9dBoN IbOm+xBx5nmMkr0/oxZD/ETbM74iVm7xM274OKHLD1spiozqwGNV4voEvt/JVQ8W ux+8SFa8HC0GoDqm6Ha51RstiBGFpuvKYUpeRmSEvsRl9DpyHhnQeZg/gwiQm/ON n74twLTXTIBO7oVxTpffsZgRPfvZtIcNmwwL+gyYBrjb5DZ1+qCTPGYx0/R18wfr NrXIYv8Ka6qbDiwWhmf6m5qrNsYNq95t2J6ulxor1LVcAIZODBpcEfdQ51fwYd3f +xZuCTzPRZ2w8Mha2II6LvOjk0640MBa/nQnOEup8dTWX0jARPKnKcjb84tuhlna ZGolHABLUhzZIAQjkZjbVSw0HL6lAQ/mk8sZevcCmB2zCln+HneeFCVCEWjy+i2I zejhFJMT3/MqLqZNav+MzkkYB1aW3q/RQz7OwzwPK7Rlw0IRwd64c0D9r9Qj2/Gl uWVOnLmgDwKgeq7464kA4ELPTW0i9gG7SIQe18xzkL2SY9wzQN0S1ERx3aDCCAw2 lyuh/Cdz7nnCyDBmuqNUfvp8XAX1d0d12vTbHcW1VHMw68fE4OBVNxn4jUXHZt5O OmOoox/sn/1I+Ce30C70 =qoZY -----END PGP SIGNATURE----- Merge tag 'spi-v3.15' into spi-linus spi: Updates for v3.15 A busy release for both cleanups and new drivers this time along with further factoring out of replicated code into the core: - Provide support in the core for DMA mapping transfers - essentially all drivers weren't implementing this properly, now there's no excuse. - Dual and quad mode support for spidev. - Fix handling of cs_change in the generic implementation. - Remove the S3C_DMA code from the s3c64xx driver now that all the platforms using it have been converted to dmaengine. - Lots of improvements to the Renesas SPI controllers. - Drivers for Allwinner A10 and A31, Qualcomm QUP and Xylinx xtfpga. - Removal of the bitrotted ti-ssp driver. # gpg: Signature made Mon 31 Mar 2014 12:03:09 BST using RSA key ID 7EA229BD # gpg: Good signature from "Mark Brown <broonie@sirena.org.uk>" # gpg: aka "Mark Brown <broonie@debian.org>" # gpg: aka "Mark Brown <broonie@kernel.org>" # gpg: aka "Mark Brown <broonie@tardis.ed.ac.uk>" # gpg: aka "Mark Brown <broonie@linaro.org>" # gpg: aka "Mark Brown <Mark.Brown@linaro.org>"
This commit is contained in:
commit
09c0998efc
|
@ -3,24 +3,24 @@
|
||||||
Required properties:
|
Required properties:
|
||||||
- #address-cells: see spi-bus.txt
|
- #address-cells: see spi-bus.txt
|
||||||
- #size-cells: see spi-bus.txt
|
- #size-cells: see spi-bus.txt
|
||||||
- compatible: should be "efm32,spi"
|
- compatible: should be "energymicro,efm32-spi"
|
||||||
- reg: Offset and length of the register set for the controller
|
- reg: Offset and length of the register set for the controller
|
||||||
- interrupts: pair specifying rx and tx irq
|
- interrupts: pair specifying rx and tx irq
|
||||||
- clocks: phandle to the spi clock
|
- clocks: phandle to the spi clock
|
||||||
- cs-gpios: see spi-bus.txt
|
- cs-gpios: see spi-bus.txt
|
||||||
- location: Value to write to the ROUTE register's LOCATION bitfield to configure the pinmux for the device, see datasheet for values.
|
- efm32,location: Value to write to the ROUTE register's LOCATION bitfield to configure the pinmux for the device, see datasheet for values.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
spi1: spi@0x4000c400 { /* USART1 */
|
spi1: spi@0x4000c400 { /* USART1 */
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
compatible = "efm32,spi";
|
compatible = "energymicro,efm32-spi";
|
||||||
reg = <0x4000c400 0x400>;
|
reg = <0x4000c400 0x400>;
|
||||||
interrupts = <15 16>;
|
interrupts = <15 16>;
|
||||||
clocks = <&cmu 20>;
|
clocks = <&cmu 20>;
|
||||||
cs-gpios = <&gpio 51 1>; // D3
|
cs-gpios = <&gpio 51 1>; // D3
|
||||||
location = <1>;
|
efm32,location = <1>;
|
||||||
status = "ok";
|
status = "ok";
|
||||||
|
|
||||||
ks8851@0 {
|
ks8851@0 {
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI)
|
||||||
|
|
||||||
|
The QUP core is an 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: Should contain "qcom,spi-qup-v2.1.1" or "qcom,spi-qup-v2.2.1"
|
||||||
|
- reg: Should contain base register location and length
|
||||||
|
- interrupts: Interrupt number used by this controller
|
||||||
|
|
||||||
|
- clocks: Should contain the core clock and the AHB clock.
|
||||||
|
- clock-names: Should be "core" for the core clock and "iface" for the
|
||||||
|
AHB clock.
|
||||||
|
|
||||||
|
- #address-cells: Number of cells required to define a chip select
|
||||||
|
address on the SPI bus. Should be set to 1.
|
||||||
|
- #size-cells: Should be zero.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- spi-max-frequency: Specifies maximum SPI clock frequency,
|
||||||
|
Units - Hz. Definition as per
|
||||||
|
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
spi_8: spi@f9964000 { /* BLSP2 QUP2 */
|
||||||
|
|
||||||
|
compatible = "qcom,spi-qup-v2";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0xf9964000 0x1000>;
|
||||||
|
interrupts = <0 102 0>;
|
||||||
|
spi-max-frequency = <19200000>;
|
||||||
|
|
||||||
|
clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>;
|
||||||
|
clock-names = "core", "iface";
|
||||||
|
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&spi8_default>;
|
||||||
|
|
||||||
|
device@0 {
|
||||||
|
compatible = "arm,pl022-dummy";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
reg = <0>; /* Chip select 0 */
|
||||||
|
spi-max-frequency = <19200000>;
|
||||||
|
spi-cpol;
|
||||||
|
};
|
||||||
|
|
||||||
|
device@1 {
|
||||||
|
compatible = "arm,pl022-dummy";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
reg = <1>; /* Chip select 1 */
|
||||||
|
spi-max-frequency = <9600000>;
|
||||||
|
spi-cpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
device@2 {
|
||||||
|
compatible = "arm,pl022-dummy";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
reg = <2>; /* Chip select 2 */
|
||||||
|
spi-max-frequency = <19200000>;
|
||||||
|
spi-cpol;
|
||||||
|
spi-cpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
device@3 {
|
||||||
|
compatible = "arm,pl022-dummy";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
reg = <3>; /* Chip select 3 */
|
||||||
|
spi-max-frequency = <19200000>;
|
||||||
|
spi-cpol;
|
||||||
|
spi-cpha;
|
||||||
|
spi-cs-high;
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,7 +1,29 @@
|
||||||
Renesas HSPI.
|
Renesas HSPI.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : "renesas,hspi"
|
- compatible : "renesas,hspi-<soctype>", "renesas,hspi" as fallback.
|
||||||
- reg : Offset and length of the register set for the device
|
Examples with soctypes are:
|
||||||
- interrupts : interrupt line used by HSPI
|
- "renesas,hspi-r8a7778" (R-Car M1)
|
||||||
|
- "renesas,hspi-r8a7779" (R-Car H1)
|
||||||
|
- reg : Offset and length of the register set for the device
|
||||||
|
- interrupt-parent : The phandle for the interrupt controller that
|
||||||
|
services interrupts for this device
|
||||||
|
- interrupts : Interrupt specifier
|
||||||
|
- #address-cells : Must be <1>
|
||||||
|
- #size-cells : Must be <0>
|
||||||
|
|
||||||
|
Pinctrl properties might be needed, too. See
|
||||||
|
Documentation/devicetree/bindings/pinctrl/renesas,*.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
hspi0: spi@fffc7000 {
|
||||||
|
compatible = "renesas,hspi-r8a7778", "renesas,hspi";
|
||||||
|
reg = <0xfffc7000 0x18>;
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
|
interrupts = <0 63 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,40 @@
|
||||||
Renesas MSIOF spi controller
|
Renesas MSIOF spi controller
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : "renesas,sh-msiof" for SuperH or
|
- compatible : "renesas,msiof-<soctype>" for SoCs,
|
||||||
"renesas,sh-mobile-msiof" for SH Mobile series
|
"renesas,sh-msiof" for SuperH, or
|
||||||
- reg : Offset and length of the register set for the device
|
"renesas,sh-mobile-msiof" for SH Mobile series.
|
||||||
- interrupts : interrupt line used by MSIOF
|
Examples with soctypes are:
|
||||||
|
"renesas,msiof-r8a7790" (R-Car H2)
|
||||||
|
"renesas,msiof-r8a7791" (R-Car M2)
|
||||||
|
- reg : Offset and length of the register set for the device
|
||||||
|
- interrupt-parent : The phandle for the interrupt controller that
|
||||||
|
services interrupts for this device
|
||||||
|
- interrupts : Interrupt specifier
|
||||||
|
- #address-cells : Must be <1>
|
||||||
|
- #size-cells : Must be <0>
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- num-cs : total number of chip-selects
|
- clocks : Must contain a reference to the functional clock.
|
||||||
- renesas,tx-fifo-size : Overrides the default tx fifo size given in words
|
- num-cs : Total number of chip-selects (default is 1)
|
||||||
- renesas,rx-fifo-size : Overrides the default rx fifo size given in words
|
|
||||||
|
Optional properties, deprecated for soctype-specific bindings:
|
||||||
|
- renesas,tx-fifo-size : Overrides the default tx fifo size given in words
|
||||||
|
(default is 64)
|
||||||
|
- renesas,rx-fifo-size : Overrides the default rx fifo size given in words
|
||||||
|
(default is 64, or 256 on R-Car H2 and M2)
|
||||||
|
|
||||||
|
Pinctrl properties might be needed, too. See
|
||||||
|
Documentation/devicetree/bindings/pinctrl/renesas,*.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
msiof0: spi@e6e20000 {
|
||||||
|
compatible = "renesas,msiof-r8a7791";
|
||||||
|
reg = <0 0xe6e20000 0 0x0064>;
|
||||||
|
interrupts = <0 156 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&mstp0_clks R8A7791_CLK_MSIOF0>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
|
@ -10,6 +10,7 @@ Required properties:
|
||||||
- pinctrl-names: must contain a "default" entry.
|
- pinctrl-names: must contain a "default" entry.
|
||||||
- spi-num-chipselects : the number of the chipselect signals.
|
- spi-num-chipselects : the number of the chipselect signals.
|
||||||
- bus-num : the slave chip chipselect signal number.
|
- bus-num : the slave chip chipselect signal number.
|
||||||
|
- big-endian : if DSPI modudle is big endian, the bool will be set in node.
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
dspi0@4002c000 {
|
dspi0@4002c000 {
|
||||||
|
@ -24,6 +25,7 @@ dspi0@4002c000 {
|
||||||
bus-num = <0>;
|
bus-num = <0>;
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
pinctrl-0 = <&pinctrl_dspi0_1>;
|
pinctrl-0 = <&pinctrl_dspi0_1>;
|
||||||
|
big-endian;
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
|
||||||
sflash: at26df081a@0 {
|
sflash: at26df081a@0 {
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
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:
|
||||||
|
"renesas,rspi-<soctype>", "renesas,rspi-rz" as fallback.
|
||||||
|
For Quad Serial Peripheral Interface on R-Car Gen2:
|
||||||
|
"renesas,qspi-<soctype>", "renesas,qspi" as fallback.
|
||||||
|
Examples with soctypes are:
|
||||||
|
- "renesas,rspi-sh7757" (SH)
|
||||||
|
- "renesas,rspi-r7s72100" (RZ/A1H)
|
||||||
|
- "renesas,qspi-r8a7790" (R-Car H2)
|
||||||
|
- "renesas,qspi-r8a7791" (R-Car M2)
|
||||||
|
- reg : Address start and address range size of the device
|
||||||
|
- interrupts : A list of interrupt-specifiers, one for each entry in
|
||||||
|
interrupt-names.
|
||||||
|
If interrupt-names is not present, an interrupt specifier
|
||||||
|
for a single muxed interrupt.
|
||||||
|
- interrupt-names : A list of interrupt names. Should contain (if present):
|
||||||
|
- "error" for SPEI,
|
||||||
|
- "rx" for SPRI,
|
||||||
|
- "tx" to SPTI,
|
||||||
|
- "mux" for a single muxed interrupt.
|
||||||
|
- interrupt-parent : The phandle for the interrupt controller that
|
||||||
|
services interrupts for this device.
|
||||||
|
- num-cs : Number of chip selects. Some RSPI cores have more than 1.
|
||||||
|
- #address-cells : Must be <1>
|
||||||
|
- #size-cells : Must be <0>
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- clocks : Must contain a reference to the functional clock.
|
||||||
|
|
||||||
|
Pinctrl properties might be needed, too. See
|
||||||
|
Documentation/devicetree/bindings/pinctrl/renesas,*.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
spi0: spi@e800c800 {
|
||||||
|
compatible = "renesas,rspi-r7s72100", "renesas,rspi-rz";
|
||||||
|
reg = <0xe800c800 0x24>;
|
||||||
|
interrupts = <0 238 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<0 239 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<0 240 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
interrupt-names = "error", "rx", "tx";
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
|
num-cs = <1>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
spi: spi@e6b10000 {
|
||||||
|
compatible = "renesas,qspi-r8a7791", "renesas,qspi";
|
||||||
|
reg = <0 0xe6b10000 0 0x2c>;
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
|
interrupts = <0 184 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&mstp9_clks R8A7791_CLK_QSPI_MOD>;
|
||||||
|
num-cs = <1>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
Allwinner A10 SPI controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Should be "allwinner,sun4-a10-spi".
|
||||||
|
- reg: Should contain register location and length.
|
||||||
|
- interrupts: Should contain interrupt.
|
||||||
|
- clocks: phandle to the clocks feeding the SPI controller. Two are
|
||||||
|
needed:
|
||||||
|
- "ahb": the gated AHB parent clock
|
||||||
|
- "mod": the parent module clock
|
||||||
|
- clock-names: Must contain the clock names described just above
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
spi1: spi@01c06000 {
|
||||||
|
compatible = "allwinner,sun4i-a10-spi";
|
||||||
|
reg = <0x01c06000 0x1000>;
|
||||||
|
interrupts = <11>;
|
||||||
|
clocks = <&ahb_gates 21>, <&spi1_clk>;
|
||||||
|
clock-names = "ahb", "mod";
|
||||||
|
status = "disabled";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
Allwinner A31 SPI controller
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: Should be "allwinner,sun6i-a31-spi".
|
||||||
|
- reg: Should contain register location and length.
|
||||||
|
- interrupts: Should contain interrupt.
|
||||||
|
- clocks: phandle to the clocks feeding the SPI controller. Two are
|
||||||
|
needed:
|
||||||
|
- "ahb": the gated AHB parent clock
|
||||||
|
- "mod": the parent module clock
|
||||||
|
- clock-names: Must contain the clock names described just above
|
||||||
|
- resets: phandle to the reset controller asserting this device in
|
||||||
|
reset
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
spi1: spi@01c69000 {
|
||||||
|
compatible = "allwinner,sun6i-a31-spi";
|
||||||
|
reg = <0x01c69000 0x1000>;
|
||||||
|
interrupts = <0 66 4>;
|
||||||
|
clocks = <&ahb1_gates 21>, <&spi1_clk>;
|
||||||
|
clock-names = "ahb", "mod";
|
||||||
|
resets = <&ahb1_rst 21>;
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
Cadence Xtensa XTFPGA platform SPI controller.
|
||||||
|
|
||||||
|
This simple SPI master controller is built into xtfpga bitstreams and is used
|
||||||
|
to control daughterboard audio codec.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be "cdns,xtfpga-spi".
|
||||||
|
- reg: physical base address of the controller and length of memory mapped
|
||||||
|
region.
|
|
@ -85,6 +85,12 @@ settings for data transfer parameters:
|
||||||
SPI_MODE_0..SPI_MODE_3; or if you prefer you can combine SPI_CPOL
|
SPI_MODE_0..SPI_MODE_3; or if you prefer you can combine SPI_CPOL
|
||||||
(clock polarity, idle high iff this is set) or SPI_CPHA (clock phase,
|
(clock polarity, idle high iff this is set) or SPI_CPHA (clock phase,
|
||||||
sample on trailing edge iff this is set) flags.
|
sample on trailing edge iff this is set) flags.
|
||||||
|
Note that this request is limited to SPI mode flags that fit in a
|
||||||
|
single byte.
|
||||||
|
|
||||||
|
SPI_IOC_RD_MODE32, SPI_IOC_WR_MODE32 ... pass a pointer to a uin32_t
|
||||||
|
which will return (RD) or assign (WR) the full SPI transfer mode,
|
||||||
|
not limited to the bits that fit in one byte.
|
||||||
|
|
||||||
SPI_IOC_RD_LSB_FIRST, SPI_IOC_WR_LSB_FIRST ... pass a pointer to a byte
|
SPI_IOC_RD_LSB_FIRST, SPI_IOC_WR_LSB_FIRST ... pass a pointer to a byte
|
||||||
which will return (RD) or assign (WR) the bit justification used to
|
which will return (RD) or assign (WR) the bit justification used to
|
||||||
|
|
|
@ -78,10 +78,10 @@ static void do_msg(int fd, int len)
|
||||||
|
|
||||||
static void dumpstat(const char *name, int fd)
|
static void dumpstat(const char *name, int fd)
|
||||||
{
|
{
|
||||||
__u8 mode, lsb, bits;
|
__u8 lsb, bits;
|
||||||
__u32 speed;
|
__u32 mode, speed;
|
||||||
|
|
||||||
if (ioctl(fd, SPI_IOC_RD_MODE, &mode) < 0) {
|
if (ioctl(fd, SPI_IOC_RD_MODE32, &mode) < 0) {
|
||||||
perror("SPI rd_mode");
|
perror("SPI rd_mode");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ static void dumpstat(const char *name, int fd)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s: spi mode %d, %d bits %sper word, %d Hz max\n",
|
printf("%s: spi mode 0x%x, %d bits %sper word, %d Hz max\n",
|
||||||
name, mode, bits, lsb ? "(lsb first) " : "", speed);
|
name, mode, bits, lsb ? "(lsb first) " : "", speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ static void pabort(const char *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *device = "/dev/spidev1.1";
|
static const char *device = "/dev/spidev1.1";
|
||||||
static uint8_t mode;
|
static uint32_t mode;
|
||||||
static uint8_t bits = 8;
|
static uint8_t bits = 8;
|
||||||
static uint32_t speed = 500000;
|
static uint32_t speed = 500000;
|
||||||
static uint16_t delay;
|
static uint16_t delay;
|
||||||
|
@ -57,6 +57,21 @@ static void transfer(int fd)
|
||||||
.bits_per_word = bits,
|
.bits_per_word = bits,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (mode & SPI_TX_QUAD)
|
||||||
|
tr.tx_nbits = 4;
|
||||||
|
else if (mode & SPI_TX_DUAL)
|
||||||
|
tr.tx_nbits = 2;
|
||||||
|
if (mode & SPI_RX_QUAD)
|
||||||
|
tr.rx_nbits = 4;
|
||||||
|
else if (mode & SPI_RX_DUAL)
|
||||||
|
tr.rx_nbits = 2;
|
||||||
|
if (!(mode & SPI_LOOP)) {
|
||||||
|
if (mode & (SPI_TX_QUAD | SPI_TX_DUAL))
|
||||||
|
tr.rx_buf = 0;
|
||||||
|
else if (mode & (SPI_RX_QUAD | SPI_RX_DUAL))
|
||||||
|
tr.tx_buf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
|
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
|
||||||
if (ret < 1)
|
if (ret < 1)
|
||||||
pabort("can't send spi message");
|
pabort("can't send spi message");
|
||||||
|
@ -81,7 +96,11 @@ static void print_usage(const char *prog)
|
||||||
" -O --cpol clock polarity\n"
|
" -O --cpol clock polarity\n"
|
||||||
" -L --lsb least significant bit first\n"
|
" -L --lsb least significant bit first\n"
|
||||||
" -C --cs-high chip select active high\n"
|
" -C --cs-high chip select active high\n"
|
||||||
" -3 --3wire SI/SO signals shared\n");
|
" -3 --3wire SI/SO signals shared\n"
|
||||||
|
" -N --no-cs no chip select\n"
|
||||||
|
" -R --ready slave pulls low to pause\n"
|
||||||
|
" -2 --dual dual transfer\n"
|
||||||
|
" -4 --quad quad transfer\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,11 +120,13 @@ static void parse_opts(int argc, char *argv[])
|
||||||
{ "3wire", 0, 0, '3' },
|
{ "3wire", 0, 0, '3' },
|
||||||
{ "no-cs", 0, 0, 'N' },
|
{ "no-cs", 0, 0, 'N' },
|
||||||
{ "ready", 0, 0, 'R' },
|
{ "ready", 0, 0, 'R' },
|
||||||
|
{ "dual", 0, 0, '2' },
|
||||||
|
{ "quad", 0, 0, '4' },
|
||||||
{ NULL, 0, 0, 0 },
|
{ NULL, 0, 0, 0 },
|
||||||
};
|
};
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);
|
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24", lopts, NULL);
|
||||||
|
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
@ -147,11 +168,23 @@ static void parse_opts(int argc, char *argv[])
|
||||||
case 'R':
|
case 'R':
|
||||||
mode |= SPI_READY;
|
mode |= SPI_READY;
|
||||||
break;
|
break;
|
||||||
|
case '2':
|
||||||
|
mode |= SPI_TX_DUAL;
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
mode |= SPI_TX_QUAD;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mode & SPI_LOOP) {
|
||||||
|
if (mode & SPI_TX_DUAL)
|
||||||
|
mode |= SPI_RX_DUAL;
|
||||||
|
if (mode & SPI_TX_QUAD)
|
||||||
|
mode |= SPI_RX_QUAD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
|
@ -168,11 +201,11 @@ int main(int argc, char *argv[])
|
||||||
/*
|
/*
|
||||||
* spi mode
|
* spi mode
|
||||||
*/
|
*/
|
||||||
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
|
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
pabort("can't set spi mode");
|
pabort("can't set spi mode");
|
||||||
|
|
||||||
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
|
ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
pabort("can't get spi mode");
|
pabort("can't get spi mode");
|
||||||
|
|
||||||
|
@ -198,7 +231,7 @@ int main(int argc, char *argv[])
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
pabort("can't get max speed hz");
|
pabort("can't get max speed hz");
|
||||||
|
|
||||||
printf("spi mode: %d\n", mode);
|
printf("spi mode: 0x%x\n", mode);
|
||||||
printf("bits per word: %d\n", bits);
|
printf("bits per word: %d\n", bits);
|
||||||
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
|
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
|
||||||
|
|
||||||
|
|
|
@ -9783,6 +9783,12 @@ L: linux-serial@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/tty/serial/uartlite.c
|
F: drivers/tty/serial/uartlite.c
|
||||||
|
|
||||||
|
XTENSA XTFPGA PLATFORM SUPPORT
|
||||||
|
M: Max Filippov <jcmvbkbc@gmail.com>
|
||||||
|
L: linux-xtensa@linux-xtensa.org
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/spi/spi-xtensa-xtfpga.c
|
||||||
|
|
||||||
YAM DRIVER FOR AX.25
|
YAM DRIVER FOR AX.25
|
||||||
M: Jean-Paul Roubelat <jpr@f6fbb.org>
|
M: Jean-Paul Roubelat <jpr@f6fbb.org>
|
||||||
L: linux-hams@vger.kernel.org
|
L: linux-hams@vger.kernel.org
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o
|
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o
|
||||||
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
|
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
|
||||||
obj-$(CONFIG_PM_RUNTIME) += runtime.o
|
|
||||||
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
|
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
|
||||||
obj-$(CONFIG_PM_OPP) += opp.o
|
obj-$(CONFIG_PM_OPP) += opp.o
|
||||||
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
|
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
|
||||||
|
|
|
@ -13,6 +13,43 @@
|
||||||
#include <trace/events/rpm.h>
|
#include <trace/events/rpm.h>
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
|
|
||||||
|
#define RPM_GET_CALLBACK(dev, cb) \
|
||||||
|
({ \
|
||||||
|
int (*__rpm_cb)(struct device *__d); \
|
||||||
|
\
|
||||||
|
if (dev->pm_domain) \
|
||||||
|
__rpm_cb = dev->pm_domain->ops.cb; \
|
||||||
|
else if (dev->type && dev->type->pm) \
|
||||||
|
__rpm_cb = dev->type->pm->cb; \
|
||||||
|
else if (dev->class && dev->class->pm) \
|
||||||
|
__rpm_cb = dev->class->pm->cb; \
|
||||||
|
else if (dev->bus && dev->bus->pm) \
|
||||||
|
__rpm_cb = dev->bus->pm->cb; \
|
||||||
|
else \
|
||||||
|
__rpm_cb = NULL; \
|
||||||
|
\
|
||||||
|
if (!__rpm_cb && dev->driver && dev->driver->pm) \
|
||||||
|
__rpm_cb = dev->driver->pm->cb; \
|
||||||
|
\
|
||||||
|
__rpm_cb; \
|
||||||
|
})
|
||||||
|
|
||||||
|
static int (*rpm_get_suspend_cb(struct device *dev))(struct device *)
|
||||||
|
{
|
||||||
|
return RPM_GET_CALLBACK(dev, runtime_suspend);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int (*rpm_get_resume_cb(struct device *dev))(struct device *)
|
||||||
|
{
|
||||||
|
return RPM_GET_CALLBACK(dev, runtime_resume);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static int (*rpm_get_idle_cb(struct device *dev))(struct device *)
|
||||||
|
{
|
||||||
|
return RPM_GET_CALLBACK(dev, runtime_idle);
|
||||||
|
}
|
||||||
|
|
||||||
static int rpm_resume(struct device *dev, int rpmflags);
|
static int rpm_resume(struct device *dev, int rpmflags);
|
||||||
static int rpm_suspend(struct device *dev, int rpmflags);
|
static int rpm_suspend(struct device *dev, int rpmflags);
|
||||||
|
|
||||||
|
@ -310,19 +347,7 @@ static int rpm_idle(struct device *dev, int rpmflags)
|
||||||
|
|
||||||
dev->power.idle_notification = true;
|
dev->power.idle_notification = true;
|
||||||
|
|
||||||
if (dev->pm_domain)
|
callback = rpm_get_idle_cb(dev);
|
||||||
callback = dev->pm_domain->ops.runtime_idle;
|
|
||||||
else if (dev->type && dev->type->pm)
|
|
||||||
callback = dev->type->pm->runtime_idle;
|
|
||||||
else if (dev->class && dev->class->pm)
|
|
||||||
callback = dev->class->pm->runtime_idle;
|
|
||||||
else if (dev->bus && dev->bus->pm)
|
|
||||||
callback = dev->bus->pm->runtime_idle;
|
|
||||||
else
|
|
||||||
callback = NULL;
|
|
||||||
|
|
||||||
if (!callback && dev->driver && dev->driver->pm)
|
|
||||||
callback = dev->driver->pm->runtime_idle;
|
|
||||||
|
|
||||||
if (callback)
|
if (callback)
|
||||||
retval = __rpm_callback(callback, dev);
|
retval = __rpm_callback(callback, dev);
|
||||||
|
@ -492,19 +517,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||||
|
|
||||||
__update_runtime_status(dev, RPM_SUSPENDING);
|
__update_runtime_status(dev, RPM_SUSPENDING);
|
||||||
|
|
||||||
if (dev->pm_domain)
|
callback = rpm_get_suspend_cb(dev);
|
||||||
callback = dev->pm_domain->ops.runtime_suspend;
|
|
||||||
else if (dev->type && dev->type->pm)
|
|
||||||
callback = dev->type->pm->runtime_suspend;
|
|
||||||
else if (dev->class && dev->class->pm)
|
|
||||||
callback = dev->class->pm->runtime_suspend;
|
|
||||||
else if (dev->bus && dev->bus->pm)
|
|
||||||
callback = dev->bus->pm->runtime_suspend;
|
|
||||||
else
|
|
||||||
callback = NULL;
|
|
||||||
|
|
||||||
if (!callback && dev->driver && dev->driver->pm)
|
|
||||||
callback = dev->driver->pm->runtime_suspend;
|
|
||||||
|
|
||||||
retval = rpm_callback(callback, dev);
|
retval = rpm_callback(callback, dev);
|
||||||
if (retval)
|
if (retval)
|
||||||
|
@ -724,19 +737,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
|
||||||
|
|
||||||
__update_runtime_status(dev, RPM_RESUMING);
|
__update_runtime_status(dev, RPM_RESUMING);
|
||||||
|
|
||||||
if (dev->pm_domain)
|
callback = rpm_get_resume_cb(dev);
|
||||||
callback = dev->pm_domain->ops.runtime_resume;
|
|
||||||
else if (dev->type && dev->type->pm)
|
|
||||||
callback = dev->type->pm->runtime_resume;
|
|
||||||
else if (dev->class && dev->class->pm)
|
|
||||||
callback = dev->class->pm->runtime_resume;
|
|
||||||
else if (dev->bus && dev->bus->pm)
|
|
||||||
callback = dev->bus->pm->runtime_resume;
|
|
||||||
else
|
|
||||||
callback = NULL;
|
|
||||||
|
|
||||||
if (!callback && dev->driver && dev->driver->pm)
|
|
||||||
callback = dev->driver->pm->runtime_resume;
|
|
||||||
|
|
||||||
retval = rpm_callback(callback, dev);
|
retval = rpm_callback(callback, dev);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
|
@ -1401,3 +1402,86 @@ void pm_runtime_remove(struct device *dev)
|
||||||
if (dev->power.irq_safe && dev->parent)
|
if (dev->power.irq_safe && dev->parent)
|
||||||
pm_runtime_put(dev->parent);
|
pm_runtime_put(dev->parent);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pm_runtime_force_suspend - Force a device into suspend state if needed.
|
||||||
|
* @dev: Device to suspend.
|
||||||
|
*
|
||||||
|
* Disable runtime PM so we safely can check the device's runtime PM status and
|
||||||
|
* if it is active, invoke it's .runtime_suspend callback to bring it into
|
||||||
|
* suspend state. Keep runtime PM disabled to preserve the state unless we
|
||||||
|
* encounter errors.
|
||||||
|
*
|
||||||
|
* Typically this function may be invoked from a system suspend callback to make
|
||||||
|
* sure the device is put into low power state.
|
||||||
|
*/
|
||||||
|
int pm_runtime_force_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
int (*callback)(struct device *);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that pm_runtime_status_suspended() returns false while
|
||||||
|
* !CONFIG_PM_RUNTIME, which means the device will be put into low
|
||||||
|
* power state.
|
||||||
|
*/
|
||||||
|
if (pm_runtime_status_suspended(dev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
callback = rpm_get_suspend_cb(dev);
|
||||||
|
|
||||||
|
if (!callback) {
|
||||||
|
ret = -ENOSYS;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = callback(dev);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
pm_runtime_set_suspended(dev);
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pm_runtime_force_suspend);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pm_runtime_force_resume - Force a device into resume state.
|
||||||
|
* @dev: Device to resume.
|
||||||
|
*
|
||||||
|
* Prior invoking this function we expect the user to have brought the device
|
||||||
|
* into low power state by a call to pm_runtime_force_suspend(). Here we reverse
|
||||||
|
* those actions and brings the device into full power. We update the runtime PM
|
||||||
|
* status and re-enables runtime PM.
|
||||||
|
*
|
||||||
|
* Typically this function may be invoked from a system resume callback to make
|
||||||
|
* sure the device is put into full power state.
|
||||||
|
*/
|
||||||
|
int pm_runtime_force_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
int (*callback)(struct device *);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
callback = rpm_get_resume_cb(dev);
|
||||||
|
|
||||||
|
if (!callback) {
|
||||||
|
ret = -ENOSYS;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = callback(dev);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
pm_runtime_set_active(dev);
|
||||||
|
pm_runtime_mark_last_busy(dev);
|
||||||
|
out:
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pm_runtime_force_resume);
|
||||||
|
|
|
@ -150,7 +150,7 @@ config SPI_BUTTERFLY
|
||||||
|
|
||||||
config SPI_CLPS711X
|
config SPI_CLPS711X
|
||||||
tristate "CLPS711X host SPI controller"
|
tristate "CLPS711X host SPI controller"
|
||||||
depends on ARCH_CLPS711X
|
depends on ARCH_CLPS711X || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This enables dedicated general purpose SPI/Microwire1-compatible
|
This enables dedicated general purpose SPI/Microwire1-compatible
|
||||||
master mode interface (SSI1) for CLPS711X-based CPUs.
|
master mode interface (SSI1) for CLPS711X-based CPUs.
|
||||||
|
@ -212,7 +212,6 @@ config SPI_IMX
|
||||||
tristate "Freescale i.MX SPI controllers"
|
tristate "Freescale i.MX SPI controllers"
|
||||||
depends on ARCH_MXC || COMPILE_TEST
|
depends on ARCH_MXC || COMPILE_TEST
|
||||||
select SPI_BITBANG
|
select SPI_BITBANG
|
||||||
default m if IMX_HAVE_PLATFORM_SPI_IMX
|
|
||||||
help
|
help
|
||||||
This enables using the Freescale i.MX SPI controllers in master
|
This enables using the Freescale i.MX SPI controllers in master
|
||||||
mode.
|
mode.
|
||||||
|
@ -270,6 +269,7 @@ config SPI_FSL_SPI
|
||||||
config SPI_FSL_DSPI
|
config SPI_FSL_DSPI
|
||||||
tristate "Freescale DSPI controller"
|
tristate "Freescale DSPI controller"
|
||||||
select SPI_BITBANG
|
select SPI_BITBANG
|
||||||
|
select REGMAP_MMIO
|
||||||
depends on SOC_VF610 || COMPILE_TEST
|
depends on SOC_VF610 || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This enables support for the Freescale DSPI controller in master
|
This enables support for the Freescale DSPI controller in master
|
||||||
|
@ -307,7 +307,7 @@ config SPI_OMAP_UWIRE
|
||||||
|
|
||||||
config SPI_OMAP24XX
|
config SPI_OMAP24XX
|
||||||
tristate "McSPI driver for OMAP"
|
tristate "McSPI driver for OMAP"
|
||||||
depends on ARM || ARM64 || AVR32 || HEXAGON || MIPS || SH
|
depends on ARM || ARM64 || AVR32 || HEXAGON || MIPS || SUPERH
|
||||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||||
help
|
help
|
||||||
SPI master controller for OMAP24XX and later Multichannel SPI
|
SPI master controller for OMAP24XX and later Multichannel SPI
|
||||||
|
@ -381,6 +381,19 @@ config SPI_RSPI
|
||||||
help
|
help
|
||||||
SPI driver for Renesas RSPI and QSPI blocks.
|
SPI driver for Renesas RSPI and QSPI blocks.
|
||||||
|
|
||||||
|
config SPI_QUP
|
||||||
|
tristate "Qualcomm SPI controller with QUP interface"
|
||||||
|
depends on ARCH_MSM_DT || (ARM && COMPILE_TEST)
|
||||||
|
help
|
||||||
|
Qualcomm Universal Peripheral (QUP) core is an 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.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called spi_qup.
|
||||||
|
|
||||||
config SPI_S3C24XX
|
config SPI_S3C24XX
|
||||||
tristate "Samsung S3C24XX series SPI"
|
tristate "Samsung S3C24XX series SPI"
|
||||||
depends on ARCH_S3C24XX
|
depends on ARCH_S3C24XX
|
||||||
|
@ -416,7 +429,6 @@ config SPI_SH_MSIOF
|
||||||
tristate "SuperH MSIOF SPI controller"
|
tristate "SuperH MSIOF SPI controller"
|
||||||
depends on HAVE_CLK
|
depends on HAVE_CLK
|
||||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||||
select SPI_BITBANG
|
|
||||||
help
|
help
|
||||||
SPI driver for SuperH and SH Mobile MSIOF blocks.
|
SPI driver for SuperH and SH Mobile MSIOF blocks.
|
||||||
|
|
||||||
|
@ -446,6 +458,19 @@ config SPI_SIRF
|
||||||
help
|
help
|
||||||
SPI driver for CSR SiRFprimaII SoCs
|
SPI driver for CSR SiRFprimaII SoCs
|
||||||
|
|
||||||
|
config SPI_SUN4I
|
||||||
|
tristate "Allwinner A10 SoCs SPI controller"
|
||||||
|
depends on ARCH_SUNXI || COMPILE_TEST
|
||||||
|
help
|
||||||
|
SPI driver for Allwinner sun4i, sun5i and sun7i SoCs
|
||||||
|
|
||||||
|
config SPI_SUN6I
|
||||||
|
tristate "Allwinner A31 SPI controller"
|
||||||
|
depends on ARCH_SUNXI || COMPILE_TEST
|
||||||
|
depends on RESET_CONTROLLER
|
||||||
|
help
|
||||||
|
This enables using the SPI controller on the Allwinner A31 SoCs.
|
||||||
|
|
||||||
config SPI_MXS
|
config SPI_MXS
|
||||||
tristate "Freescale MXS SPI controller"
|
tristate "Freescale MXS SPI controller"
|
||||||
depends on ARCH_MXS
|
depends on ARCH_MXS
|
||||||
|
@ -478,13 +503,6 @@ config SPI_TEGRA20_SLINK
|
||||||
help
|
help
|
||||||
SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface.
|
SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface.
|
||||||
|
|
||||||
config SPI_TI_SSP
|
|
||||||
tristate "TI Sequencer Serial Port - SPI Support"
|
|
||||||
depends on MFD_TI_SSP
|
|
||||||
help
|
|
||||||
This selects an SPI master implementation using a TI sequencer
|
|
||||||
serial port.
|
|
||||||
|
|
||||||
config SPI_TOPCLIFF_PCH
|
config SPI_TOPCLIFF_PCH
|
||||||
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI"
|
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
|
@ -520,6 +538,19 @@ config SPI_XILINX
|
||||||
|
|
||||||
Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)"
|
Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)"
|
||||||
|
|
||||||
|
config SPI_XTENSA_XTFPGA
|
||||||
|
tristate "Xtensa SPI controller for xtfpga"
|
||||||
|
depends on (XTENSA && XTENSA_PLATFORM_XTFPGA) || COMPILE_TEST
|
||||||
|
select SPI_BITBANG
|
||||||
|
help
|
||||||
|
SPI driver for xtfpga SPI master controller.
|
||||||
|
|
||||||
|
This simple SPI master controller is built into xtfpga bitstreams
|
||||||
|
and is used to control daughterboard audio codec. It always transfers
|
||||||
|
16 bit words in SPI mode 0, automatically asserting CS on transfer
|
||||||
|
start and deasserting on end.
|
||||||
|
|
||||||
|
|
||||||
config SPI_NUC900
|
config SPI_NUC900
|
||||||
tristate "Nuvoton NUC900 series SPI"
|
tristate "Nuvoton NUC900 series SPI"
|
||||||
depends on ARCH_W90X900
|
depends on ARCH_W90X900
|
||||||
|
@ -546,7 +577,7 @@ config SPI_DW_MID_DMA
|
||||||
|
|
||||||
config SPI_DW_MMIO
|
config SPI_DW_MMIO
|
||||||
tristate "Memory-mapped io interface driver for DW SPI core"
|
tristate "Memory-mapped io interface driver for DW SPI core"
|
||||||
depends on SPI_DESIGNWARE && HAVE_CLK
|
depends on SPI_DESIGNWARE
|
||||||
|
|
||||||
#
|
#
|
||||||
# There are lots of SPI device types, with sensors and memory
|
# There are lots of SPI device types, with sensors and memory
|
||||||
|
|
|
@ -59,6 +59,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o
|
||||||
spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
|
spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
|
||||||
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
|
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
|
||||||
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
|
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
|
||||||
|
obj-$(CONFIG_SPI_QUP) += spi-qup.o
|
||||||
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
|
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
|
||||||
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
|
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
|
||||||
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
||||||
|
@ -70,12 +71,14 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o
|
||||||
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
|
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
|
||||||
obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
|
obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
|
||||||
obj-$(CONFIG_SPI_SIRF) += spi-sirf.o
|
obj-$(CONFIG_SPI_SIRF) += spi-sirf.o
|
||||||
|
obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o
|
||||||
|
obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o
|
||||||
obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o
|
obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o
|
||||||
obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o
|
obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o
|
||||||
obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o
|
obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o
|
||||||
obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
|
|
||||||
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
|
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
|
||||||
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
|
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
|
||||||
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
|
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
|
||||||
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
|
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
|
||||||
obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
|
obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
|
||||||
|
obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -200,7 +199,6 @@ static irqreturn_t altera_spi_irq(int irq, void *dev)
|
||||||
|
|
||||||
static int altera_spi_probe(struct platform_device *pdev)
|
static int altera_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct altera_spi_platform_data *platp = dev_get_platdata(&pdev->dev);
|
|
||||||
struct altera_spi *hw;
|
struct altera_spi *hw;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
@ -214,6 +212,8 @@ static int altera_spi_probe(struct platform_device *pdev)
|
||||||
master->bus_num = pdev->id;
|
master->bus_num = pdev->id;
|
||||||
master->num_chipselect = 16;
|
master->num_chipselect = 16;
|
||||||
master->mode_bits = SPI_CS_HIGH;
|
master->mode_bits = SPI_CS_HIGH;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
hw = spi_master_get_devdata(master);
|
hw = spi_master_get_devdata(master);
|
||||||
platform_set_drvdata(pdev, hw);
|
platform_set_drvdata(pdev, hw);
|
||||||
|
@ -245,9 +245,6 @@ static int altera_spi_probe(struct platform_device *pdev)
|
||||||
if (err)
|
if (err)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
/* find platform data */
|
|
||||||
if (!platp)
|
|
||||||
hw->bitbang.master->dev.of_node = pdev->dev.of_node;
|
|
||||||
|
|
||||||
/* register our spi controller */
|
/* register our spi controller */
|
||||||
err = spi_bitbang_start(&hw->bitbang);
|
err = spi_bitbang_start(&hw->bitbang);
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
@ -26,6 +25,7 @@
|
||||||
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
|
||||||
/* SPI register offsets */
|
/* SPI register offsets */
|
||||||
#define SPI_CR 0x0000
|
#define SPI_CR 0x0000
|
||||||
|
@ -993,13 +993,6 @@ static int atmel_spi_setup(struct spi_device *spi)
|
||||||
|
|
||||||
as = spi_master_get_devdata(spi->master);
|
as = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
if (spi->chip_select > spi->master->num_chipselect) {
|
|
||||||
dev_dbg(&spi->dev,
|
|
||||||
"setup: invalid chipselect %u (%u defined)\n",
|
|
||||||
spi->chip_select, spi->master->num_chipselect);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see notes above re chipselect */
|
/* see notes above re chipselect */
|
||||||
if (!atmel_spi_is_v2(as)
|
if (!atmel_spi_is_v2(as)
|
||||||
&& spi->chip_select == 0
|
&& spi->chip_select == 0
|
||||||
|
@ -1087,14 +1080,6 @@ static int atmel_spi_one_transfer(struct spi_master *master,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xfer->bits_per_word > 8) {
|
|
||||||
if (xfer->len % 2) {
|
|
||||||
dev_dbg(&spi->dev,
|
|
||||||
"buffer len should be 16 bits aligned\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DMA map early, for performance (empties dcache ASAP) and
|
* DMA map early, for performance (empties dcache ASAP) and
|
||||||
* better fault reporting.
|
* better fault reporting.
|
||||||
|
@ -1221,9 +1206,6 @@ static int atmel_spi_transfer_one_message(struct spi_master *master,
|
||||||
dev_dbg(&spi->dev, "new message %p submitted for %s\n",
|
dev_dbg(&spi->dev, "new message %p submitted for %s\n",
|
||||||
msg, dev_name(&spi->dev));
|
msg, dev_name(&spi->dev));
|
||||||
|
|
||||||
if (unlikely(list_empty(&msg->transfers)))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
atmel_spi_lock(as);
|
atmel_spi_lock(as);
|
||||||
cs_activate(as, spi);
|
cs_activate(as, spi);
|
||||||
|
|
||||||
|
@ -1244,10 +1226,10 @@ static int atmel_spi_transfer_one_message(struct spi_master *master,
|
||||||
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||||
dev_dbg(&spi->dev,
|
dev_dbg(&spi->dev,
|
||||||
" xfer %p: len %u tx %p/%08x rx %p/%08x\n",
|
" xfer %p: len %u tx %p/%pad rx %p/%pad\n",
|
||||||
xfer, xfer->len,
|
xfer, xfer->len,
|
||||||
xfer->tx_buf, xfer->tx_dma,
|
xfer->tx_buf, &xfer->tx_dma,
|
||||||
xfer->rx_buf, xfer->rx_dma);
|
xfer->rx_buf, &xfer->rx_dma);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_done:
|
msg_done:
|
||||||
|
@ -1303,6 +1285,9 @@ static int atmel_spi_probe(struct platform_device *pdev)
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct atmel_spi *as;
|
struct atmel_spi *as;
|
||||||
|
|
||||||
|
/* Select default pin state */
|
||||||
|
pinctrl_pm_select_default_state(&pdev->dev);
|
||||||
|
|
||||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!regs)
|
if (!regs)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
@ -1465,6 +1450,9 @@ static int atmel_spi_suspend(struct device *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_disable_unprepare(as->clk);
|
clk_disable_unprepare(as->clk);
|
||||||
|
|
||||||
|
pinctrl_pm_select_sleep_state(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1474,6 +1462,8 @@ static int atmel_spi_resume(struct device *dev)
|
||||||
struct atmel_spi *as = spi_master_get_devdata(master);
|
struct atmel_spi *as = spi_master_get_devdata(master);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
pinctrl_pm_select_default_state(dev);
|
||||||
|
|
||||||
clk_prepare_enable(as->clk);
|
clk_prepare_enable(as->clk);
|
||||||
|
|
||||||
/* Start the queue running */
|
/* Start the queue running */
|
||||||
|
|
|
@ -55,8 +55,6 @@ struct au1550_spi {
|
||||||
|
|
||||||
volatile psc_spi_t __iomem *regs;
|
volatile psc_spi_t __iomem *regs;
|
||||||
int irq;
|
int irq;
|
||||||
unsigned freq_max;
|
|
||||||
unsigned freq_min;
|
|
||||||
|
|
||||||
unsigned len;
|
unsigned len;
|
||||||
unsigned tx_count;
|
unsigned tx_count;
|
||||||
|
@ -248,11 +246,8 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
hz = t->speed_hz;
|
hz = t->speed_hz;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hz > spi->max_speed_hz || hz > hw->freq_max || hz < hw->freq_min) {
|
if (!hz)
|
||||||
dev_err(&spi->dev, "setupxfer: clock rate=%d out of range\n",
|
|
||||||
hz);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
|
||||||
|
|
||||||
au1550_spi_bits_handlers_set(hw, spi->bits_per_word);
|
au1550_spi_bits_handlers_set(hw, spi->bits_per_word);
|
||||||
|
|
||||||
|
@ -287,23 +282,6 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int au1550_spi_setup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct au1550_spi *hw = spi_master_get_devdata(spi->master);
|
|
||||||
|
|
||||||
if (spi->max_speed_hz == 0)
|
|
||||||
spi->max_speed_hz = hw->freq_max;
|
|
||||||
if (spi->max_speed_hz > hw->freq_max
|
|
||||||
|| spi->max_speed_hz < hw->freq_min)
|
|
||||||
return -EINVAL;
|
|
||||||
/*
|
|
||||||
* NOTE: cannot change speed and other hw settings immediately,
|
|
||||||
* otherwise sharing of spi bus is not possible,
|
|
||||||
* so do not call setupxfer(spi, NULL) here
|
|
||||||
*/
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* for dma spi transfers, we have to setup rx channel, otherwise there is
|
* for dma spi transfers, we have to setup rx channel, otherwise there is
|
||||||
* no reliable way how to recognize that spi transfer is done
|
* no reliable way how to recognize that spi transfer is done
|
||||||
|
@ -838,7 +816,6 @@ static int au1550_spi_probe(struct platform_device *pdev)
|
||||||
hw->bitbang.master = hw->master;
|
hw->bitbang.master = hw->master;
|
||||||
hw->bitbang.setup_transfer = au1550_spi_setupxfer;
|
hw->bitbang.setup_transfer = au1550_spi_setupxfer;
|
||||||
hw->bitbang.chipselect = au1550_spi_chipsel;
|
hw->bitbang.chipselect = au1550_spi_chipsel;
|
||||||
hw->bitbang.master->setup = au1550_spi_setup;
|
|
||||||
hw->bitbang.txrx_bufs = au1550_spi_txrx_bufs;
|
hw->bitbang.txrx_bufs = au1550_spi_txrx_bufs;
|
||||||
|
|
||||||
if (hw->usedma) {
|
if (hw->usedma) {
|
||||||
|
@ -909,8 +886,9 @@ static int au1550_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int min_div = (2 << 0) * (2 * (4 + 1));
|
int min_div = (2 << 0) * (2 * (4 + 1));
|
||||||
int max_div = (2 << 3) * (2 * (63 + 1));
|
int max_div = (2 << 3) * (2 * (63 + 1));
|
||||||
hw->freq_max = hw->pdata->mainclk_hz / min_div;
|
master->max_speed_hz = hw->pdata->mainclk_hz / min_div;
|
||||||
hw->freq_min = hw->pdata->mainclk_hz / (max_div + 1) + 1;
|
master->min_speed_hz =
|
||||||
|
hw->pdata->mainclk_hz / (max_div + 1) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
au1550_spi_setup_psc_as_spi(hw);
|
au1550_spi_setup_psc_as_spi(hw);
|
||||||
|
|
|
@ -315,7 +315,6 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
master->mode_bits = BCM2835_SPI_MODE_BITS;
|
master->mode_bits = BCM2835_SPI_MODE_BITS;
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
master->bus_num = -1;
|
|
||||||
master->num_chipselect = 3;
|
master->num_chipselect = 3;
|
||||||
master->transfer_one_message = bcm2835_spi_transfer_one;
|
master->transfer_one_message = bcm2835_spi_transfer_one;
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
|
@ -180,7 +180,7 @@ static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t)
|
||||||
while (pending > 0) {
|
while (pending > 0) {
|
||||||
int curr_step = min_t(int, step_size, pending);
|
int curr_step = min_t(int, step_size, pending);
|
||||||
|
|
||||||
init_completion(&bs->done);
|
reinit_completion(&bs->done);
|
||||||
if (tx) {
|
if (tx) {
|
||||||
memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN, tx, curr_step);
|
memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN, tx, curr_step);
|
||||||
tx += curr_step;
|
tx += curr_step;
|
||||||
|
@ -369,6 +369,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
|
||||||
bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0));
|
bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0));
|
||||||
|
|
||||||
mutex_init(&bs->bus_mutex);
|
mutex_init(&bs->bus_mutex);
|
||||||
|
init_completion(&bs->done);
|
||||||
|
|
||||||
master->bus_num = HSSPI_BUS_NUM;
|
master->bus_num = HSSPI_BUS_NUM;
|
||||||
master->num_chipselect = 8;
|
master->num_chipselect = 8;
|
||||||
|
@ -453,9 +454,8 @@ static int bcm63xx_hsspi_resume(struct device *dev)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct dev_pm_ops bcm63xx_hsspi_pm_ops = {
|
static SIMPLE_DEV_PM_OPS(bcm63xx_hsspi_pm_ops, bcm63xx_hsspi_suspend,
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(bcm63xx_hsspi_suspend, bcm63xx_hsspi_resume)
|
bcm63xx_hsspi_resume);
|
||||||
};
|
|
||||||
|
|
||||||
static struct platform_driver bcm63xx_hsspi_driver = {
|
static struct platform_driver bcm63xx_hsspi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -35,8 +34,6 @@
|
||||||
|
|
||||||
#include <bcm63xx_dev_spi.h>
|
#include <bcm63xx_dev_spi.h>
|
||||||
|
|
||||||
#define PFX KBUILD_MODNAME
|
|
||||||
|
|
||||||
#define BCM63XX_SPI_MAX_PREPEND 15
|
#define BCM63XX_SPI_MAX_PREPEND 15
|
||||||
|
|
||||||
struct bcm63xx_spi {
|
struct bcm63xx_spi {
|
||||||
|
@ -169,7 +166,7 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first,
|
||||||
transfer_list);
|
transfer_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
init_completion(&bs->done);
|
reinit_completion(&bs->done);
|
||||||
|
|
||||||
/* Fill in the Message control register */
|
/* Fill in the Message control register */
|
||||||
msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
|
msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
|
||||||
|
@ -353,6 +350,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
bs = spi_master_get_devdata(master);
|
bs = spi_master_get_devdata(master);
|
||||||
|
init_completion(&bs->done);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
bs->pdev = pdev;
|
bs->pdev = pdev;
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
* Licensed under the GPL-2 or later.
|
* Licensed under the GPL-2 or later.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
|
|
@ -822,7 +822,8 @@ static int bfin_spi_probe(struct platform_device *pdev)
|
||||||
master->cleanup = bfin_spi_cleanup;
|
master->cleanup = bfin_spi_cleanup;
|
||||||
master->setup = bfin_spi_setup;
|
master->setup = bfin_spi_setup;
|
||||||
master->transfer_one_message = bfin_spi_transfer_one_message;
|
master->transfer_one_message = bfin_spi_transfer_one_message;
|
||||||
master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1);
|
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
|
||||||
|
SPI_BPW_MASK(8);
|
||||||
|
|
||||||
drv_data = spi_master_get_devdata(master);
|
drv_data = spi_master_get_devdata(master);
|
||||||
drv_data->master = master;
|
drv_data->master = master;
|
||||||
|
|
|
@ -350,7 +350,6 @@ static void *bfin_spi_next_transfer(struct bfin_spi_master_data *drv_data)
|
||||||
static void bfin_spi_giveback(struct bfin_spi_master_data *drv_data)
|
static void bfin_spi_giveback(struct bfin_spi_master_data *drv_data)
|
||||||
{
|
{
|
||||||
struct bfin_spi_slave_data *chip = drv_data->cur_chip;
|
struct bfin_spi_slave_data *chip = drv_data->cur_chip;
|
||||||
struct spi_transfer *last_transfer;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct spi_message *msg;
|
struct spi_message *msg;
|
||||||
|
|
||||||
|
@ -362,9 +361,6 @@ static void bfin_spi_giveback(struct bfin_spi_master_data *drv_data)
|
||||||
queue_work(drv_data->workqueue, &drv_data->pump_messages);
|
queue_work(drv_data->workqueue, &drv_data->pump_messages);
|
||||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
spin_unlock_irqrestore(&drv_data->lock, flags);
|
||||||
|
|
||||||
last_transfer = list_entry(msg->transfers.prev,
|
|
||||||
struct spi_transfer, transfer_list);
|
|
||||||
|
|
||||||
msg->state = NULL;
|
msg->state = NULL;
|
||||||
|
|
||||||
if (!drv_data->cs_change)
|
if (!drv_data->cs_change)
|
||||||
|
@ -1030,10 +1026,6 @@ static int bfin_spi_setup(struct spi_device *spi)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* translate common spi framework into our register */
|
/* translate common spi framework into our register */
|
||||||
if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) {
|
|
||||||
dev_err(&spi->dev, "unsupported spi modes detected\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (spi->mode & SPI_CPOL)
|
if (spi->mode & SPI_CPOL)
|
||||||
chip->ctl_reg |= BIT_CTL_CPOL;
|
chip->ctl_reg |= BIT_CTL_CPOL;
|
||||||
if (spi->mode & SPI_CPHA)
|
if (spi->mode & SPI_CPHA)
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
@ -467,11 +466,9 @@ EXPORT_SYMBOL_GPL(spi_bitbang_start);
|
||||||
/**
|
/**
|
||||||
* spi_bitbang_stop - stops the task providing spi communication
|
* spi_bitbang_stop - stops the task providing spi communication
|
||||||
*/
|
*/
|
||||||
int spi_bitbang_stop(struct spi_bitbang *bitbang)
|
void spi_bitbang_stop(struct spi_bitbang *bitbang)
|
||||||
{
|
{
|
||||||
spi_unregister_master(bitbang->master);
|
spi_unregister_master(bitbang->master);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(spi_bitbang_stop);
|
EXPORT_SYMBOL_GPL(spi_bitbang_stop);
|
||||||
|
|
||||||
|
|
|
@ -309,7 +309,6 @@ done:
|
||||||
static void butterfly_detach(struct parport *p)
|
static void butterfly_detach(struct parport *p)
|
||||||
{
|
{
|
||||||
struct butterfly *pp;
|
struct butterfly *pp;
|
||||||
int status;
|
|
||||||
|
|
||||||
/* FIXME this global is ugly ... but, how to quickly get from
|
/* FIXME this global is ugly ... but, how to quickly get from
|
||||||
* the parport to the "struct butterfly" associated with it?
|
* the parport to the "struct butterfly" associated with it?
|
||||||
|
@ -321,7 +320,7 @@ static void butterfly_detach(struct parport *p)
|
||||||
butterfly = NULL;
|
butterfly = NULL;
|
||||||
|
|
||||||
/* stop() unregisters child devices too */
|
/* stop() unregisters child devices too */
|
||||||
status = spi_bitbang_stop(&pp->bitbang);
|
spi_bitbang_stop(&pp->bitbang);
|
||||||
|
|
||||||
/* turn off VCC */
|
/* turn off VCC */
|
||||||
parport_write_data(pp->port, 0);
|
parport_write_data(pp->port, 0);
|
||||||
|
|
|
@ -11,158 +11,125 @@
|
||||||
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
|
#include <linux/mfd/syscon/clps711x.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/platform_data/spi-clps711x.h>
|
#include <linux/platform_data/spi-clps711x.h>
|
||||||
|
|
||||||
#include <mach/hardware.h>
|
|
||||||
|
|
||||||
#define DRIVER_NAME "spi-clps711x"
|
#define DRIVER_NAME "spi-clps711x"
|
||||||
|
|
||||||
struct spi_clps711x_data {
|
#define SYNCIO_FRMLEN(x) ((x) << 8)
|
||||||
struct completion done;
|
#define SYNCIO_TXFRMEN (1 << 14)
|
||||||
|
|
||||||
|
struct spi_clps711x_data {
|
||||||
|
void __iomem *syncio;
|
||||||
|
struct regmap *syscon;
|
||||||
|
struct regmap *syscon1;
|
||||||
struct clk *spi_clk;
|
struct clk *spi_clk;
|
||||||
u32 max_speed_hz;
|
|
||||||
|
|
||||||
u8 *tx_buf;
|
u8 *tx_buf;
|
||||||
u8 *rx_buf;
|
u8 *rx_buf;
|
||||||
int count;
|
unsigned int bpw;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
int chipselect[0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int spi_clps711x_setup(struct spi_device *spi)
|
static int spi_clps711x_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master);
|
|
||||||
|
|
||||||
/* We are expect that SPI-device is not selected */
|
/* We are expect that SPI-device is not selected */
|
||||||
gpio_direction_output(hw->chipselect[spi->chip_select],
|
gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
|
||||||
!(spi->mode & SPI_CS_HIGH));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spi_clps711x_setup_mode(struct spi_device *spi)
|
static void spi_clps711x_setup_xfer(struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
/* Setup edge for transfer */
|
struct spi_master *master = spi->master;
|
||||||
if (spi->mode & SPI_CPHA)
|
struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
||||||
clps_writew(clps_readw(SYSCON3) | SYSCON3_ADCCKNSEN, SYSCON3);
|
|
||||||
else
|
|
||||||
clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCKNSEN, SYSCON3);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int spi_clps711x_setup_xfer(struct spi_device *spi,
|
|
||||||
struct spi_transfer *xfer)
|
|
||||||
{
|
|
||||||
u32 speed = xfer->speed_hz ? : spi->max_speed_hz;
|
|
||||||
u8 bpw = xfer->bits_per_word;
|
|
||||||
struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master);
|
|
||||||
|
|
||||||
if (bpw != 8) {
|
|
||||||
dev_err(&spi->dev, "Unsupported master bus width %i\n", bpw);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup SPI frequency divider */
|
/* Setup SPI frequency divider */
|
||||||
if (!speed || (speed >= hw->max_speed_hz))
|
if (xfer->speed_hz >= master->max_speed_hz)
|
||||||
clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
|
regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
||||||
SYSCON1_ADCKSEL(3), SYSCON1);
|
SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(3));
|
||||||
else if (speed >= (hw->max_speed_hz / 2))
|
else if (xfer->speed_hz >= (master->max_speed_hz / 2))
|
||||||
clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
|
regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
||||||
SYSCON1_ADCKSEL(2), SYSCON1);
|
SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(2));
|
||||||
else if (speed >= (hw->max_speed_hz / 8))
|
else if (xfer->speed_hz >= (master->max_speed_hz / 8))
|
||||||
clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
|
regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
||||||
SYSCON1_ADCKSEL(1), SYSCON1);
|
SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(1));
|
||||||
else
|
else
|
||||||
clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
|
regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
||||||
SYSCON1_ADCKSEL(0), SYSCON1);
|
SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(0));
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spi_clps711x_transfer_one_message(struct spi_master *master,
|
static int spi_clps711x_prepare_message(struct spi_master *master,
|
||||||
struct spi_message *msg)
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
||||||
struct spi_transfer *xfer;
|
struct spi_device *spi = msg->spi;
|
||||||
int status = 0, cs = hw->chipselect[msg->spi->chip_select];
|
|
||||||
u32 data;
|
|
||||||
|
|
||||||
spi_clps711x_setup_mode(msg->spi);
|
/* Setup mode for transfer */
|
||||||
|
return regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCKNSEN,
|
||||||
|
(spi->mode & SPI_CPHA) ?
|
||||||
|
SYSCON3_ADCCKNSEN : 0);
|
||||||
|
}
|
||||||
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
static int spi_clps711x_transfer_one(struct spi_master *master,
|
||||||
if (spi_clps711x_setup_xfer(msg->spi, xfer)) {
|
struct spi_device *spi,
|
||||||
status = -EINVAL;
|
struct spi_transfer *xfer)
|
||||||
goto out_xfr;
|
{
|
||||||
}
|
struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
||||||
|
u8 data;
|
||||||
|
|
||||||
gpio_set_value(cs, !!(msg->spi->mode & SPI_CS_HIGH));
|
spi_clps711x_setup_xfer(spi, xfer);
|
||||||
|
|
||||||
reinit_completion(&hw->done);
|
hw->len = xfer->len;
|
||||||
|
hw->bpw = xfer->bits_per_word;
|
||||||
|
hw->tx_buf = (u8 *)xfer->tx_buf;
|
||||||
|
hw->rx_buf = (u8 *)xfer->rx_buf;
|
||||||
|
|
||||||
hw->count = 0;
|
/* Initiate transfer */
|
||||||
hw->len = xfer->len;
|
data = hw->tx_buf ? *hw->tx_buf++ : 0;
|
||||||
hw->tx_buf = (u8 *)xfer->tx_buf;
|
writel(data | SYNCIO_FRMLEN(hw->bpw) | SYNCIO_TXFRMEN, hw->syncio);
|
||||||
hw->rx_buf = (u8 *)xfer->rx_buf;
|
|
||||||
|
|
||||||
/* Initiate transfer */
|
return 1;
|
||||||
data = hw->tx_buf ? hw->tx_buf[hw->count] : 0;
|
|
||||||
clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO);
|
|
||||||
|
|
||||||
wait_for_completion(&hw->done);
|
|
||||||
|
|
||||||
if (xfer->delay_usecs)
|
|
||||||
udelay(xfer->delay_usecs);
|
|
||||||
|
|
||||||
if (xfer->cs_change ||
|
|
||||||
list_is_last(&xfer->transfer_list, &msg->transfers))
|
|
||||||
gpio_set_value(cs, !(msg->spi->mode & SPI_CS_HIGH));
|
|
||||||
|
|
||||||
msg->actual_length += xfer->len;
|
|
||||||
}
|
|
||||||
|
|
||||||
out_xfr:
|
|
||||||
msg->status = status;
|
|
||||||
spi_finalize_current_message(master);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t spi_clps711x_isr(int irq, void *dev_id)
|
static irqreturn_t spi_clps711x_isr(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct spi_clps711x_data *hw = (struct spi_clps711x_data *)dev_id;
|
struct spi_master *master = dev_id;
|
||||||
u32 data;
|
struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
||||||
|
u8 data;
|
||||||
|
|
||||||
/* Handle RX */
|
/* Handle RX */
|
||||||
data = clps_readb(SYNCIO);
|
data = readb(hw->syncio);
|
||||||
if (hw->rx_buf)
|
if (hw->rx_buf)
|
||||||
hw->rx_buf[hw->count] = (u8)data;
|
*hw->rx_buf++ = data;
|
||||||
|
|
||||||
hw->count++;
|
|
||||||
|
|
||||||
/* Handle TX */
|
/* Handle TX */
|
||||||
if (hw->count < hw->len) {
|
if (--hw->len > 0) {
|
||||||
data = hw->tx_buf ? hw->tx_buf[hw->count] : 0;
|
data = hw->tx_buf ? *hw->tx_buf++ : 0;
|
||||||
clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO);
|
writel(data | SYNCIO_FRMLEN(hw->bpw) | SYNCIO_TXFRMEN,
|
||||||
|
hw->syncio);
|
||||||
} else
|
} else
|
||||||
complete(&hw->done);
|
spi_finalize_current_transfer(master);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spi_clps711x_probe(struct platform_device *pdev)
|
static int spi_clps711x_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int i, ret;
|
|
||||||
struct spi_master *master;
|
|
||||||
struct spi_clps711x_data *hw;
|
struct spi_clps711x_data *hw;
|
||||||
struct spi_clps711x_pdata *pdata = dev_get_platdata(&pdev->dev);
|
struct spi_clps711x_pdata *pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
struct spi_master *master;
|
||||||
|
struct resource *res;
|
||||||
|
int i, irq, ret;
|
||||||
|
|
||||||
if (!pdata) {
|
if (!pdata) {
|
||||||
dev_err(&pdev->dev, "No platform data supplied\n");
|
dev_err(&pdev->dev, "No platform data supplied\n");
|
||||||
|
@ -174,33 +141,37 @@ static int spi_clps711x_probe(struct platform_device *pdev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev,
|
irq = platform_get_irq(pdev, 0);
|
||||||
sizeof(struct spi_clps711x_data) +
|
if (irq < 0)
|
||||||
sizeof(int) * pdata->num_chipselect);
|
return irq;
|
||||||
if (!master) {
|
|
||||||
dev_err(&pdev->dev, "SPI allocating memory error\n");
|
master = spi_alloc_master(&pdev->dev, sizeof(*hw));
|
||||||
|
if (!master)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
master->cs_gpios = devm_kzalloc(&pdev->dev, sizeof(int) *
|
||||||
|
pdata->num_chipselect, GFP_KERNEL);
|
||||||
|
if (!master->cs_gpios) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
master->bus_num = pdev->id;
|
master->bus_num = pdev->id;
|
||||||
master->mode_bits = SPI_CPHA | SPI_CS_HIGH;
|
master->mode_bits = SPI_CPHA | SPI_CS_HIGH;
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 8);
|
||||||
master->num_chipselect = pdata->num_chipselect;
|
master->num_chipselect = pdata->num_chipselect;
|
||||||
master->setup = spi_clps711x_setup;
|
master->setup = spi_clps711x_setup;
|
||||||
master->transfer_one_message = spi_clps711x_transfer_one_message;
|
master->prepare_message = spi_clps711x_prepare_message;
|
||||||
|
master->transfer_one = spi_clps711x_transfer_one;
|
||||||
|
|
||||||
hw = spi_master_get_devdata(master);
|
hw = spi_master_get_devdata(master);
|
||||||
|
|
||||||
for (i = 0; i < master->num_chipselect; i++) {
|
for (i = 0; i < master->num_chipselect; i++) {
|
||||||
hw->chipselect[i] = pdata->chipselect[i];
|
master->cs_gpios[i] = pdata->chipselect[i];
|
||||||
if (!gpio_is_valid(hw->chipselect[i])) {
|
ret = devm_gpio_request(&pdev->dev, master->cs_gpios[i],
|
||||||
dev_err(&pdev->dev, "Invalid CS GPIO %i\n", i);
|
DRIVER_NAME);
|
||||||
ret = -EINVAL;
|
if (ret) {
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
if (devm_gpio_request(&pdev->dev, hw->chipselect[i], NULL)) {
|
|
||||||
dev_err(&pdev->dev, "Can't get CS GPIO %i\n", i);
|
dev_err(&pdev->dev, "Can't get CS GPIO %i\n", i);
|
||||||
ret = -EINVAL;
|
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,29 +182,45 @@ static int spi_clps711x_probe(struct platform_device *pdev)
|
||||||
ret = PTR_ERR(hw->spi_clk);
|
ret = PTR_ERR(hw->spi_clk);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
hw->max_speed_hz = clk_get_rate(hw->spi_clk);
|
master->max_speed_hz = clk_get_rate(hw->spi_clk);
|
||||||
|
|
||||||
init_completion(&hw->done);
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
/* Disable extended mode due hardware problems */
|
hw->syscon = syscon_regmap_lookup_by_pdevname("syscon.3");
|
||||||
clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCON, SYSCON3);
|
if (IS_ERR(hw->syscon)) {
|
||||||
|
ret = PTR_ERR(hw->syscon);
|
||||||
/* Clear possible pending interrupt */
|
|
||||||
clps_readl(SYNCIO);
|
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, IRQ_SSEOTI, spi_clps711x_isr, 0,
|
|
||||||
dev_name(&pdev->dev), hw);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(&pdev->dev, "Can't request IRQ\n");
|
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hw->syscon1 = syscon_regmap_lookup_by_pdevname("syscon.1");
|
||||||
|
if (IS_ERR(hw->syscon1)) {
|
||||||
|
ret = PTR_ERR(hw->syscon1);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
hw->syncio = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(hw->syncio)) {
|
||||||
|
ret = PTR_ERR(hw->syncio);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable extended mode due hardware problems */
|
||||||
|
regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCON, 0);
|
||||||
|
|
||||||
|
/* Clear possible pending interrupt */
|
||||||
|
readl(hw->syncio);
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, irq, spi_clps711x_isr, 0,
|
||||||
|
dev_name(&pdev->dev), master);
|
||||||
|
if (ret)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
ret = devm_spi_register_master(&pdev->dev, master);
|
ret = devm_spi_register_master(&pdev->dev, master);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
dev_info(&pdev->dev,
|
dev_info(&pdev->dev,
|
||||||
"SPI bus driver initialized. Master clock %u Hz\n",
|
"SPI bus driver initialized. Master clock %u Hz\n",
|
||||||
hw->max_speed_hz);
|
master->max_speed_hz);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,8 +77,6 @@ struct mcfqspi {
|
||||||
struct mcfqspi_cs_control *cs_control;
|
struct mcfqspi_cs_control *cs_control;
|
||||||
|
|
||||||
wait_queue_head_t waitq;
|
wait_queue_head_t waitq;
|
||||||
|
|
||||||
struct device *dev;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void mcfqspi_wr_qmr(struct mcfqspi *mcfqspi, u16 val)
|
static void mcfqspi_wr_qmr(struct mcfqspi *mcfqspi, u16 val)
|
||||||
|
@ -135,13 +133,13 @@ static void mcfqspi_cs_deselect(struct mcfqspi *mcfqspi, u8 chip_select,
|
||||||
|
|
||||||
static int mcfqspi_cs_setup(struct mcfqspi *mcfqspi)
|
static int mcfqspi_cs_setup(struct mcfqspi *mcfqspi)
|
||||||
{
|
{
|
||||||
return (mcfqspi->cs_control && mcfqspi->cs_control->setup) ?
|
return (mcfqspi->cs_control->setup) ?
|
||||||
mcfqspi->cs_control->setup(mcfqspi->cs_control) : 0;
|
mcfqspi->cs_control->setup(mcfqspi->cs_control) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mcfqspi_cs_teardown(struct mcfqspi *mcfqspi)
|
static void mcfqspi_cs_teardown(struct mcfqspi *mcfqspi)
|
||||||
{
|
{
|
||||||
if (mcfqspi->cs_control && mcfqspi->cs_control->teardown)
|
if (mcfqspi->cs_control->teardown)
|
||||||
mcfqspi->cs_control->teardown(mcfqspi->cs_control);
|
mcfqspi->cs_control->teardown(mcfqspi->cs_control);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,68 +298,45 @@ static void mcfqspi_transfer_msg16(struct mcfqspi *mcfqspi, unsigned count,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mcfqspi_transfer_one_message(struct spi_master *master,
|
static void mcfqspi_set_cs(struct spi_device *spi, bool enable)
|
||||||
struct spi_message *msg)
|
{
|
||||||
|
struct mcfqspi *mcfqspi = spi_master_get_devdata(spi->master);
|
||||||
|
bool cs_high = spi->mode & SPI_CS_HIGH;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
mcfqspi_cs_select(mcfqspi, spi->chip_select, cs_high);
|
||||||
|
else
|
||||||
|
mcfqspi_cs_deselect(mcfqspi, spi->chip_select, cs_high);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mcfqspi_transfer_one(struct spi_master *master,
|
||||||
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
|
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
|
||||||
struct spi_device *spi = msg->spi;
|
u16 qmr = MCFQSPI_QMR_MSTR;
|
||||||
struct spi_transfer *t;
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
list_for_each_entry(t, &msg->transfers, transfer_list) {
|
qmr |= t->bits_per_word << 10;
|
||||||
bool cs_high = spi->mode & SPI_CS_HIGH;
|
if (spi->mode & SPI_CPHA)
|
||||||
u16 qmr = MCFQSPI_QMR_MSTR;
|
qmr |= MCFQSPI_QMR_CPHA;
|
||||||
|
if (spi->mode & SPI_CPOL)
|
||||||
|
qmr |= MCFQSPI_QMR_CPOL;
|
||||||
|
qmr |= mcfqspi_qmr_baud(t->speed_hz);
|
||||||
|
mcfqspi_wr_qmr(mcfqspi, qmr);
|
||||||
|
|
||||||
qmr |= t->bits_per_word << 10;
|
mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE);
|
||||||
if (spi->mode & SPI_CPHA)
|
if (t->bits_per_word == 8)
|
||||||
qmr |= MCFQSPI_QMR_CPHA;
|
mcfqspi_transfer_msg8(mcfqspi, t->len, t->tx_buf, t->rx_buf);
|
||||||
if (spi->mode & SPI_CPOL)
|
else
|
||||||
qmr |= MCFQSPI_QMR_CPOL;
|
mcfqspi_transfer_msg16(mcfqspi, t->len / 2, t->tx_buf,
|
||||||
if (t->speed_hz)
|
t->rx_buf);
|
||||||
qmr |= mcfqspi_qmr_baud(t->speed_hz);
|
mcfqspi_wr_qir(mcfqspi, 0);
|
||||||
else
|
|
||||||
qmr |= mcfqspi_qmr_baud(spi->max_speed_hz);
|
|
||||||
mcfqspi_wr_qmr(mcfqspi, qmr);
|
|
||||||
|
|
||||||
mcfqspi_cs_select(mcfqspi, spi->chip_select, cs_high);
|
|
||||||
|
|
||||||
mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE);
|
|
||||||
if (t->bits_per_word == 8)
|
|
||||||
mcfqspi_transfer_msg8(mcfqspi, t->len, t->tx_buf,
|
|
||||||
t->rx_buf);
|
|
||||||
else
|
|
||||||
mcfqspi_transfer_msg16(mcfqspi, t->len / 2, t->tx_buf,
|
|
||||||
t->rx_buf);
|
|
||||||
mcfqspi_wr_qir(mcfqspi, 0);
|
|
||||||
|
|
||||||
if (t->delay_usecs)
|
|
||||||
udelay(t->delay_usecs);
|
|
||||||
if (t->cs_change) {
|
|
||||||
if (!list_is_last(&t->transfer_list, &msg->transfers))
|
|
||||||
mcfqspi_cs_deselect(mcfqspi, spi->chip_select,
|
|
||||||
cs_high);
|
|
||||||
} else {
|
|
||||||
if (list_is_last(&t->transfer_list, &msg->transfers))
|
|
||||||
mcfqspi_cs_deselect(mcfqspi, spi->chip_select,
|
|
||||||
cs_high);
|
|
||||||
}
|
|
||||||
msg->actual_length += t->len;
|
|
||||||
}
|
|
||||||
msg->status = status;
|
|
||||||
spi_finalize_current_message(master);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mcfqspi_setup(struct spi_device *spi)
|
static int mcfqspi_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
if (spi->chip_select >= spi->master->num_chipselect) {
|
|
||||||
dev_dbg(&spi->dev, "%d chip select is out of range\n",
|
|
||||||
spi->chip_select);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mcfqspi_cs_deselect(spi_master_get_devdata(spi->master),
|
mcfqspi_cs_deselect(spi_master_get_devdata(spi->master),
|
||||||
spi->chip_select, spi->mode & SPI_CS_HIGH);
|
spi->chip_select, spi->mode & SPI_CS_HIGH);
|
||||||
|
|
||||||
|
@ -388,6 +363,11 @@ static int mcfqspi_probe(struct platform_device *pdev)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!pdata->cs_control) {
|
||||||
|
dev_dbg(&pdev->dev, "pdata->cs_control is NULL\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof(*mcfqspi));
|
master = spi_alloc_master(&pdev->dev, sizeof(*mcfqspi));
|
||||||
if (master == NULL) {
|
if (master == NULL) {
|
||||||
dev_dbg(&pdev->dev, "spi_alloc_master failed\n");
|
dev_dbg(&pdev->dev, "spi_alloc_master failed\n");
|
||||||
|
@ -436,12 +416,12 @@ static int mcfqspi_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
init_waitqueue_head(&mcfqspi->waitq);
|
init_waitqueue_head(&mcfqspi->waitq);
|
||||||
mcfqspi->dev = &pdev->dev;
|
|
||||||
|
|
||||||
master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA;
|
master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA;
|
||||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16);
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16);
|
||||||
master->setup = mcfqspi_setup;
|
master->setup = mcfqspi_setup;
|
||||||
master->transfer_one_message = mcfqspi_transfer_one_message;
|
master->set_cs = mcfqspi_set_cs;
|
||||||
|
master->transfer_one = mcfqspi_transfer_one;
|
||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
@ -451,7 +431,7 @@ static int mcfqspi_probe(struct platform_device *pdev)
|
||||||
dev_dbg(&pdev->dev, "spi_register_master failed\n");
|
dev_dbg(&pdev->dev, "spi_register_master failed\n");
|
||||||
goto fail2;
|
goto fail2;
|
||||||
}
|
}
|
||||||
pm_runtime_enable(mcfqspi->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "Coldfire QSPI bus driver\n");
|
dev_info(&pdev->dev, "Coldfire QSPI bus driver\n");
|
||||||
|
|
||||||
|
@ -473,9 +453,8 @@ static int mcfqspi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct spi_master *master = platform_get_drvdata(pdev);
|
struct spi_master *master = platform_get_drvdata(pdev);
|
||||||
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
|
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
|
||||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
|
|
||||||
pm_runtime_disable(mcfqspi->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
/* disable the hardware (set the baud rate to 0) */
|
/* disable the hardware (set the baud rate to 0) */
|
||||||
mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR);
|
mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR);
|
||||||
|
|
||||||
|
@ -490,8 +469,11 @@ static int mcfqspi_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct spi_master *master = dev_get_drvdata(dev);
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
|
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
spi_master_suspend(master);
|
ret = spi_master_suspend(master);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
clk_disable(mcfqspi->clk);
|
clk_disable(mcfqspi->clk);
|
||||||
|
|
||||||
|
@ -503,11 +485,9 @@ static int mcfqspi_resume(struct device *dev)
|
||||||
struct spi_master *master = dev_get_drvdata(dev);
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
|
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
spi_master_resume(master);
|
|
||||||
|
|
||||||
clk_enable(mcfqspi->clk);
|
clk_enable(mcfqspi->clk);
|
||||||
|
|
||||||
return 0;
|
return spi_master_resume(master);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -802,8 +802,7 @@ static int spi_davinci_get_pdata(struct platform_device *pdev,
|
||||||
pdata = &dspi->pdata;
|
pdata = &dspi->pdata;
|
||||||
|
|
||||||
pdata->version = SPI_VERSION_1;
|
pdata->version = SPI_VERSION_1;
|
||||||
match = of_match_device(of_match_ptr(davinci_spi_of_match),
|
match = of_match_device(davinci_spi_of_match, &pdev->dev);
|
||||||
&pdev->dev);
|
|
||||||
if (!match)
|
if (!match)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
@ -824,7 +823,6 @@ static int spi_davinci_get_pdata(struct platform_device *pdev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define davinci_spi_of_match NULL
|
|
||||||
static struct davinci_spi_platform_data
|
static struct davinci_spi_platform_data
|
||||||
*spi_davinci_get_pdata(struct platform_device *pdev,
|
*spi_davinci_get_pdata(struct platform_device *pdev,
|
||||||
struct davinci_spi *dspi)
|
struct davinci_spi *dspi)
|
||||||
|
@ -864,10 +862,6 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
dspi = spi_master_get_devdata(master);
|
dspi = spi_master_get_devdata(master);
|
||||||
if (dspi == NULL) {
|
|
||||||
ret = -ENOENT;
|
|
||||||
goto free_master;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev_get_platdata(&pdev->dev)) {
|
if (dev_get_platdata(&pdev->dev)) {
|
||||||
pdata = dev_get_platdata(&pdev->dev);
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
@ -908,10 +902,6 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
||||||
goto free_master;
|
goto free_master;
|
||||||
|
|
||||||
dspi->bitbang.master = master;
|
dspi->bitbang.master = master;
|
||||||
if (dspi->bitbang.master == NULL) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto free_master;
|
|
||||||
}
|
|
||||||
|
|
||||||
dspi->clk = devm_clk_get(&pdev->dev, NULL);
|
dspi->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(dspi->clk)) {
|
if (IS_ERR(dspi->clk)) {
|
||||||
|
@ -1040,7 +1030,7 @@ static struct platform_driver davinci_spi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "spi_davinci",
|
.name = "spi_davinci",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.of_match_table = davinci_spi_of_match,
|
.of_match_table = of_match_ptr(davinci_spi_of_match),
|
||||||
},
|
},
|
||||||
.probe = davinci_spi_probe,
|
.probe = davinci_spi_probe,
|
||||||
.remove = davinci_spi_remove,
|
.remove = davinci_spi_remove,
|
||||||
|
|
|
@ -66,7 +66,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
dws->bus_num = 0;
|
dws->bus_num = pdev->id;
|
||||||
dws->num_cs = 4;
|
dws->num_cs = 4;
|
||||||
dws->max_freq = clk_get_rate(dwsmmio->clk);
|
dws->max_freq = clk_get_rate(dwsmmio->clk);
|
||||||
|
|
||||||
|
|
|
@ -276,8 +276,7 @@ static void giveback(struct dw_spi *dws)
|
||||||
queue_work(dws->workqueue, &dws->pump_messages);
|
queue_work(dws->workqueue, &dws->pump_messages);
|
||||||
spin_unlock_irqrestore(&dws->lock, flags);
|
spin_unlock_irqrestore(&dws->lock, flags);
|
||||||
|
|
||||||
last_transfer = list_entry(msg->transfers.prev,
|
last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
|
||||||
struct spi_transfer,
|
|
||||||
transfer_list);
|
transfer_list);
|
||||||
|
|
||||||
if (!last_transfer->cs_change && dws->cs_control)
|
if (!last_transfer->cs_change && dws->cs_control)
|
||||||
|
@ -439,12 +438,6 @@ static void pump_transfers(unsigned long data)
|
||||||
|
|
||||||
if (transfer->speed_hz != speed) {
|
if (transfer->speed_hz != speed) {
|
||||||
speed = transfer->speed_hz;
|
speed = transfer->speed_hz;
|
||||||
if (speed > dws->max_freq) {
|
|
||||||
printk(KERN_ERR "MRST SPI0: unsupported"
|
|
||||||
"freq: %dHz\n", speed);
|
|
||||||
message->status = -EIO;
|
|
||||||
goto early_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clk_div doesn't support odd number */
|
/* clk_div doesn't support odd number */
|
||||||
clk_div = dws->max_freq / speed;
|
clk_div = dws->max_freq / speed;
|
||||||
|
@ -671,12 +664,6 @@ static int dw_spi_setup(struct spi_device *spi)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dw_spi_cleanup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct chip_data *chip = spi_get_ctldata(spi);
|
|
||||||
kfree(chip);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int init_queue(struct dw_spi *dws)
|
static int init_queue(struct dw_spi *dws)
|
||||||
{
|
{
|
||||||
INIT_LIST_HEAD(&dws->queue);
|
INIT_LIST_HEAD(&dws->queue);
|
||||||
|
@ -806,9 +793,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
||||||
master->bus_num = dws->bus_num;
|
master->bus_num = dws->bus_num;
|
||||||
master->num_chipselect = dws->num_cs;
|
master->num_chipselect = dws->num_cs;
|
||||||
master->cleanup = dw_spi_cleanup;
|
|
||||||
master->setup = dw_spi_setup;
|
master->setup = dw_spi_setup;
|
||||||
master->transfer = dw_spi_transfer;
|
master->transfer = dw_spi_transfer;
|
||||||
|
master->max_speed_hz = dws->max_freq;
|
||||||
|
|
||||||
/* Basic HW init */
|
/* Basic HW init */
|
||||||
spi_hw_init(dws);
|
spi_hw_init(dws);
|
||||||
|
|
|
@ -198,7 +198,7 @@ static int efm32_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
|
||||||
|
|
||||||
efm32_spi_filltx(ddata);
|
efm32_spi_filltx(ddata);
|
||||||
|
|
||||||
init_completion(&ddata->done);
|
reinit_completion(&ddata->done);
|
||||||
|
|
||||||
efm32_spi_write32(ddata, REG_IF_TXBL | REG_IF_RXDATAV, REG_IEN);
|
efm32_spi_write32(ddata, REG_IF_TXBL | REG_IF_RXDATAV, REG_IEN);
|
||||||
|
|
||||||
|
@ -287,17 +287,17 @@ static u32 efm32_spi_get_configured_location(struct efm32_spi_ddata *ddata)
|
||||||
return (reg & REG_ROUTE_LOCATION__MASK) >> __ffs(REG_ROUTE_LOCATION__MASK);
|
return (reg & REG_ROUTE_LOCATION__MASK) >> __ffs(REG_ROUTE_LOCATION__MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int efm32_spi_probe_dt(struct platform_device *pdev,
|
static void efm32_spi_probe_dt(struct platform_device *pdev,
|
||||||
struct spi_master *master, struct efm32_spi_ddata *ddata)
|
struct spi_master *master, struct efm32_spi_ddata *ddata)
|
||||||
{
|
{
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
u32 location;
|
u32 location;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!np)
|
ret = of_property_read_u32(np, "efm32,location", &location);
|
||||||
return 1;
|
if (ret)
|
||||||
|
/* fall back to old and (wrongly) generic property "location" */
|
||||||
ret = of_property_read_u32(np, "location", &location);
|
ret = of_property_read_u32(np, "location", &location);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
dev_dbg(&pdev->dev, "using location %u\n", location);
|
dev_dbg(&pdev->dev, "using location %u\n", location);
|
||||||
} else {
|
} else {
|
||||||
|
@ -308,11 +308,6 @@ static int efm32_spi_probe_dt(struct platform_device *pdev,
|
||||||
}
|
}
|
||||||
|
|
||||||
ddata->pdata.location = location;
|
ddata->pdata.location = location;
|
||||||
|
|
||||||
/* spi core takes care about the bus number using an alias */
|
|
||||||
master->bus_num = -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int efm32_spi_probe(struct platform_device *pdev)
|
static int efm32_spi_probe(struct platform_device *pdev)
|
||||||
|
@ -322,9 +317,14 @@ static int efm32_spi_probe(struct platform_device *pdev)
|
||||||
int ret;
|
int ret;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
unsigned int num_cs, i;
|
int num_cs, i;
|
||||||
|
|
||||||
|
if (!np)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
num_cs = of_gpio_named_count(np, "cs-gpios");
|
num_cs = of_gpio_named_count(np, "cs-gpios");
|
||||||
|
if (num_cs < 0)
|
||||||
|
return num_cs;
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev,
|
master = spi_alloc_master(&pdev->dev,
|
||||||
sizeof(*ddata) + num_cs * sizeof(unsigned));
|
sizeof(*ddata) + num_cs * sizeof(unsigned));
|
||||||
|
@ -349,6 +349,7 @@ static int efm32_spi_probe(struct platform_device *pdev)
|
||||||
ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs;
|
ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs;
|
||||||
|
|
||||||
spin_lock_init(&ddata->lock);
|
spin_lock_init(&ddata->lock);
|
||||||
|
init_completion(&ddata->done);
|
||||||
|
|
||||||
ddata->clk = devm_clk_get(&pdev->dev, NULL);
|
ddata->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(ddata->clk)) {
|
if (IS_ERR(ddata->clk)) {
|
||||||
|
@ -415,23 +416,7 @@ static int efm32_spi_probe(struct platform_device *pdev)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = efm32_spi_probe_dt(pdev, master, ddata);
|
efm32_spi_probe_dt(pdev, master, ddata);
|
||||||
if (ret > 0) {
|
|
||||||
/* not created by device tree */
|
|
||||||
const struct efm32_spi_pdata *pdata =
|
|
||||||
dev_get_platdata(&pdev->dev);
|
|
||||||
|
|
||||||
if (pdata)
|
|
||||||
ddata->pdata = *pdata;
|
|
||||||
else
|
|
||||||
ddata->pdata.location =
|
|
||||||
efm32_spi_get_configured_location(ddata);
|
|
||||||
|
|
||||||
master->bus_num = pdev->id;
|
|
||||||
|
|
||||||
} else if (ret < 0) {
|
|
||||||
goto err_disable_clk;
|
|
||||||
}
|
|
||||||
|
|
||||||
efm32_spi_write32(ddata, 0, REG_IEN);
|
efm32_spi_write32(ddata, 0, REG_IEN);
|
||||||
efm32_spi_write32(ddata, REG_ROUTE_TXPEN | REG_ROUTE_RXPEN |
|
efm32_spi_write32(ddata, REG_ROUTE_TXPEN | REG_ROUTE_RXPEN |
|
||||||
|
@ -487,6 +472,9 @@ static int efm32_spi_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
static const struct of_device_id efm32_spi_dt_ids[] = {
|
static const struct of_device_id efm32_spi_dt_ids[] = {
|
||||||
{
|
{
|
||||||
|
.compatible = "energymicro,efm32-spi",
|
||||||
|
}, {
|
||||||
|
/* doesn't follow the "vendor,device" scheme, don't use */
|
||||||
.compatible = "efm32,spi",
|
.compatible = "efm32,spi",
|
||||||
}, {
|
}, {
|
||||||
/* sentinel */
|
/* sentinel */
|
||||||
|
|
|
@ -73,8 +73,6 @@
|
||||||
* @clk: clock for the controller
|
* @clk: clock for the controller
|
||||||
* @regs_base: pointer to ioremap()'d registers
|
* @regs_base: pointer to ioremap()'d registers
|
||||||
* @sspdr_phys: physical address of the SSPDR register
|
* @sspdr_phys: physical address of the SSPDR register
|
||||||
* @min_rate: minimum clock rate (in Hz) supported by the controller
|
|
||||||
* @max_rate: maximum clock rate (in Hz) supported by the controller
|
|
||||||
* @wait: wait here until given transfer is completed
|
* @wait: wait here until given transfer is completed
|
||||||
* @current_msg: message that is currently processed (or %NULL if none)
|
* @current_msg: message that is currently processed (or %NULL if none)
|
||||||
* @tx: current byte in transfer to transmit
|
* @tx: current byte in transfer to transmit
|
||||||
|
@ -95,8 +93,6 @@ struct ep93xx_spi {
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
void __iomem *regs_base;
|
void __iomem *regs_base;
|
||||||
unsigned long sspdr_phys;
|
unsigned long sspdr_phys;
|
||||||
unsigned long min_rate;
|
|
||||||
unsigned long max_rate;
|
|
||||||
struct completion wait;
|
struct completion wait;
|
||||||
struct spi_message *current_msg;
|
struct spi_message *current_msg;
|
||||||
size_t tx;
|
size_t tx;
|
||||||
|
@ -199,9 +195,9 @@ static void ep93xx_spi_disable_interrupts(const struct ep93xx_spi *espi)
|
||||||
* @div_scr: pointer to return the scr divider
|
* @div_scr: pointer to return the scr divider
|
||||||
*/
|
*/
|
||||||
static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi,
|
static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi,
|
||||||
unsigned long rate,
|
u32 rate, u8 *div_cpsr, u8 *div_scr)
|
||||||
u8 *div_cpsr, u8 *div_scr)
|
|
||||||
{
|
{
|
||||||
|
struct spi_master *master = platform_get_drvdata(espi->pdev);
|
||||||
unsigned long spi_clk_rate = clk_get_rate(espi->clk);
|
unsigned long spi_clk_rate = clk_get_rate(espi->clk);
|
||||||
int cpsr, scr;
|
int cpsr, scr;
|
||||||
|
|
||||||
|
@ -210,7 +206,7 @@ static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi,
|
||||||
* controller. Note that minimum value is already checked in
|
* controller. Note that minimum value is already checked in
|
||||||
* ep93xx_spi_transfer_one_message().
|
* ep93xx_spi_transfer_one_message().
|
||||||
*/
|
*/
|
||||||
rate = clamp(rate, espi->min_rate, espi->max_rate);
|
rate = clamp(rate, master->min_speed_hz, master->max_speed_hz);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate divisors so that we can get speed according the
|
* Calculate divisors so that we can get speed according the
|
||||||
|
@ -735,13 +731,6 @@ static int ep93xx_spi_transfer_one_message(struct spi_master *master,
|
||||||
struct spi_message *msg)
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||||
struct spi_transfer *t;
|
|
||||||
|
|
||||||
/* first validate each transfer */
|
|
||||||
list_for_each_entry(t, &msg->transfers, transfer_list) {
|
|
||||||
if (t->speed_hz < espi->min_rate)
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg->state = NULL;
|
msg->state = NULL;
|
||||||
msg->status = 0;
|
msg->status = 0;
|
||||||
|
@ -917,8 +906,8 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
|
||||||
* Calculate maximum and minimum supported clock rates
|
* Calculate maximum and minimum supported clock rates
|
||||||
* for the controller.
|
* for the controller.
|
||||||
*/
|
*/
|
||||||
espi->max_rate = clk_get_rate(espi->clk) / 2;
|
master->max_speed_hz = clk_get_rate(espi->clk) / 2;
|
||||||
espi->min_rate = clk_get_rate(espi->clk) / (254 * 256);
|
master->min_speed_hz = clk_get_rate(espi->clk) / (254 * 256);
|
||||||
espi->pdev = pdev;
|
espi->pdev = pdev;
|
||||||
|
|
||||||
espi->sspdr_phys = res->start + SSPDR;
|
espi->sspdr_phys = res->start + SSPDR;
|
||||||
|
|
|
@ -312,9 +312,6 @@ static int falcon_sflash_setup(struct spi_device *spi)
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
if (spi->chip_select > 0)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&ebu_lock, flags);
|
spin_lock_irqsave(&ebu_lock, flags);
|
||||||
|
|
||||||
if (spi->max_speed_hz >= CLOCK_100M) {
|
if (spi->max_speed_hz >= CLOCK_100M) {
|
||||||
|
@ -422,9 +419,7 @@ static int falcon_sflash_probe(struct platform_device *pdev)
|
||||||
priv->master = master;
|
priv->master = master;
|
||||||
|
|
||||||
master->mode_bits = SPI_MODE_3;
|
master->mode_bits = SPI_MODE_3;
|
||||||
master->num_chipselect = 1;
|
|
||||||
master->flags = SPI_MASTER_HALF_DUPLEX;
|
master->flags = SPI_MASTER_HALF_DUPLEX;
|
||||||
master->bus_num = -1;
|
|
||||||
master->setup = falcon_sflash_setup;
|
master->setup = falcon_sflash_setup;
|
||||||
master->prepare_transfer_hardware = falcon_sflash_prepare_xfer;
|
master->prepare_transfer_hardware = falcon_sflash_prepare_xfer;
|
||||||
master->transfer_one_message = falcon_sflash_xfer_one;
|
master->transfer_one_message = falcon_sflash_xfer_one;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
@ -108,11 +109,11 @@ struct fsl_dspi {
|
||||||
struct spi_bitbang bitbang;
|
struct spi_bitbang bitbang;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
|
|
||||||
void __iomem *base;
|
struct regmap *regmap;
|
||||||
int irq;
|
int irq;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
|
||||||
struct spi_transfer *cur_transfer;
|
struct spi_transfer *cur_transfer;
|
||||||
struct chip_data *cur_chip;
|
struct chip_data *cur_chip;
|
||||||
size_t len;
|
size_t len;
|
||||||
void *tx;
|
void *tx;
|
||||||
|
@ -123,24 +124,17 @@ struct fsl_dspi {
|
||||||
u8 cs;
|
u8 cs;
|
||||||
u16 void_write_data;
|
u16 void_write_data;
|
||||||
|
|
||||||
wait_queue_head_t waitq;
|
wait_queue_head_t waitq;
|
||||||
u32 waitflags;
|
u32 waitflags;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int is_double_byte_mode(struct fsl_dspi *dspi)
|
static inline int is_double_byte_mode(struct fsl_dspi *dspi)
|
||||||
{
|
{
|
||||||
return ((readl(dspi->base + SPI_CTAR(dspi->cs)) & SPI_FRAME_BITS_MASK)
|
unsigned int val;
|
||||||
== SPI_FRAME_BITS(8)) ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_bit_mode(struct fsl_dspi *dspi, unsigned char bits)
|
regmap_read(dspi->regmap, SPI_CTAR(dspi->cs), &val);
|
||||||
{
|
|
||||||
u32 temp;
|
|
||||||
|
|
||||||
temp = readl(dspi->base + SPI_CTAR(dspi->cs));
|
return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1;
|
||||||
temp &= ~SPI_FRAME_BITS_MASK;
|
|
||||||
temp |= SPI_FRAME_BITS(bits);
|
|
||||||
writel(temp, dspi->base + SPI_CTAR(dspi->cs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hz_to_spi_baud(char *pbr, char *br, int speed_hz,
|
static void hz_to_spi_baud(char *pbr, char *br, int speed_hz,
|
||||||
|
@ -188,7 +182,8 @@ static int dspi_transfer_write(struct fsl_dspi *dspi)
|
||||||
*/
|
*/
|
||||||
if (tx_word && (dspi->len == 1)) {
|
if (tx_word && (dspi->len == 1)) {
|
||||||
dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
|
dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
|
||||||
set_bit_mode(dspi, 8);
|
regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
|
||||||
|
SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
|
||||||
tx_word = 0;
|
tx_word = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +233,8 @@ static int dspi_transfer_write(struct fsl_dspi *dspi)
|
||||||
dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */
|
dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */
|
||||||
}
|
}
|
||||||
|
|
||||||
writel(dspi_pushr, dspi->base + SPI_PUSHR);
|
regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr);
|
||||||
|
|
||||||
tx_count++;
|
tx_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,17 +249,23 @@ static int dspi_transfer_read(struct fsl_dspi *dspi)
|
||||||
while ((dspi->rx < dspi->rx_end)
|
while ((dspi->rx < dspi->rx_end)
|
||||||
&& (rx_count < DSPI_FIFO_SIZE)) {
|
&& (rx_count < DSPI_FIFO_SIZE)) {
|
||||||
if (rx_word) {
|
if (rx_word) {
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
if ((dspi->rx_end - dspi->rx) == 1)
|
if ((dspi->rx_end - dspi->rx) == 1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR));
|
regmap_read(dspi->regmap, SPI_POPR, &val);
|
||||||
|
d = SPI_POPR_RXDATA(val);
|
||||||
|
|
||||||
if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
|
if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
|
||||||
*(u16 *)dspi->rx = d;
|
*(u16 *)dspi->rx = d;
|
||||||
dspi->rx += 2;
|
dspi->rx += 2;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR));
|
unsigned int val;
|
||||||
|
|
||||||
|
regmap_read(dspi->regmap, SPI_POPR, &val);
|
||||||
|
d = SPI_POPR_RXDATA(val);
|
||||||
if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
|
if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
|
||||||
*(u8 *)dspi->rx = d;
|
*(u8 *)dspi->rx = d;
|
||||||
dspi->rx++;
|
dspi->rx++;
|
||||||
|
@ -295,13 +297,13 @@ static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
if (!dspi->tx)
|
if (!dspi->tx)
|
||||||
dspi->dataflags |= TRAN_STATE_TX_VOID;
|
dspi->dataflags |= TRAN_STATE_TX_VOID;
|
||||||
|
|
||||||
writel(dspi->cur_chip->mcr_val, dspi->base + SPI_MCR);
|
regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val);
|
||||||
writel(dspi->cur_chip->ctar_val, dspi->base + SPI_CTAR(dspi->cs));
|
regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), dspi->cur_chip->ctar_val);
|
||||||
writel(SPI_RSER_EOQFE, dspi->base + SPI_RSER);
|
regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE);
|
||||||
|
|
||||||
if (t->speed_hz)
|
if (t->speed_hz)
|
||||||
writel(dspi->cur_chip->ctar_val,
|
regmap_write(dspi->regmap, SPI_CTAR(dspi->cs),
|
||||||
dspi->base + SPI_CTAR(dspi->cs));
|
dspi->cur_chip->ctar_val);
|
||||||
|
|
||||||
dspi_transfer_write(dspi);
|
dspi_transfer_write(dspi);
|
||||||
|
|
||||||
|
@ -315,7 +317,9 @@ static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
static void dspi_chipselect(struct spi_device *spi, int value)
|
static void dspi_chipselect(struct spi_device *spi, int value)
|
||||||
{
|
{
|
||||||
struct fsl_dspi *dspi = spi_master_get_devdata(spi->master);
|
struct fsl_dspi *dspi = spi_master_get_devdata(spi->master);
|
||||||
u32 pushr = readl(dspi->base + SPI_PUSHR);
|
unsigned int pushr;
|
||||||
|
|
||||||
|
regmap_read(dspi->regmap, SPI_PUSHR, &pushr);
|
||||||
|
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case BITBANG_CS_ACTIVE:
|
case BITBANG_CS_ACTIVE:
|
||||||
|
@ -326,7 +330,7 @@ static void dspi_chipselect(struct spi_device *spi, int value)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
writel(pushr, dspi->base + SPI_PUSHR);
|
regmap_write(dspi->regmap, SPI_PUSHR, pushr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
|
@ -338,7 +342,8 @@ static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
/* Only alloc on first setup */
|
/* Only alloc on first setup */
|
||||||
chip = spi_get_ctldata(spi);
|
chip = spi_get_ctldata(spi);
|
||||||
if (chip == NULL) {
|
if (chip == NULL) {
|
||||||
chip = kcalloc(1, sizeof(struct chip_data), GFP_KERNEL);
|
chip = devm_kzalloc(&spi->dev, sizeof(struct chip_data),
|
||||||
|
GFP_KERNEL);
|
||||||
if (!chip)
|
if (!chip)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -349,7 +354,6 @@ static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
fmsz = spi->bits_per_word - 1;
|
fmsz = spi->bits_per_word - 1;
|
||||||
} else {
|
} else {
|
||||||
pr_err("Invalid wordsize\n");
|
pr_err("Invalid wordsize\n");
|
||||||
kfree(chip);
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,13 +386,15 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id;
|
struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id;
|
||||||
|
|
||||||
writel(SPI_SR_EOQF, dspi->base + SPI_SR);
|
regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF);
|
||||||
|
|
||||||
dspi_transfer_read(dspi);
|
dspi_transfer_read(dspi);
|
||||||
|
|
||||||
if (!dspi->len) {
|
if (!dspi->len) {
|
||||||
if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM)
|
if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM)
|
||||||
set_bit_mode(dspi, 16);
|
regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
|
||||||
|
SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16));
|
||||||
|
|
||||||
dspi->waitflags = 1;
|
dspi->waitflags = 1;
|
||||||
wake_up_interruptible(&dspi->waitq);
|
wake_up_interruptible(&dspi->waitq);
|
||||||
} else {
|
} else {
|
||||||
|
@ -430,8 +436,13 @@ static int dspi_resume(struct device *dev)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_PM_SLEEP */
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
static const struct dev_pm_ops dspi_pm = {
|
static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume);
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(dspi_suspend, dspi_resume)
|
|
||||||
|
static struct regmap_config dspi_regmap_config = {
|
||||||
|
.reg_bits = 32,
|
||||||
|
.val_bits = 32,
|
||||||
|
.reg_stride = 4,
|
||||||
|
.max_register = 0x88,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int dspi_probe(struct platform_device *pdev)
|
static int dspi_probe(struct platform_device *pdev)
|
||||||
|
@ -440,6 +451,7 @@ static int dspi_probe(struct platform_device *pdev)
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct fsl_dspi *dspi;
|
struct fsl_dspi *dspi;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
void __iomem *base;
|
||||||
int ret = 0, cs_num, bus_num;
|
int ret = 0, cs_num, bus_num;
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi));
|
master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi));
|
||||||
|
@ -474,12 +486,24 @@ static int dspi_probe(struct platform_device *pdev)
|
||||||
master->bus_num = bus_num;
|
master->bus_num = bus_num;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
dspi->base = devm_ioremap_resource(&pdev->dev, res);
|
base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(dspi->base)) {
|
if (IS_ERR(base)) {
|
||||||
ret = PTR_ERR(dspi->base);
|
ret = PTR_ERR(base);
|
||||||
goto out_master_put;
|
goto out_master_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dspi_regmap_config.lock_arg = dspi;
|
||||||
|
dspi_regmap_config.val_format_endian =
|
||||||
|
of_property_read_bool(np, "big-endian")
|
||||||
|
? REGMAP_ENDIAN_BIG : REGMAP_ENDIAN_DEFAULT;
|
||||||
|
dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base,
|
||||||
|
&dspi_regmap_config);
|
||||||
|
if (IS_ERR(dspi->regmap)) {
|
||||||
|
dev_err(&pdev->dev, "failed to init regmap: %ld\n",
|
||||||
|
PTR_ERR(dspi->regmap));
|
||||||
|
return PTR_ERR(dspi->regmap);
|
||||||
|
}
|
||||||
|
|
||||||
dspi->irq = platform_get_irq(pdev, 0);
|
dspi->irq = platform_get_irq(pdev, 0);
|
||||||
if (dspi->irq < 0) {
|
if (dspi->irq < 0) {
|
||||||
dev_err(&pdev->dev, "can't get platform irq\n");
|
dev_err(&pdev->dev, "can't get platform irq\n");
|
||||||
|
|
|
@ -219,13 +219,8 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t)
|
||||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||||
struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base;
|
struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base;
|
||||||
unsigned int len = t->len;
|
unsigned int len = t->len;
|
||||||
u8 bits_per_word;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
bits_per_word = spi->bits_per_word;
|
|
||||||
if (t->bits_per_word)
|
|
||||||
bits_per_word = t->bits_per_word;
|
|
||||||
|
|
||||||
mpc8xxx_spi->len = t->len;
|
mpc8xxx_spi->len = t->len;
|
||||||
len = roundup(len, 4) / 4;
|
len = roundup(len, 4) / 4;
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,7 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev)
|
||||||
const void *prop;
|
const void *prop;
|
||||||
int ret = -ENOMEM;
|
int ret = -ENOMEM;
|
||||||
|
|
||||||
pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
|
pinfo = devm_kzalloc(&ofdev->dev, sizeof(*pinfo), GFP_KERNEL);
|
||||||
if (!pinfo)
|
if (!pinfo)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -215,15 +215,13 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev)
|
||||||
pdata->sysclk = get_brgfreq();
|
pdata->sysclk = get_brgfreq();
|
||||||
if (pdata->sysclk == -1) {
|
if (pdata->sysclk == -1) {
|
||||||
pdata->sysclk = fsl_get_sys_freq();
|
pdata->sysclk = fsl_get_sys_freq();
|
||||||
if (pdata->sysclk == -1) {
|
if (pdata->sysclk == -1)
|
||||||
ret = -ENODEV;
|
return -ENODEV;
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
ret = of_property_read_u32(np, "clock-frequency", &pdata->sysclk);
|
ret = of_property_read_u32(np, "clock-frequency", &pdata->sysclk);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
return ret;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
prop = of_get_property(np, "mode", NULL);
|
prop = of_get_property(np, "mode", NULL);
|
||||||
|
@ -237,8 +235,4 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev)
|
||||||
pdata->flags = SPI_CPM_MODE | SPI_CPM1;
|
pdata->flags = SPI_CPM_MODE | SPI_CPM1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
|
||||||
kfree(pinfo);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,12 +239,6 @@ static int fsl_spi_setup_transfer(struct spi_device *spi,
|
||||||
if (!bits_per_word)
|
if (!bits_per_word)
|
||||||
bits_per_word = spi->bits_per_word;
|
bits_per_word = spi->bits_per_word;
|
||||||
|
|
||||||
/* Make sure its a bit width we support [4..16, 32] */
|
|
||||||
if ((bits_per_word < 4)
|
|
||||||
|| ((bits_per_word > 16) && (bits_per_word != 32))
|
|
||||||
|| (bits_per_word > mpc8xxx_spi->max_bits_per_word))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (!hz)
|
if (!hz)
|
||||||
hz = spi->max_speed_hz;
|
hz = spi->max_speed_hz;
|
||||||
|
|
||||||
|
@ -362,18 +356,28 @@ static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
|
||||||
static void fsl_spi_do_one_msg(struct spi_message *m)
|
static void fsl_spi_do_one_msg(struct spi_message *m)
|
||||||
{
|
{
|
||||||
struct spi_device *spi = m->spi;
|
struct spi_device *spi = m->spi;
|
||||||
struct spi_transfer *t;
|
struct spi_transfer *t, *first;
|
||||||
unsigned int cs_change;
|
unsigned int cs_change;
|
||||||
const int nsecs = 50;
|
const int nsecs = 50;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
/* Don't allow changes if CS is active */
|
||||||
|
first = list_first_entry(&m->transfers, struct spi_transfer,
|
||||||
|
transfer_list);
|
||||||
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
|
if ((first->bits_per_word != t->bits_per_word) ||
|
||||||
|
(first->speed_hz != t->speed_hz)) {
|
||||||
|
status = -EINVAL;
|
||||||
|
dev_err(&spi->dev,
|
||||||
|
"bits_per_word/speed_hz should be same for the same SPI transfer\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cs_change = 1;
|
cs_change = 1;
|
||||||
status = 0;
|
status = -EINVAL;
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
if (t->bits_per_word || t->speed_hz) {
|
if (t->bits_per_word || t->speed_hz) {
|
||||||
/* Don't allow changes if CS is active */
|
|
||||||
status = -EINVAL;
|
|
||||||
|
|
||||||
if (cs_change)
|
if (cs_change)
|
||||||
status = fsl_spi_setup_transfer(spi, t);
|
status = fsl_spi_setup_transfer(spi, t);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
|
@ -641,6 +645,10 @@ static struct spi_master * fsl_spi_probe(struct device *dev,
|
||||||
if (mpc8xxx_spi->type == TYPE_GRLIB)
|
if (mpc8xxx_spi->type == TYPE_GRLIB)
|
||||||
fsl_spi_grlib_probe(dev);
|
fsl_spi_grlib_probe(dev);
|
||||||
|
|
||||||
|
master->bits_per_word_mask =
|
||||||
|
(SPI_BPW_RANGE_MASK(4, 16) | SPI_BPW_MASK(32)) &
|
||||||
|
SPI_BPW_RANGE_MASK(1, mpc8xxx_spi->max_bits_per_word);
|
||||||
|
|
||||||
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
|
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
|
||||||
mpc8xxx_spi->set_shifts = fsl_spi_qe_cpu_set_shifts;
|
mpc8xxx_spi->set_shifts = fsl_spi_qe_cpu_set_shifts;
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
*/
|
*/
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
@ -250,7 +249,7 @@ static int spi_gpio_setup(struct spi_device *spi)
|
||||||
/*
|
/*
|
||||||
* ... otherwise, take it from spi->controller_data
|
* ... otherwise, take it from spi->controller_data
|
||||||
*/
|
*/
|
||||||
cs = (unsigned int) spi->controller_data;
|
cs = (unsigned int)(uintptr_t) spi->controller_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!spi->controller_state) {
|
if (!spi->controller_state) {
|
||||||
|
@ -503,13 +502,12 @@ static int spi_gpio_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct spi_gpio *spi_gpio;
|
struct spi_gpio *spi_gpio;
|
||||||
struct spi_gpio_platform_data *pdata;
|
struct spi_gpio_platform_data *pdata;
|
||||||
int status;
|
|
||||||
|
|
||||||
spi_gpio = platform_get_drvdata(pdev);
|
spi_gpio = platform_get_drvdata(pdev);
|
||||||
pdata = dev_get_platdata(&pdev->dev);
|
pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
|
||||||
/* stop() unregisters child devices too */
|
/* stop() unregisters child devices too */
|
||||||
status = spi_bitbang_stop(&spi_gpio->bitbang);
|
spi_bitbang_stop(&spi_gpio->bitbang);
|
||||||
|
|
||||||
if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)
|
if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)
|
||||||
gpio_free(SPI_MISO_GPIO);
|
gpio_free(SPI_MISO_GPIO);
|
||||||
|
@ -518,7 +516,7 @@ static int spi_gpio_remove(struct platform_device *pdev)
|
||||||
gpio_free(SPI_SCK_GPIO);
|
gpio_free(SPI_SCK_GPIO);
|
||||||
spi_master_put(spi_gpio->bitbang.master);
|
spi_master_put(spi_gpio->bitbang.master);
|
||||||
|
|
||||||
return status;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
@ -741,7 +740,7 @@ static int spi_imx_transfer(struct spi_device *spi,
|
||||||
spi_imx->count = transfer->len;
|
spi_imx->count = transfer->len;
|
||||||
spi_imx->txfifo = 0;
|
spi_imx->txfifo = 0;
|
||||||
|
|
||||||
init_completion(&spi_imx->xfer_done);
|
reinit_completion(&spi_imx->xfer_done);
|
||||||
|
|
||||||
spi_imx_push(spi_imx);
|
spi_imx_push(spi_imx);
|
||||||
|
|
||||||
|
@ -880,12 +879,12 @@ static int spi_imx_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
spi_imx->irq = platform_get_irq(pdev, 0);
|
spi_imx->irq = platform_get_irq(pdev, 0);
|
||||||
if (spi_imx->irq < 0) {
|
if (spi_imx->irq < 0) {
|
||||||
ret = -EINVAL;
|
ret = spi_imx->irq;
|
||||||
goto out_master_put;
|
goto out_master_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, spi_imx->irq, spi_imx_isr, 0,
|
ret = devm_request_irq(&pdev->dev, spi_imx->irq, spi_imx_isr, 0,
|
||||||
DRIVER_NAME, spi_imx);
|
dev_name(&pdev->dev), spi_imx);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret);
|
dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret);
|
||||||
goto out_master_put;
|
goto out_master_put;
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
|
@ -466,10 +465,8 @@ static void mpc512x_spi_cs_control(struct spi_device *spi, bool onoff)
|
||||||
gpio_set_value(spi->cs_gpio, onoff);
|
gpio_set_value(spi->cs_gpio, onoff);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* bus_num is used only for the case dev->platform_data == NULL */
|
|
||||||
static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
|
static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
|
||||||
u32 size, unsigned int irq,
|
u32 size, unsigned int irq)
|
||||||
s16 bus_num)
|
|
||||||
{
|
{
|
||||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||||
struct mpc512x_psc_spi *mps;
|
struct mpc512x_psc_spi *mps;
|
||||||
|
@ -488,7 +485,6 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
|
||||||
|
|
||||||
if (pdata == NULL) {
|
if (pdata == NULL) {
|
||||||
mps->cs_control = mpc512x_spi_cs_control;
|
mps->cs_control = mpc512x_spi_cs_control;
|
||||||
master->bus_num = bus_num;
|
|
||||||
} else {
|
} else {
|
||||||
mps->cs_control = pdata->cs_control;
|
mps->cs_control = pdata->cs_control;
|
||||||
master->bus_num = pdata->bus_num;
|
master->bus_num = pdata->bus_num;
|
||||||
|
@ -574,7 +570,6 @@ static int mpc512x_psc_spi_of_probe(struct platform_device *op)
|
||||||
{
|
{
|
||||||
const u32 *regaddr_p;
|
const u32 *regaddr_p;
|
||||||
u64 regaddr64, size64;
|
u64 regaddr64, size64;
|
||||||
s16 id = -1;
|
|
||||||
|
|
||||||
regaddr_p = of_get_address(op->dev.of_node, 0, &size64, NULL);
|
regaddr_p = of_get_address(op->dev.of_node, 0, &size64, NULL);
|
||||||
if (!regaddr_p) {
|
if (!regaddr_p) {
|
||||||
|
@ -583,16 +578,8 @@ static int mpc512x_psc_spi_of_probe(struct platform_device *op)
|
||||||
}
|
}
|
||||||
regaddr64 = of_translate_address(op->dev.of_node, regaddr_p);
|
regaddr64 = of_translate_address(op->dev.of_node, regaddr_p);
|
||||||
|
|
||||||
/* get PSC id (0..11, used by port_config) */
|
|
||||||
id = of_alias_get_id(op->dev.of_node, "spi");
|
|
||||||
if (id < 0) {
|
|
||||||
dev_err(&op->dev, "no alias id for %s\n",
|
|
||||||
op->dev.of_node->full_name);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mpc512x_psc_spi_do_probe(&op->dev, (u32) regaddr64, (u32) size64,
|
return mpc512x_psc_spi_do_probe(&op->dev, (u32) regaddr64, (u32) size64,
|
||||||
irq_of_parse_and_map(op->dev.of_node, 0), id);
|
irq_of_parse_and_map(op->dev.of_node, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mpc512x_psc_spi_of_remove(struct platform_device *op)
|
static int mpc512x_psc_spi_of_remove(struct platform_device *op)
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
@ -357,20 +356,6 @@ static void mpc52xx_spi_wq(struct work_struct *work)
|
||||||
* spi_master ops
|
* spi_master ops
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int mpc52xx_spi_setup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
if (spi->bits_per_word % 8)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (spi->chip_select >= spi->master->num_chipselect)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m)
|
static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m)
|
||||||
{
|
{
|
||||||
struct mpc52xx_spi *ms = spi_master_get_devdata(spi->master);
|
struct mpc52xx_spi *ms = spi_master_get_devdata(spi->master);
|
||||||
|
@ -433,9 +418,9 @@ static int mpc52xx_spi_probe(struct platform_device *op)
|
||||||
goto err_alloc;
|
goto err_alloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
master->setup = mpc52xx_spi_setup;
|
|
||||||
master->transfer = mpc52xx_spi_transfer;
|
master->transfer = mpc52xx_spi_transfer;
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
master->dev.of_node = op->dev.of_node;
|
master->dev.of_node = op->dev.of_node;
|
||||||
|
|
||||||
platform_set_drvdata(op, master);
|
platform_set_drvdata(op, master);
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
@ -371,7 +370,7 @@ static int mxs_spi_transfer_one(struct spi_master *master,
|
||||||
{
|
{
|
||||||
struct mxs_spi *spi = spi_master_get_devdata(master);
|
struct mxs_spi *spi = spi_master_get_devdata(master);
|
||||||
struct mxs_ssp *ssp = &spi->ssp;
|
struct mxs_ssp *ssp = &spi->ssp;
|
||||||
struct spi_transfer *t, *tmp_t;
|
struct spi_transfer *t;
|
||||||
unsigned int flag;
|
unsigned int flag;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
|
@ -381,7 +380,7 @@ static int mxs_spi_transfer_one(struct spi_master *master,
|
||||||
writel(mxs_spi_cs_to_reg(m->spi->chip_select),
|
writel(mxs_spi_cs_to_reg(m->spi->chip_select),
|
||||||
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
|
||||||
|
|
||||||
list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) {
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
|
|
||||||
status = mxs_spi_setup_transfer(m->spi, t);
|
status = mxs_spi_setup_transfer(m->spi, t);
|
||||||
if (status)
|
if (status)
|
||||||
|
@ -473,7 +472,7 @@ static int mxs_spi_probe(struct platform_device *pdev)
|
||||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
irq_err = platform_get_irq(pdev, 0);
|
irq_err = platform_get_irq(pdev, 0);
|
||||||
if (irq_err < 0)
|
if (irq_err < 0)
|
||||||
return -EINVAL;
|
return irq_err;
|
||||||
|
|
||||||
base = devm_ioremap_resource(&pdev->dev, iores);
|
base = devm_ioremap_resource(&pdev->dev, iores);
|
||||||
if (IS_ERR(base))
|
if (IS_ERR(base))
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
@ -38,7 +37,9 @@
|
||||||
/* usi register bit */
|
/* usi register bit */
|
||||||
#define ENINT (0x01 << 17)
|
#define ENINT (0x01 << 17)
|
||||||
#define ENFLG (0x01 << 16)
|
#define ENFLG (0x01 << 16)
|
||||||
|
#define SLEEP (0x0f << 12)
|
||||||
#define TXNUM (0x03 << 8)
|
#define TXNUM (0x03 << 8)
|
||||||
|
#define TXBITLEN (0x1f << 3)
|
||||||
#define TXNEG (0x01 << 2)
|
#define TXNEG (0x01 << 2)
|
||||||
#define RXNEG (0x01 << 1)
|
#define RXNEG (0x01 << 1)
|
||||||
#define LSB (0x01 << 10)
|
#define LSB (0x01 << 10)
|
||||||
|
@ -58,11 +59,8 @@ struct nuc900_spi {
|
||||||
unsigned char *rx;
|
unsigned char *rx;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct spi_device *curdev;
|
|
||||||
struct device *dev;
|
|
||||||
struct nuc900_spi_info *pdata;
|
struct nuc900_spi_info *pdata;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
struct resource *res;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct nuc900_spi *to_hw(struct spi_device *sdev)
|
static inline struct nuc900_spi *to_hw(struct spi_device *sdev)
|
||||||
|
@ -119,19 +117,16 @@ static void nuc900_spi_chipsel(struct spi_device *spi, int value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nuc900_spi_setup_txnum(struct nuc900_spi *hw,
|
static void nuc900_spi_setup_txnum(struct nuc900_spi *hw, unsigned int txnum)
|
||||||
unsigned int txnum)
|
|
||||||
{
|
{
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&hw->lock, flags);
|
spin_lock_irqsave(&hw->lock, flags);
|
||||||
|
|
||||||
val = __raw_readl(hw->regs + USI_CNT);
|
val = __raw_readl(hw->regs + USI_CNT) & ~TXNUM;
|
||||||
|
|
||||||
if (!txnum)
|
if (txnum)
|
||||||
val &= ~TXNUM;
|
|
||||||
else
|
|
||||||
val |= txnum << 0x08;
|
val |= txnum << 0x08;
|
||||||
|
|
||||||
__raw_writel(val, hw->regs + USI_CNT);
|
__raw_writel(val, hw->regs + USI_CNT);
|
||||||
|
@ -148,7 +143,7 @@ static void nuc900_spi_setup_txbitlen(struct nuc900_spi *hw,
|
||||||
|
|
||||||
spin_lock_irqsave(&hw->lock, flags);
|
spin_lock_irqsave(&hw->lock, flags);
|
||||||
|
|
||||||
val = __raw_readl(hw->regs + USI_CNT);
|
val = __raw_readl(hw->regs + USI_CNT) & ~TXBITLEN;
|
||||||
|
|
||||||
val |= (txbitlen << 0x03);
|
val |= (txbitlen << 0x03);
|
||||||
|
|
||||||
|
@ -287,12 +282,11 @@ static void nuc900_set_sleep(struct nuc900_spi *hw, unsigned int sleep)
|
||||||
|
|
||||||
spin_lock_irqsave(&hw->lock, flags);
|
spin_lock_irqsave(&hw->lock, flags);
|
||||||
|
|
||||||
val = __raw_readl(hw->regs + USI_CNT);
|
val = __raw_readl(hw->regs + USI_CNT) & ~SLEEP;
|
||||||
|
|
||||||
if (sleep)
|
if (sleep)
|
||||||
val |= (sleep << 12);
|
val |= (sleep << 12);
|
||||||
else
|
|
||||||
val &= ~(0x0f << 12);
|
|
||||||
__raw_writel(val, hw->regs + USI_CNT);
|
__raw_writel(val, hw->regs + USI_CNT);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&hw->lock, flags);
|
spin_unlock_irqrestore(&hw->lock, flags);
|
||||||
|
@ -338,6 +332,7 @@ static int nuc900_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct nuc900_spi *hw;
|
struct nuc900_spi *hw;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
|
struct resource *res;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof(struct nuc900_spi));
|
master = spi_alloc_master(&pdev->dev, sizeof(struct nuc900_spi));
|
||||||
|
@ -349,7 +344,6 @@ static int nuc900_spi_probe(struct platform_device *pdev)
|
||||||
hw = spi_master_get_devdata(master);
|
hw = spi_master_get_devdata(master);
|
||||||
hw->master = master;
|
hw->master = master;
|
||||||
hw->pdata = dev_get_platdata(&pdev->dev);
|
hw->pdata = dev_get_platdata(&pdev->dev);
|
||||||
hw->dev = &pdev->dev;
|
|
||||||
|
|
||||||
if (hw->pdata == NULL) {
|
if (hw->pdata == NULL) {
|
||||||
dev_err(&pdev->dev, "No platform data supplied\n");
|
dev_err(&pdev->dev, "No platform data supplied\n");
|
||||||
|
@ -369,8 +363,8 @@ static int nuc900_spi_probe(struct platform_device *pdev)
|
||||||
hw->bitbang.chipselect = nuc900_spi_chipsel;
|
hw->bitbang.chipselect = nuc900_spi_chipsel;
|
||||||
hw->bitbang.txrx_bufs = nuc900_spi_txrx;
|
hw->bitbang.txrx_bufs = nuc900_spi_txrx;
|
||||||
|
|
||||||
hw->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
hw->regs = devm_ioremap_resource(&pdev->dev, hw->res);
|
hw->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(hw->regs)) {
|
if (IS_ERR(hw->regs)) {
|
||||||
err = PTR_ERR(hw->regs);
|
err = PTR_ERR(hw->regs);
|
||||||
goto err_pdata;
|
goto err_pdata;
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -267,8 +266,6 @@ static int tiny_spi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* setup the state for the bitbang driver */
|
/* setup the state for the bitbang driver */
|
||||||
hw->bitbang.master = master;
|
hw->bitbang.master = master;
|
||||||
if (!hw->bitbang.master)
|
|
||||||
return err;
|
|
||||||
hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
|
hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
|
||||||
hw->bitbang.chipselect = tiny_spi_chipselect;
|
hw->bitbang.chipselect = tiny_spi_chipselect;
|
||||||
hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
|
hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
|
||||||
|
@ -33,13 +32,6 @@ struct octeon_spi {
|
||||||
u64 cs_enax;
|
u64 cs_enax;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct octeon_spi_setup {
|
|
||||||
u32 max_speed_hz;
|
|
||||||
u8 chip_select;
|
|
||||||
u8 mode;
|
|
||||||
u8 bits_per_word;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void octeon_spi_wait_ready(struct octeon_spi *p)
|
static void octeon_spi_wait_ready(struct octeon_spi *p)
|
||||||
{
|
{
|
||||||
union cvmx_mpi_sts mpi_sts;
|
union cvmx_mpi_sts mpi_sts;
|
||||||
|
@ -57,6 +49,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
|
||||||
struct spi_transfer *xfer,
|
struct spi_transfer *xfer,
|
||||||
bool last_xfer)
|
bool last_xfer)
|
||||||
{
|
{
|
||||||
|
struct spi_device *spi = msg->spi;
|
||||||
union cvmx_mpi_cfg mpi_cfg;
|
union cvmx_mpi_cfg mpi_cfg;
|
||||||
union cvmx_mpi_tx mpi_tx;
|
union cvmx_mpi_tx mpi_tx;
|
||||||
unsigned int clkdiv;
|
unsigned int clkdiv;
|
||||||
|
@ -68,18 +61,11 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
|
||||||
int len;
|
int len;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
struct octeon_spi_setup *msg_setup = spi_get_ctldata(msg->spi);
|
mode = spi->mode;
|
||||||
|
|
||||||
speed_hz = msg_setup->max_speed_hz;
|
|
||||||
mode = msg_setup->mode;
|
|
||||||
cpha = mode & SPI_CPHA;
|
cpha = mode & SPI_CPHA;
|
||||||
cpol = mode & SPI_CPOL;
|
cpol = mode & SPI_CPOL;
|
||||||
|
|
||||||
if (xfer->speed_hz)
|
speed_hz = xfer->speed_hz ? : spi->max_speed_hz;
|
||||||
speed_hz = xfer->speed_hz;
|
|
||||||
|
|
||||||
if (speed_hz > OCTEON_SPI_MAX_CLOCK_HZ)
|
|
||||||
speed_hz = OCTEON_SPI_MAX_CLOCK_HZ;
|
|
||||||
|
|
||||||
clkdiv = octeon_get_io_clock_rate() / (2 * speed_hz);
|
clkdiv = octeon_get_io_clock_rate() / (2 * speed_hz);
|
||||||
|
|
||||||
|
@ -93,8 +79,8 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
|
||||||
mpi_cfg.s.cslate = cpha ? 1 : 0;
|
mpi_cfg.s.cslate = cpha ? 1 : 0;
|
||||||
mpi_cfg.s.enable = 1;
|
mpi_cfg.s.enable = 1;
|
||||||
|
|
||||||
if (msg_setup->chip_select < 4)
|
if (spi->chip_select < 4)
|
||||||
p->cs_enax |= 1ull << (12 + msg_setup->chip_select);
|
p->cs_enax |= 1ull << (12 + spi->chip_select);
|
||||||
mpi_cfg.u64 |= p->cs_enax;
|
mpi_cfg.u64 |= p->cs_enax;
|
||||||
|
|
||||||
if (mpi_cfg.u64 != p->last_cfg) {
|
if (mpi_cfg.u64 != p->last_cfg) {
|
||||||
|
@ -114,7 +100,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
|
||||||
cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d);
|
cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d);
|
||||||
}
|
}
|
||||||
mpi_tx.u64 = 0;
|
mpi_tx.u64 = 0;
|
||||||
mpi_tx.s.csid = msg_setup->chip_select;
|
mpi_tx.s.csid = spi->chip_select;
|
||||||
mpi_tx.s.leavecs = 1;
|
mpi_tx.s.leavecs = 1;
|
||||||
mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0;
|
mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0;
|
||||||
mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES;
|
mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES;
|
||||||
|
@ -139,7 +125,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
|
||||||
}
|
}
|
||||||
|
|
||||||
mpi_tx.u64 = 0;
|
mpi_tx.u64 = 0;
|
||||||
mpi_tx.s.csid = msg_setup->chip_select;
|
mpi_tx.s.csid = spi->chip_select;
|
||||||
if (last_xfer)
|
if (last_xfer)
|
||||||
mpi_tx.s.leavecs = xfer->cs_change;
|
mpi_tx.s.leavecs = xfer->cs_change;
|
||||||
else
|
else
|
||||||
|
@ -169,17 +155,9 @@ static int octeon_spi_transfer_one_message(struct spi_master *master,
|
||||||
int status = 0;
|
int status = 0;
|
||||||
struct spi_transfer *xfer;
|
struct spi_transfer *xfer;
|
||||||
|
|
||||||
/*
|
|
||||||
* We better have set the configuration via a call to .setup
|
|
||||||
* before we get here.
|
|
||||||
*/
|
|
||||||
if (spi_get_ctldata(msg->spi) == NULL) {
|
|
||||||
status = -EINVAL;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||||
bool last_xfer = &xfer->transfer_list == msg->transfers.prev;
|
bool last_xfer = list_is_last(&xfer->transfer_list,
|
||||||
|
&msg->transfers);
|
||||||
int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer);
|
int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
status = r;
|
status = r;
|
||||||
|
@ -194,41 +172,6 @@ err:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct octeon_spi_setup *octeon_spi_new_setup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct octeon_spi_setup *setup = kzalloc(sizeof(*setup), GFP_KERNEL);
|
|
||||||
if (!setup)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
setup->max_speed_hz = spi->max_speed_hz;
|
|
||||||
setup->chip_select = spi->chip_select;
|
|
||||||
setup->mode = spi->mode;
|
|
||||||
setup->bits_per_word = spi->bits_per_word;
|
|
||||||
return setup;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int octeon_spi_setup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct octeon_spi_setup *new_setup;
|
|
||||||
struct octeon_spi_setup *old_setup = spi_get_ctldata(spi);
|
|
||||||
|
|
||||||
new_setup = octeon_spi_new_setup(spi);
|
|
||||||
if (!new_setup)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
spi_set_ctldata(spi, new_setup);
|
|
||||||
kfree(old_setup);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void octeon_spi_cleanup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct octeon_spi_setup *old_setup = spi_get_ctldata(spi);
|
|
||||||
spi_set_ctldata(spi, NULL);
|
|
||||||
kfree(old_setup);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int octeon_spi_probe(struct platform_device *pdev)
|
static int octeon_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct resource *res_mem;
|
struct resource *res_mem;
|
||||||
|
@ -257,8 +200,6 @@ static int octeon_spi_probe(struct platform_device *pdev)
|
||||||
p->register_base = (u64)devm_ioremap(&pdev->dev, res_mem->start,
|
p->register_base = (u64)devm_ioremap(&pdev->dev, res_mem->start,
|
||||||
resource_size(res_mem));
|
resource_size(res_mem));
|
||||||
|
|
||||||
/* Dynamic bus numbering */
|
|
||||||
master->bus_num = -1;
|
|
||||||
master->num_chipselect = 4;
|
master->num_chipselect = 4;
|
||||||
master->mode_bits = SPI_CPHA |
|
master->mode_bits = SPI_CPHA |
|
||||||
SPI_CPOL |
|
SPI_CPOL |
|
||||||
|
@ -266,10 +207,9 @@ static int octeon_spi_probe(struct platform_device *pdev)
|
||||||
SPI_LSB_FIRST |
|
SPI_LSB_FIRST |
|
||||||
SPI_3WIRE;
|
SPI_3WIRE;
|
||||||
|
|
||||||
master->setup = octeon_spi_setup;
|
|
||||||
master->cleanup = octeon_spi_cleanup;
|
|
||||||
master->transfer_one_message = octeon_spi_transfer_one_message;
|
master->transfer_one_message = octeon_spi_transfer_one_message;
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
|
master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ;
|
||||||
|
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
err = devm_spi_register_master(&pdev->dev, master);
|
err = devm_spi_register_master(&pdev->dev, master);
|
||||||
|
|
|
@ -83,15 +83,11 @@
|
||||||
#define SPI_SHUTDOWN 1
|
#define SPI_SHUTDOWN 1
|
||||||
|
|
||||||
struct omap1_spi100k {
|
struct omap1_spi100k {
|
||||||
struct spi_master *master;
|
|
||||||
struct clk *ick;
|
struct clk *ick;
|
||||||
struct clk *fck;
|
struct clk *fck;
|
||||||
|
|
||||||
/* Virtual base address of the controller */
|
/* Virtual base address of the controller */
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
|
||||||
/* State of the SPI */
|
|
||||||
unsigned int state;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct omap1_spi100k_cs {
|
struct omap1_spi100k_cs {
|
||||||
|
@ -99,13 +95,6 @@ struct omap1_spi100k_cs {
|
||||||
int word_len;
|
int word_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MOD_REG_BIT(val, mask, set) do { \
|
|
||||||
if (set) \
|
|
||||||
val |= mask; \
|
|
||||||
else \
|
|
||||||
val &= ~mask; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
static void spi100k_enable_clock(struct spi_master *master)
|
static void spi100k_enable_clock(struct spi_master *master)
|
||||||
{
|
{
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
|
@ -139,7 +128,7 @@ static void spi100k_write_data(struct spi_master *master, int len, int data)
|
||||||
}
|
}
|
||||||
|
|
||||||
spi100k_enable_clock(master);
|
spi100k_enable_clock(master);
|
||||||
writew( data , spi100k->base + SPI_TX_MSB);
|
writew(data , spi100k->base + SPI_TX_MSB);
|
||||||
|
|
||||||
writew(SPI_CTRL_SEN(0) |
|
writew(SPI_CTRL_SEN(0) |
|
||||||
SPI_CTRL_WORD_SIZE(len) |
|
SPI_CTRL_WORD_SIZE(len) |
|
||||||
|
@ -147,7 +136,8 @@ static void spi100k_write_data(struct spi_master *master, int len, int data)
|
||||||
spi100k->base + SPI_CTRL);
|
spi100k->base + SPI_CTRL);
|
||||||
|
|
||||||
/* Wait for bit ack send change */
|
/* Wait for bit ack send change */
|
||||||
while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE);
|
while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE)
|
||||||
|
;
|
||||||
udelay(1000);
|
udelay(1000);
|
||||||
|
|
||||||
spi100k_disable_clock(master);
|
spi100k_disable_clock(master);
|
||||||
|
@ -155,7 +145,7 @@ static void spi100k_write_data(struct spi_master *master, int len, int data)
|
||||||
|
|
||||||
static int spi100k_read_data(struct spi_master *master, int len)
|
static int spi100k_read_data(struct spi_master *master, int len)
|
||||||
{
|
{
|
||||||
int dataH,dataL;
|
int dataH, dataL;
|
||||||
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
||||||
|
|
||||||
/* Always do at least 16 bits */
|
/* Always do at least 16 bits */
|
||||||
|
@ -168,7 +158,8 @@ static int spi100k_read_data(struct spi_master *master, int len)
|
||||||
SPI_CTRL_RD,
|
SPI_CTRL_RD,
|
||||||
spi100k->base + SPI_CTRL);
|
spi100k->base + SPI_CTRL);
|
||||||
|
|
||||||
while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD);
|
while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD)
|
||||||
|
;
|
||||||
udelay(1000);
|
udelay(1000);
|
||||||
|
|
||||||
dataL = readw(spi100k->base + SPI_RX_LSB);
|
dataL = readw(spi100k->base + SPI_RX_LSB);
|
||||||
|
@ -204,12 +195,10 @@ static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable)
|
||||||
static unsigned
|
static unsigned
|
||||||
omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct omap1_spi100k *spi100k;
|
|
||||||
struct omap1_spi100k_cs *cs = spi->controller_state;
|
struct omap1_spi100k_cs *cs = spi->controller_state;
|
||||||
unsigned int count, c;
|
unsigned int count, c;
|
||||||
int word_len;
|
int word_len;
|
||||||
|
|
||||||
spi100k = spi_master_get_devdata(spi->master);
|
|
||||||
count = xfer->len;
|
count = xfer->len;
|
||||||
c = count;
|
c = count;
|
||||||
word_len = cs->word_len;
|
word_len = cs->word_len;
|
||||||
|
@ -221,12 +210,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
rx = xfer->rx_buf;
|
rx = xfer->rx_buf;
|
||||||
tx = xfer->tx_buf;
|
tx = xfer->tx_buf;
|
||||||
do {
|
do {
|
||||||
c-=1;
|
c -= 1;
|
||||||
if (xfer->tx_buf != NULL)
|
if (xfer->tx_buf != NULL)
|
||||||
spi100k_write_data(spi->master, word_len, *tx++);
|
spi100k_write_data(spi->master, word_len, *tx++);
|
||||||
if (xfer->rx_buf != NULL)
|
if (xfer->rx_buf != NULL)
|
||||||
*rx++ = spi100k_read_data(spi->master, word_len);
|
*rx++ = spi100k_read_data(spi->master, word_len);
|
||||||
} while(c);
|
} while (c);
|
||||||
} else if (word_len <= 16) {
|
} else if (word_len <= 16) {
|
||||||
u16 *rx;
|
u16 *rx;
|
||||||
const u16 *tx;
|
const u16 *tx;
|
||||||
|
@ -234,12 +223,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
rx = xfer->rx_buf;
|
rx = xfer->rx_buf;
|
||||||
tx = xfer->tx_buf;
|
tx = xfer->tx_buf;
|
||||||
do {
|
do {
|
||||||
c-=2;
|
c -= 2;
|
||||||
if (xfer->tx_buf != NULL)
|
if (xfer->tx_buf != NULL)
|
||||||
spi100k_write_data(spi->master,word_len, *tx++);
|
spi100k_write_data(spi->master, word_len, *tx++);
|
||||||
if (xfer->rx_buf != NULL)
|
if (xfer->rx_buf != NULL)
|
||||||
*rx++ = spi100k_read_data(spi->master,word_len);
|
*rx++ = spi100k_read_data(spi->master, word_len);
|
||||||
} while(c);
|
} while (c);
|
||||||
} else if (word_len <= 32) {
|
} else if (word_len <= 32) {
|
||||||
u32 *rx;
|
u32 *rx;
|
||||||
const u32 *tx;
|
const u32 *tx;
|
||||||
|
@ -247,12 +236,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
rx = xfer->rx_buf;
|
rx = xfer->rx_buf;
|
||||||
tx = xfer->tx_buf;
|
tx = xfer->tx_buf;
|
||||||
do {
|
do {
|
||||||
c-=4;
|
c -= 4;
|
||||||
if (xfer->tx_buf != NULL)
|
if (xfer->tx_buf != NULL)
|
||||||
spi100k_write_data(spi->master,word_len, *tx);
|
spi100k_write_data(spi->master, word_len, *tx);
|
||||||
if (xfer->rx_buf != NULL)
|
if (xfer->rx_buf != NULL)
|
||||||
*rx = spi100k_read_data(spi->master,word_len);
|
*rx = spi100k_read_data(spi->master, word_len);
|
||||||
} while(c);
|
} while (c);
|
||||||
}
|
}
|
||||||
return count - c;
|
return count - c;
|
||||||
}
|
}
|
||||||
|
@ -294,7 +283,7 @@ static int omap1_spi100k_setup(struct spi_device *spi)
|
||||||
spi100k = spi_master_get_devdata(spi->master);
|
spi100k = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
if (!cs) {
|
if (!cs) {
|
||||||
cs = kzalloc(sizeof *cs, GFP_KERNEL);
|
cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL);
|
||||||
if (!cs)
|
if (!cs)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
cs->base = spi100k->base + spi->chip_select * 0x14;
|
cs->base = spi100k->base + spi->chip_select * 0x14;
|
||||||
|
@ -411,14 +400,14 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
||||||
if (!pdev->id)
|
if (!pdev->id)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof *spi100k);
|
master = spi_alloc_master(&pdev->dev, sizeof(*spi100k));
|
||||||
if (master == NULL) {
|
if (master == NULL) {
|
||||||
dev_dbg(&pdev->dev, "master allocation failed\n");
|
dev_dbg(&pdev->dev, "master allocation failed\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdev->id != -1)
|
if (pdev->id != -1)
|
||||||
master->bus_num = pdev->id;
|
master->bus_num = pdev->id;
|
||||||
|
|
||||||
master->setup = omap1_spi100k_setup;
|
master->setup = omap1_spi100k_setup;
|
||||||
master->transfer_one_message = omap1_spi100k_transfer_one_message;
|
master->transfer_one_message = omap1_spi100k_transfer_one_message;
|
||||||
|
@ -434,7 +423,6 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
spi100k = spi_master_get_devdata(master);
|
spi100k = spi_master_get_devdata(master);
|
||||||
spi100k->master = master;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The memory region base address is taken as the platform_data.
|
* The memory region base address is taken as the platform_data.
|
||||||
|
@ -461,8 +449,6 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
spi100k->state = SPI_RUNNING;
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
|
|
@ -99,7 +99,6 @@ struct uwire_spi {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uwire_state {
|
struct uwire_state {
|
||||||
unsigned bits_per_word;
|
|
||||||
unsigned div1_idx;
|
unsigned div1_idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -210,9 +209,8 @@ static void uwire_chipselect(struct spi_device *spi, int value)
|
||||||
|
|
||||||
static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t)
|
static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
struct uwire_state *ust = spi->controller_state;
|
|
||||||
unsigned len = t->len;
|
unsigned len = t->len;
|
||||||
unsigned bits = ust->bits_per_word;
|
unsigned bits = t->bits_per_word ? : spi->bits_per_word;
|
||||||
unsigned bytes;
|
unsigned bytes;
|
||||||
u16 val, w;
|
u16 val, w;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
@ -220,10 +218,6 @@ static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t)
|
||||||
if (!t->tx_buf && !t->rx_buf)
|
if (!t->tx_buf && !t->rx_buf)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Microwire doesn't read and write concurrently */
|
|
||||||
if (t->tx_buf && t->rx_buf)
|
|
||||||
return -EPERM;
|
|
||||||
|
|
||||||
w = spi->chip_select << 10;
|
w = spi->chip_select << 10;
|
||||||
w |= CS_CMD;
|
w |= CS_CMD;
|
||||||
|
|
||||||
|
@ -322,7 +316,6 @@ static int uwire_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
struct uwire_state *ust = spi->controller_state;
|
struct uwire_state *ust = spi->controller_state;
|
||||||
struct uwire_spi *uwire;
|
struct uwire_spi *uwire;
|
||||||
unsigned flags = 0;
|
unsigned flags = 0;
|
||||||
unsigned bits;
|
|
||||||
unsigned hz;
|
unsigned hz;
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
int div1_idx;
|
int div1_idx;
|
||||||
|
@ -332,23 +325,6 @@ static int uwire_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
|
|
||||||
uwire = spi_master_get_devdata(spi->master);
|
uwire = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
if (spi->chip_select > 3) {
|
|
||||||
pr_debug("%s: cs%d?\n", dev_name(&spi->dev), spi->chip_select);
|
|
||||||
status = -ENODEV;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
bits = spi->bits_per_word;
|
|
||||||
if (t != NULL && t->bits_per_word)
|
|
||||||
bits = t->bits_per_word;
|
|
||||||
|
|
||||||
if (bits > 16) {
|
|
||||||
pr_debug("%s: wordsize %d?\n", dev_name(&spi->dev), bits);
|
|
||||||
status = -ENODEV;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
ust->bits_per_word = bits;
|
|
||||||
|
|
||||||
/* mode 0..3, clock inverted separately;
|
/* mode 0..3, clock inverted separately;
|
||||||
* standard nCS signaling;
|
* standard nCS signaling;
|
||||||
* don't treat DI=high as "not ready"
|
* don't treat DI=high as "not ready"
|
||||||
|
@ -502,6 +478,7 @@ static int uwire_probe(struct platform_device *pdev)
|
||||||
status = PTR_ERR(uwire->ck);
|
status = PTR_ERR(uwire->ck);
|
||||||
dev_dbg(&pdev->dev, "no functional clock?\n");
|
dev_dbg(&pdev->dev, "no functional clock?\n");
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
|
iounmap(uwire_base);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
clk_enable(uwire->ck);
|
clk_enable(uwire->ck);
|
||||||
|
@ -515,7 +492,7 @@ static int uwire_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* the spi->mode bits understood by this driver: */
|
/* the spi->mode bits understood by this driver: */
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
|
||||||
master->flags = SPI_MASTER_HALF_DUPLEX;
|
master->flags = SPI_MASTER_HALF_DUPLEX;
|
||||||
|
|
||||||
master->bus_num = 2; /* "official" */
|
master->bus_num = 2; /* "official" */
|
||||||
|
@ -539,14 +516,13 @@ static int uwire_probe(struct platform_device *pdev)
|
||||||
static int uwire_remove(struct platform_device *pdev)
|
static int uwire_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct uwire_spi *uwire = platform_get_drvdata(pdev);
|
struct uwire_spi *uwire = platform_get_drvdata(pdev);
|
||||||
int status;
|
|
||||||
|
|
||||||
// FIXME remove all child devices, somewhere ...
|
// FIXME remove all child devices, somewhere ...
|
||||||
|
|
||||||
status = spi_bitbang_stop(&uwire->bitbang);
|
spi_bitbang_stop(&uwire->bitbang);
|
||||||
uwire_off(uwire);
|
uwire_off(uwire);
|
||||||
iounmap(uwire_base);
|
iounmap(uwire_base);
|
||||||
return status;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* work with hotplug and coldplug */
|
/* work with hotplug and coldplug */
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
@ -45,6 +44,7 @@
|
||||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||||
|
|
||||||
#define OMAP2_MCSPI_MAX_FREQ 48000000
|
#define OMAP2_MCSPI_MAX_FREQ 48000000
|
||||||
|
#define OMAP2_MCSPI_MAX_DIVIDER 4096
|
||||||
#define OMAP2_MCSPI_MAX_FIFODEPTH 64
|
#define OMAP2_MCSPI_MAX_FIFODEPTH 64
|
||||||
#define OMAP2_MCSPI_MAX_FIFOWCNT 0xFFFF
|
#define OMAP2_MCSPI_MAX_FIFOWCNT 0xFFFF
|
||||||
#define SPI_AUTOSUSPEND_TIMEOUT 2000
|
#define SPI_AUTOSUSPEND_TIMEOUT 2000
|
||||||
|
@ -89,6 +89,7 @@
|
||||||
#define OMAP2_MCSPI_CHCONF_FORCE BIT(20)
|
#define OMAP2_MCSPI_CHCONF_FORCE BIT(20)
|
||||||
#define OMAP2_MCSPI_CHCONF_FFET BIT(27)
|
#define OMAP2_MCSPI_CHCONF_FFET BIT(27)
|
||||||
#define OMAP2_MCSPI_CHCONF_FFER BIT(28)
|
#define OMAP2_MCSPI_CHCONF_FFER BIT(28)
|
||||||
|
#define OMAP2_MCSPI_CHCONF_CLKG BIT(29)
|
||||||
|
|
||||||
#define OMAP2_MCSPI_CHSTAT_RXS BIT(0)
|
#define OMAP2_MCSPI_CHSTAT_RXS BIT(0)
|
||||||
#define OMAP2_MCSPI_CHSTAT_TXS BIT(1)
|
#define OMAP2_MCSPI_CHSTAT_TXS BIT(1)
|
||||||
|
@ -96,6 +97,7 @@
|
||||||
#define OMAP2_MCSPI_CHSTAT_TXFFE BIT(3)
|
#define OMAP2_MCSPI_CHSTAT_TXFFE BIT(3)
|
||||||
|
|
||||||
#define OMAP2_MCSPI_CHCTRL_EN BIT(0)
|
#define OMAP2_MCSPI_CHCTRL_EN BIT(0)
|
||||||
|
#define OMAP2_MCSPI_CHCTRL_EXTCLK_MASK (0xff << 8)
|
||||||
|
|
||||||
#define OMAP2_MCSPI_WAKEUPENABLE_WKEN BIT(0)
|
#define OMAP2_MCSPI_WAKEUPENABLE_WKEN BIT(0)
|
||||||
|
|
||||||
|
@ -149,7 +151,7 @@ struct omap2_mcspi_cs {
|
||||||
int word_len;
|
int word_len;
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
/* Context save and restore shadow register */
|
/* Context save and restore shadow register */
|
||||||
u32 chconf0;
|
u32 chconf0, chctrl0;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void mcspi_write_reg(struct spi_master *master,
|
static inline void mcspi_write_reg(struct spi_master *master,
|
||||||
|
@ -230,10 +232,16 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi,
|
||||||
|
|
||||||
static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable)
|
static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable)
|
||||||
{
|
{
|
||||||
|
struct omap2_mcspi_cs *cs = spi->controller_state;
|
||||||
u32 l;
|
u32 l;
|
||||||
|
|
||||||
l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0;
|
l = cs->chctrl0;
|
||||||
mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l);
|
if (enable)
|
||||||
|
l |= OMAP2_MCSPI_CHCTRL_EN;
|
||||||
|
else
|
||||||
|
l &= ~OMAP2_MCSPI_CHCTRL_EN;
|
||||||
|
cs->chctrl0 = l;
|
||||||
|
mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, cs->chctrl0);
|
||||||
/* Flash post-writes */
|
/* Flash post-writes */
|
||||||
mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0);
|
mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0);
|
||||||
}
|
}
|
||||||
|
@ -840,7 +848,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
|
||||||
struct omap2_mcspi_cs *cs = spi->controller_state;
|
struct omap2_mcspi_cs *cs = spi->controller_state;
|
||||||
struct omap2_mcspi *mcspi;
|
struct omap2_mcspi *mcspi;
|
||||||
struct spi_master *spi_cntrl;
|
struct spi_master *spi_cntrl;
|
||||||
u32 l = 0, div = 0;
|
u32 l = 0, clkd = 0, div, extclk = 0, clkg = 0;
|
||||||
u8 word_len = spi->bits_per_word;
|
u8 word_len = spi->bits_per_word;
|
||||||
u32 speed_hz = spi->max_speed_hz;
|
u32 speed_hz = spi->max_speed_hz;
|
||||||
|
|
||||||
|
@ -856,7 +864,17 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
|
||||||
speed_hz = t->speed_hz;
|
speed_hz = t->speed_hz;
|
||||||
|
|
||||||
speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ);
|
speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ);
|
||||||
div = omap2_mcspi_calc_divisor(speed_hz);
|
if (speed_hz < (OMAP2_MCSPI_MAX_FREQ / OMAP2_MCSPI_MAX_DIVIDER)) {
|
||||||
|
clkd = omap2_mcspi_calc_divisor(speed_hz);
|
||||||
|
speed_hz = OMAP2_MCSPI_MAX_FREQ >> clkd;
|
||||||
|
clkg = 0;
|
||||||
|
} else {
|
||||||
|
div = (OMAP2_MCSPI_MAX_FREQ + speed_hz - 1) / speed_hz;
|
||||||
|
speed_hz = OMAP2_MCSPI_MAX_FREQ / div;
|
||||||
|
clkd = (div - 1) & 0xf;
|
||||||
|
extclk = (div - 1) >> 4;
|
||||||
|
clkg = OMAP2_MCSPI_CHCONF_CLKG;
|
||||||
|
}
|
||||||
|
|
||||||
l = mcspi_cached_chconf0(spi);
|
l = mcspi_cached_chconf0(spi);
|
||||||
|
|
||||||
|
@ -885,7 +903,16 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
|
||||||
|
|
||||||
/* set clock divisor */
|
/* set clock divisor */
|
||||||
l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK;
|
l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK;
|
||||||
l |= div << 2;
|
l |= clkd << 2;
|
||||||
|
|
||||||
|
/* set clock granularity */
|
||||||
|
l &= ~OMAP2_MCSPI_CHCONF_CLKG;
|
||||||
|
l |= clkg;
|
||||||
|
if (clkg) {
|
||||||
|
cs->chctrl0 &= ~OMAP2_MCSPI_CHCTRL_EXTCLK_MASK;
|
||||||
|
cs->chctrl0 |= extclk << 8;
|
||||||
|
mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, cs->chctrl0);
|
||||||
|
}
|
||||||
|
|
||||||
/* set SPI mode 0..3 */
|
/* set SPI mode 0..3 */
|
||||||
if (spi->mode & SPI_CPOL)
|
if (spi->mode & SPI_CPOL)
|
||||||
|
@ -900,7 +927,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
|
||||||
mcspi_write_chconf0(spi, l);
|
mcspi_write_chconf0(spi, l);
|
||||||
|
|
||||||
dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n",
|
dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n",
|
||||||
OMAP2_MCSPI_MAX_FREQ >> div,
|
speed_hz,
|
||||||
(spi->mode & SPI_CPHA) ? "trailing" : "leading",
|
(spi->mode & SPI_CPHA) ? "trailing" : "leading",
|
||||||
(spi->mode & SPI_CPOL) ? "inverted" : "normal");
|
(spi->mode & SPI_CPOL) ? "inverted" : "normal");
|
||||||
|
|
||||||
|
@ -972,6 +999,7 @@ static int omap2_mcspi_setup(struct spi_device *spi)
|
||||||
cs->base = mcspi->base + spi->chip_select * 0x14;
|
cs->base = mcspi->base + spi->chip_select * 0x14;
|
||||||
cs->phys = mcspi->phys + spi->chip_select * 0x14;
|
cs->phys = mcspi->phys + spi->chip_select * 0x14;
|
||||||
cs->chconf0 = 0;
|
cs->chconf0 = 0;
|
||||||
|
cs->chctrl0 = 0;
|
||||||
spi->controller_state = cs;
|
spi->controller_state = cs;
|
||||||
/* Link this to context save list */
|
/* Link this to context save list */
|
||||||
list_add_tail(&cs->node, &ctx->cs);
|
list_add_tail(&cs->node, &ctx->cs);
|
||||||
|
@ -1057,12 +1085,15 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
||||||
status = -EINVAL;
|
status = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (par_override || t->speed_hz || t->bits_per_word) {
|
if (par_override ||
|
||||||
|
(t->speed_hz != spi->max_speed_hz) ||
|
||||||
|
(t->bits_per_word != spi->bits_per_word)) {
|
||||||
par_override = 1;
|
par_override = 1;
|
||||||
status = omap2_mcspi_setup_transfer(spi, t);
|
status = omap2_mcspi_setup_transfer(spi, t);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
break;
|
break;
|
||||||
if (!t->speed_hz && !t->bits_per_word)
|
if (t->speed_hz == spi->max_speed_hz &&
|
||||||
|
t->bits_per_word == spi->bits_per_word)
|
||||||
par_override = 0;
|
par_override = 0;
|
||||||
}
|
}
|
||||||
if (cd && cd->cs_per_word) {
|
if (cd && cd->cs_per_word) {
|
||||||
|
@ -1176,16 +1207,12 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
||||||
m->actual_length = 0;
|
m->actual_length = 0;
|
||||||
m->status = 0;
|
m->status = 0;
|
||||||
|
|
||||||
/* reject invalid messages and transfers */
|
|
||||||
if (list_empty(&m->transfers))
|
|
||||||
return -EINVAL;
|
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
const void *tx_buf = t->tx_buf;
|
const void *tx_buf = t->tx_buf;
|
||||||
void *rx_buf = t->rx_buf;
|
void *rx_buf = t->rx_buf;
|
||||||
unsigned len = t->len;
|
unsigned len = t->len;
|
||||||
|
|
||||||
if (t->speed_hz > OMAP2_MCSPI_MAX_FREQ
|
if ((len && !(rx_buf || tx_buf))) {
|
||||||
|| (len && !(rx_buf || tx_buf))) {
|
|
||||||
dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
|
dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
|
||||||
t->speed_hz,
|
t->speed_hz,
|
||||||
len,
|
len,
|
||||||
|
@ -1194,12 +1221,6 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
||||||
t->bits_per_word);
|
t->bits_per_word);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) {
|
|
||||||
dev_dbg(mcspi->dev, "speed_hz %d below minimum %d Hz\n",
|
|
||||||
t->speed_hz,
|
|
||||||
OMAP2_MCSPI_MAX_FREQ >> 15);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m->is_dma_mapped || len < DMA_MIN_BYTES)
|
if (m->is_dma_mapped || len < DMA_MIN_BYTES)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1311,6 +1332,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
||||||
master->transfer_one_message = omap2_mcspi_transfer_one_message;
|
master->transfer_one_message = omap2_mcspi_transfer_one_message;
|
||||||
master->cleanup = omap2_mcspi_cleanup;
|
master->cleanup = omap2_mcspi_cleanup;
|
||||||
master->dev.of_node = node;
|
master->dev.of_node = node;
|
||||||
|
master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ;
|
||||||
|
master->min_speed_hz = OMAP2_MCSPI_MAX_FREQ >> 15;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
@ -43,8 +42,6 @@
|
||||||
struct orion_spi {
|
struct orion_spi {
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
unsigned int max_speed;
|
|
||||||
unsigned int min_speed;
|
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,23 +72,6 @@ orion_spi_clrbits(struct orion_spi *orion_spi, u32 reg, u32 mask)
|
||||||
writel(val, reg_addr);
|
writel(val, reg_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int orion_spi_set_transfer_size(struct orion_spi *orion_spi, int size)
|
|
||||||
{
|
|
||||||
if (size == 16) {
|
|
||||||
orion_spi_setbits(orion_spi, ORION_SPI_IF_CONFIG_REG,
|
|
||||||
ORION_SPI_IF_8_16_BIT_MODE);
|
|
||||||
} else if (size == 8) {
|
|
||||||
orion_spi_clrbits(orion_spi, ORION_SPI_IF_CONFIG_REG,
|
|
||||||
ORION_SPI_IF_8_16_BIT_MODE);
|
|
||||||
} else {
|
|
||||||
pr_debug("Bad bits per word value %d (only 8 or 16 are allowed).\n",
|
|
||||||
size);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
|
static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
|
||||||
{
|
{
|
||||||
u32 tclk_hz;
|
u32 tclk_hz;
|
||||||
|
@ -170,7 +150,14 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
return orion_spi_set_transfer_size(orion_spi, bits_per_word);
|
if (bits_per_word == 16)
|
||||||
|
orion_spi_setbits(orion_spi, ORION_SPI_IF_CONFIG_REG,
|
||||||
|
ORION_SPI_IF_8_16_BIT_MODE);
|
||||||
|
else
|
||||||
|
orion_spi_clrbits(orion_spi, ORION_SPI_IF_CONFIG_REG,
|
||||||
|
ORION_SPI_IF_8_16_BIT_MODE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void orion_spi_set_cs(struct orion_spi *orion_spi, int enable)
|
static void orion_spi_set_cs(struct orion_spi *orion_spi, int enable)
|
||||||
|
@ -260,11 +247,9 @@ orion_spi_write_read_16bit(struct spi_device *spi,
|
||||||
static unsigned int
|
static unsigned int
|
||||||
orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
|
orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct orion_spi *orion_spi;
|
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
int word_len;
|
int word_len;
|
||||||
|
|
||||||
orion_spi = spi_master_get_devdata(spi->master);
|
|
||||||
word_len = spi->bits_per_word;
|
word_len = spi->bits_per_word;
|
||||||
count = xfer->len;
|
count = xfer->len;
|
||||||
|
|
||||||
|
@ -310,27 +295,6 @@ static int orion_spi_transfer_one_message(struct spi_master *master,
|
||||||
goto msg_done;
|
goto msg_done;
|
||||||
|
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
/* make sure buffer length is even when working in 16
|
|
||||||
* bit mode*/
|
|
||||||
if ((t->bits_per_word == 16) && (t->len & 1)) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"message rejected : "
|
|
||||||
"odd data length %d while in 16 bit mode\n",
|
|
||||||
t->len);
|
|
||||||
status = -EIO;
|
|
||||||
goto msg_done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t->speed_hz && t->speed_hz < orion_spi->min_speed) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"message rejected : "
|
|
||||||
"device min speed (%d Hz) exceeds "
|
|
||||||
"required transfer speed (%d Hz)\n",
|
|
||||||
orion_spi->min_speed, t->speed_hz);
|
|
||||||
status = -EIO;
|
|
||||||
goto msg_done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (par_override || t->speed_hz || t->bits_per_word) {
|
if (par_override || t->speed_hz || t->bits_per_word) {
|
||||||
par_override = 1;
|
par_override = 1;
|
||||||
status = orion_spi_setup_transfer(spi, t);
|
status = orion_spi_setup_transfer(spi, t);
|
||||||
|
@ -375,28 +339,6 @@ static int orion_spi_reset(struct orion_spi *orion_spi)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int orion_spi_setup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct orion_spi *orion_spi;
|
|
||||||
|
|
||||||
orion_spi = spi_master_get_devdata(spi->master);
|
|
||||||
|
|
||||||
if ((spi->max_speed_hz == 0)
|
|
||||||
|| (spi->max_speed_hz > orion_spi->max_speed))
|
|
||||||
spi->max_speed_hz = orion_spi->max_speed;
|
|
||||||
|
|
||||||
if (spi->max_speed_hz < orion_spi->min_speed) {
|
|
||||||
dev_err(&spi->dev, "setup: requested speed too low %d Hz\n",
|
|
||||||
spi->max_speed_hz);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* baudrate & width will be set orion_spi_setup_transfer
|
|
||||||
*/
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int orion_spi_probe(struct platform_device *pdev)
|
static int orion_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
|
@ -425,9 +367,9 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||||
/* we support only mode 0, and no options */
|
/* we support only mode 0, and no options */
|
||||||
master->mode_bits = SPI_CPHA | SPI_CPOL;
|
master->mode_bits = SPI_CPHA | SPI_CPOL;
|
||||||
|
|
||||||
master->setup = orion_spi_setup;
|
|
||||||
master->transfer_one_message = orion_spi_transfer_one_message;
|
master->transfer_one_message = orion_spi_transfer_one_message;
|
||||||
master->num_chipselect = ORION_NUM_CHIPSELECTS;
|
master->num_chipselect = ORION_NUM_CHIPSELECTS;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
|
@ -443,8 +385,8 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||||
clk_prepare(spi->clk);
|
clk_prepare(spi->clk);
|
||||||
clk_enable(spi->clk);
|
clk_enable(spi->clk);
|
||||||
tclk_hz = clk_get_rate(spi->clk);
|
tclk_hz = clk_get_rate(spi->clk);
|
||||||
spi->max_speed = DIV_ROUND_UP(tclk_hz, 4);
|
master->max_speed_hz = DIV_ROUND_UP(tclk_hz, 4);
|
||||||
spi->min_speed = DIV_ROUND_UP(tclk_hz, 30);
|
master->min_speed_hz = DIV_ROUND_UP(tclk_hz, 30);
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
spi->base = devm_ioremap_resource(&pdev->dev, r);
|
spi->base = devm_ioremap_resource(&pdev->dev, r);
|
||||||
|
|
|
@ -459,9 +459,8 @@ static void giveback(struct pl022 *pl022)
|
||||||
struct spi_transfer *last_transfer;
|
struct spi_transfer *last_transfer;
|
||||||
pl022->next_msg_cs_active = false;
|
pl022->next_msg_cs_active = false;
|
||||||
|
|
||||||
last_transfer = list_entry(pl022->cur_msg->transfers.prev,
|
last_transfer = list_last_entry(&pl022->cur_msg->transfers,
|
||||||
struct spi_transfer,
|
struct spi_transfer, transfer_list);
|
||||||
transfer_list);
|
|
||||||
|
|
||||||
/* Delay if requested before any change in chip select */
|
/* Delay if requested before any change in chip select */
|
||||||
if (last_transfer->delay_usecs)
|
if (last_transfer->delay_usecs)
|
||||||
|
@ -2109,8 +2108,6 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||||
pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int),
|
pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
|
||||||
pinctrl_pm_select_default_state(dev);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bus Number Which has been Assigned to this SSP controller
|
* Bus Number Which has been Assigned to this SSP controller
|
||||||
* on this board
|
* on this board
|
||||||
|
@ -2183,13 +2180,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||||
goto err_no_clk;
|
goto err_no_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = clk_prepare(pl022->clk);
|
status = clk_prepare_enable(pl022->clk);
|
||||||
if (status) {
|
|
||||||
dev_err(&adev->dev, "could not prepare SSP/SPI bus clock\n");
|
|
||||||
goto err_clk_prep;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = clk_enable(pl022->clk);
|
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_err(&adev->dev, "could not enable SSP/SPI bus clock\n");
|
dev_err(&adev->dev, "could not enable SSP/SPI bus clock\n");
|
||||||
goto err_no_clk_en;
|
goto err_no_clk_en;
|
||||||
|
@ -2250,10 +2241,8 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||||
if (platform_info->enable_dma)
|
if (platform_info->enable_dma)
|
||||||
pl022_dma_remove(pl022);
|
pl022_dma_remove(pl022);
|
||||||
err_no_irq:
|
err_no_irq:
|
||||||
clk_disable(pl022->clk);
|
clk_disable_unprepare(pl022->clk);
|
||||||
err_no_clk_en:
|
err_no_clk_en:
|
||||||
clk_unprepare(pl022->clk);
|
|
||||||
err_clk_prep:
|
|
||||||
err_no_clk:
|
err_no_clk:
|
||||||
err_no_ioremap:
|
err_no_ioremap:
|
||||||
amba_release_regions(adev);
|
amba_release_regions(adev);
|
||||||
|
@ -2281,42 +2270,13 @@ pl022_remove(struct amba_device *adev)
|
||||||
if (pl022->master_info->enable_dma)
|
if (pl022->master_info->enable_dma)
|
||||||
pl022_dma_remove(pl022);
|
pl022_dma_remove(pl022);
|
||||||
|
|
||||||
clk_disable(pl022->clk);
|
clk_disable_unprepare(pl022->clk);
|
||||||
clk_unprepare(pl022->clk);
|
|
||||||
amba_release_regions(adev);
|
amba_release_regions(adev);
|
||||||
tasklet_disable(&pl022->pump_transfers);
|
tasklet_disable(&pl022->pump_transfers);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME)
|
#ifdef CONFIG_PM_SLEEP
|
||||||
/*
|
|
||||||
* These two functions are used from both suspend/resume and
|
|
||||||
* the runtime counterparts to handle external resources like
|
|
||||||
* clocks, pins and regulators when going to sleep.
|
|
||||||
*/
|
|
||||||
static void pl022_suspend_resources(struct pl022 *pl022, bool runtime)
|
|
||||||
{
|
|
||||||
clk_disable(pl022->clk);
|
|
||||||
|
|
||||||
if (runtime)
|
|
||||||
pinctrl_pm_select_idle_state(&pl022->adev->dev);
|
|
||||||
else
|
|
||||||
pinctrl_pm_select_sleep_state(&pl022->adev->dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pl022_resume_resources(struct pl022 *pl022, bool runtime)
|
|
||||||
{
|
|
||||||
/* First go to the default state */
|
|
||||||
pinctrl_pm_select_default_state(&pl022->adev->dev);
|
|
||||||
if (!runtime)
|
|
||||||
/* Then let's idle the pins until the next transfer happens */
|
|
||||||
pinctrl_pm_select_idle_state(&pl022->adev->dev);
|
|
||||||
|
|
||||||
clk_enable(pl022->clk);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_SUSPEND
|
|
||||||
static int pl022_suspend(struct device *dev)
|
static int pl022_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||||
|
@ -2328,8 +2288,13 @@ static int pl022_suspend(struct device *dev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_runtime_get_sync(dev);
|
ret = pm_runtime_force_suspend(dev);
|
||||||
pl022_suspend_resources(pl022, false);
|
if (ret) {
|
||||||
|
spi_master_resume(pl022->master);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pinctrl_pm_select_sleep_state(dev);
|
||||||
|
|
||||||
dev_dbg(dev, "suspended\n");
|
dev_dbg(dev, "suspended\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2340,8 +2305,9 @@ static int pl022_resume(struct device *dev)
|
||||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pl022_resume_resources(pl022, false);
|
ret = pm_runtime_force_resume(dev);
|
||||||
pm_runtime_put(dev);
|
if (ret)
|
||||||
|
dev_err(dev, "problem resuming\n");
|
||||||
|
|
||||||
/* Start the queue running */
|
/* Start the queue running */
|
||||||
ret = spi_master_resume(pl022->master);
|
ret = spi_master_resume(pl022->master);
|
||||||
|
@ -2352,14 +2318,16 @@ static int pl022_resume(struct device *dev)
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_PM */
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM_RUNTIME
|
#ifdef CONFIG_PM
|
||||||
static int pl022_runtime_suspend(struct device *dev)
|
static int pl022_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||||
|
|
||||||
pl022_suspend_resources(pl022, true);
|
clk_disable_unprepare(pl022->clk);
|
||||||
|
pinctrl_pm_select_idle_state(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2367,14 +2335,16 @@ static int pl022_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||||
|
|
||||||
pl022_resume_resources(pl022, true);
|
pinctrl_pm_select_default_state(dev);
|
||||||
|
clk_prepare_enable(pl022->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct dev_pm_ops pl022_dev_pm_ops = {
|
static const struct dev_pm_ops pl022_dev_pm_ops = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume)
|
||||||
SET_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL)
|
SET_PM_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct vendor_data vendor_arm = {
|
static struct vendor_data vendor_arm = {
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
|
|
|
@ -362,8 +362,7 @@ static void giveback(struct driver_data *drv_data)
|
||||||
drv_data->cur_msg = NULL;
|
drv_data->cur_msg = NULL;
|
||||||
drv_data->cur_transfer = NULL;
|
drv_data->cur_transfer = NULL;
|
||||||
|
|
||||||
last_transfer = list_entry(msg->transfers.prev,
|
last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
|
||||||
struct spi_transfer,
|
|
||||||
transfer_list);
|
transfer_list);
|
||||||
|
|
||||||
/* Delay if requested before any change in chip select */
|
/* Delay if requested before any change in chip select */
|
||||||
|
|
|
@ -0,0 +1,779 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008-2014, The Linux foundation. All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License rev 2 and
|
||||||
|
* only rev 2 as published by the free Software foundation.
|
||||||
|
*
|
||||||
|
* 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>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
#define QUP_CONFIG 0x0000
|
||||||
|
#define QUP_STATE 0x0004
|
||||||
|
#define QUP_IO_M_MODES 0x0008
|
||||||
|
#define QUP_SW_RESET 0x000c
|
||||||
|
#define QUP_OPERATIONAL 0x0018
|
||||||
|
#define QUP_ERROR_FLAGS 0x001c
|
||||||
|
#define QUP_ERROR_FLAGS_EN 0x0020
|
||||||
|
#define QUP_OPERATIONAL_MASK 0x0028
|
||||||
|
#define QUP_HW_VERSION 0x0030
|
||||||
|
#define QUP_MX_OUTPUT_CNT 0x0100
|
||||||
|
#define QUP_OUTPUT_FIFO 0x0110
|
||||||
|
#define QUP_MX_WRITE_CNT 0x0150
|
||||||
|
#define QUP_MX_INPUT_CNT 0x0200
|
||||||
|
#define QUP_MX_READ_CNT 0x0208
|
||||||
|
#define QUP_INPUT_FIFO 0x0218
|
||||||
|
|
||||||
|
#define SPI_CONFIG 0x0300
|
||||||
|
#define SPI_IO_CONTROL 0x0304
|
||||||
|
#define SPI_ERROR_FLAGS 0x0308
|
||||||
|
#define SPI_ERROR_FLAGS_EN 0x030c
|
||||||
|
|
||||||
|
/* QUP_CONFIG fields */
|
||||||
|
#define QUP_CONFIG_SPI_MODE (1 << 8)
|
||||||
|
#define QUP_CONFIG_CLOCK_AUTO_GATE BIT(13)
|
||||||
|
#define QUP_CONFIG_NO_INPUT BIT(7)
|
||||||
|
#define QUP_CONFIG_NO_OUTPUT BIT(6)
|
||||||
|
#define QUP_CONFIG_N 0x001f
|
||||||
|
|
||||||
|
/* QUP_STATE fields */
|
||||||
|
#define QUP_STATE_VALID BIT(2)
|
||||||
|
#define QUP_STATE_RESET 0
|
||||||
|
#define QUP_STATE_RUN 1
|
||||||
|
#define QUP_STATE_PAUSE 3
|
||||||
|
#define QUP_STATE_MASK 3
|
||||||
|
#define QUP_STATE_CLEAR 2
|
||||||
|
|
||||||
|
#define QUP_HW_VERSION_2_1_1 0x20010001
|
||||||
|
|
||||||
|
/* QUP_IO_M_MODES fields */
|
||||||
|
#define QUP_IO_M_PACK_EN BIT(15)
|
||||||
|
#define QUP_IO_M_UNPACK_EN BIT(14)
|
||||||
|
#define QUP_IO_M_INPUT_MODE_MASK_SHIFT 12
|
||||||
|
#define QUP_IO_M_OUTPUT_MODE_MASK_SHIFT 10
|
||||||
|
#define QUP_IO_M_INPUT_MODE_MASK (3 << QUP_IO_M_INPUT_MODE_MASK_SHIFT)
|
||||||
|
#define QUP_IO_M_OUTPUT_MODE_MASK (3 << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT)
|
||||||
|
|
||||||
|
#define QUP_IO_M_OUTPUT_BLOCK_SIZE(x) (((x) & (0x03 << 0)) >> 0)
|
||||||
|
#define QUP_IO_M_OUTPUT_FIFO_SIZE(x) (((x) & (0x07 << 2)) >> 2)
|
||||||
|
#define QUP_IO_M_INPUT_BLOCK_SIZE(x) (((x) & (0x03 << 5)) >> 5)
|
||||||
|
#define QUP_IO_M_INPUT_FIFO_SIZE(x) (((x) & (0x07 << 7)) >> 7)
|
||||||
|
|
||||||
|
#define QUP_IO_M_MODE_FIFO 0
|
||||||
|
#define QUP_IO_M_MODE_BLOCK 1
|
||||||
|
#define QUP_IO_M_MODE_DMOV 2
|
||||||
|
#define QUP_IO_M_MODE_BAM 3
|
||||||
|
|
||||||
|
/* QUP_OPERATIONAL fields */
|
||||||
|
#define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11)
|
||||||
|
#define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10)
|
||||||
|
#define QUP_OP_IN_SERVICE_FLAG BIT(9)
|
||||||
|
#define QUP_OP_OUT_SERVICE_FLAG BIT(8)
|
||||||
|
#define QUP_OP_IN_FIFO_FULL BIT(7)
|
||||||
|
#define QUP_OP_OUT_FIFO_FULL BIT(6)
|
||||||
|
#define QUP_OP_IN_FIFO_NOT_EMPTY BIT(5)
|
||||||
|
#define QUP_OP_OUT_FIFO_NOT_EMPTY BIT(4)
|
||||||
|
|
||||||
|
/* QUP_ERROR_FLAGS and QUP_ERROR_FLAGS_EN fields */
|
||||||
|
#define QUP_ERROR_OUTPUT_OVER_RUN BIT(5)
|
||||||
|
#define QUP_ERROR_INPUT_UNDER_RUN BIT(4)
|
||||||
|
#define QUP_ERROR_OUTPUT_UNDER_RUN BIT(3)
|
||||||
|
#define QUP_ERROR_INPUT_OVER_RUN BIT(2)
|
||||||
|
|
||||||
|
/* SPI_CONFIG fields */
|
||||||
|
#define SPI_CONFIG_HS_MODE BIT(10)
|
||||||
|
#define SPI_CONFIG_INPUT_FIRST BIT(9)
|
||||||
|
#define SPI_CONFIG_LOOPBACK BIT(8)
|
||||||
|
|
||||||
|
/* SPI_IO_CONTROL fields */
|
||||||
|
#define SPI_IO_C_FORCE_CS BIT(11)
|
||||||
|
#define SPI_IO_C_CLK_IDLE_HIGH BIT(10)
|
||||||
|
#define SPI_IO_C_MX_CS_MODE BIT(8)
|
||||||
|
#define SPI_IO_C_CS_N_POLARITY_0 BIT(4)
|
||||||
|
#define SPI_IO_C_CS_SELECT(x) (((x) & 3) << 2)
|
||||||
|
#define SPI_IO_C_CS_SELECT_MASK 0x000c
|
||||||
|
#define SPI_IO_C_TRISTATE_CS BIT(1)
|
||||||
|
#define SPI_IO_C_NO_TRI_STATE BIT(0)
|
||||||
|
|
||||||
|
/* SPI_ERROR_FLAGS and SPI_ERROR_FLAGS_EN fields */
|
||||||
|
#define SPI_ERROR_CLK_OVER_RUN BIT(1)
|
||||||
|
#define SPI_ERROR_CLK_UNDER_RUN BIT(0)
|
||||||
|
|
||||||
|
#define SPI_NUM_CHIPSELECTS 4
|
||||||
|
|
||||||
|
/* high speed mode is when bus rate is greater then 26MHz */
|
||||||
|
#define SPI_HS_MIN_RATE 26000000
|
||||||
|
#define SPI_MAX_RATE 50000000
|
||||||
|
|
||||||
|
#define SPI_DELAY_THRESHOLD 1
|
||||||
|
#define SPI_DELAY_RETRY 10
|
||||||
|
|
||||||
|
struct spi_qup {
|
||||||
|
void __iomem *base;
|
||||||
|
struct device *dev;
|
||||||
|
struct clk *cclk; /* core clock */
|
||||||
|
struct clk *iclk; /* interface clock */
|
||||||
|
int irq;
|
||||||
|
spinlock_t lock;
|
||||||
|
|
||||||
|
int in_fifo_sz;
|
||||||
|
int out_fifo_sz;
|
||||||
|
int in_blk_sz;
|
||||||
|
int out_blk_sz;
|
||||||
|
|
||||||
|
struct spi_transfer *xfer;
|
||||||
|
struct completion done;
|
||||||
|
int error;
|
||||||
|
int w_size; /* bytes per SPI word */
|
||||||
|
int tx_bytes;
|
||||||
|
int rx_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
|
||||||
|
{
|
||||||
|
u32 opstate = readl_relaxed(controller->base + QUP_STATE);
|
||||||
|
|
||||||
|
return opstate & QUP_STATE_VALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_set_state(struct spi_qup *controller, u32 state)
|
||||||
|
{
|
||||||
|
unsigned long loop;
|
||||||
|
u32 cur_state;
|
||||||
|
|
||||||
|
loop = 0;
|
||||||
|
while (!spi_qup_is_valid_state(controller)) {
|
||||||
|
|
||||||
|
usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2);
|
||||||
|
|
||||||
|
if (++loop > SPI_DELAY_RETRY)
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loop)
|
||||||
|
dev_dbg(controller->dev, "invalid state for %ld,us %d\n",
|
||||||
|
loop, state);
|
||||||
|
|
||||||
|
cur_state = readl_relaxed(controller->base + QUP_STATE);
|
||||||
|
/*
|
||||||
|
* Per spec: for PAUSE_STATE to RESET_STATE, two writes
|
||||||
|
* of (b10) are required
|
||||||
|
*/
|
||||||
|
if (((cur_state & QUP_STATE_MASK) == QUP_STATE_PAUSE) &&
|
||||||
|
(state == QUP_STATE_RESET)) {
|
||||||
|
writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE);
|
||||||
|
writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE);
|
||||||
|
} else {
|
||||||
|
cur_state &= ~QUP_STATE_MASK;
|
||||||
|
cur_state |= state;
|
||||||
|
writel_relaxed(cur_state, controller->base + QUP_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop = 0;
|
||||||
|
while (!spi_qup_is_valid_state(controller)) {
|
||||||
|
|
||||||
|
usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2);
|
||||||
|
|
||||||
|
if (++loop > SPI_DELAY_RETRY)
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void spi_qup_fifo_read(struct spi_qup *controller,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
u8 *rx_buf = xfer->rx_buf;
|
||||||
|
u32 word, state;
|
||||||
|
int idx, shift, w_size;
|
||||||
|
|
||||||
|
w_size = controller->w_size;
|
||||||
|
|
||||||
|
while (controller->rx_bytes < xfer->len) {
|
||||||
|
|
||||||
|
state = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||||
|
if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
|
||||||
|
break;
|
||||||
|
|
||||||
|
word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
|
||||||
|
|
||||||
|
if (!rx_buf) {
|
||||||
|
controller->rx_bytes += w_size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
|
||||||
|
/*
|
||||||
|
* The data format depends on bytes per SPI word:
|
||||||
|
* 4 bytes: 0x12345678
|
||||||
|
* 2 bytes: 0x00001234
|
||||||
|
* 1 byte : 0x00000012
|
||||||
|
*/
|
||||||
|
shift = BITS_PER_BYTE;
|
||||||
|
shift *= (w_size - idx - 1);
|
||||||
|
rx_buf[controller->rx_bytes] = word >> shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_qup_fifo_write(struct spi_qup *controller,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
const u8 *tx_buf = xfer->tx_buf;
|
||||||
|
u32 word, state, data;
|
||||||
|
int idx, w_size;
|
||||||
|
|
||||||
|
w_size = controller->w_size;
|
||||||
|
|
||||||
|
while (controller->tx_bytes < xfer->len) {
|
||||||
|
|
||||||
|
state = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||||
|
if (state & QUP_OP_OUT_FIFO_FULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
word = 0;
|
||||||
|
for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
|
||||||
|
|
||||||
|
if (!tx_buf) {
|
||||||
|
controller->tx_bytes += w_size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = tx_buf[controller->tx_bytes];
|
||||||
|
word |= data << (BITS_PER_BYTE * (3 - idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct spi_qup *controller = dev_id;
|
||||||
|
struct spi_transfer *xfer;
|
||||||
|
u32 opflags, qup_err, spi_err;
|
||||||
|
unsigned long flags;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&controller->lock, flags);
|
||||||
|
xfer = controller->xfer;
|
||||||
|
controller->xfer = NULL;
|
||||||
|
spin_unlock_irqrestore(&controller->lock, flags);
|
||||||
|
|
||||||
|
qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
|
||||||
|
spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS);
|
||||||
|
opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||||
|
|
||||||
|
writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
|
||||||
|
writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
|
||||||
|
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
|
||||||
|
|
||||||
|
if (!xfer) {
|
||||||
|
dev_err_ratelimited(controller->dev, "unexpected irq %x08 %x08 %x08\n",
|
||||||
|
qup_err, spi_err, opflags);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qup_err) {
|
||||||
|
if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN)
|
||||||
|
dev_warn(controller->dev, "OUTPUT_OVER_RUN\n");
|
||||||
|
if (qup_err & QUP_ERROR_INPUT_UNDER_RUN)
|
||||||
|
dev_warn(controller->dev, "INPUT_UNDER_RUN\n");
|
||||||
|
if (qup_err & QUP_ERROR_OUTPUT_UNDER_RUN)
|
||||||
|
dev_warn(controller->dev, "OUTPUT_UNDER_RUN\n");
|
||||||
|
if (qup_err & QUP_ERROR_INPUT_OVER_RUN)
|
||||||
|
dev_warn(controller->dev, "INPUT_OVER_RUN\n");
|
||||||
|
|
||||||
|
error = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spi_err) {
|
||||||
|
if (spi_err & SPI_ERROR_CLK_OVER_RUN)
|
||||||
|
dev_warn(controller->dev, "CLK_OVER_RUN\n");
|
||||||
|
if (spi_err & SPI_ERROR_CLK_UNDER_RUN)
|
||||||
|
dev_warn(controller->dev, "CLK_UNDER_RUN\n");
|
||||||
|
|
||||||
|
error = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opflags & QUP_OP_IN_SERVICE_FLAG)
|
||||||
|
spi_qup_fifo_read(controller, xfer);
|
||||||
|
|
||||||
|
if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
||||||
|
spi_qup_fifo_write(controller, xfer);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&controller->lock, flags);
|
||||||
|
controller->error = error;
|
||||||
|
controller->xfer = xfer;
|
||||||
|
spin_unlock_irqrestore(&controller->lock, flags);
|
||||||
|
|
||||||
|
if (controller->rx_bytes == xfer->len || error)
|
||||||
|
complete(&controller->done);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* set clock freq ... bits per word */
|
||||||
|
static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(spi->master);
|
||||||
|
u32 config, iomode, mode;
|
||||||
|
int ret, n_words, w_size;
|
||||||
|
|
||||||
|
if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
|
||||||
|
dev_err(controller->dev, "too big size for loopback %d > %d\n",
|
||||||
|
xfer->len, controller->in_fifo_sz);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_set_rate(controller->cclk, xfer->speed_hz);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(controller->dev, "fail to set frequency %d",
|
||||||
|
xfer->speed_hz);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
|
||||||
|
dev_err(controller->dev, "cannot set RESET state\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
w_size = 4;
|
||||||
|
if (xfer->bits_per_word <= 8)
|
||||||
|
w_size = 1;
|
||||||
|
else if (xfer->bits_per_word <= 16)
|
||||||
|
w_size = 2;
|
||||||
|
|
||||||
|
n_words = xfer->len / w_size;
|
||||||
|
controller->w_size = w_size;
|
||||||
|
|
||||||
|
if (n_words <= controller->in_fifo_sz) {
|
||||||
|
mode = QUP_IO_M_MODE_FIFO;
|
||||||
|
writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
|
||||||
|
writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
|
||||||
|
/* must be zero for FIFO */
|
||||||
|
writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
|
||||||
|
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
|
||||||
|
} else {
|
||||||
|
mode = QUP_IO_M_MODE_BLOCK;
|
||||||
|
writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
|
||||||
|
writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
|
||||||
|
/* must be zero for BLOCK and BAM */
|
||||||
|
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
|
||||||
|
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
|
||||||
|
/* Set input and output transfer mode */
|
||||||
|
iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
|
||||||
|
iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
|
||||||
|
iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
|
||||||
|
iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
|
||||||
|
|
||||||
|
writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
|
||||||
|
|
||||||
|
config = readl_relaxed(controller->base + SPI_CONFIG);
|
||||||
|
|
||||||
|
if (spi->mode & SPI_LOOP)
|
||||||
|
config |= SPI_CONFIG_LOOPBACK;
|
||||||
|
else
|
||||||
|
config &= ~SPI_CONFIG_LOOPBACK;
|
||||||
|
|
||||||
|
if (spi->mode & SPI_CPHA)
|
||||||
|
config &= ~SPI_CONFIG_INPUT_FIRST;
|
||||||
|
else
|
||||||
|
config |= SPI_CONFIG_INPUT_FIRST;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HS_MODE improves signal stability for spi-clk high rates,
|
||||||
|
* but is invalid in loop back mode.
|
||||||
|
*/
|
||||||
|
if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP))
|
||||||
|
config |= SPI_CONFIG_HS_MODE;
|
||||||
|
else
|
||||||
|
config &= ~SPI_CONFIG_HS_MODE;
|
||||||
|
|
||||||
|
writel_relaxed(config, controller->base + SPI_CONFIG);
|
||||||
|
|
||||||
|
config = readl_relaxed(controller->base + QUP_CONFIG);
|
||||||
|
config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
|
||||||
|
config |= xfer->bits_per_word - 1;
|
||||||
|
config |= QUP_CONFIG_SPI_MODE;
|
||||||
|
writel_relaxed(config, controller->base + QUP_CONFIG);
|
||||||
|
|
||||||
|
writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_qup_set_cs(struct spi_device *spi, bool enable)
|
||||||
|
{
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
|
u32 iocontol, mask;
|
||||||
|
|
||||||
|
iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL);
|
||||||
|
|
||||||
|
/* Disable auto CS toggle and use manual */
|
||||||
|
iocontol &= ~SPI_IO_C_MX_CS_MODE;
|
||||||
|
iocontol |= SPI_IO_C_FORCE_CS;
|
||||||
|
|
||||||
|
iocontol &= ~SPI_IO_C_CS_SELECT_MASK;
|
||||||
|
iocontol |= SPI_IO_C_CS_SELECT(spi->chip_select);
|
||||||
|
|
||||||
|
mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
iocontol |= mask;
|
||||||
|
else
|
||||||
|
iocontol &= ~mask;
|
||||||
|
|
||||||
|
writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_transfer_one(struct spi_master *master,
|
||||||
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
unsigned long timeout, flags;
|
||||||
|
int ret = -EIO;
|
||||||
|
|
||||||
|
ret = spi_qup_io_config(spi, xfer);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
|
||||||
|
timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
|
||||||
|
timeout = 100 * msecs_to_jiffies(timeout);
|
||||||
|
|
||||||
|
reinit_completion(&controller->done);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&controller->lock, flags);
|
||||||
|
controller->xfer = xfer;
|
||||||
|
controller->error = 0;
|
||||||
|
controller->rx_bytes = 0;
|
||||||
|
controller->tx_bytes = 0;
|
||||||
|
spin_unlock_irqrestore(&controller->lock, flags);
|
||||||
|
|
||||||
|
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
||||||
|
dev_warn(controller->dev, "cannot set RUN state\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
|
||||||
|
dev_warn(controller->dev, "cannot set PAUSE state\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_qup_fifo_write(controller, xfer);
|
||||||
|
|
||||||
|
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
|
||||||
|
dev_warn(controller->dev, "cannot set EXECUTE state\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wait_for_completion_timeout(&controller->done, timeout))
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
exit:
|
||||||
|
spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
|
spin_lock_irqsave(&controller->lock, flags);
|
||||||
|
controller->xfer = NULL;
|
||||||
|
if (!ret)
|
||||||
|
ret = controller->error;
|
||||||
|
spin_unlock_irqrestore(&controller->lock, flags);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct spi_master *master;
|
||||||
|
struct clk *iclk, *cclk;
|
||||||
|
struct spi_qup *controller;
|
||||||
|
struct resource *res;
|
||||||
|
struct device *dev;
|
||||||
|
void __iomem *base;
|
||||||
|
u32 data, max_freq, iomode;
|
||||||
|
int ret, irq, size;
|
||||||
|
|
||||||
|
dev = &pdev->dev;
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
base = devm_ioremap_resource(dev, res);
|
||||||
|
if (IS_ERR(base))
|
||||||
|
return PTR_ERR(base);
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
|
||||||
|
cclk = devm_clk_get(dev, "core");
|
||||||
|
if (IS_ERR(cclk))
|
||||||
|
return PTR_ERR(cclk);
|
||||||
|
|
||||||
|
iclk = devm_clk_get(dev, "iface");
|
||||||
|
if (IS_ERR(iclk))
|
||||||
|
return PTR_ERR(iclk);
|
||||||
|
|
||||||
|
/* This is optional parameter */
|
||||||
|
if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq))
|
||||||
|
max_freq = SPI_MAX_RATE;
|
||||||
|
|
||||||
|
if (!max_freq || max_freq > SPI_MAX_RATE) {
|
||||||
|
dev_err(dev, "invalid clock frequency %d\n", max_freq);
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(cclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "cannot enable core clock\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(iclk);
|
||||||
|
if (ret) {
|
||||||
|
clk_disable_unprepare(cclk);
|
||||||
|
dev_err(dev, "cannot enable iface clock\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = readl_relaxed(base + QUP_HW_VERSION);
|
||||||
|
|
||||||
|
if (data < QUP_HW_VERSION_2_1_1) {
|
||||||
|
clk_disable_unprepare(cclk);
|
||||||
|
clk_disable_unprepare(iclk);
|
||||||
|
dev_err(dev, "v.%08x is not supported\n", data);
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
master = spi_alloc_master(dev, sizeof(struct spi_qup));
|
||||||
|
if (!master) {
|
||||||
|
clk_disable_unprepare(cclk);
|
||||||
|
clk_disable_unprepare(iclk);
|
||||||
|
dev_err(dev, "cannot allocate master\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
master->bus_num = pdev->id;
|
||||||
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
|
||||||
|
master->num_chipselect = SPI_NUM_CHIPSELECTS;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||||
|
master->max_speed_hz = max_freq;
|
||||||
|
master->set_cs = spi_qup_set_cs;
|
||||||
|
master->transfer_one = spi_qup_transfer_one;
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
master->auto_runtime_pm = true;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
|
controller = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
controller->dev = dev;
|
||||||
|
controller->base = base;
|
||||||
|
controller->iclk = iclk;
|
||||||
|
controller->cclk = cclk;
|
||||||
|
controller->irq = irq;
|
||||||
|
|
||||||
|
spin_lock_init(&controller->lock);
|
||||||
|
init_completion(&controller->done);
|
||||||
|
|
||||||
|
iomode = readl_relaxed(base + QUP_IO_M_MODES);
|
||||||
|
|
||||||
|
size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode);
|
||||||
|
if (size)
|
||||||
|
controller->out_blk_sz = size * 16;
|
||||||
|
else
|
||||||
|
controller->out_blk_sz = 4;
|
||||||
|
|
||||||
|
size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode);
|
||||||
|
if (size)
|
||||||
|
controller->in_blk_sz = size * 16;
|
||||||
|
else
|
||||||
|
controller->in_blk_sz = 4;
|
||||||
|
|
||||||
|
size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode);
|
||||||
|
controller->out_fifo_sz = controller->out_blk_sz * (2 << size);
|
||||||
|
|
||||||
|
size = QUP_IO_M_INPUT_FIFO_SIZE(iomode);
|
||||||
|
controller->in_fifo_sz = controller->in_blk_sz * (2 << size);
|
||||||
|
|
||||||
|
dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
|
||||||
|
data, controller->in_blk_sz, controller->in_fifo_sz,
|
||||||
|
controller->out_blk_sz, controller->out_fifo_sz);
|
||||||
|
|
||||||
|
writel_relaxed(1, base + QUP_SW_RESET);
|
||||||
|
|
||||||
|
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "cannot set RESET state\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
writel_relaxed(0, base + QUP_OPERATIONAL);
|
||||||
|
writel_relaxed(0, base + QUP_IO_M_MODES);
|
||||||
|
writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
|
||||||
|
writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
|
||||||
|
base + SPI_ERROR_FLAGS_EN);
|
||||||
|
|
||||||
|
writel_relaxed(0, base + SPI_CONFIG);
|
||||||
|
writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
|
||||||
|
|
||||||
|
ret = devm_request_irq(dev, irq, spi_qup_qup_irq,
|
||||||
|
IRQF_TRIGGER_HIGH, pdev->name, controller);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ret = devm_spi_register_master(dev, master);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
|
||||||
|
pm_runtime_use_autosuspend(dev);
|
||||||
|
pm_runtime_set_active(dev);
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
clk_disable_unprepare(cclk);
|
||||||
|
clk_disable_unprepare(iclk);
|
||||||
|
spi_master_put(master);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static int spi_qup_pm_suspend_runtime(struct device *device)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(device);
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
u32 config;
|
||||||
|
|
||||||
|
/* Enable clocks auto gaiting */
|
||||||
|
config = readl(controller->base + QUP_CONFIG);
|
||||||
|
config |= QUP_CONFIG_CLOCK_AUTO_GATE;
|
||||||
|
writel_relaxed(config, controller->base + QUP_CONFIG);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_pm_resume_runtime(struct device *device)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(device);
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
u32 config;
|
||||||
|
|
||||||
|
/* Disable clocks auto gaiting */
|
||||||
|
config = readl_relaxed(controller->base + QUP_CONFIG);
|
||||||
|
config &= ~QUP_CONFIG_CLOCK_AUTO_GATE;
|
||||||
|
writel_relaxed(config, controller->base + QUP_CONFIG);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM_RUNTIME */
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int spi_qup_suspend(struct device *device)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(device);
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = spi_master_suspend(master);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
clk_disable_unprepare(controller->cclk);
|
||||||
|
clk_disable_unprepare(controller->iclk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_qup_resume(struct device *device)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(device);
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(controller->iclk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(controller->cclk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return spi_master_resume(master);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
static int spi_qup_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(&pdev->dev);
|
||||||
|
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_get_sync(&pdev->dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
clk_disable_unprepare(controller->cclk);
|
||||||
|
clk_disable_unprepare(controller->iclk);
|
||||||
|
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct of_device_id spi_qup_dt_match[] = {
|
||||||
|
{ .compatible = "qcom,spi-qup-v2.1.1", },
|
||||||
|
{ .compatible = "qcom,spi-qup-v2.2.1", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, spi_qup_dt_match);
|
||||||
|
|
||||||
|
static const struct dev_pm_ops spi_qup_dev_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime,
|
||||||
|
spi_qup_pm_resume_runtime,
|
||||||
|
NULL)
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver spi_qup_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "spi_qup",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.pm = &spi_qup_dev_pm_ops,
|
||||||
|
.of_match_table = spi_qup_dt_match,
|
||||||
|
},
|
||||||
|
.probe = spi_qup_probe,
|
||||||
|
.remove = spi_qup_remove,
|
||||||
|
};
|
||||||
|
module_platform_driver(spi_qup_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_ALIAS("platform:spi_qup");
|
File diff suppressed because it is too large
Load Diff
|
@ -9,7 +9,6 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
@ -123,25 +122,15 @@ static int s3c24xx_spi_update_state(struct spi_device *spi,
|
||||||
{
|
{
|
||||||
struct s3c24xx_spi *hw = to_hw(spi);
|
struct s3c24xx_spi *hw = to_hw(spi);
|
||||||
struct s3c24xx_spi_devstate *cs = spi->controller_state;
|
struct s3c24xx_spi_devstate *cs = spi->controller_state;
|
||||||
unsigned int bpw;
|
|
||||||
unsigned int hz;
|
unsigned int hz;
|
||||||
unsigned int div;
|
unsigned int div;
|
||||||
unsigned long clk;
|
unsigned long clk;
|
||||||
|
|
||||||
bpw = t ? t->bits_per_word : spi->bits_per_word;
|
|
||||||
hz = t ? t->speed_hz : spi->max_speed_hz;
|
hz = t ? t->speed_hz : spi->max_speed_hz;
|
||||||
|
|
||||||
if (!bpw)
|
|
||||||
bpw = 8;
|
|
||||||
|
|
||||||
if (!hz)
|
if (!hz)
|
||||||
hz = spi->max_speed_hz;
|
hz = spi->max_speed_hz;
|
||||||
|
|
||||||
if (bpw != 8) {
|
|
||||||
dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spi->mode != cs->mode) {
|
if (spi->mode != cs->mode) {
|
||||||
u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK;
|
u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK;
|
||||||
|
|
||||||
|
@ -544,6 +533,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
master->num_chipselect = hw->pdata->num_cs;
|
master->num_chipselect = hw->pdata->num_cs;
|
||||||
master->bus_num = pdata->bus_num;
|
master->bus_num = pdata->bus_num;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
|
|
||||||
/* setup the state for the bitbang driver */
|
/* setup the state for the bitbang driver */
|
||||||
|
|
||||||
|
@ -643,6 +633,11 @@ static int s3c24xx_spi_remove(struct platform_device *dev)
|
||||||
static int s3c24xx_spi_suspend(struct device *dev)
|
static int s3c24xx_spi_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct s3c24xx_spi *hw = dev_get_drvdata(dev);
|
struct s3c24xx_spi *hw = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = spi_master_suspend(hw->master);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (hw->pdata && hw->pdata->gpio_setup)
|
if (hw->pdata && hw->pdata->gpio_setup)
|
||||||
hw->pdata->gpio_setup(hw->pdata, 0);
|
hw->pdata->gpio_setup(hw->pdata, 0);
|
||||||
|
@ -656,7 +651,7 @@ static int s3c24xx_spi_resume(struct device *dev)
|
||||||
struct s3c24xx_spi *hw = dev_get_drvdata(dev);
|
struct s3c24xx_spi *hw = dev_get_drvdata(dev);
|
||||||
|
|
||||||
s3c24xx_spi_initialsetup(hw);
|
s3c24xx_spi_initialsetup(hw);
|
||||||
return 0;
|
return spi_master_resume(hw->master);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct dev_pm_ops s3c24xx_spi_pmops = {
|
static const struct dev_pm_ops s3c24xx_spi_pmops = {
|
||||||
|
|
|
@ -34,10 +34,6 @@
|
||||||
|
|
||||||
#include <linux/platform_data/spi-s3c64xx.h>
|
#include <linux/platform_data/spi-s3c64xx.h>
|
||||||
|
|
||||||
#ifdef CONFIG_S3C_DMA
|
|
||||||
#include <mach/dma.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAX_SPI_PORTS 3
|
#define MAX_SPI_PORTS 3
|
||||||
#define S3C64XX_SPI_QUIRK_POLL (1 << 0)
|
#define S3C64XX_SPI_QUIRK_POLL (1 << 0)
|
||||||
|
|
||||||
|
@ -200,9 +196,6 @@ struct s3c64xx_spi_driver_data {
|
||||||
unsigned cur_speed;
|
unsigned cur_speed;
|
||||||
struct s3c64xx_spi_dma_data rx_dma;
|
struct s3c64xx_spi_dma_data rx_dma;
|
||||||
struct s3c64xx_spi_dma_data tx_dma;
|
struct s3c64xx_spi_dma_data tx_dma;
|
||||||
#ifdef CONFIG_S3C_DMA
|
|
||||||
struct samsung_dma_ops *ops;
|
|
||||||
#endif
|
|
||||||
struct s3c64xx_spi_port_config *port_conf;
|
struct s3c64xx_spi_port_config *port_conf;
|
||||||
unsigned int port_id;
|
unsigned int port_id;
|
||||||
bool cs_gpio;
|
bool cs_gpio;
|
||||||
|
@ -284,104 +277,8 @@ static void s3c64xx_spi_dmacb(void *data)
|
||||||
spin_unlock_irqrestore(&sdd->lock, flags);
|
spin_unlock_irqrestore(&sdd->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_S3C_DMA
|
|
||||||
/* FIXME: remove this section once arch/arm/mach-s3c64xx uses dmaengine */
|
|
||||||
|
|
||||||
static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
|
|
||||||
.name = "samsung-spi-dma",
|
|
||||||
};
|
|
||||||
|
|
||||||
static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
||||||
unsigned len, dma_addr_t buf)
|
struct sg_table *sgt)
|
||||||
{
|
|
||||||
struct s3c64xx_spi_driver_data *sdd;
|
|
||||||
struct samsung_dma_prep info;
|
|
||||||
struct samsung_dma_config config;
|
|
||||||
|
|
||||||
if (dma->direction == DMA_DEV_TO_MEM) {
|
|
||||||
sdd = container_of((void *)dma,
|
|
||||||
struct s3c64xx_spi_driver_data, rx_dma);
|
|
||||||
config.direction = sdd->rx_dma.direction;
|
|
||||||
config.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA;
|
|
||||||
config.width = sdd->cur_bpw / 8;
|
|
||||||
sdd->ops->config((enum dma_ch)sdd->rx_dma.ch, &config);
|
|
||||||
} else {
|
|
||||||
sdd = container_of((void *)dma,
|
|
||||||
struct s3c64xx_spi_driver_data, tx_dma);
|
|
||||||
config.direction = sdd->tx_dma.direction;
|
|
||||||
config.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA;
|
|
||||||
config.width = sdd->cur_bpw / 8;
|
|
||||||
sdd->ops->config((enum dma_ch)sdd->tx_dma.ch, &config);
|
|
||||||
}
|
|
||||||
|
|
||||||
info.cap = DMA_SLAVE;
|
|
||||||
info.len = len;
|
|
||||||
info.fp = s3c64xx_spi_dmacb;
|
|
||||||
info.fp_param = dma;
|
|
||||||
info.direction = dma->direction;
|
|
||||||
info.buf = buf;
|
|
||||||
|
|
||||||
sdd->ops->prepare((enum dma_ch)dma->ch, &info);
|
|
||||||
sdd->ops->trigger((enum dma_ch)dma->ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
|
|
||||||
{
|
|
||||||
struct samsung_dma_req req;
|
|
||||||
struct device *dev = &sdd->pdev->dev;
|
|
||||||
|
|
||||||
sdd->ops = samsung_dma_get_ops();
|
|
||||||
|
|
||||||
req.cap = DMA_SLAVE;
|
|
||||||
req.client = &s3c64xx_spi_dma_client;
|
|
||||||
|
|
||||||
sdd->rx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request(
|
|
||||||
sdd->rx_dma.dmach, &req, dev, "rx");
|
|
||||||
sdd->tx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request(
|
|
||||||
sdd->tx_dma.dmach, &req, dev, "tx");
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
|
||||||
{
|
|
||||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If DMA resource was not available during
|
|
||||||
* probe, no need to continue with dma requests
|
|
||||||
* else Acquire DMA channels
|
|
||||||
*/
|
|
||||||
while (!is_polling(sdd) && !acquire_dma(sdd))
|
|
||||||
usleep_range(10000, 11000);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
|
|
||||||
{
|
|
||||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
|
|
||||||
|
|
||||||
/* Free DMA channels */
|
|
||||||
if (!is_polling(sdd)) {
|
|
||||||
sdd->ops->release((enum dma_ch)sdd->rx_dma.ch,
|
|
||||||
&s3c64xx_spi_dma_client);
|
|
||||||
sdd->ops->release((enum dma_ch)sdd->tx_dma.ch,
|
|
||||||
&s3c64xx_spi_dma_client);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd,
|
|
||||||
struct s3c64xx_spi_dma_data *dma)
|
|
||||||
{
|
|
||||||
sdd->ops->stop((enum dma_ch)dma->ch);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
|
|
||||||
static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
|
||||||
unsigned len, dma_addr_t buf)
|
|
||||||
{
|
{
|
||||||
struct s3c64xx_spi_driver_data *sdd;
|
struct s3c64xx_spi_driver_data *sdd;
|
||||||
struct dma_slave_config config;
|
struct dma_slave_config config;
|
||||||
|
@ -407,8 +304,8 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
||||||
dmaengine_slave_config(dma->ch, &config);
|
dmaengine_slave_config(dma->ch, &config);
|
||||||
}
|
}
|
||||||
|
|
||||||
desc = dmaengine_prep_slave_single(dma->ch, buf, len,
|
desc = dmaengine_prep_slave_sg(dma->ch, sgt->sgl, sgt->nents,
|
||||||
dma->direction, DMA_PREP_INTERRUPT);
|
dma->direction, DMA_PREP_INTERRUPT);
|
||||||
|
|
||||||
desc->callback = s3c64xx_spi_dmacb;
|
desc->callback = s3c64xx_spi_dmacb;
|
||||||
desc->callback_param = dma;
|
desc->callback_param = dma;
|
||||||
|
@ -437,6 +334,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
spi->dma_rx = sdd->rx_dma.ch;
|
||||||
|
|
||||||
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
||||||
(void *)sdd->tx_dma.dmach, dev, "tx");
|
(void *)sdd->tx_dma.dmach, dev, "tx");
|
||||||
|
@ -445,6 +343,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto out_rx;
|
goto out_rx;
|
||||||
}
|
}
|
||||||
|
spi->dma_tx = sdd->tx_dma.ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = pm_runtime_get_sync(&sdd->pdev->dev);
|
ret = pm_runtime_get_sync(&sdd->pdev->dev);
|
||||||
|
@ -477,12 +376,14 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd,
|
static bool s3c64xx_spi_can_dma(struct spi_master *master,
|
||||||
struct s3c64xx_spi_dma_data *dma)
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
dmaengine_terminate_all(dma->ch);
|
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
||||||
struct spi_device *spi,
|
struct spi_device *spi,
|
||||||
|
@ -515,7 +416,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
||||||
chcfg |= S3C64XX_SPI_CH_TXCH_ON;
|
chcfg |= S3C64XX_SPI_CH_TXCH_ON;
|
||||||
if (dma_mode) {
|
if (dma_mode) {
|
||||||
modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
|
modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
|
||||||
prepare_dma(&sdd->tx_dma, xfer->len, xfer->tx_dma);
|
prepare_dma(&sdd->tx_dma, &xfer->tx_sg);
|
||||||
} else {
|
} else {
|
||||||
switch (sdd->cur_bpw) {
|
switch (sdd->cur_bpw) {
|
||||||
case 32:
|
case 32:
|
||||||
|
@ -547,7 +448,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
||||||
writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
|
writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
|
||||||
| S3C64XX_SPI_PACKET_CNT_EN,
|
| S3C64XX_SPI_PACKET_CNT_EN,
|
||||||
regs + S3C64XX_SPI_PACKET_CNT);
|
regs + S3C64XX_SPI_PACKET_CNT);
|
||||||
prepare_dma(&sdd->rx_dma, xfer->len, xfer->rx_dma);
|
prepare_dma(&sdd->rx_dma, &xfer->rx_sg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,23 +456,6 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
||||||
writel(chcfg, regs + S3C64XX_SPI_CH_CFG);
|
writel(chcfg, regs + S3C64XX_SPI_CH_CFG);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd,
|
|
||||||
struct spi_device *spi)
|
|
||||||
{
|
|
||||||
if (sdd->tgl_spi != NULL) { /* If last device toggled after mssg */
|
|
||||||
if (sdd->tgl_spi != spi) { /* if last mssg on diff device */
|
|
||||||
/* Deselect the last toggled device */
|
|
||||||
if (spi->cs_gpio >= 0)
|
|
||||||
gpio_set_value(spi->cs_gpio,
|
|
||||||
spi->mode & SPI_CS_HIGH ? 0 : 1);
|
|
||||||
}
|
|
||||||
sdd->tgl_spi = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spi->cs_gpio >= 0)
|
|
||||||
gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd,
|
static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd,
|
||||||
int timeout_ms)
|
int timeout_ms)
|
||||||
{
|
{
|
||||||
|
@ -593,112 +477,111 @@ static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd,
|
||||||
return RX_FIFO_LVL(status, sdd);
|
return RX_FIFO_LVL(status, sdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
|
static int wait_for_dma(struct s3c64xx_spi_driver_data *sdd,
|
||||||
struct spi_transfer *xfer, int dma_mode)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
void __iomem *regs = sdd->regs;
|
void __iomem *regs = sdd->regs;
|
||||||
unsigned long val;
|
unsigned long val;
|
||||||
|
u32 status;
|
||||||
int ms;
|
int ms;
|
||||||
|
|
||||||
/* millisecs to xfer 'len' bytes @ 'cur_speed' */
|
/* millisecs to xfer 'len' bytes @ 'cur_speed' */
|
||||||
ms = xfer->len * 8 * 1000 / sdd->cur_speed;
|
ms = xfer->len * 8 * 1000 / sdd->cur_speed;
|
||||||
ms += 10; /* some tolerance */
|
ms += 10; /* some tolerance */
|
||||||
|
|
||||||
if (dma_mode) {
|
val = msecs_to_jiffies(ms) + 10;
|
||||||
val = msecs_to_jiffies(ms) + 10;
|
val = wait_for_completion_timeout(&sdd->xfer_completion, val);
|
||||||
val = wait_for_completion_timeout(&sdd->xfer_completion, val);
|
|
||||||
} else {
|
/*
|
||||||
u32 status;
|
* If the previous xfer was completed within timeout, then
|
||||||
val = msecs_to_loops(ms);
|
* proceed further else return -EIO.
|
||||||
do {
|
* DmaTx returns after simply writing data in the FIFO,
|
||||||
|
* w/o waiting for real transmission on the bus to finish.
|
||||||
|
* DmaRx returns only after Dma read data from FIFO which
|
||||||
|
* needs bus transmission to finish, so we don't worry if
|
||||||
|
* Xfer involved Rx(with or without Tx).
|
||||||
|
*/
|
||||||
|
if (val && !xfer->rx_buf) {
|
||||||
|
val = msecs_to_loops(10);
|
||||||
|
status = readl(regs + S3C64XX_SPI_STATUS);
|
||||||
|
while ((TX_FIFO_LVL(status, sdd)
|
||||||
|
|| !S3C64XX_SPI_ST_TX_DONE(status, sdd))
|
||||||
|
&& --val) {
|
||||||
|
cpu_relax();
|
||||||
status = readl(regs + S3C64XX_SPI_STATUS);
|
status = readl(regs + S3C64XX_SPI_STATUS);
|
||||||
} while (RX_FIFO_LVL(status, sdd) < xfer->len && --val);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dma_mode) {
|
|
||||||
u32 status;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the previous xfer was completed within timeout, then
|
|
||||||
* proceed further else return -EIO.
|
|
||||||
* DmaTx returns after simply writing data in the FIFO,
|
|
||||||
* w/o waiting for real transmission on the bus to finish.
|
|
||||||
* DmaRx returns only after Dma read data from FIFO which
|
|
||||||
* needs bus transmission to finish, so we don't worry if
|
|
||||||
* Xfer involved Rx(with or without Tx).
|
|
||||||
*/
|
|
||||||
if (val && !xfer->rx_buf) {
|
|
||||||
val = msecs_to_loops(10);
|
|
||||||
status = readl(regs + S3C64XX_SPI_STATUS);
|
|
||||||
while ((TX_FIFO_LVL(status, sdd)
|
|
||||||
|| !S3C64XX_SPI_ST_TX_DONE(status, sdd))
|
|
||||||
&& --val) {
|
|
||||||
cpu_relax();
|
|
||||||
status = readl(regs + S3C64XX_SPI_STATUS);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If timed out while checking rx/tx status return error */
|
|
||||||
if (!val)
|
|
||||||
return -EIO;
|
|
||||||
} else {
|
|
||||||
int loops;
|
|
||||||
u32 cpy_len;
|
|
||||||
u8 *buf;
|
|
||||||
|
|
||||||
/* If it was only Tx */
|
|
||||||
if (!xfer->rx_buf) {
|
|
||||||
sdd->state &= ~TXBUSY;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the receive length is bigger than the controller fifo
|
|
||||||
* size, calculate the loops and read the fifo as many times.
|
|
||||||
* loops = length / max fifo size (calculated by using the
|
|
||||||
* fifo mask).
|
|
||||||
* For any size less than the fifo size the below code is
|
|
||||||
* executed atleast once.
|
|
||||||
*/
|
|
||||||
loops = xfer->len / ((FIFO_LVL_MASK(sdd) >> 1) + 1);
|
|
||||||
buf = xfer->rx_buf;
|
|
||||||
do {
|
|
||||||
/* wait for data to be received in the fifo */
|
|
||||||
cpy_len = s3c64xx_spi_wait_for_timeout(sdd,
|
|
||||||
(loops ? ms : 0));
|
|
||||||
|
|
||||||
switch (sdd->cur_bpw) {
|
|
||||||
case 32:
|
|
||||||
ioread32_rep(regs + S3C64XX_SPI_RX_DATA,
|
|
||||||
buf, cpy_len / 4);
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
ioread16_rep(regs + S3C64XX_SPI_RX_DATA,
|
|
||||||
buf, cpy_len / 2);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ioread8_rep(regs + S3C64XX_SPI_RX_DATA,
|
|
||||||
buf, cpy_len);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = buf + cpy_len;
|
|
||||||
} while (loops--);
|
|
||||||
sdd->state &= ~RXBUSY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If timed out while checking rx/tx status return error */
|
||||||
|
if (!val)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd,
|
static int wait_for_pio(struct s3c64xx_spi_driver_data *sdd,
|
||||||
struct spi_device *spi)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
if (sdd->tgl_spi == spi)
|
void __iomem *regs = sdd->regs;
|
||||||
sdd->tgl_spi = NULL;
|
unsigned long val;
|
||||||
|
u32 status;
|
||||||
|
int loops;
|
||||||
|
u32 cpy_len;
|
||||||
|
u8 *buf;
|
||||||
|
int ms;
|
||||||
|
|
||||||
if (spi->cs_gpio >= 0)
|
/* millisecs to xfer 'len' bytes @ 'cur_speed' */
|
||||||
gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1);
|
ms = xfer->len * 8 * 1000 / sdd->cur_speed;
|
||||||
|
ms += 10; /* some tolerance */
|
||||||
|
|
||||||
|
val = msecs_to_loops(ms);
|
||||||
|
do {
|
||||||
|
status = readl(regs + S3C64XX_SPI_STATUS);
|
||||||
|
} while (RX_FIFO_LVL(status, sdd) < xfer->len && --val);
|
||||||
|
|
||||||
|
|
||||||
|
/* If it was only Tx */
|
||||||
|
if (!xfer->rx_buf) {
|
||||||
|
sdd->state &= ~TXBUSY;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the receive length is bigger than the controller fifo
|
||||||
|
* size, calculate the loops and read the fifo as many times.
|
||||||
|
* loops = length / max fifo size (calculated by using the
|
||||||
|
* fifo mask).
|
||||||
|
* For any size less than the fifo size the below code is
|
||||||
|
* executed atleast once.
|
||||||
|
*/
|
||||||
|
loops = xfer->len / ((FIFO_LVL_MASK(sdd) >> 1) + 1);
|
||||||
|
buf = xfer->rx_buf;
|
||||||
|
do {
|
||||||
|
/* wait for data to be received in the fifo */
|
||||||
|
cpy_len = s3c64xx_spi_wait_for_timeout(sdd,
|
||||||
|
(loops ? ms : 0));
|
||||||
|
|
||||||
|
switch (sdd->cur_bpw) {
|
||||||
|
case 32:
|
||||||
|
ioread32_rep(regs + S3C64XX_SPI_RX_DATA,
|
||||||
|
buf, cpy_len / 4);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
ioread16_rep(regs + S3C64XX_SPI_RX_DATA,
|
||||||
|
buf, cpy_len / 2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ioread8_rep(regs + S3C64XX_SPI_RX_DATA,
|
||||||
|
buf, cpy_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf + cpy_len;
|
||||||
|
} while (loops--);
|
||||||
|
sdd->state &= ~RXBUSY;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
|
static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
|
||||||
|
@ -774,81 +657,6 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
|
||||||
|
|
||||||
#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32)
|
#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32)
|
||||||
|
|
||||||
static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
|
|
||||||
struct spi_message *msg)
|
|
||||||
{
|
|
||||||
struct device *dev = &sdd->pdev->dev;
|
|
||||||
struct spi_transfer *xfer;
|
|
||||||
|
|
||||||
if (is_polling(sdd) || msg->is_dma_mapped)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* First mark all xfer unmapped */
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
|
||||||
xfer->rx_dma = XFER_DMAADDR_INVALID;
|
|
||||||
xfer->tx_dma = XFER_DMAADDR_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map until end or first fail */
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
|
||||||
|
|
||||||
if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (xfer->tx_buf != NULL) {
|
|
||||||
xfer->tx_dma = dma_map_single(dev,
|
|
||||||
(void *)xfer->tx_buf, xfer->len,
|
|
||||||
DMA_TO_DEVICE);
|
|
||||||
if (dma_mapping_error(dev, xfer->tx_dma)) {
|
|
||||||
dev_err(dev, "dma_map_single Tx failed\n");
|
|
||||||
xfer->tx_dma = XFER_DMAADDR_INVALID;
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xfer->rx_buf != NULL) {
|
|
||||||
xfer->rx_dma = dma_map_single(dev, xfer->rx_buf,
|
|
||||||
xfer->len, DMA_FROM_DEVICE);
|
|
||||||
if (dma_mapping_error(dev, xfer->rx_dma)) {
|
|
||||||
dev_err(dev, "dma_map_single Rx failed\n");
|
|
||||||
dma_unmap_single(dev, xfer->tx_dma,
|
|
||||||
xfer->len, DMA_TO_DEVICE);
|
|
||||||
xfer->tx_dma = XFER_DMAADDR_INVALID;
|
|
||||||
xfer->rx_dma = XFER_DMAADDR_INVALID;
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
|
|
||||||
struct spi_message *msg)
|
|
||||||
{
|
|
||||||
struct device *dev = &sdd->pdev->dev;
|
|
||||||
struct spi_transfer *xfer;
|
|
||||||
|
|
||||||
if (is_polling(sdd) || msg->is_dma_mapped)
|
|
||||||
return;
|
|
||||||
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
|
||||||
|
|
||||||
if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (xfer->rx_buf != NULL
|
|
||||||
&& xfer->rx_dma != XFER_DMAADDR_INVALID)
|
|
||||||
dma_unmap_single(dev, xfer->rx_dma,
|
|
||||||
xfer->len, DMA_FROM_DEVICE);
|
|
||||||
|
|
||||||
if (xfer->tx_buf != NULL
|
|
||||||
&& xfer->tx_dma != XFER_DMAADDR_INVALID)
|
|
||||||
dma_unmap_single(dev, xfer->tx_dma,
|
|
||||||
xfer->len, DMA_TO_DEVICE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int s3c64xx_spi_prepare_message(struct spi_master *master,
|
static int s3c64xx_spi_prepare_message(struct spi_master *master,
|
||||||
struct spi_message *msg)
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
|
@ -866,13 +674,6 @@ static int s3c64xx_spi_prepare_message(struct spi_master *master,
|
||||||
s3c64xx_spi_config(sdd);
|
s3c64xx_spi_config(sdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Map all the transfers if needed */
|
|
||||||
if (s3c64xx_spi_map_mssg(sdd, msg)) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"Xfer: Unable to map message buffers!\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Configure feedback delay */
|
/* Configure feedback delay */
|
||||||
writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
|
writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
|
||||||
|
|
||||||
|
@ -896,13 +697,6 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
||||||
bpw = xfer->bits_per_word;
|
bpw = xfer->bits_per_word;
|
||||||
speed = xfer->speed_hz ? : spi->max_speed_hz;
|
speed = xfer->speed_hz ? : spi->max_speed_hz;
|
||||||
|
|
||||||
if (xfer->len % (bpw / 8)) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"Xfer length(%u) not a multiple of word size(%u)\n",
|
|
||||||
xfer->len, bpw / 8);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) {
|
if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) {
|
||||||
sdd->cur_bpw = bpw;
|
sdd->cur_bpw = bpw;
|
||||||
sdd->cur_speed = speed;
|
sdd->cur_speed = speed;
|
||||||
|
@ -929,7 +723,10 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
||||||
|
|
||||||
spin_unlock_irqrestore(&sdd->lock, flags);
|
spin_unlock_irqrestore(&sdd->lock, flags);
|
||||||
|
|
||||||
status = wait_for_xfer(sdd, xfer, use_dma);
|
if (use_dma)
|
||||||
|
status = wait_for_dma(sdd, xfer);
|
||||||
|
else
|
||||||
|
status = wait_for_pio(sdd, xfer);
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
|
dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
|
||||||
|
@ -941,10 +738,10 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
||||||
if (use_dma) {
|
if (use_dma) {
|
||||||
if (xfer->tx_buf != NULL
|
if (xfer->tx_buf != NULL
|
||||||
&& (sdd->state & TXBUSY))
|
&& (sdd->state & TXBUSY))
|
||||||
s3c64xx_spi_dma_stop(sdd, &sdd->tx_dma);
|
dmaengine_terminate_all(sdd->tx_dma.ch);
|
||||||
if (xfer->rx_buf != NULL
|
if (xfer->rx_buf != NULL
|
||||||
&& (sdd->state & RXBUSY))
|
&& (sdd->state & RXBUSY))
|
||||||
s3c64xx_spi_dma_stop(sdd, &sdd->rx_dma);
|
dmaengine_terminate_all(sdd->rx_dma.ch);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
flush_fifo(sdd);
|
flush_fifo(sdd);
|
||||||
|
@ -953,16 +750,6 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s3c64xx_spi_unprepare_message(struct spi_master *master,
|
|
||||||
struct spi_message *msg)
|
|
||||||
{
|
|
||||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
|
||||||
|
|
||||||
s3c64xx_spi_unmap_mssg(sdd, msg);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
|
static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
|
||||||
struct spi_device *spi)
|
struct spi_device *spi)
|
||||||
{
|
{
|
||||||
|
@ -1092,14 +879,12 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||||
|
|
||||||
pm_runtime_put(&sdd->pdev->dev);
|
pm_runtime_put(&sdd->pdev->dev);
|
||||||
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
|
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
|
||||||
disable_cs(sdd, spi);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
setup_exit:
|
setup_exit:
|
||||||
pm_runtime_put(&sdd->pdev->dev);
|
pm_runtime_put(&sdd->pdev->dev);
|
||||||
/* setup() returns with device de-selected */
|
/* setup() returns with device de-selected */
|
||||||
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
|
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
|
||||||
disable_cs(sdd, spi);
|
|
||||||
|
|
||||||
gpio_free(cs->line);
|
gpio_free(cs->line);
|
||||||
spi_set_ctldata(spi, NULL);
|
spi_set_ctldata(spi, NULL);
|
||||||
|
@ -1338,7 +1123,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
||||||
master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
|
master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
|
||||||
master->prepare_message = s3c64xx_spi_prepare_message;
|
master->prepare_message = s3c64xx_spi_prepare_message;
|
||||||
master->transfer_one = s3c64xx_spi_transfer_one;
|
master->transfer_one = s3c64xx_spi_transfer_one;
|
||||||
master->unprepare_message = s3c64xx_spi_unprepare_message;
|
|
||||||
master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
|
master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
|
||||||
master->num_chipselect = sci->num_cs;
|
master->num_chipselect = sci->num_cs;
|
||||||
master->dma_alignment = 8;
|
master->dma_alignment = 8;
|
||||||
|
@ -1347,6 +1131,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
||||||
/* the spi->mode bits understood by this driver: */
|
/* the spi->mode bits understood by this driver: */
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
|
if (!is_polling(sdd))
|
||||||
|
master->can_dma = s3c64xx_spi_can_dma;
|
||||||
|
|
||||||
sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res);
|
sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res);
|
||||||
if (IS_ERR(sdd->regs)) {
|
if (IS_ERR(sdd->regs)) {
|
||||||
|
|
|
@ -183,17 +183,9 @@ static int sc18is602_setup_transfer(struct sc18is602 *hw, u32 hz, u8 mode)
|
||||||
static int sc18is602_check_transfer(struct spi_device *spi,
|
static int sc18is602_check_transfer(struct spi_device *spi,
|
||||||
struct spi_transfer *t, int tlen)
|
struct spi_transfer *t, int tlen)
|
||||||
{
|
{
|
||||||
uint32_t hz;
|
|
||||||
|
|
||||||
if (t && t->len + tlen > SC18IS602_BUFSIZ)
|
if (t && t->len + tlen > SC18IS602_BUFSIZ)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
hz = spi->max_speed_hz;
|
|
||||||
if (t && t->speed_hz)
|
|
||||||
hz = t->speed_hz;
|
|
||||||
if (hz == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,22 +197,15 @@ static int sc18is602_transfer_one(struct spi_master *master,
|
||||||
struct spi_transfer *t;
|
struct spi_transfer *t;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
/* SC18IS602 does not support CS2 */
|
|
||||||
if (hw->id == sc18is602 && spi->chip_select == 2) {
|
|
||||||
status = -ENXIO;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
hw->tlen = 0;
|
hw->tlen = 0;
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
u32 hz = t->speed_hz ? : spi->max_speed_hz;
|
|
||||||
bool do_transfer;
|
bool do_transfer;
|
||||||
|
|
||||||
status = sc18is602_check_transfer(spi, t, hw->tlen);
|
status = sc18is602_check_transfer(spi, t, hw->tlen);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
status = sc18is602_setup_transfer(hw, hz, spi->mode);
|
status = sc18is602_setup_transfer(hw, t->speed_hz, spi->mode);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -238,7 +223,6 @@ static int sc18is602_transfer_one(struct spi_master *master,
|
||||||
if (t->delay_usecs)
|
if (t->delay_usecs)
|
||||||
udelay(t->delay_usecs);
|
udelay(t->delay_usecs);
|
||||||
}
|
}
|
||||||
error:
|
|
||||||
m->status = status;
|
m->status = status;
|
||||||
spi_finalize_current_message(master);
|
spi_finalize_current_message(master);
|
||||||
|
|
||||||
|
@ -247,10 +231,13 @@ error:
|
||||||
|
|
||||||
static int sc18is602_setup(struct spi_device *spi)
|
static int sc18is602_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
if (spi->mode & ~(SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST))
|
struct sc18is602 *hw = spi_master_get_devdata(spi->master);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return sc18is602_check_transfer(spi, NULL, 0);
|
/* SC18IS602 does not support CS2 */
|
||||||
|
if (hw->id == sc18is602 && spi->chip_select == 2)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sc18is602_probe(struct i2c_client *client,
|
static int sc18is602_probe(struct i2c_client *client,
|
||||||
|
@ -309,6 +296,8 @@ static int sc18is602_probe(struct i2c_client *client,
|
||||||
master->setup = sc18is602_setup;
|
master->setup = sc18is602_setup;
|
||||||
master->transfer_one_message = sc18is602_transfer_one;
|
master->transfer_one_message = sc18is602_transfer_one;
|
||||||
master->dev.of_node = np;
|
master->dev.of_node = np;
|
||||||
|
master->min_speed_hz = hw->freq / 128;
|
||||||
|
master->max_speed_hz = hw->freq / 4;
|
||||||
|
|
||||||
error = devm_spi_register_master(dev, master);
|
error = devm_spi_register_master(dev, master);
|
||||||
if (error)
|
if (error)
|
||||||
|
|
|
@ -46,8 +46,6 @@
|
||||||
/* SPSR */
|
/* SPSR */
|
||||||
#define RXFL (1 << 2)
|
#define RXFL (1 << 2)
|
||||||
|
|
||||||
#define hspi2info(h) (h->dev->platform_data)
|
|
||||||
|
|
||||||
struct hspi_priv {
|
struct hspi_priv {
|
||||||
void __iomem *addr;
|
void __iomem *addr;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
|
@ -113,14 +111,9 @@ static void hspi_hw_setup(struct hspi_priv *hspi,
|
||||||
{
|
{
|
||||||
struct spi_device *spi = msg->spi;
|
struct spi_device *spi = msg->spi;
|
||||||
struct device *dev = hspi->dev;
|
struct device *dev = hspi->dev;
|
||||||
u32 target_rate;
|
|
||||||
u32 spcr, idiv_clk;
|
u32 spcr, idiv_clk;
|
||||||
u32 rate, best_rate, min, tmp;
|
u32 rate, best_rate, min, tmp;
|
||||||
|
|
||||||
target_rate = t ? t->speed_hz : 0;
|
|
||||||
if (!target_rate)
|
|
||||||
target_rate = spi->max_speed_hz;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* find best IDIV/CLKCx settings
|
* find best IDIV/CLKCx settings
|
||||||
*/
|
*/
|
||||||
|
@ -140,7 +133,7 @@ static void hspi_hw_setup(struct hspi_priv *hspi,
|
||||||
rate /= (((idiv_clk & 0x1F) + 1) * 2);
|
rate /= (((idiv_clk & 0x1F) + 1) * 2);
|
||||||
|
|
||||||
/* save best settings */
|
/* save best settings */
|
||||||
tmp = abs(target_rate - rate);
|
tmp = abs(t->speed_hz - rate);
|
||||||
if (tmp < min) {
|
if (tmp < min) {
|
||||||
min = tmp;
|
min = tmp;
|
||||||
spcr = idiv_clk;
|
spcr = idiv_clk;
|
||||||
|
@ -153,7 +146,7 @@ static void hspi_hw_setup(struct hspi_priv *hspi,
|
||||||
if (spi->mode & SPI_CPOL)
|
if (spi->mode & SPI_CPOL)
|
||||||
spcr |= 1 << 6;
|
spcr |= 1 << 6;
|
||||||
|
|
||||||
dev_dbg(dev, "speed %d/%d\n", target_rate, best_rate);
|
dev_dbg(dev, "speed %d/%d\n", t->speed_hz, best_rate);
|
||||||
|
|
||||||
hspi_write(hspi, SPCR, spcr);
|
hspi_write(hspi, SPCR, spcr);
|
||||||
hspi_write(hspi, SPSR, 0x0);
|
hspi_write(hspi, SPSR, 0x0);
|
||||||
|
@ -230,29 +223,6 @@ static int hspi_transfer_one_message(struct spi_master *master,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hspi_setup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct hspi_priv *hspi = spi_master_get_devdata(spi->master);
|
|
||||||
struct device *dev = hspi->dev;
|
|
||||||
|
|
||||||
if (8 != spi->bits_per_word) {
|
|
||||||
dev_err(dev, "bits_per_word should be 8\n");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_dbg(dev, "%s setup\n", spi->modalias);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hspi_cleanup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct hspi_priv *hspi = spi_master_get_devdata(spi->master);
|
|
||||||
struct device *dev = hspi->dev;
|
|
||||||
|
|
||||||
dev_dbg(dev, "%s cleanup\n", spi->modalias);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hspi_probe(struct platform_device *pdev)
|
static int hspi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
@ -298,22 +268,23 @@ static int hspi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
master->num_chipselect = 1;
|
|
||||||
master->bus_num = pdev->id;
|
master->bus_num = pdev->id;
|
||||||
master->setup = hspi_setup;
|
|
||||||
master->cleanup = hspi_cleanup;
|
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
master->transfer_one_message = hspi_transfer_one_message;
|
master->transfer_one_message = hspi_transfer_one_message;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
|
|
||||||
ret = devm_spi_register_master(&pdev->dev, master);
|
ret = devm_spi_register_master(&pdev->dev, master);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "spi_register_master error.\n");
|
dev_err(&pdev->dev, "spi_register_master error.\n");
|
||||||
goto error1;
|
goto error2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error2:
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
error1:
|
error1:
|
||||||
clk_put(clk);
|
clk_put(clk);
|
||||||
error0:
|
error0:
|
||||||
|
|
|
@ -15,59 +15,108 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <linux/spi/sh_msiof.h>
|
#include <linux/spi/sh_msiof.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/spi_bitbang.h>
|
|
||||||
|
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct sh_msiof_chipdata {
|
||||||
|
u16 tx_fifo_size;
|
||||||
|
u16 rx_fifo_size;
|
||||||
|
u16 master_flags;
|
||||||
|
};
|
||||||
|
|
||||||
struct sh_msiof_spi_priv {
|
struct sh_msiof_spi_priv {
|
||||||
struct spi_bitbang bitbang; /* must be first for spi_bitbang.c */
|
|
||||||
void __iomem *mapbase;
|
void __iomem *mapbase;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
|
const struct sh_msiof_chipdata *chipdata;
|
||||||
struct sh_msiof_spi_info *info;
|
struct sh_msiof_spi_info *info;
|
||||||
struct completion done;
|
struct completion done;
|
||||||
unsigned long flags;
|
|
||||||
int tx_fifo_size;
|
int tx_fifo_size;
|
||||||
int rx_fifo_size;
|
int rx_fifo_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TMDR1 0x00
|
#define TMDR1 0x00 /* Transmit Mode Register 1 */
|
||||||
#define TMDR2 0x04
|
#define TMDR2 0x04 /* Transmit Mode Register 2 */
|
||||||
#define TMDR3 0x08
|
#define TMDR3 0x08 /* Transmit Mode Register 3 */
|
||||||
#define RMDR1 0x10
|
#define RMDR1 0x10 /* Receive Mode Register 1 */
|
||||||
#define RMDR2 0x14
|
#define RMDR2 0x14 /* Receive Mode Register 2 */
|
||||||
#define RMDR3 0x18
|
#define RMDR3 0x18 /* Receive Mode Register 3 */
|
||||||
#define TSCR 0x20
|
#define TSCR 0x20 /* Transmit Clock Select Register */
|
||||||
#define RSCR 0x22
|
#define RSCR 0x22 /* Receive Clock Select Register (SH, A1, APE6) */
|
||||||
#define CTR 0x28
|
#define CTR 0x28 /* Control Register */
|
||||||
#define FCTR 0x30
|
#define FCTR 0x30 /* FIFO Control Register */
|
||||||
#define STR 0x40
|
#define STR 0x40 /* Status Register */
|
||||||
#define IER 0x44
|
#define IER 0x44 /* Interrupt Enable Register */
|
||||||
#define TDR1 0x48
|
#define TDR1 0x48 /* Transmit Control Data Register 1 (SH, A1) */
|
||||||
#define TDR2 0x4c
|
#define TDR2 0x4c /* Transmit Control Data Register 2 (SH, A1) */
|
||||||
#define TFDR 0x50
|
#define TFDR 0x50 /* Transmit FIFO Data Register */
|
||||||
#define RDR1 0x58
|
#define RDR1 0x58 /* Receive Control Data Register 1 (SH, A1) */
|
||||||
#define RDR2 0x5c
|
#define RDR2 0x5c /* Receive Control Data Register 2 (SH, A1) */
|
||||||
#define RFDR 0x60
|
#define RFDR 0x60 /* Receive FIFO Data Register */
|
||||||
|
|
||||||
#define CTR_TSCKE (1 << 15)
|
/* TMDR1 and RMDR1 */
|
||||||
#define CTR_TFSE (1 << 14)
|
#define MDR1_TRMD 0x80000000 /* Transfer Mode (1 = Master mode) */
|
||||||
#define CTR_TXE (1 << 9)
|
#define MDR1_SYNCMD_MASK 0x30000000 /* SYNC Mode */
|
||||||
#define CTR_RXE (1 << 8)
|
#define MDR1_SYNCMD_SPI 0x20000000 /* Level mode/SPI */
|
||||||
|
#define MDR1_SYNCMD_LR 0x30000000 /* L/R mode */
|
||||||
|
#define MDR1_SYNCAC_SHIFT 25 /* Sync Polarity (1 = Active-low) */
|
||||||
|
#define MDR1_BITLSB_SHIFT 24 /* MSB/LSB First (1 = LSB first) */
|
||||||
|
#define MDR1_FLD_MASK 0x000000c0 /* Frame Sync Signal Interval (0-3) */
|
||||||
|
#define MDR1_FLD_SHIFT 2
|
||||||
|
#define MDR1_XXSTP 0x00000001 /* Transmission/Reception Stop on FIFO */
|
||||||
|
/* TMDR1 */
|
||||||
|
#define TMDR1_PCON 0x40000000 /* Transfer Signal Connection */
|
||||||
|
|
||||||
|
/* TMDR2 and RMDR2 */
|
||||||
|
#define MDR2_BITLEN1(i) (((i) - 1) << 24) /* Data Size (8-32 bits) */
|
||||||
|
#define MDR2_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */
|
||||||
|
#define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */
|
||||||
|
|
||||||
|
/* TSCR and RSCR */
|
||||||
|
#define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */
|
||||||
|
#define SCR_BRPS(i) (((i) - 1) << 8)
|
||||||
|
#define SCR_BRDV_MASK 0x0007 /* Baud Rate Generator's Division Ratio */
|
||||||
|
#define SCR_BRDV_DIV_2 0x0000
|
||||||
|
#define SCR_BRDV_DIV_4 0x0001
|
||||||
|
#define SCR_BRDV_DIV_8 0x0002
|
||||||
|
#define SCR_BRDV_DIV_16 0x0003
|
||||||
|
#define SCR_BRDV_DIV_32 0x0004
|
||||||
|
#define SCR_BRDV_DIV_1 0x0007
|
||||||
|
|
||||||
|
/* CTR */
|
||||||
|
#define CTR_TSCKIZ_MASK 0xc0000000 /* Transmit Clock I/O Polarity Select */
|
||||||
|
#define CTR_TSCKIZ_SCK 0x80000000 /* Disable SCK when TX disabled */
|
||||||
|
#define CTR_TSCKIZ_POL_SHIFT 30 /* Transmit Clock Polarity */
|
||||||
|
#define CTR_RSCKIZ_MASK 0x30000000 /* Receive Clock Polarity Select */
|
||||||
|
#define CTR_RSCKIZ_SCK 0x20000000 /* Must match CTR_TSCKIZ_SCK */
|
||||||
|
#define CTR_RSCKIZ_POL_SHIFT 28 /* Receive Clock Polarity */
|
||||||
|
#define CTR_TEDG_SHIFT 27 /* Transmit Timing (1 = falling edge) */
|
||||||
|
#define CTR_REDG_SHIFT 26 /* Receive Timing (1 = falling edge) */
|
||||||
|
#define CTR_TXDIZ_MASK 0x00c00000 /* Pin Output When TX is Disabled */
|
||||||
|
#define CTR_TXDIZ_LOW 0x00000000 /* 0 */
|
||||||
|
#define CTR_TXDIZ_HIGH 0x00400000 /* 1 */
|
||||||
|
#define CTR_TXDIZ_HIZ 0x00800000 /* High-impedance */
|
||||||
|
#define CTR_TSCKE 0x00008000 /* Transmit Serial Clock Output Enable */
|
||||||
|
#define CTR_TFSE 0x00004000 /* Transmit Frame Sync Signal Output Enable */
|
||||||
|
#define CTR_TXE 0x00000200 /* Transmit Enable */
|
||||||
|
#define CTR_RXE 0x00000100 /* Receive Enable */
|
||||||
|
|
||||||
|
/* STR and IER */
|
||||||
|
#define STR_TEOF 0x00800000 /* Frame Transmission End */
|
||||||
|
#define STR_REOF 0x00000080 /* Frame Reception End */
|
||||||
|
|
||||||
#define STR_TEOF (1 << 23)
|
|
||||||
#define STR_REOF (1 << 7)
|
|
||||||
|
|
||||||
static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
|
static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
|
||||||
{
|
{
|
||||||
|
@ -131,22 +180,21 @@ static struct {
|
||||||
unsigned short div;
|
unsigned short div;
|
||||||
unsigned short scr;
|
unsigned short scr;
|
||||||
} const sh_msiof_spi_clk_table[] = {
|
} const sh_msiof_spi_clk_table[] = {
|
||||||
{ 1, 0x0007 },
|
{ 1, SCR_BRPS( 1) | SCR_BRDV_DIV_1 },
|
||||||
{ 2, 0x0000 },
|
{ 2, SCR_BRPS( 1) | SCR_BRDV_DIV_2 },
|
||||||
{ 4, 0x0001 },
|
{ 4, SCR_BRPS( 1) | SCR_BRDV_DIV_4 },
|
||||||
{ 8, 0x0002 },
|
{ 8, SCR_BRPS( 1) | SCR_BRDV_DIV_8 },
|
||||||
{ 16, 0x0003 },
|
{ 16, SCR_BRPS( 1) | SCR_BRDV_DIV_16 },
|
||||||
{ 32, 0x0004 },
|
{ 32, SCR_BRPS( 1) | SCR_BRDV_DIV_32 },
|
||||||
{ 64, 0x1f00 },
|
{ 64, SCR_BRPS(32) | SCR_BRDV_DIV_2 },
|
||||||
{ 128, 0x1f01 },
|
{ 128, SCR_BRPS(32) | SCR_BRDV_DIV_4 },
|
||||||
{ 256, 0x1f02 },
|
{ 256, SCR_BRPS(32) | SCR_BRDV_DIV_8 },
|
||||||
{ 512, 0x1f03 },
|
{ 512, SCR_BRPS(32) | SCR_BRDV_DIV_16 },
|
||||||
{ 1024, 0x1f04 },
|
{ 1024, SCR_BRPS(32) | SCR_BRDV_DIV_32 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
|
static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
|
||||||
unsigned long parent_rate,
|
unsigned long parent_rate, u32 spi_hz)
|
||||||
unsigned long spi_hz)
|
|
||||||
{
|
{
|
||||||
unsigned long div = 1024;
|
unsigned long div = 1024;
|
||||||
size_t k;
|
size_t k;
|
||||||
|
@ -164,7 +212,8 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
|
||||||
k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_clk_table) - 1);
|
k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_clk_table) - 1);
|
||||||
|
|
||||||
sh_msiof_write(p, TSCR, sh_msiof_spi_clk_table[k].scr);
|
sh_msiof_write(p, TSCR, sh_msiof_spi_clk_table[k].scr);
|
||||||
sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr);
|
if (!(p->chipdata->master_flags & SPI_MASTER_MUST_TX))
|
||||||
|
sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
|
static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
|
||||||
|
@ -183,21 +232,25 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
|
||||||
*/
|
*/
|
||||||
sh_msiof_write(p, FCTR, 0);
|
sh_msiof_write(p, FCTR, 0);
|
||||||
|
|
||||||
tmp = 0;
|
tmp = MDR1_SYNCMD_SPI | 1 << MDR1_FLD_SHIFT | MDR1_XXSTP;
|
||||||
tmp |= !cs_high << 25;
|
tmp |= !cs_high << MDR1_SYNCAC_SHIFT;
|
||||||
tmp |= lsb_first << 24;
|
tmp |= lsb_first << MDR1_BITLSB_SHIFT;
|
||||||
sh_msiof_write(p, TMDR1, 0xe0000005 | tmp);
|
sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
|
||||||
sh_msiof_write(p, RMDR1, 0x20000005 | tmp);
|
if (p->chipdata->master_flags & SPI_MASTER_MUST_TX) {
|
||||||
|
/* These bits are reserved if RX needs TX */
|
||||||
|
tmp &= ~0x0000ffff;
|
||||||
|
}
|
||||||
|
sh_msiof_write(p, RMDR1, tmp);
|
||||||
|
|
||||||
tmp = 0xa0000000;
|
tmp = 0;
|
||||||
tmp |= cpol << 30; /* TSCKIZ */
|
tmp |= CTR_TSCKIZ_SCK | cpol << CTR_TSCKIZ_POL_SHIFT;
|
||||||
tmp |= cpol << 28; /* RSCKIZ */
|
tmp |= CTR_RSCKIZ_SCK | cpol << CTR_RSCKIZ_POL_SHIFT;
|
||||||
|
|
||||||
edge = cpol ^ !cpha;
|
edge = cpol ^ !cpha;
|
||||||
|
|
||||||
tmp |= edge << 27; /* TEDG */
|
tmp |= edge << CTR_TEDG_SHIFT;
|
||||||
tmp |= edge << 26; /* REDG */
|
tmp |= edge << CTR_REDG_SHIFT;
|
||||||
tmp |= (tx_hi_z ? 2 : 0) << 22; /* TXDIZ */
|
tmp |= tx_hi_z ? CTR_TXDIZ_HIZ : CTR_TXDIZ_LOW;
|
||||||
sh_msiof_write(p, CTR, tmp);
|
sh_msiof_write(p, CTR, tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,12 +258,12 @@ static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p,
|
||||||
const void *tx_buf, void *rx_buf,
|
const void *tx_buf, void *rx_buf,
|
||||||
u32 bits, u32 words)
|
u32 bits, u32 words)
|
||||||
{
|
{
|
||||||
u32 dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
|
u32 dr2 = MDR2_BITLEN1(bits) | MDR2_WDLEN1(words);
|
||||||
|
|
||||||
if (tx_buf)
|
if (tx_buf || (p->chipdata->master_flags & SPI_MASTER_MUST_TX))
|
||||||
sh_msiof_write(p, TMDR2, dr2);
|
sh_msiof_write(p, TMDR2, dr2);
|
||||||
else
|
else
|
||||||
sh_msiof_write(p, TMDR2, dr2 | 1);
|
sh_msiof_write(p, TMDR2, dr2 | MDR2_GRPMASK1);
|
||||||
|
|
||||||
if (rx_buf)
|
if (rx_buf)
|
||||||
sh_msiof_write(p, RMDR2, dr2);
|
sh_msiof_write(p, RMDR2, dr2);
|
||||||
|
@ -363,77 +416,45 @@ static void sh_msiof_spi_read_fifo_s32u(struct sh_msiof_spi_priv *p,
|
||||||
put_unaligned(swab32(sh_msiof_read(p, RFDR) >> fs), &buf_32[k]);
|
put_unaligned(swab32(sh_msiof_read(p, RFDR) >> fs), &buf_32[k]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t)
|
static int sh_msiof_spi_setup(struct spi_device *spi)
|
||||||
{
|
|
||||||
int bits;
|
|
||||||
|
|
||||||
bits = t ? t->bits_per_word : 0;
|
|
||||||
if (!bits)
|
|
||||||
bits = spi->bits_per_word;
|
|
||||||
return bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long sh_msiof_spi_hz(struct spi_device *spi,
|
|
||||||
struct spi_transfer *t)
|
|
||||||
{
|
|
||||||
unsigned long hz;
|
|
||||||
|
|
||||||
hz = t ? t->speed_hz : 0;
|
|
||||||
if (!hz)
|
|
||||||
hz = spi->max_speed_hz;
|
|
||||||
return hz;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sh_msiof_spi_setup_transfer(struct spi_device *spi,
|
|
||||||
struct spi_transfer *t)
|
|
||||||
{
|
|
||||||
int bits;
|
|
||||||
|
|
||||||
/* noting to check hz values against since parent clock is disabled */
|
|
||||||
|
|
||||||
bits = sh_msiof_spi_bits(spi, t);
|
|
||||||
if (bits < 8)
|
|
||||||
return -EINVAL;
|
|
||||||
if (bits > 32)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return spi_bitbang_setup_transfer(spi, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sh_msiof_spi_chipselect(struct spi_device *spi, int is_on)
|
|
||||||
{
|
{
|
||||||
|
struct device_node *np = spi->master->dev.of_node;
|
||||||
struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
|
struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
|
||||||
int value;
|
|
||||||
|
|
||||||
/* chip select is active low unless SPI_CS_HIGH is set */
|
if (!np) {
|
||||||
if (spi->mode & SPI_CS_HIGH)
|
/*
|
||||||
value = (is_on == BITBANG_CS_ACTIVE) ? 1 : 0;
|
* Use spi->controller_data for CS (same strategy as spi_gpio),
|
||||||
else
|
* if any. otherwise let HW control CS
|
||||||
value = (is_on == BITBANG_CS_ACTIVE) ? 0 : 1;
|
*/
|
||||||
|
spi->cs_gpio = (uintptr_t)spi->controller_data;
|
||||||
if (is_on == BITBANG_CS_ACTIVE) {
|
|
||||||
if (!test_and_set_bit(0, &p->flags)) {
|
|
||||||
pm_runtime_get_sync(&p->pdev->dev);
|
|
||||||
clk_enable(p->clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Configure pins before asserting CS */
|
|
||||||
sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL),
|
|
||||||
!!(spi->mode & SPI_CPHA),
|
|
||||||
!!(spi->mode & SPI_3WIRE),
|
|
||||||
!!(spi->mode & SPI_LSB_FIRST),
|
|
||||||
!!(spi->mode & SPI_CS_HIGH));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* use spi->controller data for CS (same strategy as spi_gpio) */
|
/* Configure pins before deasserting CS */
|
||||||
gpio_set_value((uintptr_t)spi->controller_data, value);
|
sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL),
|
||||||
|
!!(spi->mode & SPI_CPHA),
|
||||||
|
!!(spi->mode & SPI_3WIRE),
|
||||||
|
!!(spi->mode & SPI_LSB_FIRST),
|
||||||
|
!!(spi->mode & SPI_CS_HIGH));
|
||||||
|
|
||||||
if (is_on == BITBANG_CS_INACTIVE) {
|
if (spi->cs_gpio >= 0)
|
||||||
if (test_and_clear_bit(0, &p->flags)) {
|
gpio_set_value(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
|
||||||
clk_disable(p->clk);
|
|
||||||
pm_runtime_put(&p->pdev->dev);
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
static int sh_msiof_prepare_message(struct spi_master *master,
|
||||||
|
struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct sh_msiof_spi_priv *p = spi_master_get_devdata(master);
|
||||||
|
const struct spi_device *spi = msg->spi;
|
||||||
|
|
||||||
|
/* Configure pins before asserting CS */
|
||||||
|
sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL),
|
||||||
|
!!(spi->mode & SPI_CPHA),
|
||||||
|
!!(spi->mode & SPI_3WIRE),
|
||||||
|
!!(spi->mode & SPI_LSB_FIRST),
|
||||||
|
!!(spi->mode & SPI_CS_HIGH));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
||||||
|
@ -487,7 +508,7 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
||||||
/* clear status bits */
|
/* clear status bits */
|
||||||
sh_msiof_reset_str(p);
|
sh_msiof_reset_str(p);
|
||||||
|
|
||||||
/* shut down frame, tx/tx and clock signals */
|
/* shut down frame, rx/tx and clock signals */
|
||||||
ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
|
ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
|
||||||
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
|
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
|
||||||
if (rx_buf)
|
if (rx_buf)
|
||||||
|
@ -505,9 +526,11 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
static int sh_msiof_transfer_one(struct spi_master *master,
|
||||||
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
|
struct sh_msiof_spi_priv *p = spi_master_get_devdata(master);
|
||||||
void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int);
|
void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int);
|
||||||
void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int);
|
void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int);
|
||||||
int bits;
|
int bits;
|
||||||
|
@ -517,7 +540,7 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
||||||
int n;
|
int n;
|
||||||
bool swab;
|
bool swab;
|
||||||
|
|
||||||
bits = sh_msiof_spi_bits(spi, t);
|
bits = t->bits_per_word;
|
||||||
|
|
||||||
if (bits <= 8 && t->len > 15 && !(t->len & 3)) {
|
if (bits <= 8 && t->len > 15 && !(t->len & 3)) {
|
||||||
bits = 32;
|
bits = 32;
|
||||||
|
@ -567,8 +590,7 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* setup clocks (clock already enabled in chipselect()) */
|
/* setup clocks (clock already enabled in chipselect()) */
|
||||||
sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk),
|
sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
|
||||||
sh_msiof_spi_hz(spi, t));
|
|
||||||
|
|
||||||
/* transfer in fifo sized chunks */
|
/* transfer in fifo sized chunks */
|
||||||
words = t->len / bytes_per_word;
|
words = t->len / bytes_per_word;
|
||||||
|
@ -588,22 +610,36 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
||||||
words -= n;
|
words -= n;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes_done;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 sh_msiof_spi_txrx_word(struct spi_device *spi, unsigned nsecs,
|
|
||||||
u32 word, u8 bits)
|
|
||||||
{
|
|
||||||
BUG(); /* unused but needed by bitbang code */
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct sh_msiof_chipdata sh_data = {
|
||||||
|
.tx_fifo_size = 64,
|
||||||
|
.rx_fifo_size = 64,
|
||||||
|
.master_flags = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sh_msiof_chipdata r8a779x_data = {
|
||||||
|
.tx_fifo_size = 64,
|
||||||
|
.rx_fifo_size = 256,
|
||||||
|
.master_flags = SPI_MASTER_MUST_TX,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id sh_msiof_match[] = {
|
||||||
|
{ .compatible = "renesas,sh-msiof", .data = &sh_data },
|
||||||
|
{ .compatible = "renesas,sh-mobile-msiof", .data = &sh_data },
|
||||||
|
{ .compatible = "renesas,msiof-r8a7790", .data = &r8a779x_data },
|
||||||
|
{ .compatible = "renesas,msiof-r8a7791", .data = &r8a779x_data },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sh_msiof_match);
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev)
|
static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev)
|
||||||
{
|
{
|
||||||
struct sh_msiof_spi_info *info;
|
struct sh_msiof_spi_info *info;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
u32 num_cs = 0;
|
u32 num_cs = 1;
|
||||||
|
|
||||||
info = devm_kzalloc(dev, sizeof(struct sh_msiof_spi_info), GFP_KERNEL);
|
info = devm_kzalloc(dev, sizeof(struct sh_msiof_spi_info), GFP_KERNEL);
|
||||||
if (!info) {
|
if (!info) {
|
||||||
|
@ -633,6 +669,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
|
const struct of_device_id *of_id;
|
||||||
struct sh_msiof_spi_priv *p;
|
struct sh_msiof_spi_priv *p;
|
||||||
int i;
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -646,10 +683,15 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
||||||
p = spi_master_get_devdata(master);
|
p = spi_master_get_devdata(master);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, p);
|
platform_set_drvdata(pdev, p);
|
||||||
if (pdev->dev.of_node)
|
|
||||||
|
of_id = of_match_device(sh_msiof_match, &pdev->dev);
|
||||||
|
if (of_id) {
|
||||||
|
p->chipdata = of_id->data;
|
||||||
p->info = sh_msiof_spi_parse_dt(&pdev->dev);
|
p->info = sh_msiof_spi_parse_dt(&pdev->dev);
|
||||||
else
|
} else {
|
||||||
|
p->chipdata = (const void *)pdev->id_entry->driver_data;
|
||||||
p->info = dev_get_platdata(&pdev->dev);
|
p->info = dev_get_platdata(&pdev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
if (!p->info) {
|
if (!p->info) {
|
||||||
dev_err(&pdev->dev, "failed to obtain device info\n");
|
dev_err(&pdev->dev, "failed to obtain device info\n");
|
||||||
|
@ -687,49 +729,40 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
||||||
goto err1;
|
goto err1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_prepare(p->clk);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&pdev->dev, "unable to prepare clock\n");
|
|
||||||
goto err1;
|
|
||||||
}
|
|
||||||
|
|
||||||
p->pdev = pdev;
|
p->pdev = pdev;
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
/* The standard version of MSIOF use 64 word FIFOs */
|
|
||||||
p->tx_fifo_size = 64;
|
|
||||||
p->rx_fifo_size = 64;
|
|
||||||
|
|
||||||
/* Platform data may override FIFO sizes */
|
/* Platform data may override FIFO sizes */
|
||||||
|
p->tx_fifo_size = p->chipdata->tx_fifo_size;
|
||||||
|
p->rx_fifo_size = p->chipdata->rx_fifo_size;
|
||||||
if (p->info->tx_fifo_override)
|
if (p->info->tx_fifo_override)
|
||||||
p->tx_fifo_size = p->info->tx_fifo_override;
|
p->tx_fifo_size = p->info->tx_fifo_override;
|
||||||
if (p->info->rx_fifo_override)
|
if (p->info->rx_fifo_override)
|
||||||
p->rx_fifo_size = p->info->rx_fifo_override;
|
p->rx_fifo_size = p->info->rx_fifo_override;
|
||||||
|
|
||||||
/* init master and bitbang code */
|
/* init master code */
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||||
master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE;
|
master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE;
|
||||||
master->flags = 0;
|
master->flags = p->chipdata->master_flags;
|
||||||
master->bus_num = pdev->id;
|
master->bus_num = pdev->id;
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
master->num_chipselect = p->info->num_chipselect;
|
master->num_chipselect = p->info->num_chipselect;
|
||||||
master->setup = spi_bitbang_setup;
|
master->setup = sh_msiof_spi_setup;
|
||||||
master->cleanup = spi_bitbang_cleanup;
|
master->prepare_message = sh_msiof_prepare_message;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
|
||||||
|
master->auto_runtime_pm = true;
|
||||||
|
master->transfer_one = sh_msiof_transfer_one;
|
||||||
|
|
||||||
p->bitbang.master = master;
|
ret = devm_spi_register_master(&pdev->dev, master);
|
||||||
p->bitbang.chipselect = sh_msiof_spi_chipselect;
|
if (ret < 0) {
|
||||||
p->bitbang.setup_transfer = sh_msiof_spi_setup_transfer;
|
dev_err(&pdev->dev, "spi_register_master error.\n");
|
||||||
p->bitbang.txrx_bufs = sh_msiof_spi_txrx;
|
goto err2;
|
||||||
p->bitbang.txrx_word[SPI_MODE_0] = sh_msiof_spi_txrx_word;
|
}
|
||||||
p->bitbang.txrx_word[SPI_MODE_1] = sh_msiof_spi_txrx_word;
|
|
||||||
p->bitbang.txrx_word[SPI_MODE_2] = sh_msiof_spi_txrx_word;
|
|
||||||
p->bitbang.txrx_word[SPI_MODE_3] = sh_msiof_spi_txrx_word;
|
|
||||||
|
|
||||||
ret = spi_bitbang_start(&p->bitbang);
|
return 0;
|
||||||
if (ret == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
|
err2:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
clk_unprepare(p->clk);
|
|
||||||
err1:
|
err1:
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -737,30 +770,22 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
static int sh_msiof_spi_remove(struct platform_device *pdev)
|
static int sh_msiof_spi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
int ret;
|
return 0;
|
||||||
|
|
||||||
ret = spi_bitbang_stop(&p->bitbang);
|
|
||||||
if (!ret) {
|
|
||||||
pm_runtime_disable(&pdev->dev);
|
|
||||||
clk_unprepare(p->clk);
|
|
||||||
spi_master_put(p->bitbang.master);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
static struct platform_device_id spi_driver_ids[] = {
|
||||||
static const struct of_device_id sh_msiof_match[] = {
|
{ "spi_sh_msiof", (kernel_ulong_t)&sh_data },
|
||||||
{ .compatible = "renesas,sh-msiof", },
|
{ "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||||
{ .compatible = "renesas,sh-mobile-msiof", },
|
{ "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, sh_msiof_match);
|
MODULE_DEVICE_TABLE(platform, spi_driver_ids);
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct platform_driver sh_msiof_spi_drv = {
|
static struct platform_driver sh_msiof_spi_drv = {
|
||||||
.probe = sh_msiof_spi_probe,
|
.probe = sh_msiof_spi_probe,
|
||||||
.remove = sh_msiof_spi_remove,
|
.remove = sh_msiof_spi_remove,
|
||||||
|
.id_table = spi_driver_ids,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "spi_sh_msiof",
|
.name = "spi_sh_msiof",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
@ -109,7 +108,7 @@ static void sh_sci_spi_chipselect(struct spi_device *dev, int value)
|
||||||
{
|
{
|
||||||
struct sh_sci_spi *sp = spi_master_get_devdata(dev->master);
|
struct sh_sci_spi *sp = spi_master_get_devdata(dev->master);
|
||||||
|
|
||||||
if (sp->info && sp->info->chip_select)
|
if (sp->info->chip_select)
|
||||||
(sp->info->chip_select)(sp->info, dev->chip_select, value);
|
(sp->info->chip_select)(sp->info, dev->chip_select, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +130,11 @@ static int sh_sci_spi_probe(struct platform_device *dev)
|
||||||
|
|
||||||
platform_set_drvdata(dev, sp);
|
platform_set_drvdata(dev, sp);
|
||||||
sp->info = dev_get_platdata(&dev->dev);
|
sp->info = dev_get_platdata(&dev->dev);
|
||||||
|
if (!sp->info) {
|
||||||
|
dev_err(&dev->dev, "platform data is missing\n");
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto err1;
|
||||||
|
}
|
||||||
|
|
||||||
/* setup spi bitbang adaptor */
|
/* setup spi bitbang adaptor */
|
||||||
sp->bitbang.master = master;
|
sp->bitbang.master = master;
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/dma-direction.h>
|
#include <linux/dma-direction.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/sirfsoc_dma.h>
|
|
||||||
|
|
||||||
#define DRIVER_NAME "sirfsoc_spi"
|
#define DRIVER_NAME "sirfsoc_spi"
|
||||||
|
|
||||||
|
@ -132,6 +131,8 @@
|
||||||
#define IS_DMA_VALID(x) (x && ALIGNED(x->tx_buf) && ALIGNED(x->rx_buf) && \
|
#define IS_DMA_VALID(x) (x && ALIGNED(x->tx_buf) && ALIGNED(x->rx_buf) && \
|
||||||
ALIGNED(x->len) && (x->len < 2 * PAGE_SIZE))
|
ALIGNED(x->len) && (x->len < 2 * PAGE_SIZE))
|
||||||
|
|
||||||
|
#define SIRFSOC_MAX_CMD_BYTES 4
|
||||||
|
|
||||||
struct sirfsoc_spi {
|
struct sirfsoc_spi {
|
||||||
struct spi_bitbang bitbang;
|
struct spi_bitbang bitbang;
|
||||||
struct completion rx_done;
|
struct completion rx_done;
|
||||||
|
@ -162,6 +163,12 @@ struct sirfsoc_spi {
|
||||||
void *dummypage;
|
void *dummypage;
|
||||||
int word_width; /* in bytes */
|
int word_width; /* in bytes */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if tx size is not more than 4 and rx size is NULL, use
|
||||||
|
* command model
|
||||||
|
*/
|
||||||
|
bool tx_by_cmd;
|
||||||
|
|
||||||
int chipselect[0];
|
int chipselect[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -260,6 +267,12 @@ static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id)
|
||||||
|
|
||||||
writel(spi_stat, sspi->base + SIRFSOC_SPI_INT_STATUS);
|
writel(spi_stat, sspi->base + SIRFSOC_SPI_INT_STATUS);
|
||||||
|
|
||||||
|
if (sspi->tx_by_cmd && (spi_stat & SIRFSOC_SPI_FRM_END)) {
|
||||||
|
complete(&sspi->tx_done);
|
||||||
|
writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
/* Error Conditions */
|
/* Error Conditions */
|
||||||
if (spi_stat & SIRFSOC_SPI_RX_OFLOW ||
|
if (spi_stat & SIRFSOC_SPI_RX_OFLOW ||
|
||||||
spi_stat & SIRFSOC_SPI_TX_UFLOW) {
|
spi_stat & SIRFSOC_SPI_TX_UFLOW) {
|
||||||
|
@ -310,6 +323,34 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
|
|
||||||
writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS);
|
writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fill tx_buf into command register and wait for its completion
|
||||||
|
*/
|
||||||
|
if (sspi->tx_by_cmd) {
|
||||||
|
u32 cmd;
|
||||||
|
memcpy(&cmd, sspi->tx, t->len);
|
||||||
|
|
||||||
|
if (sspi->word_width == 1 && !(spi->mode & SPI_LSB_FIRST))
|
||||||
|
cmd = cpu_to_be32(cmd) >>
|
||||||
|
((SIRFSOC_MAX_CMD_BYTES - t->len) * 8);
|
||||||
|
if (sspi->word_width == 2 && t->len == 4 &&
|
||||||
|
(!(spi->mode & SPI_LSB_FIRST)))
|
||||||
|
cmd = ((cmd & 0xffff) << 16) | (cmd >> 16);
|
||||||
|
|
||||||
|
writel(cmd, sspi->base + SIRFSOC_SPI_CMD);
|
||||||
|
writel(SIRFSOC_SPI_FRM_END_INT_EN,
|
||||||
|
sspi->base + SIRFSOC_SPI_INT_EN);
|
||||||
|
writel(SIRFSOC_SPI_CMD_TX_EN,
|
||||||
|
sspi->base + SIRFSOC_SPI_TX_RX_EN);
|
||||||
|
|
||||||
|
if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) {
|
||||||
|
dev_err(&spi->dev, "transfer timeout\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return t->len;
|
||||||
|
}
|
||||||
|
|
||||||
if (sspi->left_tx_word == 1) {
|
if (sspi->left_tx_word == 1) {
|
||||||
writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
|
writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
|
||||||
SIRFSOC_SPI_ENA_AUTO_CLR,
|
SIRFSOC_SPI_ENA_AUTO_CLR,
|
||||||
|
@ -459,11 +500,6 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_8;
|
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_8;
|
||||||
sspi->rx_word = spi_sirfsoc_rx_word_u8;
|
sspi->rx_word = spi_sirfsoc_rx_word_u8;
|
||||||
sspi->tx_word = spi_sirfsoc_tx_word_u8;
|
sspi->tx_word = spi_sirfsoc_tx_word_u8;
|
||||||
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_BYTE;
|
|
||||||
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_BYTE;
|
|
||||||
sspi->word_width = 1;
|
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
case 16:
|
case 16:
|
||||||
|
@ -471,26 +507,22 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
SIRFSOC_SPI_TRAN_DAT_FORMAT_16;
|
SIRFSOC_SPI_TRAN_DAT_FORMAT_16;
|
||||||
sspi->rx_word = spi_sirfsoc_rx_word_u16;
|
sspi->rx_word = spi_sirfsoc_rx_word_u16;
|
||||||
sspi->tx_word = spi_sirfsoc_tx_word_u16;
|
sspi->tx_word = spi_sirfsoc_tx_word_u16;
|
||||||
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_WORD;
|
|
||||||
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_WORD;
|
|
||||||
sspi->word_width = 2;
|
|
||||||
break;
|
break;
|
||||||
case 32:
|
case 32:
|
||||||
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_32;
|
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_32;
|
||||||
sspi->rx_word = spi_sirfsoc_rx_word_u32;
|
sspi->rx_word = spi_sirfsoc_rx_word_u32;
|
||||||
sspi->tx_word = spi_sirfsoc_tx_word_u32;
|
sspi->tx_word = spi_sirfsoc_tx_word_u32;
|
||||||
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_DWORD;
|
|
||||||
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
|
||||||
SIRFSOC_SPI_FIFO_WIDTH_DWORD;
|
|
||||||
sspi->word_width = 4;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sspi->word_width = DIV_ROUND_UP(bits_per_word, 8);
|
||||||
|
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
||||||
|
sspi->word_width;
|
||||||
|
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
|
||||||
|
sspi->word_width;
|
||||||
|
|
||||||
if (!(spi->mode & SPI_CS_HIGH))
|
if (!(spi->mode & SPI_CS_HIGH))
|
||||||
regval |= SIRFSOC_SPI_CS_IDLE_STAT;
|
regval |= SIRFSOC_SPI_CS_IDLE_STAT;
|
||||||
if (!(spi->mode & SPI_LSB_FIRST))
|
if (!(spi->mode & SPI_LSB_FIRST))
|
||||||
|
@ -519,6 +551,14 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||||
writel(txfifo_ctrl, sspi->base + SIRFSOC_SPI_TXFIFO_CTRL);
|
writel(txfifo_ctrl, sspi->base + SIRFSOC_SPI_TXFIFO_CTRL);
|
||||||
writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL);
|
writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL);
|
||||||
|
|
||||||
|
if (t && t->tx_buf && !t->rx_buf && (t->len <= SIRFSOC_MAX_CMD_BYTES)) {
|
||||||
|
regval |= (SIRFSOC_SPI_CMD_BYTE_NUM((t->len - 1)) |
|
||||||
|
SIRFSOC_SPI_CMD_MODE);
|
||||||
|
sspi->tx_by_cmd = true;
|
||||||
|
} else {
|
||||||
|
regval &= ~SIRFSOC_SPI_CMD_MODE;
|
||||||
|
sspi->tx_by_cmd = false;
|
||||||
|
}
|
||||||
writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
|
writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
|
||||||
|
|
||||||
if (IS_DMA_VALID(t)) {
|
if (IS_DMA_VALID(t)) {
|
||||||
|
@ -548,8 +588,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct resource *mem_res;
|
struct resource *mem_res;
|
||||||
int num_cs, cs_gpio, irq;
|
int num_cs, cs_gpio, irq;
|
||||||
u32 rx_dma_ch, tx_dma_ch;
|
|
||||||
dma_cap_mask_t dma_cap_mask;
|
|
||||||
int i;
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -560,20 +598,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
||||||
goto err_cs;
|
goto err_cs;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = of_property_read_u32(pdev->dev.of_node,
|
|
||||||
"sirf,spi-dma-rx-channel", &rx_dma_ch);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&pdev->dev, "Unable to get rx dma channel\n");
|
|
||||||
goto err_cs;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = of_property_read_u32(pdev->dev.of_node,
|
|
||||||
"sirf,spi-dma-tx-channel", &tx_dma_ch);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&pdev->dev, "Unable to get tx dma channel\n");
|
|
||||||
goto err_cs;
|
|
||||||
}
|
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof(*sspi) + sizeof(int) * num_cs);
|
master = spi_alloc_master(&pdev->dev, sizeof(*sspi) + sizeof(int) * num_cs);
|
||||||
if (!master) {
|
if (!master) {
|
||||||
dev_err(&pdev->dev, "Unable to allocate SPI master\n");
|
dev_err(&pdev->dev, "Unable to allocate SPI master\n");
|
||||||
|
@ -637,18 +661,13 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
||||||
sspi->bitbang.master->dev.of_node = pdev->dev.of_node;
|
sspi->bitbang.master->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
/* request DMA channels */
|
/* request DMA channels */
|
||||||
dma_cap_zero(dma_cap_mask);
|
sspi->rx_chan = dma_request_slave_channel(&pdev->dev, "rx");
|
||||||
dma_cap_set(DMA_INTERLEAVE, dma_cap_mask);
|
|
||||||
|
|
||||||
sspi->rx_chan = dma_request_channel(dma_cap_mask, (dma_filter_fn)sirfsoc_dma_filter_id,
|
|
||||||
(void *)rx_dma_ch);
|
|
||||||
if (!sspi->rx_chan) {
|
if (!sspi->rx_chan) {
|
||||||
dev_err(&pdev->dev, "can not allocate rx dma channel\n");
|
dev_err(&pdev->dev, "can not allocate rx dma channel\n");
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto free_master;
|
goto free_master;
|
||||||
}
|
}
|
||||||
sspi->tx_chan = dma_request_channel(dma_cap_mask, (dma_filter_fn)sirfsoc_dma_filter_id,
|
sspi->tx_chan = dma_request_slave_channel(&pdev->dev, "tx");
|
||||||
(void *)tx_dma_ch);
|
|
||||||
if (!sspi->tx_chan) {
|
if (!sspi->tx_chan) {
|
||||||
dev_err(&pdev->dev, "can not allocate tx dma channel\n");
|
dev_err(&pdev->dev, "can not allocate tx dma channel\n");
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
|
@ -724,11 +743,16 @@ static int spi_sirfsoc_remove(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int spi_sirfsoc_suspend(struct device *dev)
|
static int spi_sirfsoc_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct spi_master *master = dev_get_drvdata(dev);
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
|
struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = spi_master_suspend(master);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
clk_disable(sspi->clk);
|
clk_disable(sspi->clk);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -745,15 +769,13 @@ static int spi_sirfsoc_resume(struct device *dev)
|
||||||
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
|
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
|
||||||
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
|
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
|
||||||
|
|
||||||
return 0;
|
return spi_master_resume(master);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct dev_pm_ops spi_sirfsoc_pm_ops = {
|
|
||||||
.suspend = spi_sirfsoc_suspend,
|
|
||||||
.resume = spi_sirfsoc_resume,
|
|
||||||
};
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(spi_sirfsoc_pm_ops, spi_sirfsoc_suspend,
|
||||||
|
spi_sirfsoc_resume);
|
||||||
|
|
||||||
static const struct of_device_id spi_sirfsoc_of_match[] = {
|
static const struct of_device_id spi_sirfsoc_of_match[] = {
|
||||||
{ .compatible = "sirf,prima2-spi", },
|
{ .compatible = "sirf,prima2-spi", },
|
||||||
{ .compatible = "sirf,marco-spi", },
|
{ .compatible = "sirf,marco-spi", },
|
||||||
|
@ -765,9 +787,7 @@ static struct platform_driver spi_sirfsoc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
#ifdef CONFIG_PM
|
|
||||||
.pm = &spi_sirfsoc_pm_ops,
|
.pm = &spi_sirfsoc_pm_ops,
|
||||||
#endif
|
|
||||||
.of_match_table = spi_sirfsoc_of_match,
|
.of_match_table = spi_sirfsoc_of_match,
|
||||||
},
|
},
|
||||||
.probe = spi_sirfsoc_probe,
|
.probe = spi_sirfsoc_probe,
|
||||||
|
|
|
@ -0,0 +1,478 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 - 2014 Allwinner Tech
|
||||||
|
* Pan Nan <pannan@allwinnertech.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Maxime Ripard
|
||||||
|
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
#define SUN4I_FIFO_DEPTH 64
|
||||||
|
|
||||||
|
#define SUN4I_RXDATA_REG 0x00
|
||||||
|
|
||||||
|
#define SUN4I_TXDATA_REG 0x04
|
||||||
|
|
||||||
|
#define SUN4I_CTL_REG 0x08
|
||||||
|
#define SUN4I_CTL_ENABLE BIT(0)
|
||||||
|
#define SUN4I_CTL_MASTER BIT(1)
|
||||||
|
#define SUN4I_CTL_CPHA BIT(2)
|
||||||
|
#define SUN4I_CTL_CPOL BIT(3)
|
||||||
|
#define SUN4I_CTL_CS_ACTIVE_LOW BIT(4)
|
||||||
|
#define SUN4I_CTL_LMTF BIT(6)
|
||||||
|
#define SUN4I_CTL_TF_RST BIT(8)
|
||||||
|
#define SUN4I_CTL_RF_RST BIT(9)
|
||||||
|
#define SUN4I_CTL_XCH BIT(10)
|
||||||
|
#define SUN4I_CTL_CS_MASK 0x3000
|
||||||
|
#define SUN4I_CTL_CS(cs) (((cs) << 12) & SUN4I_CTL_CS_MASK)
|
||||||
|
#define SUN4I_CTL_DHB BIT(15)
|
||||||
|
#define SUN4I_CTL_CS_MANUAL BIT(16)
|
||||||
|
#define SUN4I_CTL_CS_LEVEL BIT(17)
|
||||||
|
#define SUN4I_CTL_TP BIT(18)
|
||||||
|
|
||||||
|
#define SUN4I_INT_CTL_REG 0x0c
|
||||||
|
#define SUN4I_INT_CTL_TC BIT(16)
|
||||||
|
|
||||||
|
#define SUN4I_INT_STA_REG 0x10
|
||||||
|
|
||||||
|
#define SUN4I_DMA_CTL_REG 0x14
|
||||||
|
|
||||||
|
#define SUN4I_WAIT_REG 0x18
|
||||||
|
|
||||||
|
#define SUN4I_CLK_CTL_REG 0x1c
|
||||||
|
#define SUN4I_CLK_CTL_CDR2_MASK 0xff
|
||||||
|
#define SUN4I_CLK_CTL_CDR2(div) ((div) & SUN4I_CLK_CTL_CDR2_MASK)
|
||||||
|
#define SUN4I_CLK_CTL_CDR1_MASK 0xf
|
||||||
|
#define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8)
|
||||||
|
#define SUN4I_CLK_CTL_DRS BIT(12)
|
||||||
|
|
||||||
|
#define SUN4I_BURST_CNT_REG 0x20
|
||||||
|
#define SUN4I_BURST_CNT(cnt) ((cnt) & 0xffffff)
|
||||||
|
|
||||||
|
#define SUN4I_XMIT_CNT_REG 0x24
|
||||||
|
#define SUN4I_XMIT_CNT(cnt) ((cnt) & 0xffffff)
|
||||||
|
|
||||||
|
#define SUN4I_FIFO_STA_REG 0x28
|
||||||
|
#define SUN4I_FIFO_STA_RF_CNT_MASK 0x7f
|
||||||
|
#define SUN4I_FIFO_STA_RF_CNT_BITS 0
|
||||||
|
#define SUN4I_FIFO_STA_TF_CNT_MASK 0x7f
|
||||||
|
#define SUN4I_FIFO_STA_TF_CNT_BITS 16
|
||||||
|
|
||||||
|
struct sun4i_spi {
|
||||||
|
struct spi_master *master;
|
||||||
|
void __iomem *base_addr;
|
||||||
|
struct clk *hclk;
|
||||||
|
struct clk *mclk;
|
||||||
|
|
||||||
|
struct completion done;
|
||||||
|
|
||||||
|
const u8 *tx_buf;
|
||||||
|
u8 *rx_buf;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline u32 sun4i_spi_read(struct sun4i_spi *sspi, u32 reg)
|
||||||
|
{
|
||||||
|
return readl(sspi->base_addr + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sun4i_spi_write(struct sun4i_spi *sspi, u32 reg, u32 value)
|
||||||
|
{
|
||||||
|
writel(value, sspi->base_addr + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len)
|
||||||
|
{
|
||||||
|
u32 reg, cnt;
|
||||||
|
u8 byte;
|
||||||
|
|
||||||
|
/* See how much data is available */
|
||||||
|
reg = sun4i_spi_read(sspi, SUN4I_FIFO_STA_REG);
|
||||||
|
reg &= SUN4I_FIFO_STA_RF_CNT_MASK;
|
||||||
|
cnt = reg >> SUN4I_FIFO_STA_RF_CNT_BITS;
|
||||||
|
|
||||||
|
if (len > cnt)
|
||||||
|
len = cnt;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
byte = readb(sspi->base_addr + SUN4I_RXDATA_REG);
|
||||||
|
if (sspi->rx_buf)
|
||||||
|
*sspi->rx_buf++ = byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len)
|
||||||
|
{
|
||||||
|
u8 byte;
|
||||||
|
|
||||||
|
if (len > sspi->len)
|
||||||
|
len = sspi->len;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
byte = sspi->tx_buf ? *sspi->tx_buf++ : 0;
|
||||||
|
writeb(byte, sspi->base_addr + SUN4I_TXDATA_REG);
|
||||||
|
sspi->len--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sun4i_spi_set_cs(struct spi_device *spi, bool enable)
|
||||||
|
{
|
||||||
|
struct sun4i_spi *sspi = spi_master_get_devdata(spi->master);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
|
||||||
|
|
||||||
|
reg &= ~SUN4I_CTL_CS_MASK;
|
||||||
|
reg |= SUN4I_CTL_CS(spi->chip_select);
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
reg |= SUN4I_CTL_CS_LEVEL;
|
||||||
|
else
|
||||||
|
reg &= ~SUN4I_CTL_CS_LEVEL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Even though this looks irrelevant since we are supposed to
|
||||||
|
* be controlling the chip select manually, this bit also
|
||||||
|
* controls the levels of the chip select for inactive
|
||||||
|
* devices.
|
||||||
|
*
|
||||||
|
* If we don't set it, the chip select level will go low by
|
||||||
|
* default when the device is idle, which is not really
|
||||||
|
* expected in the common case where the chip select is active
|
||||||
|
* low.
|
||||||
|
*/
|
||||||
|
if (spi->mode & SPI_CS_HIGH)
|
||||||
|
reg &= ~SUN4I_CTL_CS_ACTIVE_LOW;
|
||||||
|
else
|
||||||
|
reg |= SUN4I_CTL_CS_ACTIVE_LOW;
|
||||||
|
|
||||||
|
sun4i_spi_write(sspi, SUN4I_CTL_REG, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_spi_transfer_one(struct spi_master *master,
|
||||||
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *tfr)
|
||||||
|
{
|
||||||
|
struct sun4i_spi *sspi = spi_master_get_devdata(master);
|
||||||
|
unsigned int mclk_rate, div, timeout;
|
||||||
|
unsigned int tx_len = 0;
|
||||||
|
int ret = 0;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
/* We don't support transfer larger than the FIFO */
|
||||||
|
if (tfr->len > SUN4I_FIFO_DEPTH)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
reinit_completion(&sspi->done);
|
||||||
|
sspi->tx_buf = tfr->tx_buf;
|
||||||
|
sspi->rx_buf = tfr->rx_buf;
|
||||||
|
sspi->len = tfr->len;
|
||||||
|
|
||||||
|
/* Clear pending interrupts */
|
||||||
|
sun4i_spi_write(sspi, SUN4I_INT_STA_REG, ~0);
|
||||||
|
|
||||||
|
|
||||||
|
reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
|
||||||
|
|
||||||
|
/* Reset FIFOs */
|
||||||
|
sun4i_spi_write(sspi, SUN4I_CTL_REG,
|
||||||
|
reg | SUN4I_CTL_RF_RST | SUN4I_CTL_TF_RST);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup the transfer control register: Chip Select,
|
||||||
|
* polarities, etc.
|
||||||
|
*/
|
||||||
|
if (spi->mode & SPI_CPOL)
|
||||||
|
reg |= SUN4I_CTL_CPOL;
|
||||||
|
else
|
||||||
|
reg &= ~SUN4I_CTL_CPOL;
|
||||||
|
|
||||||
|
if (spi->mode & SPI_CPHA)
|
||||||
|
reg |= SUN4I_CTL_CPHA;
|
||||||
|
else
|
||||||
|
reg &= ~SUN4I_CTL_CPHA;
|
||||||
|
|
||||||
|
if (spi->mode & SPI_LSB_FIRST)
|
||||||
|
reg |= SUN4I_CTL_LMTF;
|
||||||
|
else
|
||||||
|
reg &= ~SUN4I_CTL_LMTF;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's a TX only transfer, we don't want to fill the RX
|
||||||
|
* FIFO with bogus data
|
||||||
|
*/
|
||||||
|
if (sspi->rx_buf)
|
||||||
|
reg &= ~SUN4I_CTL_DHB;
|
||||||
|
else
|
||||||
|
reg |= SUN4I_CTL_DHB;
|
||||||
|
|
||||||
|
/* We want to control the chip select manually */
|
||||||
|
reg |= SUN4I_CTL_CS_MANUAL;
|
||||||
|
|
||||||
|
sun4i_spi_write(sspi, SUN4I_CTL_REG, reg);
|
||||||
|
|
||||||
|
/* Ensure that we have a parent clock fast enough */
|
||||||
|
mclk_rate = clk_get_rate(sspi->mclk);
|
||||||
|
if (mclk_rate < (2 * spi->max_speed_hz)) {
|
||||||
|
clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz);
|
||||||
|
mclk_rate = clk_get_rate(sspi->mclk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup clock divider.
|
||||||
|
*
|
||||||
|
* We have two choices there. Either we can use the clock
|
||||||
|
* divide rate 1, which is calculated thanks to this formula:
|
||||||
|
* SPI_CLK = MOD_CLK / (2 ^ (cdr + 1))
|
||||||
|
* Or we can use CDR2, which is calculated with the formula:
|
||||||
|
* SPI_CLK = MOD_CLK / (2 * (cdr + 1))
|
||||||
|
* Wether we use the former or the latter is set through the
|
||||||
|
* DRS bit.
|
||||||
|
*
|
||||||
|
* First try CDR2, and if we can't reach the expected
|
||||||
|
* frequency, fall back to CDR1.
|
||||||
|
*/
|
||||||
|
div = mclk_rate / (2 * spi->max_speed_hz);
|
||||||
|
if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
|
||||||
|
if (div > 0)
|
||||||
|
div--;
|
||||||
|
|
||||||
|
reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS;
|
||||||
|
} else {
|
||||||
|
div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz);
|
||||||
|
reg = SUN4I_CLK_CTL_CDR1(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
sun4i_spi_write(sspi, SUN4I_CLK_CTL_REG, reg);
|
||||||
|
|
||||||
|
/* Setup the transfer now... */
|
||||||
|
if (sspi->tx_buf)
|
||||||
|
tx_len = tfr->len;
|
||||||
|
|
||||||
|
/* Setup the counters */
|
||||||
|
sun4i_spi_write(sspi, SUN4I_BURST_CNT_REG, SUN4I_BURST_CNT(tfr->len));
|
||||||
|
sun4i_spi_write(sspi, SUN4I_XMIT_CNT_REG, SUN4I_XMIT_CNT(tx_len));
|
||||||
|
|
||||||
|
/* Fill the TX FIFO */
|
||||||
|
sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH);
|
||||||
|
|
||||||
|
/* Enable the interrupts */
|
||||||
|
sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, SUN4I_INT_CTL_TC);
|
||||||
|
|
||||||
|
/* Start the transfer */
|
||||||
|
reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
|
||||||
|
sun4i_spi_write(sspi, SUN4I_CTL_REG, reg | SUN4I_CTL_XCH);
|
||||||
|
|
||||||
|
timeout = wait_for_completion_timeout(&sspi->done,
|
||||||
|
msecs_to_jiffies(1000));
|
||||||
|
if (!timeout) {
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH);
|
||||||
|
|
||||||
|
out:
|
||||||
|
sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t sun4i_spi_handler(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct sun4i_spi *sspi = dev_id;
|
||||||
|
u32 status = sun4i_spi_read(sspi, SUN4I_INT_STA_REG);
|
||||||
|
|
||||||
|
/* Transfer complete */
|
||||||
|
if (status & SUN4I_INT_CTL_TC) {
|
||||||
|
sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TC);
|
||||||
|
complete(&sspi->done);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_spi_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
|
struct sun4i_spi *sspi = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(sspi->hclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Couldn't enable AHB clock\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(sspi->mclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Couldn't enable module clock\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
sun4i_spi_write(sspi, SUN4I_CTL_REG,
|
||||||
|
SUN4I_CTL_ENABLE | SUN4I_CTL_MASTER | SUN4I_CTL_TP);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
clk_disable_unprepare(sspi->hclk);
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_spi_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
|
struct sun4i_spi *sspi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
clk_disable_unprepare(sspi->mclk);
|
||||||
|
clk_disable_unprepare(sspi->hclk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_spi_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct spi_master *master;
|
||||||
|
struct sun4i_spi *sspi;
|
||||||
|
struct resource *res;
|
||||||
|
int ret = 0, irq;
|
||||||
|
|
||||||
|
master = spi_alloc_master(&pdev->dev, sizeof(struct sun4i_spi));
|
||||||
|
if (!master) {
|
||||||
|
dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, master);
|
||||||
|
sspi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
sspi->base_addr = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(sspi->base_addr)) {
|
||||||
|
ret = PTR_ERR(sspi->base_addr);
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "No spi IRQ specified\n");
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, irq, sun4i_spi_handler,
|
||||||
|
0, "sun4i-spi", sspi);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Cannot request IRQ\n");
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
sspi->master = master;
|
||||||
|
master->set_cs = sun4i_spi_set_cs;
|
||||||
|
master->transfer_one = sun4i_spi_transfer_one;
|
||||||
|
master->num_chipselect = 4;
|
||||||
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
master->auto_runtime_pm = true;
|
||||||
|
|
||||||
|
sspi->hclk = devm_clk_get(&pdev->dev, "ahb");
|
||||||
|
if (IS_ERR(sspi->hclk)) {
|
||||||
|
dev_err(&pdev->dev, "Unable to acquire AHB clock\n");
|
||||||
|
ret = PTR_ERR(sspi->hclk);
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
sspi->mclk = devm_clk_get(&pdev->dev, "mod");
|
||||||
|
if (IS_ERR(sspi->mclk)) {
|
||||||
|
dev_err(&pdev->dev, "Unable to acquire module clock\n");
|
||||||
|
ret = PTR_ERR(sspi->mclk);
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_completion(&sspi->done);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This wake-up/shutdown pattern is to be able to have the
|
||||||
|
* device woken up, even if runtime_pm is disabled
|
||||||
|
*/
|
||||||
|
ret = sun4i_spi_runtime_resume(&pdev->dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't resume the device\n");
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_runtime_set_active(&pdev->dev);
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
pm_runtime_idle(&pdev->dev);
|
||||||
|
|
||||||
|
ret = devm_spi_register_master(&pdev->dev, master);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "cannot register SPI master\n");
|
||||||
|
goto err_pm_disable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_pm_disable:
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
sun4i_spi_runtime_suspend(&pdev->dev);
|
||||||
|
err_free_master:
|
||||||
|
spi_master_put(master);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_spi_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id sun4i_spi_match[] = {
|
||||||
|
{ .compatible = "allwinner,sun4i-a10-spi", },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sun4i_spi_match);
|
||||||
|
|
||||||
|
static const struct dev_pm_ops sun4i_spi_pm_ops = {
|
||||||
|
.runtime_resume = sun4i_spi_runtime_resume,
|
||||||
|
.runtime_suspend = sun4i_spi_runtime_suspend,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver sun4i_spi_driver = {
|
||||||
|
.probe = sun4i_spi_probe,
|
||||||
|
.remove = sun4i_spi_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "sun4i-spi",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = sun4i_spi_match,
|
||||||
|
.pm = &sun4i_spi_pm_ops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(sun4i_spi_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Pan Nan <pannan@allwinnertech.com>");
|
||||||
|
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
|
||||||
|
MODULE_DESCRIPTION("Allwinner A1X/A20 SPI controller driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,484 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 - 2014 Allwinner Tech
|
||||||
|
* Pan Nan <pannan@allwinnertech.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Maxime Ripard
|
||||||
|
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/reset.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
#define SUN6I_FIFO_DEPTH 128
|
||||||
|
|
||||||
|
#define SUN6I_GBL_CTL_REG 0x04
|
||||||
|
#define SUN6I_GBL_CTL_BUS_ENABLE BIT(0)
|
||||||
|
#define SUN6I_GBL_CTL_MASTER BIT(1)
|
||||||
|
#define SUN6I_GBL_CTL_TP BIT(7)
|
||||||
|
#define SUN6I_GBL_CTL_RST BIT(31)
|
||||||
|
|
||||||
|
#define SUN6I_TFR_CTL_REG 0x08
|
||||||
|
#define SUN6I_TFR_CTL_CPHA BIT(0)
|
||||||
|
#define SUN6I_TFR_CTL_CPOL BIT(1)
|
||||||
|
#define SUN6I_TFR_CTL_SPOL BIT(2)
|
||||||
|
#define SUN6I_TFR_CTL_CS_MASK 0x30
|
||||||
|
#define SUN6I_TFR_CTL_CS(cs) (((cs) << 4) & SUN6I_TFR_CTL_CS_MASK)
|
||||||
|
#define SUN6I_TFR_CTL_CS_MANUAL BIT(6)
|
||||||
|
#define SUN6I_TFR_CTL_CS_LEVEL BIT(7)
|
||||||
|
#define SUN6I_TFR_CTL_DHB BIT(8)
|
||||||
|
#define SUN6I_TFR_CTL_FBS BIT(12)
|
||||||
|
#define SUN6I_TFR_CTL_XCH BIT(31)
|
||||||
|
|
||||||
|
#define SUN6I_INT_CTL_REG 0x10
|
||||||
|
#define SUN6I_INT_CTL_RF_OVF BIT(8)
|
||||||
|
#define SUN6I_INT_CTL_TC BIT(12)
|
||||||
|
|
||||||
|
#define SUN6I_INT_STA_REG 0x14
|
||||||
|
|
||||||
|
#define SUN6I_FIFO_CTL_REG 0x18
|
||||||
|
#define SUN6I_FIFO_CTL_RF_RST BIT(15)
|
||||||
|
#define SUN6I_FIFO_CTL_TF_RST BIT(31)
|
||||||
|
|
||||||
|
#define SUN6I_FIFO_STA_REG 0x1c
|
||||||
|
#define SUN6I_FIFO_STA_RF_CNT_MASK 0x7f
|
||||||
|
#define SUN6I_FIFO_STA_RF_CNT_BITS 0
|
||||||
|
#define SUN6I_FIFO_STA_TF_CNT_MASK 0x7f
|
||||||
|
#define SUN6I_FIFO_STA_TF_CNT_BITS 16
|
||||||
|
|
||||||
|
#define SUN6I_CLK_CTL_REG 0x24
|
||||||
|
#define SUN6I_CLK_CTL_CDR2_MASK 0xff
|
||||||
|
#define SUN6I_CLK_CTL_CDR2(div) (((div) & SUN6I_CLK_CTL_CDR2_MASK) << 0)
|
||||||
|
#define SUN6I_CLK_CTL_CDR1_MASK 0xf
|
||||||
|
#define SUN6I_CLK_CTL_CDR1(div) (((div) & SUN6I_CLK_CTL_CDR1_MASK) << 8)
|
||||||
|
#define SUN6I_CLK_CTL_DRS BIT(12)
|
||||||
|
|
||||||
|
#define SUN6I_BURST_CNT_REG 0x30
|
||||||
|
#define SUN6I_BURST_CNT(cnt) ((cnt) & 0xffffff)
|
||||||
|
|
||||||
|
#define SUN6I_XMIT_CNT_REG 0x34
|
||||||
|
#define SUN6I_XMIT_CNT(cnt) ((cnt) & 0xffffff)
|
||||||
|
|
||||||
|
#define SUN6I_BURST_CTL_CNT_REG 0x38
|
||||||
|
#define SUN6I_BURST_CTL_CNT_STC(cnt) ((cnt) & 0xffffff)
|
||||||
|
|
||||||
|
#define SUN6I_TXDATA_REG 0x200
|
||||||
|
#define SUN6I_RXDATA_REG 0x300
|
||||||
|
|
||||||
|
struct sun6i_spi {
|
||||||
|
struct spi_master *master;
|
||||||
|
void __iomem *base_addr;
|
||||||
|
struct clk *hclk;
|
||||||
|
struct clk *mclk;
|
||||||
|
struct reset_control *rstc;
|
||||||
|
|
||||||
|
struct completion done;
|
||||||
|
|
||||||
|
const u8 *tx_buf;
|
||||||
|
u8 *rx_buf;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg)
|
||||||
|
{
|
||||||
|
return readl(sspi->base_addr + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value)
|
||||||
|
{
|
||||||
|
writel(value, sspi->base_addr + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len)
|
||||||
|
{
|
||||||
|
u32 reg, cnt;
|
||||||
|
u8 byte;
|
||||||
|
|
||||||
|
/* See how much data is available */
|
||||||
|
reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
|
||||||
|
reg &= SUN6I_FIFO_STA_RF_CNT_MASK;
|
||||||
|
cnt = reg >> SUN6I_FIFO_STA_RF_CNT_BITS;
|
||||||
|
|
||||||
|
if (len > cnt)
|
||||||
|
len = cnt;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
byte = readb(sspi->base_addr + SUN6I_RXDATA_REG);
|
||||||
|
if (sspi->rx_buf)
|
||||||
|
*sspi->rx_buf++ = byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len)
|
||||||
|
{
|
||||||
|
u8 byte;
|
||||||
|
|
||||||
|
if (len > sspi->len)
|
||||||
|
len = sspi->len;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
byte = sspi->tx_buf ? *sspi->tx_buf++ : 0;
|
||||||
|
writeb(byte, sspi->base_addr + SUN6I_TXDATA_REG);
|
||||||
|
sspi->len--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sun6i_spi_set_cs(struct spi_device *spi, bool enable)
|
||||||
|
{
|
||||||
|
struct sun6i_spi *sspi = spi_master_get_devdata(spi->master);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
|
||||||
|
reg &= ~SUN6I_TFR_CTL_CS_MASK;
|
||||||
|
reg |= SUN6I_TFR_CTL_CS(spi->chip_select);
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
reg |= SUN6I_TFR_CTL_CS_LEVEL;
|
||||||
|
else
|
||||||
|
reg &= ~SUN6I_TFR_CTL_CS_LEVEL;
|
||||||
|
|
||||||
|
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int sun6i_spi_transfer_one(struct spi_master *master,
|
||||||
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *tfr)
|
||||||
|
{
|
||||||
|
struct sun6i_spi *sspi = spi_master_get_devdata(master);
|
||||||
|
unsigned int mclk_rate, div, timeout;
|
||||||
|
unsigned int tx_len = 0;
|
||||||
|
int ret = 0;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
/* We don't support transfer larger than the FIFO */
|
||||||
|
if (tfr->len > SUN6I_FIFO_DEPTH)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
reinit_completion(&sspi->done);
|
||||||
|
sspi->tx_buf = tfr->tx_buf;
|
||||||
|
sspi->rx_buf = tfr->rx_buf;
|
||||||
|
sspi->len = tfr->len;
|
||||||
|
|
||||||
|
/* Clear pending interrupts */
|
||||||
|
sun6i_spi_write(sspi, SUN6I_INT_STA_REG, ~0);
|
||||||
|
|
||||||
|
/* Reset FIFO */
|
||||||
|
sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG,
|
||||||
|
SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup the transfer control register: Chip Select,
|
||||||
|
* polarities, etc.
|
||||||
|
*/
|
||||||
|
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
|
||||||
|
|
||||||
|
if (spi->mode & SPI_CPOL)
|
||||||
|
reg |= SUN6I_TFR_CTL_CPOL;
|
||||||
|
else
|
||||||
|
reg &= ~SUN6I_TFR_CTL_CPOL;
|
||||||
|
|
||||||
|
if (spi->mode & SPI_CPHA)
|
||||||
|
reg |= SUN6I_TFR_CTL_CPHA;
|
||||||
|
else
|
||||||
|
reg &= ~SUN6I_TFR_CTL_CPHA;
|
||||||
|
|
||||||
|
if (spi->mode & SPI_LSB_FIRST)
|
||||||
|
reg |= SUN6I_TFR_CTL_FBS;
|
||||||
|
else
|
||||||
|
reg &= ~SUN6I_TFR_CTL_FBS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's a TX only transfer, we don't want to fill the RX
|
||||||
|
* FIFO with bogus data
|
||||||
|
*/
|
||||||
|
if (sspi->rx_buf)
|
||||||
|
reg &= ~SUN6I_TFR_CTL_DHB;
|
||||||
|
else
|
||||||
|
reg |= SUN6I_TFR_CTL_DHB;
|
||||||
|
|
||||||
|
/* We want to control the chip select manually */
|
||||||
|
reg |= SUN6I_TFR_CTL_CS_MANUAL;
|
||||||
|
|
||||||
|
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
|
||||||
|
|
||||||
|
/* Ensure that we have a parent clock fast enough */
|
||||||
|
mclk_rate = clk_get_rate(sspi->mclk);
|
||||||
|
if (mclk_rate < (2 * spi->max_speed_hz)) {
|
||||||
|
clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz);
|
||||||
|
mclk_rate = clk_get_rate(sspi->mclk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup clock divider.
|
||||||
|
*
|
||||||
|
* We have two choices there. Either we can use the clock
|
||||||
|
* divide rate 1, which is calculated thanks to this formula:
|
||||||
|
* SPI_CLK = MOD_CLK / (2 ^ cdr)
|
||||||
|
* Or we can use CDR2, which is calculated with the formula:
|
||||||
|
* SPI_CLK = MOD_CLK / (2 * (cdr + 1))
|
||||||
|
* Wether we use the former or the latter is set through the
|
||||||
|
* DRS bit.
|
||||||
|
*
|
||||||
|
* First try CDR2, and if we can't reach the expected
|
||||||
|
* frequency, fall back to CDR1.
|
||||||
|
*/
|
||||||
|
div = mclk_rate / (2 * spi->max_speed_hz);
|
||||||
|
if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
|
||||||
|
if (div > 0)
|
||||||
|
div--;
|
||||||
|
|
||||||
|
reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS;
|
||||||
|
} else {
|
||||||
|
div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz);
|
||||||
|
reg = SUN6I_CLK_CTL_CDR1(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
|
||||||
|
|
||||||
|
/* Setup the transfer now... */
|
||||||
|
if (sspi->tx_buf)
|
||||||
|
tx_len = tfr->len;
|
||||||
|
|
||||||
|
/* Setup the counters */
|
||||||
|
sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, SUN6I_BURST_CNT(tfr->len));
|
||||||
|
sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, SUN6I_XMIT_CNT(tx_len));
|
||||||
|
sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG,
|
||||||
|
SUN6I_BURST_CTL_CNT_STC(tx_len));
|
||||||
|
|
||||||
|
/* Fill the TX FIFO */
|
||||||
|
sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH);
|
||||||
|
|
||||||
|
/* Enable the interrupts */
|
||||||
|
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC);
|
||||||
|
|
||||||
|
/* Start the transfer */
|
||||||
|
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
|
||||||
|
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH);
|
||||||
|
|
||||||
|
timeout = wait_for_completion_timeout(&sspi->done,
|
||||||
|
msecs_to_jiffies(1000));
|
||||||
|
if (!timeout) {
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH);
|
||||||
|
|
||||||
|
out:
|
||||||
|
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct sun6i_spi *sspi = dev_id;
|
||||||
|
u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG);
|
||||||
|
|
||||||
|
/* Transfer complete */
|
||||||
|
if (status & SUN6I_INT_CTL_TC) {
|
||||||
|
sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC);
|
||||||
|
complete(&sspi->done);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun6i_spi_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
|
struct sun6i_spi *sspi = spi_master_get_devdata(master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(sspi->hclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Couldn't enable AHB clock\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(sspi->mclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Couldn't enable module clock\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = reset_control_deassert(sspi->rstc);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Couldn't deassert the device from reset\n");
|
||||||
|
goto err2;
|
||||||
|
}
|
||||||
|
|
||||||
|
sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG,
|
||||||
|
SUN6I_GBL_CTL_BUS_ENABLE | SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err2:
|
||||||
|
clk_disable_unprepare(sspi->mclk);
|
||||||
|
err:
|
||||||
|
clk_disable_unprepare(sspi->hclk);
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun6i_spi_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
|
struct sun6i_spi *sspi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
reset_control_assert(sspi->rstc);
|
||||||
|
clk_disable_unprepare(sspi->mclk);
|
||||||
|
clk_disable_unprepare(sspi->hclk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun6i_spi_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct spi_master *master;
|
||||||
|
struct sun6i_spi *sspi;
|
||||||
|
struct resource *res;
|
||||||
|
int ret = 0, irq;
|
||||||
|
|
||||||
|
master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi));
|
||||||
|
if (!master) {
|
||||||
|
dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, master);
|
||||||
|
sspi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
sspi->base_addr = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(sspi->base_addr)) {
|
||||||
|
ret = PTR_ERR(sspi->base_addr);
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "No spi IRQ specified\n");
|
||||||
|
ret = -ENXIO;
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler,
|
||||||
|
0, "sun6i-spi", sspi);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Cannot request IRQ\n");
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
sspi->master = master;
|
||||||
|
master->set_cs = sun6i_spi_set_cs;
|
||||||
|
master->transfer_one = sun6i_spi_transfer_one;
|
||||||
|
master->num_chipselect = 4;
|
||||||
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
master->auto_runtime_pm = true;
|
||||||
|
|
||||||
|
sspi->hclk = devm_clk_get(&pdev->dev, "ahb");
|
||||||
|
if (IS_ERR(sspi->hclk)) {
|
||||||
|
dev_err(&pdev->dev, "Unable to acquire AHB clock\n");
|
||||||
|
ret = PTR_ERR(sspi->hclk);
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
sspi->mclk = devm_clk_get(&pdev->dev, "mod");
|
||||||
|
if (IS_ERR(sspi->mclk)) {
|
||||||
|
dev_err(&pdev->dev, "Unable to acquire module clock\n");
|
||||||
|
ret = PTR_ERR(sspi->mclk);
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_completion(&sspi->done);
|
||||||
|
|
||||||
|
sspi->rstc = devm_reset_control_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(sspi->rstc)) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't get reset controller\n");
|
||||||
|
ret = PTR_ERR(sspi->rstc);
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This wake-up/shutdown pattern is to be able to have the
|
||||||
|
* device woken up, even if runtime_pm is disabled
|
||||||
|
*/
|
||||||
|
ret = sun6i_spi_runtime_resume(&pdev->dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Couldn't resume the device\n");
|
||||||
|
goto err_free_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_runtime_set_active(&pdev->dev);
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
pm_runtime_idle(&pdev->dev);
|
||||||
|
|
||||||
|
ret = devm_spi_register_master(&pdev->dev, master);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "cannot register SPI master\n");
|
||||||
|
goto err_pm_disable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_pm_disable:
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
sun6i_spi_runtime_suspend(&pdev->dev);
|
||||||
|
err_free_master:
|
||||||
|
spi_master_put(master);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun6i_spi_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id sun6i_spi_match[] = {
|
||||||
|
{ .compatible = "allwinner,sun6i-a31-spi", },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sun6i_spi_match);
|
||||||
|
|
||||||
|
static const struct dev_pm_ops sun6i_spi_pm_ops = {
|
||||||
|
.runtime_resume = sun6i_spi_runtime_resume,
|
||||||
|
.runtime_suspend = sun6i_spi_runtime_suspend,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver sun6i_spi_driver = {
|
||||||
|
.probe = sun6i_spi_probe,
|
||||||
|
.remove = sun6i_spi_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "sun6i-spi",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = sun6i_spi_match,
|
||||||
|
.pm = &sun6i_spi_pm_ops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(sun6i_spi_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Pan Nan <pannan@allwinnertech.com>");
|
||||||
|
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
|
||||||
|
MODULE_DESCRIPTION("Allwinner A31 SPI controller driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -23,7 +23,6 @@
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/dmapool.h>
|
#include <linux/dmapool.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -172,7 +171,6 @@ struct tegra_spi_data {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
phys_addr_t phys;
|
phys_addr_t phys;
|
||||||
unsigned irq;
|
unsigned irq;
|
||||||
u32 spi_max_frequency;
|
|
||||||
u32 cur_speed;
|
u32 cur_speed;
|
||||||
|
|
||||||
struct spi_device *cur_spi;
|
struct spi_device *cur_spi;
|
||||||
|
@ -761,11 +759,6 @@ static int tegra_spi_setup(struct spi_device *spi)
|
||||||
spi->mode & SPI_CPHA ? "" : "~",
|
spi->mode & SPI_CPHA ? "" : "~",
|
||||||
spi->max_speed_hz);
|
spi->max_speed_hz);
|
||||||
|
|
||||||
BUG_ON(spi->chip_select >= MAX_CHIP_SELECT);
|
|
||||||
|
|
||||||
/* Set speed to the spi max fequency if spi device has not set */
|
|
||||||
spi->max_speed_hz = spi->max_speed_hz ? : tspi->spi_max_frequency;
|
|
||||||
|
|
||||||
ret = pm_runtime_get_sync(tspi->dev);
|
ret = pm_runtime_get_sync(tspi->dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret);
|
dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret);
|
||||||
|
@ -853,8 +846,8 @@ complete_xfer:
|
||||||
SPI_COMMAND1);
|
SPI_COMMAND1);
|
||||||
tegra_spi_transfer_delay(xfer->delay_usecs);
|
tegra_spi_transfer_delay(xfer->delay_usecs);
|
||||||
goto exit;
|
goto exit;
|
||||||
} else if (msg->transfers.prev == &xfer->transfer_list) {
|
} else if (list_is_last(&xfer->transfer_list,
|
||||||
/* This is the last transfer in message */
|
&msg->transfers)) {
|
||||||
if (xfer->cs_change)
|
if (xfer->cs_change)
|
||||||
tspi->cs_control = spi;
|
tspi->cs_control = spi;
|
||||||
else {
|
else {
|
||||||
|
@ -1019,16 +1012,6 @@ static irqreturn_t tegra_spi_isr(int irq, void *context_data)
|
||||||
return IRQ_WAKE_THREAD;
|
return IRQ_WAKE_THREAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tegra_spi_parse_dt(struct platform_device *pdev,
|
|
||||||
struct tegra_spi_data *tspi)
|
|
||||||
{
|
|
||||||
struct device_node *np = pdev->dev.of_node;
|
|
||||||
|
|
||||||
if (of_property_read_u32(np, "spi-max-frequency",
|
|
||||||
&tspi->spi_max_frequency))
|
|
||||||
tspi->spi_max_frequency = 25000000; /* 25MHz */
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct of_device_id tegra_spi_of_match[] = {
|
static struct of_device_id tegra_spi_of_match[] = {
|
||||||
{ .compatible = "nvidia,tegra114-spi", },
|
{ .compatible = "nvidia,tegra114-spi", },
|
||||||
{}
|
{}
|
||||||
|
@ -1050,15 +1033,15 @@ static int tegra_spi_probe(struct platform_device *pdev)
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
tspi = spi_master_get_devdata(master);
|
tspi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
/* Parse DT */
|
if (of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
|
||||||
tegra_spi_parse_dt(pdev, tspi);
|
&master->max_speed_hz))
|
||||||
|
master->max_speed_hz = 25000000; /* 25MHz */
|
||||||
|
|
||||||
/* the spi->mode bits understood by this driver: */
|
/* the spi->mode bits understood by this driver: */
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||||
master->setup = tegra_spi_setup;
|
master->setup = tegra_spi_setup;
|
||||||
master->transfer_one_message = tegra_spi_transfer_one_message;
|
master->transfer_one_message = tegra_spi_transfer_one_message;
|
||||||
master->num_chipselect = MAX_CHIP_SELECT;
|
master->num_chipselect = MAX_CHIP_SELECT;
|
||||||
master->bus_num = -1;
|
|
||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
|
|
||||||
tspi->master = master;
|
tspi->master = master;
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -121,7 +120,6 @@ struct tegra_sflash_data {
|
||||||
struct reset_control *rst;
|
struct reset_control *rst;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
unsigned irq;
|
unsigned irq;
|
||||||
u32 spi_max_frequency;
|
|
||||||
u32 cur_speed;
|
u32 cur_speed;
|
||||||
|
|
||||||
struct spi_device *cur_spi;
|
struct spi_device *cur_spi;
|
||||||
|
@ -315,15 +313,6 @@ static int tegra_sflash_start_transfer_one(struct spi_device *spi,
|
||||||
return tegra_sflash_start_cpu_based_transfer(tsd, t);
|
return tegra_sflash_start_cpu_based_transfer(tsd, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tegra_sflash_setup(struct spi_device *spi)
|
|
||||||
{
|
|
||||||
struct tegra_sflash_data *tsd = spi_master_get_devdata(spi->master);
|
|
||||||
|
|
||||||
/* Set speed to the spi max fequency if spi device has not set */
|
|
||||||
spi->max_speed_hz = spi->max_speed_hz ? : tsd->spi_max_frequency;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tegra_sflash_transfer_one_message(struct spi_master *master,
|
static int tegra_sflash_transfer_one_message(struct spi_master *master,
|
||||||
struct spi_message *msg)
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
|
@ -430,15 +419,6 @@ static irqreturn_t tegra_sflash_isr(int irq, void *context_data)
|
||||||
return handle_cpu_based_xfer(tsd);
|
return handle_cpu_based_xfer(tsd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tegra_sflash_parse_dt(struct tegra_sflash_data *tsd)
|
|
||||||
{
|
|
||||||
struct device_node *np = tsd->dev->of_node;
|
|
||||||
|
|
||||||
if (of_property_read_u32(np, "spi-max-frequency",
|
|
||||||
&tsd->spi_max_frequency))
|
|
||||||
tsd->spi_max_frequency = 25000000; /* 25MHz */
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct of_device_id tegra_sflash_of_match[] = {
|
static struct of_device_id tegra_sflash_of_match[] = {
|
||||||
{ .compatible = "nvidia,tegra20-sflash", },
|
{ .compatible = "nvidia,tegra20-sflash", },
|
||||||
{}
|
{}
|
||||||
|
@ -467,11 +447,9 @@ static int tegra_sflash_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* the spi->mode bits understood by this driver: */
|
/* the spi->mode bits understood by this driver: */
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||||
master->setup = tegra_sflash_setup;
|
|
||||||
master->transfer_one_message = tegra_sflash_transfer_one_message;
|
master->transfer_one_message = tegra_sflash_transfer_one_message;
|
||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
master->num_chipselect = MAX_CHIP_SELECT;
|
master->num_chipselect = MAX_CHIP_SELECT;
|
||||||
master->bus_num = -1;
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
tsd = spi_master_get_devdata(master);
|
tsd = spi_master_get_devdata(master);
|
||||||
|
@ -479,7 +457,9 @@ static int tegra_sflash_probe(struct platform_device *pdev)
|
||||||
tsd->dev = &pdev->dev;
|
tsd->dev = &pdev->dev;
|
||||||
spin_lock_init(&tsd->lock);
|
spin_lock_init(&tsd->lock);
|
||||||
|
|
||||||
tegra_sflash_parse_dt(tsd);
|
if (of_property_read_u32(tsd->dev->of_node, "spi-max-frequency",
|
||||||
|
&master->max_speed_hz))
|
||||||
|
master->max_speed_hz = 25000000; /* 25MHz */
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
tsd->base = devm_ioremap_resource(&pdev->dev, r);
|
tsd->base = devm_ioremap_resource(&pdev->dev, r);
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/dmapool.h>
|
#include <linux/dmapool.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -171,7 +170,6 @@ struct tegra_slink_data {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
phys_addr_t phys;
|
phys_addr_t phys;
|
||||||
unsigned irq;
|
unsigned irq;
|
||||||
u32 spi_max_frequency;
|
|
||||||
u32 cur_speed;
|
u32 cur_speed;
|
||||||
|
|
||||||
struct spi_device *cur_spi;
|
struct spi_device *cur_spi;
|
||||||
|
@ -761,10 +759,6 @@ static int tegra_slink_setup(struct spi_device *spi)
|
||||||
spi->mode & SPI_CPHA ? "" : "~",
|
spi->mode & SPI_CPHA ? "" : "~",
|
||||||
spi->max_speed_hz);
|
spi->max_speed_hz);
|
||||||
|
|
||||||
BUG_ON(spi->chip_select >= MAX_CHIP_SELECT);
|
|
||||||
|
|
||||||
/* Set speed to the spi max fequency if spi device has not set */
|
|
||||||
spi->max_speed_hz = spi->max_speed_hz ? : tspi->spi_max_frequency;
|
|
||||||
ret = pm_runtime_get_sync(tspi->dev);
|
ret = pm_runtime_get_sync(tspi->dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret);
|
dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret);
|
||||||
|
@ -999,15 +993,6 @@ static irqreturn_t tegra_slink_isr(int irq, void *context_data)
|
||||||
return IRQ_WAKE_THREAD;
|
return IRQ_WAKE_THREAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tegra_slink_parse_dt(struct tegra_slink_data *tspi)
|
|
||||||
{
|
|
||||||
struct device_node *np = tspi->dev->of_node;
|
|
||||||
|
|
||||||
if (of_property_read_u32(np, "spi-max-frequency",
|
|
||||||
&tspi->spi_max_frequency))
|
|
||||||
tspi->spi_max_frequency = 25000000; /* 25MHz */
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct tegra_slink_chip_data tegra30_spi_cdata = {
|
static const struct tegra_slink_chip_data tegra30_spi_cdata = {
|
||||||
.cs_hold_time = true,
|
.cs_hold_time = true,
|
||||||
};
|
};
|
||||||
|
@ -1053,7 +1038,6 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
||||||
master->unprepare_message = tegra_slink_unprepare_message;
|
master->unprepare_message = tegra_slink_unprepare_message;
|
||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
master->num_chipselect = MAX_CHIP_SELECT;
|
master->num_chipselect = MAX_CHIP_SELECT;
|
||||||
master->bus_num = -1;
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
tspi = spi_master_get_devdata(master);
|
tspi = spi_master_get_devdata(master);
|
||||||
|
@ -1062,7 +1046,9 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
||||||
tspi->chip_data = cdata;
|
tspi->chip_data = cdata;
|
||||||
spin_lock_init(&tspi->lock);
|
spin_lock_init(&tspi->lock);
|
||||||
|
|
||||||
tegra_slink_parse_dt(tspi);
|
if (of_property_read_u32(tspi->dev->of_node, "spi-max-frequency",
|
||||||
|
&master->max_speed_hz))
|
||||||
|
master->max_speed_hz = 25000000; /* 25MHz */
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!r) {
|
if (!r) {
|
||||||
|
|
|
@ -429,13 +429,13 @@ static int ti_qspi_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD;
|
||||||
|
|
||||||
master->bus_num = -1;
|
|
||||||
master->flags = SPI_MASTER_HALF_DUPLEX;
|
master->flags = SPI_MASTER_HALF_DUPLEX;
|
||||||
master->setup = ti_qspi_setup;
|
master->setup = ti_qspi_setup;
|
||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
master->transfer_one_message = ti_qspi_start_transfer_one;
|
master->transfer_one_message = ti_qspi_start_transfer_one;
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1);
|
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
|
||||||
|
SPI_BPW_MASK(8);
|
||||||
|
|
||||||
if (!of_property_read_u32(np, "num-cs", &num_cs))
|
if (!of_property_read_u32(np, "num-cs", &num_cs))
|
||||||
master->num_chipselect = num_cs;
|
master->num_chipselect = num_cs;
|
||||||
|
@ -461,7 +461,6 @@ static int ti_qspi_probe(struct platform_device *pdev)
|
||||||
if (res_mmap == NULL) {
|
if (res_mmap == NULL) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"memory mapped resource not required\n");
|
"memory mapped resource not required\n");
|
||||||
return -ENODEV;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,378 +0,0 @@
|
||||||
/*
|
|
||||||
* Sequencer Serial Port (SSP) based SPI master driver
|
|
||||||
*
|
|
||||||
* Copyright (C) 2010 Texas Instruments Inc
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/completion.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/spi/spi.h>
|
|
||||||
#include <linux/mfd/ti_ssp.h>
|
|
||||||
|
|
||||||
#define MODE_BITS (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH)
|
|
||||||
|
|
||||||
struct ti_ssp_spi {
|
|
||||||
struct spi_master *master;
|
|
||||||
struct device *dev;
|
|
||||||
spinlock_t lock;
|
|
||||||
struct list_head msg_queue;
|
|
||||||
struct completion complete;
|
|
||||||
bool shutdown;
|
|
||||||
struct workqueue_struct *workqueue;
|
|
||||||
struct work_struct work;
|
|
||||||
u8 mode, bpw;
|
|
||||||
int cs_active;
|
|
||||||
u32 pc_en, pc_dis, pc_wr, pc_rd;
|
|
||||||
void (*select)(int cs);
|
|
||||||
};
|
|
||||||
|
|
||||||
static u32 ti_ssp_spi_rx(struct ti_ssp_spi *hw)
|
|
||||||
{
|
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
ti_ssp_run(hw->dev, hw->pc_rd, 0, &ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ti_ssp_spi_tx(struct ti_ssp_spi *hw, u32 data)
|
|
||||||
{
|
|
||||||
ti_ssp_run(hw->dev, hw->pc_wr, data << (32 - hw->bpw), NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ti_ssp_spi_txrx(struct ti_ssp_spi *hw, struct spi_message *msg,
|
|
||||||
struct spi_transfer *t)
|
|
||||||
{
|
|
||||||
int count;
|
|
||||||
|
|
||||||
if (hw->bpw <= 8) {
|
|
||||||
u8 *rx = t->rx_buf;
|
|
||||||
const u8 *tx = t->tx_buf;
|
|
||||||
|
|
||||||
for (count = 0; count < t->len; count += 1) {
|
|
||||||
if (t->tx_buf)
|
|
||||||
ti_ssp_spi_tx(hw, *tx++);
|
|
||||||
if (t->rx_buf)
|
|
||||||
*rx++ = ti_ssp_spi_rx(hw);
|
|
||||||
}
|
|
||||||
} else if (hw->bpw <= 16) {
|
|
||||||
u16 *rx = t->rx_buf;
|
|
||||||
const u16 *tx = t->tx_buf;
|
|
||||||
|
|
||||||
for (count = 0; count < t->len; count += 2) {
|
|
||||||
if (t->tx_buf)
|
|
||||||
ti_ssp_spi_tx(hw, *tx++);
|
|
||||||
if (t->rx_buf)
|
|
||||||
*rx++ = ti_ssp_spi_rx(hw);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
u32 *rx = t->rx_buf;
|
|
||||||
const u32 *tx = t->tx_buf;
|
|
||||||
|
|
||||||
for (count = 0; count < t->len; count += 4) {
|
|
||||||
if (t->tx_buf)
|
|
||||||
ti_ssp_spi_tx(hw, *tx++);
|
|
||||||
if (t->rx_buf)
|
|
||||||
*rx++ = ti_ssp_spi_rx(hw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
msg->actual_length += count; /* bytes transferred */
|
|
||||||
|
|
||||||
dev_dbg(&msg->spi->dev, "xfer %s%s, %d bytes, %d bpw, count %d%s\n",
|
|
||||||
t->tx_buf ? "tx" : "", t->rx_buf ? "rx" : "", t->len,
|
|
||||||
hw->bpw, count, (count < t->len) ? " (under)" : "");
|
|
||||||
|
|
||||||
return (count < t->len) ? -EIO : 0; /* left over data */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ti_ssp_spi_chip_select(struct ti_ssp_spi *hw, int cs_active)
|
|
||||||
{
|
|
||||||
cs_active = !!cs_active;
|
|
||||||
if (cs_active == hw->cs_active)
|
|
||||||
return;
|
|
||||||
ti_ssp_run(hw->dev, cs_active ? hw->pc_en : hw->pc_dis, 0, NULL);
|
|
||||||
hw->cs_active = cs_active;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define __SHIFT_OUT(bits) (SSP_OPCODE_SHIFT | SSP_OUT_MODE | \
|
|
||||||
cs_en | clk | SSP_COUNT((bits) * 2 - 1))
|
|
||||||
#define __SHIFT_IN(bits) (SSP_OPCODE_SHIFT | SSP_IN_MODE | \
|
|
||||||
cs_en | clk | SSP_COUNT((bits) * 2 - 1))
|
|
||||||
|
|
||||||
static int ti_ssp_spi_setup_transfer(struct ti_ssp_spi *hw, u8 bpw, u8 mode)
|
|
||||||
{
|
|
||||||
int error, idx = 0;
|
|
||||||
u32 seqram[16];
|
|
||||||
u32 cs_en, cs_dis, clk;
|
|
||||||
u32 topbits, botbits;
|
|
||||||
|
|
||||||
mode &= MODE_BITS;
|
|
||||||
if (mode == hw->mode && bpw == hw->bpw)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
cs_en = (mode & SPI_CS_HIGH) ? SSP_CS_HIGH : SSP_CS_LOW;
|
|
||||||
cs_dis = (mode & SPI_CS_HIGH) ? SSP_CS_LOW : SSP_CS_HIGH;
|
|
||||||
clk = (mode & SPI_CPOL) ? SSP_CLK_HIGH : SSP_CLK_LOW;
|
|
||||||
|
|
||||||
/* Construct instructions */
|
|
||||||
|
|
||||||
/* Disable Chip Select */
|
|
||||||
hw->pc_dis = idx;
|
|
||||||
seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_dis | clk;
|
|
||||||
seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_dis | clk;
|
|
||||||
|
|
||||||
/* Enable Chip Select */
|
|
||||||
hw->pc_en = idx;
|
|
||||||
seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_en | clk;
|
|
||||||
seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk;
|
|
||||||
|
|
||||||
/* Reads and writes need to be split for bpw > 16 */
|
|
||||||
topbits = (bpw > 16) ? 16 : bpw;
|
|
||||||
botbits = bpw - topbits;
|
|
||||||
|
|
||||||
/* Write */
|
|
||||||
hw->pc_wr = idx;
|
|
||||||
seqram[idx++] = __SHIFT_OUT(topbits) | SSP_ADDR_REG;
|
|
||||||
if (botbits)
|
|
||||||
seqram[idx++] = __SHIFT_OUT(botbits) | SSP_DATA_REG;
|
|
||||||
seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk;
|
|
||||||
|
|
||||||
/* Read */
|
|
||||||
hw->pc_rd = idx;
|
|
||||||
if (botbits)
|
|
||||||
seqram[idx++] = __SHIFT_IN(botbits) | SSP_ADDR_REG;
|
|
||||||
seqram[idx++] = __SHIFT_IN(topbits) | SSP_DATA_REG;
|
|
||||||
seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk;
|
|
||||||
|
|
||||||
error = ti_ssp_load(hw->dev, 0, seqram, idx);
|
|
||||||
if (error < 0)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
error = ti_ssp_set_mode(hw->dev, ((mode & SPI_CPHA) ?
|
|
||||||
0 : SSP_EARLY_DIN));
|
|
||||||
if (error < 0)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
hw->bpw = bpw;
|
|
||||||
hw->mode = mode;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ti_ssp_spi_work(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct ti_ssp_spi *hw = container_of(work, struct ti_ssp_spi, work);
|
|
||||||
|
|
||||||
spin_lock(&hw->lock);
|
|
||||||
|
|
||||||
while (!list_empty(&hw->msg_queue)) {
|
|
||||||
struct spi_message *m;
|
|
||||||
struct spi_device *spi;
|
|
||||||
struct spi_transfer *t = NULL;
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
m = container_of(hw->msg_queue.next, struct spi_message,
|
|
||||||
queue);
|
|
||||||
|
|
||||||
list_del_init(&m->queue);
|
|
||||||
|
|
||||||
spin_unlock(&hw->lock);
|
|
||||||
|
|
||||||
spi = m->spi;
|
|
||||||
|
|
||||||
if (hw->select)
|
|
||||||
hw->select(spi->chip_select);
|
|
||||||
|
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
|
||||||
int bpw = spi->bits_per_word;
|
|
||||||
int xfer_status;
|
|
||||||
|
|
||||||
if (t->bits_per_word)
|
|
||||||
bpw = t->bits_per_word;
|
|
||||||
|
|
||||||
if (ti_ssp_spi_setup_transfer(hw, bpw, spi->mode) < 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ti_ssp_spi_chip_select(hw, 1);
|
|
||||||
|
|
||||||
xfer_status = ti_ssp_spi_txrx(hw, m, t);
|
|
||||||
if (xfer_status < 0)
|
|
||||||
status = xfer_status;
|
|
||||||
|
|
||||||
if (t->delay_usecs)
|
|
||||||
udelay(t->delay_usecs);
|
|
||||||
|
|
||||||
if (t->cs_change)
|
|
||||||
ti_ssp_spi_chip_select(hw, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ti_ssp_spi_chip_select(hw, 0);
|
|
||||||
m->status = status;
|
|
||||||
m->complete(m->context);
|
|
||||||
|
|
||||||
spin_lock(&hw->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hw->shutdown)
|
|
||||||
complete(&hw->complete);
|
|
||||||
|
|
||||||
spin_unlock(&hw->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ti_ssp_spi_transfer(struct spi_device *spi, struct spi_message *m)
|
|
||||||
{
|
|
||||||
struct ti_ssp_spi *hw;
|
|
||||||
struct spi_transfer *t;
|
|
||||||
int error = 0;
|
|
||||||
|
|
||||||
m->actual_length = 0;
|
|
||||||
m->status = -EINPROGRESS;
|
|
||||||
|
|
||||||
hw = spi_master_get_devdata(spi->master);
|
|
||||||
|
|
||||||
if (list_empty(&m->transfers) || !m->complete)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
|
||||||
if (t->len && !(t->rx_buf || t->tx_buf)) {
|
|
||||||
dev_err(&spi->dev, "invalid xfer, no buffer\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t->len && t->rx_buf && t->tx_buf) {
|
|
||||||
dev_err(&spi->dev, "invalid xfer, full duplex\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock(&hw->lock);
|
|
||||||
if (hw->shutdown) {
|
|
||||||
error = -ESHUTDOWN;
|
|
||||||
goto error_unlock;
|
|
||||||
}
|
|
||||||
list_add_tail(&m->queue, &hw->msg_queue);
|
|
||||||
queue_work(hw->workqueue, &hw->work);
|
|
||||||
error_unlock:
|
|
||||||
spin_unlock(&hw->lock);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ti_ssp_spi_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
const struct ti_ssp_spi_data *pdata;
|
|
||||||
struct ti_ssp_spi *hw;
|
|
||||||
struct spi_master *master;
|
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
int error = 0;
|
|
||||||
|
|
||||||
pdata = dev_get_platdata(dev);
|
|
||||||
if (!pdata) {
|
|
||||||
dev_err(dev, "platform data not found\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
master = spi_alloc_master(dev, sizeof(struct ti_ssp_spi));
|
|
||||||
if (!master) {
|
|
||||||
dev_err(dev, "cannot allocate SPI master\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
hw = spi_master_get_devdata(master);
|
|
||||||
platform_set_drvdata(pdev, hw);
|
|
||||||
|
|
||||||
hw->master = master;
|
|
||||||
hw->dev = dev;
|
|
||||||
hw->select = pdata->select;
|
|
||||||
|
|
||||||
spin_lock_init(&hw->lock);
|
|
||||||
init_completion(&hw->complete);
|
|
||||||
INIT_LIST_HEAD(&hw->msg_queue);
|
|
||||||
INIT_WORK(&hw->work, ti_ssp_spi_work);
|
|
||||||
|
|
||||||
hw->workqueue = create_singlethread_workqueue(dev_name(dev));
|
|
||||||
if (!hw->workqueue) {
|
|
||||||
error = -ENOMEM;
|
|
||||||
dev_err(dev, "work queue creation failed\n");
|
|
||||||
goto error_wq;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = ti_ssp_set_iosel(hw->dev, pdata->iosel);
|
|
||||||
if (error < 0) {
|
|
||||||
dev_err(dev, "io setup failed\n");
|
|
||||||
goto error_iosel;
|
|
||||||
}
|
|
||||||
|
|
||||||
master->bus_num = pdev->id;
|
|
||||||
master->num_chipselect = pdata->num_cs;
|
|
||||||
master->mode_bits = MODE_BITS;
|
|
||||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
|
|
||||||
master->flags = SPI_MASTER_HALF_DUPLEX;
|
|
||||||
master->transfer = ti_ssp_spi_transfer;
|
|
||||||
|
|
||||||
error = spi_register_master(master);
|
|
||||||
if (error) {
|
|
||||||
dev_err(dev, "master registration failed\n");
|
|
||||||
goto error_reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error_reg:
|
|
||||||
error_iosel:
|
|
||||||
destroy_workqueue(hw->workqueue);
|
|
||||||
error_wq:
|
|
||||||
spi_master_put(master);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ti_ssp_spi_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct ti_ssp_spi *hw = platform_get_drvdata(pdev);
|
|
||||||
int error;
|
|
||||||
|
|
||||||
hw->shutdown = 1;
|
|
||||||
while (!list_empty(&hw->msg_queue)) {
|
|
||||||
error = wait_for_completion_interruptible(&hw->complete);
|
|
||||||
if (error < 0) {
|
|
||||||
hw->shutdown = 0;
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
destroy_workqueue(hw->workqueue);
|
|
||||||
spi_unregister_master(hw->master);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct platform_driver ti_ssp_spi_driver = {
|
|
||||||
.probe = ti_ssp_spi_probe,
|
|
||||||
.remove = ti_ssp_spi_remove,
|
|
||||||
.driver = {
|
|
||||||
.name = "ti-ssp-spi",
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
module_platform_driver(ti_ssp_spi_driver);
|
|
||||||
|
|
||||||
MODULE_DESCRIPTION("SSP SPI Master");
|
|
||||||
MODULE_AUTHOR("Cyril Chemparathy");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
MODULE_ALIAS("platform:ti-ssp-spi");
|
|
|
@ -332,7 +332,7 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
|
||||||
data->transfer_active = false;
|
data->transfer_active = false;
|
||||||
wake_up(&data->wait);
|
wake_up(&data->wait);
|
||||||
} else {
|
} else {
|
||||||
dev_err(&data->master->dev,
|
dev_vdbg(&data->master->dev,
|
||||||
"%s : Transfer is not completed",
|
"%s : Transfer is not completed",
|
||||||
__func__);
|
__func__);
|
||||||
}
|
}
|
||||||
|
@ -464,20 +464,6 @@ static void pch_spi_reset(struct spi_master *master)
|
||||||
pch_spi_writereg(master, PCH_SRST, 0x0);
|
pch_spi_writereg(master, PCH_SRST, 0x0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pch_spi_setup(struct spi_device *pspi)
|
|
||||||
{
|
|
||||||
/* Check baud rate setting */
|
|
||||||
/* if baud rate of chip is greater than
|
|
||||||
max we can support,return error */
|
|
||||||
if ((pspi->max_speed_hz) > PCH_MAX_BAUDRATE)
|
|
||||||
pspi->max_speed_hz = PCH_MAX_BAUDRATE;
|
|
||||||
|
|
||||||
dev_dbg(&pspi->dev, "%s MODE = %x\n", __func__,
|
|
||||||
(pspi->mode) & (SPI_CPOL | SPI_CPHA));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
|
static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -486,23 +472,6 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
|
||||||
int retval;
|
int retval;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
/* validate spi message and baud rate */
|
|
||||||
if (unlikely(list_empty(&pmsg->transfers) == 1)) {
|
|
||||||
dev_err(&pspi->dev, "%s list empty\n", __func__);
|
|
||||||
retval = -EINVAL;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(pspi->max_speed_hz == 0)) {
|
|
||||||
dev_err(&pspi->dev, "%s pch_spi_transfer maxspeed=%d\n",
|
|
||||||
__func__, pspi->max_speed_hz);
|
|
||||||
retval = -EINVAL;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_dbg(&pspi->dev,
|
|
||||||
"%s Transfer List not empty. Transfer Speed is set.\n", __func__);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&data->lock, flags);
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
/* validate Tx/Rx buffers and Transfer length */
|
/* validate Tx/Rx buffers and Transfer length */
|
||||||
list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
|
list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
|
||||||
|
@ -523,10 +492,6 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
|
||||||
dev_dbg(&pspi->dev,
|
dev_dbg(&pspi->dev,
|
||||||
"%s Tx/Rx buffer valid. Transfer length valid\n",
|
"%s Tx/Rx buffer valid. Transfer length valid\n",
|
||||||
__func__);
|
__func__);
|
||||||
|
|
||||||
/* if baud rate has been specified validate the same */
|
|
||||||
if (transfer->speed_hz > PCH_MAX_BAUDRATE)
|
|
||||||
transfer->speed_hz = PCH_MAX_BAUDRATE;
|
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&data->lock, flags);
|
spin_unlock_irqrestore(&data->lock, flags);
|
||||||
|
|
||||||
|
@ -1151,8 +1116,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
|
||||||
dma->nent = num;
|
dma->nent = num;
|
||||||
dma->desc_tx = desc_tx;
|
dma->desc_tx = desc_tx;
|
||||||
|
|
||||||
dev_dbg(&data->master->dev, "\n%s:Pulling down SSN low - writing "
|
dev_dbg(&data->master->dev, "%s:Pulling down SSN low - writing 0x2 to SSNXCR\n", __func__);
|
||||||
"0x2 to SSNXCR\n", __func__);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&data->lock, flags);
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW);
|
pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW);
|
||||||
|
@ -1418,10 +1382,10 @@ static int pch_spi_pd_probe(struct platform_device *plat_dev)
|
||||||
|
|
||||||
/* initialize members of SPI master */
|
/* initialize members of SPI master */
|
||||||
master->num_chipselect = PCH_MAX_CS;
|
master->num_chipselect = PCH_MAX_CS;
|
||||||
master->setup = pch_spi_setup;
|
|
||||||
master->transfer = pch_spi_transfer;
|
master->transfer = pch_spi_transfer;
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
||||||
|
master->max_speed_hz = PCH_MAX_BAUDRATE;
|
||||||
|
|
||||||
data->board_dat = board_dat;
|
data->board_dat = board_dat;
|
||||||
data->plat_dev = plat_dev;
|
data->plat_dev = plat_dev;
|
||||||
|
@ -1605,8 +1569,7 @@ static struct platform_driver pch_spi_pd_driver = {
|
||||||
.resume = pch_spi_pd_resume
|
.resume = pch_spi_pd_resume
|
||||||
};
|
};
|
||||||
|
|
||||||
static int pch_spi_probe(struct pci_dev *pdev,
|
static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||||
const struct pci_device_id *id)
|
|
||||||
{
|
{
|
||||||
struct pch_spi_board_data *board_dat;
|
struct pch_spi_board_data *board_dat;
|
||||||
struct platform_device *pd_dev = NULL;
|
struct platform_device *pd_dev = NULL;
|
||||||
|
@ -1676,6 +1639,8 @@ static int pch_spi_probe(struct pci_dev *pdev,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_platform_device:
|
err_platform_device:
|
||||||
|
while (--i >= 0)
|
||||||
|
platform_device_unregister(pd_dev_save->pd_save[i]);
|
||||||
pci_disable_device(pdev);
|
pci_disable_device(pdev);
|
||||||
pci_enable_device:
|
pci_enable_device:
|
||||||
pci_release_regions(pdev);
|
pci_release_regions(pdev);
|
||||||
|
|
|
@ -80,7 +80,6 @@ struct txx9spi {
|
||||||
void __iomem *membase;
|
void __iomem *membase;
|
||||||
int baseclk;
|
int baseclk;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
u32 max_speed_hz, min_speed_hz;
|
|
||||||
int last_chipselect;
|
int last_chipselect;
|
||||||
int last_chipselect_val;
|
int last_chipselect_val;
|
||||||
};
|
};
|
||||||
|
@ -117,9 +116,7 @@ static int txx9spi_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct txx9spi *c = spi_master_get_devdata(spi->master);
|
struct txx9spi *c = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
if (!spi->max_speed_hz
|
if (!spi->max_speed_hz)
|
||||||
|| spi->max_speed_hz > c->max_speed_hz
|
|
||||||
|| spi->max_speed_hz < c->min_speed_hz)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (gpio_direction_output(spi->chip_select,
|
if (gpio_direction_output(spi->chip_select,
|
||||||
|
@ -309,15 +306,8 @@ static int txx9spi_transfer(struct spi_device *spi, struct spi_message *m)
|
||||||
|
|
||||||
/* check each transfer's parameters */
|
/* check each transfer's parameters */
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
u32 speed_hz = t->speed_hz ? : spi->max_speed_hz;
|
|
||||||
u8 bits_per_word = t->bits_per_word;
|
|
||||||
|
|
||||||
if (!t->tx_buf && !t->rx_buf && t->len)
|
if (!t->tx_buf && !t->rx_buf && t->len)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (t->len & ((bits_per_word >> 3) - 1))
|
|
||||||
return -EINVAL;
|
|
||||||
if (speed_hz < c->min_speed_hz || speed_hz > c->max_speed_hz)
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&c->lock, flags);
|
spin_lock_irqsave(&c->lock, flags);
|
||||||
|
@ -360,17 +350,12 @@ static int txx9spi_probe(struct platform_device *dev)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
c->baseclk = clk_get_rate(c->clk);
|
c->baseclk = clk_get_rate(c->clk);
|
||||||
c->min_speed_hz = DIV_ROUND_UP(c->baseclk, SPI_MAX_DIVIDER + 1);
|
master->min_speed_hz = DIV_ROUND_UP(c->baseclk, SPI_MAX_DIVIDER + 1);
|
||||||
c->max_speed_hz = c->baseclk / (SPI_MIN_DIVIDER + 1);
|
master->max_speed_hz = c->baseclk / (SPI_MIN_DIVIDER + 1);
|
||||||
|
|
||||||
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||||
if (!res)
|
c->membase = devm_ioremap_resource(&dev->dev, res);
|
||||||
goto exit_busy;
|
if (IS_ERR(c->membase))
|
||||||
if (!devm_request_mem_region(&dev->dev, res->start, resource_size(res),
|
|
||||||
"spi_txx9"))
|
|
||||||
goto exit_busy;
|
|
||||||
c->membase = devm_ioremap(&dev->dev, res->start, resource_size(res));
|
|
||||||
if (!c->membase)
|
|
||||||
goto exit_busy;
|
goto exit_busy;
|
||||||
|
|
||||||
/* enter config mode */
|
/* enter config mode */
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
@ -74,15 +73,13 @@ static void spi_xcomm_chipselect(struct spi_xcomm *spi_xcomm,
|
||||||
static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm,
|
static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm,
|
||||||
struct spi_device *spi, struct spi_transfer *t, unsigned int *settings)
|
struct spi_device *spi, struct spi_transfer *t, unsigned int *settings)
|
||||||
{
|
{
|
||||||
unsigned int speed;
|
|
||||||
|
|
||||||
if (t->len > 62)
|
if (t->len > 62)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz;
|
if (t->speed_hz != spi_xcomm->current_speed) {
|
||||||
|
unsigned int divider;
|
||||||
|
|
||||||
if (speed != spi_xcomm->current_speed) {
|
divider = DIV_ROUND_UP(SPI_XCOMM_CLOCK, t->speed_hz);
|
||||||
unsigned int divider = DIV_ROUND_UP(SPI_XCOMM_CLOCK, speed);
|
|
||||||
if (divider >= 64)
|
if (divider >= 64)
|
||||||
*settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_64;
|
*settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_64;
|
||||||
else if (divider >= 16)
|
else if (divider >= 16)
|
||||||
|
@ -90,7 +87,7 @@ static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm,
|
||||||
else
|
else
|
||||||
*settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_4;
|
*settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_4;
|
||||||
|
|
||||||
spi_xcomm->current_speed = speed;
|
spi_xcomm->current_speed = t->speed_hz;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spi->mode & SPI_CPOL)
|
if (spi->mode & SPI_CPOL)
|
||||||
|
@ -148,8 +145,6 @@ static int spi_xcomm_transfer_one(struct spi_master *master,
|
||||||
int status = 0;
|
int status = 0;
|
||||||
bool is_last;
|
bool is_last;
|
||||||
|
|
||||||
is_first = true;
|
|
||||||
|
|
||||||
spi_xcomm_chipselect(spi_xcomm, spi, true);
|
spi_xcomm_chipselect(spi_xcomm, spi, true);
|
||||||
|
|
||||||
list_for_each_entry(t, &msg->transfers, transfer_list) {
|
list_for_each_entry(t, &msg->transfers, transfer_list) {
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
@ -88,10 +87,10 @@ struct xilinx_spi {
|
||||||
const u8 *tx_ptr; /* pointer in the Rx buffer */
|
const u8 *tx_ptr; /* pointer in the Rx buffer */
|
||||||
int remaining_bytes; /* the number of bytes left to transfer */
|
int remaining_bytes; /* the number of bytes left to transfer */
|
||||||
u8 bits_per_word;
|
u8 bits_per_word;
|
||||||
unsigned int (*read_fn) (void __iomem *);
|
unsigned int (*read_fn)(void __iomem *);
|
||||||
void (*write_fn) (u32, void __iomem *);
|
void (*write_fn)(u32, void __iomem *);
|
||||||
void (*tx_fn) (struct xilinx_spi *);
|
void (*tx_fn)(struct xilinx_spi *);
|
||||||
void (*rx_fn) (struct xilinx_spi *);
|
void (*rx_fn)(struct xilinx_spi *);
|
||||||
};
|
};
|
||||||
|
|
||||||
static void xspi_write32(u32 val, void __iomem *addr)
|
static void xspi_write32(u32 val, void __iomem *addr)
|
||||||
|
@ -209,26 +208,11 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* spi_bitbang requires custom setup_transfer() to be defined if there is a
|
/* spi_bitbang requires custom setup_transfer() to be defined if there is a
|
||||||
* custom txrx_bufs(). We have nothing to setup here as the SPI IP block
|
* custom txrx_bufs().
|
||||||
* supports 8 or 16 bits per word which cannot be changed in software.
|
|
||||||
* SPI clock can't be changed in software either.
|
|
||||||
* Check for correct bits per word. Chip select delay calculations could be
|
|
||||||
* added here as soon as bitbang_work() can be made aware of the delay value.
|
|
||||||
*/
|
*/
|
||||||
static int xilinx_spi_setup_transfer(struct spi_device *spi,
|
static int xilinx_spi_setup_transfer(struct spi_device *spi,
|
||||||
struct spi_transfer *t)
|
struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
|
|
||||||
u8 bits_per_word;
|
|
||||||
|
|
||||||
bits_per_word = (t && t->bits_per_word)
|
|
||||||
? t->bits_per_word : spi->bits_per_word;
|
|
||||||
if (bits_per_word != xspi->bits_per_word) {
|
|
||||||
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
|
|
||||||
__func__, bits_per_word);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,6 +391,7 @@ static int xilinx_spi_probe(struct platform_device *pdev)
|
||||||
xspi->write_fn = xspi_write32_be;
|
xspi->write_fn = xspi_write32_be;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(bits_per_word);
|
||||||
xspi->bits_per_word = bits_per_word;
|
xspi->bits_per_word = bits_per_word;
|
||||||
if (xspi->bits_per_word == 8) {
|
if (xspi->bits_per_word == 8) {
|
||||||
xspi->tx_fn = xspi_tx8;
|
xspi->tx_fn = xspi_tx8;
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
* Xtensa xtfpga SPI controller driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Cadence Design Systems Inc.
|
||||||
|
*
|
||||||
|
* 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/delay.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/spi/spi_bitbang.h>
|
||||||
|
|
||||||
|
#define XTFPGA_SPI_NAME "xtfpga_spi"
|
||||||
|
|
||||||
|
#define XTFPGA_SPI_START 0x0
|
||||||
|
#define XTFPGA_SPI_BUSY 0x4
|
||||||
|
#define XTFPGA_SPI_DATA 0x8
|
||||||
|
|
||||||
|
#define BUSY_WAIT_US 100
|
||||||
|
|
||||||
|
struct xtfpga_spi {
|
||||||
|
struct spi_bitbang bitbang;
|
||||||
|
void __iomem *regs;
|
||||||
|
u32 data;
|
||||||
|
unsigned data_sz;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void xtfpga_spi_write32(const struct xtfpga_spi *spi,
|
||||||
|
unsigned addr, u32 val)
|
||||||
|
{
|
||||||
|
iowrite32(val, spi->regs + addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int xtfpga_spi_read32(const struct xtfpga_spi *spi,
|
||||||
|
unsigned addr)
|
||||||
|
{
|
||||||
|
return ioread32(spi->regs + addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void xtfpga_spi_wait_busy(struct xtfpga_spi *xspi)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; xtfpga_spi_read32(xspi, XTFPGA_SPI_BUSY) &&
|
||||||
|
i < BUSY_WAIT_US; ++i)
|
||||||
|
udelay(1);
|
||||||
|
WARN_ON_ONCE(i == BUSY_WAIT_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 xtfpga_spi_txrx_word(struct spi_device *spi, unsigned nsecs,
|
||||||
|
u32 v, u8 bits)
|
||||||
|
{
|
||||||
|
struct xtfpga_spi *xspi = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
|
xspi->data = (xspi->data << bits) | (v & GENMASK(bits - 1, 0));
|
||||||
|
xspi->data_sz += bits;
|
||||||
|
if (xspi->data_sz >= 16) {
|
||||||
|
xtfpga_spi_write32(xspi, XTFPGA_SPI_DATA,
|
||||||
|
xspi->data >> (xspi->data_sz - 16));
|
||||||
|
xspi->data_sz -= 16;
|
||||||
|
xtfpga_spi_write32(xspi, XTFPGA_SPI_START, 1);
|
||||||
|
xtfpga_spi_wait_busy(xspi);
|
||||||
|
xtfpga_spi_write32(xspi, XTFPGA_SPI_START, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xtfpga_spi_chipselect(struct spi_device *spi, int is_on)
|
||||||
|
{
|
||||||
|
struct xtfpga_spi *xspi = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
|
WARN_ON(xspi->data_sz != 0);
|
||||||
|
xspi->data_sz = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xtfpga_spi_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct xtfpga_spi *xspi;
|
||||||
|
struct resource *mem;
|
||||||
|
int ret;
|
||||||
|
struct spi_master *master;
|
||||||
|
|
||||||
|
master = spi_alloc_master(&pdev->dev, sizeof(struct xtfpga_spi));
|
||||||
|
if (!master)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
master->flags = SPI_MASTER_NO_RX;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
|
||||||
|
master->bus_num = pdev->dev.id;
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
xspi = spi_master_get_devdata(master);
|
||||||
|
xspi->bitbang.master = master;
|
||||||
|
xspi->bitbang.chipselect = xtfpga_spi_chipselect;
|
||||||
|
xspi->bitbang.txrx_word[SPI_MODE_0] = xtfpga_spi_txrx_word;
|
||||||
|
|
||||||
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!mem) {
|
||||||
|
dev_err(&pdev->dev, "No memory resource\n");
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
xspi->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||||
|
if (IS_ERR(xspi->regs)) {
|
||||||
|
ret = PTR_ERR(xspi->regs);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
xtfpga_spi_write32(xspi, XTFPGA_SPI_START, 0);
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
if (xtfpga_spi_read32(xspi, XTFPGA_SPI_BUSY)) {
|
||||||
|
dev_err(&pdev->dev, "Device stuck in busy state\n");
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = spi_bitbang_start(&xspi->bitbang);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "spi_bitbang_start failed\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, master);
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
spi_master_put(master);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xtfpga_spi_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = platform_get_drvdata(pdev);
|
||||||
|
struct xtfpga_spi *xspi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
spi_bitbang_stop(&xspi->bitbang);
|
||||||
|
spi_master_put(master);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MODULE_ALIAS("platform:" XTFPGA_SPI_NAME);
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static const struct of_device_id xtfpga_spi_of_match[] = {
|
||||||
|
{ .compatible = "cdns,xtfpga-spi", },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, xtfpga_spi_of_match);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct platform_driver xtfpga_spi_driver = {
|
||||||
|
.probe = xtfpga_spi_probe,
|
||||||
|
.remove = xtfpga_spi_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = XTFPGA_SPI_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = of_match_ptr(xtfpga_spi_of_match),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(xtfpga_spi_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("xtensa xtfpga SPI driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -24,6 +24,8 @@
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/cache.h>
|
#include <linux/cache.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
|
@ -255,13 +257,12 @@ EXPORT_SYMBOL_GPL(spi_bus_type);
|
||||||
static int spi_drv_probe(struct device *dev)
|
static int spi_drv_probe(struct device *dev)
|
||||||
{
|
{
|
||||||
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
|
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
|
||||||
struct spi_device *spi = to_spi_device(dev);
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
acpi_dev_pm_attach(&spi->dev, true);
|
acpi_dev_pm_attach(dev, true);
|
||||||
ret = sdrv->probe(spi);
|
ret = sdrv->probe(to_spi_device(dev));
|
||||||
if (ret)
|
if (ret)
|
||||||
acpi_dev_pm_detach(&spi->dev, true);
|
acpi_dev_pm_detach(dev, true);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -269,11 +270,10 @@ static int spi_drv_probe(struct device *dev)
|
||||||
static int spi_drv_remove(struct device *dev)
|
static int spi_drv_remove(struct device *dev)
|
||||||
{
|
{
|
||||||
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
|
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
|
||||||
struct spi_device *spi = to_spi_device(dev);
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = sdrv->remove(spi);
|
ret = sdrv->remove(to_spi_device(dev));
|
||||||
acpi_dev_pm_detach(&spi->dev, true);
|
acpi_dev_pm_detach(dev, true);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -580,6 +580,169 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
|
||||||
spi->master->set_cs(spi, !enable);
|
spi->master->set_cs(spi, !enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int spi_map_buf(struct spi_master *master, struct device *dev,
|
||||||
|
struct sg_table *sgt, void *buf, size_t len,
|
||||||
|
enum dma_data_direction dir)
|
||||||
|
{
|
||||||
|
const bool vmalloced_buf = is_vmalloc_addr(buf);
|
||||||
|
const int desc_len = vmalloced_buf ? PAGE_SIZE : master->max_dma_len;
|
||||||
|
const int sgs = DIV_ROUND_UP(len, desc_len);
|
||||||
|
struct page *vm_page;
|
||||||
|
void *sg_buf;
|
||||||
|
size_t min;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
ret = sg_alloc_table(sgt, sgs, GFP_KERNEL);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 0; i < sgs; i++) {
|
||||||
|
min = min_t(size_t, len, desc_len);
|
||||||
|
|
||||||
|
if (vmalloced_buf) {
|
||||||
|
vm_page = vmalloc_to_page(buf);
|
||||||
|
if (!vm_page) {
|
||||||
|
sg_free_table(sgt);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
sg_buf = page_address(vm_page) +
|
||||||
|
((size_t)buf & ~PAGE_MASK);
|
||||||
|
} else {
|
||||||
|
sg_buf = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
sg_set_buf(&sgt->sgl[i], sg_buf, min);
|
||||||
|
|
||||||
|
buf += min;
|
||||||
|
len -= min;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dma_map_sg(dev, sgt->sgl, sgt->nents, dir);
|
||||||
|
if (ret < 0) {
|
||||||
|
sg_free_table(sgt);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sgt->nents = ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_unmap_buf(struct spi_master *master, struct device *dev,
|
||||||
|
struct sg_table *sgt, enum dma_data_direction dir)
|
||||||
|
{
|
||||||
|
if (sgt->orig_nents) {
|
||||||
|
dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir);
|
||||||
|
sg_free_table(sgt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_map_msg(struct spi_master *master, struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct device *tx_dev, *rx_dev;
|
||||||
|
struct spi_transfer *xfer;
|
||||||
|
void *tmp;
|
||||||
|
unsigned int max_tx, max_rx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (master->flags & (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX)) {
|
||||||
|
max_tx = 0;
|
||||||
|
max_rx = 0;
|
||||||
|
|
||||||
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||||
|
if ((master->flags & SPI_MASTER_MUST_TX) &&
|
||||||
|
!xfer->tx_buf)
|
||||||
|
max_tx = max(xfer->len, max_tx);
|
||||||
|
if ((master->flags & SPI_MASTER_MUST_RX) &&
|
||||||
|
!xfer->rx_buf)
|
||||||
|
max_rx = max(xfer->len, max_rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_tx) {
|
||||||
|
tmp = krealloc(master->dummy_tx, max_tx,
|
||||||
|
GFP_KERNEL | GFP_DMA);
|
||||||
|
if (!tmp)
|
||||||
|
return -ENOMEM;
|
||||||
|
master->dummy_tx = tmp;
|
||||||
|
memset(tmp, 0, max_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_rx) {
|
||||||
|
tmp = krealloc(master->dummy_rx, max_rx,
|
||||||
|
GFP_KERNEL | GFP_DMA);
|
||||||
|
if (!tmp)
|
||||||
|
return -ENOMEM;
|
||||||
|
master->dummy_rx = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_tx || max_rx) {
|
||||||
|
list_for_each_entry(xfer, &msg->transfers,
|
||||||
|
transfer_list) {
|
||||||
|
if (!xfer->tx_buf)
|
||||||
|
xfer->tx_buf = master->dummy_tx;
|
||||||
|
if (!xfer->rx_buf)
|
||||||
|
xfer->rx_buf = master->dummy_rx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!master->can_dma)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tx_dev = &master->dma_tx->dev->device;
|
||||||
|
rx_dev = &master->dma_rx->dev->device;
|
||||||
|
|
||||||
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||||
|
if (!master->can_dma(master, msg->spi, xfer))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (xfer->tx_buf != NULL) {
|
||||||
|
ret = spi_map_buf(master, tx_dev, &xfer->tx_sg,
|
||||||
|
(void *)xfer->tx_buf, xfer->len,
|
||||||
|
DMA_TO_DEVICE);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xfer->rx_buf != NULL) {
|
||||||
|
ret = spi_map_buf(master, rx_dev, &xfer->rx_sg,
|
||||||
|
xfer->rx_buf, xfer->len,
|
||||||
|
DMA_FROM_DEVICE);
|
||||||
|
if (ret != 0) {
|
||||||
|
spi_unmap_buf(master, tx_dev, &xfer->tx_sg,
|
||||||
|
DMA_TO_DEVICE);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
master->cur_msg_mapped = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct spi_transfer *xfer;
|
||||||
|
struct device *tx_dev, *rx_dev;
|
||||||
|
|
||||||
|
if (!master->cur_msg_mapped || !master->can_dma)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tx_dev = &master->dma_tx->dev->device;
|
||||||
|
rx_dev = &master->dma_rx->dev->device;
|
||||||
|
|
||||||
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||||
|
if (!master->can_dma(master, msg->spi, xfer))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
spi_unmap_buf(master, rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
||||||
|
spi_unmap_buf(master, tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* spi_transfer_one_message - Default implementation of transfer_one_message()
|
* spi_transfer_one_message - Default implementation of transfer_one_message()
|
||||||
*
|
*
|
||||||
|
@ -591,9 +754,9 @@ static int spi_transfer_one_message(struct spi_master *master,
|
||||||
struct spi_message *msg)
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
struct spi_transfer *xfer;
|
struct spi_transfer *xfer;
|
||||||
bool cur_cs = true;
|
|
||||||
bool keep_cs = false;
|
bool keep_cs = false;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
int ms = 1;
|
||||||
|
|
||||||
spi_set_cs(msg->spi, true);
|
spi_set_cs(msg->spi, true);
|
||||||
|
|
||||||
|
@ -611,7 +774,16 @@ static int spi_transfer_one_message(struct spi_master *master,
|
||||||
|
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
wait_for_completion(&master->xfer_completion);
|
ms = xfer->len * 8 * 1000 / xfer->speed_hz;
|
||||||
|
ms += 10; /* some tolerance */
|
||||||
|
|
||||||
|
ms = wait_for_completion_timeout(&master->xfer_completion,
|
||||||
|
msecs_to_jiffies(ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ms == 0) {
|
||||||
|
dev_err(&msg->spi->dev, "SPI transfer timed out\n");
|
||||||
|
msg->status = -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_spi_transfer_stop(msg, xfer);
|
trace_spi_transfer_stop(msg, xfer);
|
||||||
|
@ -627,8 +799,9 @@ static int spi_transfer_one_message(struct spi_master *master,
|
||||||
&msg->transfers)) {
|
&msg->transfers)) {
|
||||||
keep_cs = true;
|
keep_cs = true;
|
||||||
} else {
|
} else {
|
||||||
cur_cs = !cur_cs;
|
spi_set_cs(msg->spi, false);
|
||||||
spi_set_cs(msg->spi, cur_cs);
|
udelay(10);
|
||||||
|
spi_set_cs(msg->spi, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,6 +859,10 @@ static void spi_pump_messages(struct kthread_work *work)
|
||||||
}
|
}
|
||||||
master->busy = false;
|
master->busy = false;
|
||||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||||
|
kfree(master->dummy_rx);
|
||||||
|
master->dummy_rx = NULL;
|
||||||
|
kfree(master->dummy_tx);
|
||||||
|
master->dummy_tx = NULL;
|
||||||
if (master->unprepare_transfer_hardware &&
|
if (master->unprepare_transfer_hardware &&
|
||||||
master->unprepare_transfer_hardware(master))
|
master->unprepare_transfer_hardware(master))
|
||||||
dev_err(&master->dev,
|
dev_err(&master->dev,
|
||||||
|
@ -752,6 +929,13 @@ static void spi_pump_messages(struct kthread_work *work)
|
||||||
master->cur_msg_prepared = true;
|
master->cur_msg_prepared = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = spi_map_msg(master, master->cur_msg);
|
||||||
|
if (ret) {
|
||||||
|
master->cur_msg->status = ret;
|
||||||
|
spi_finalize_current_message(master);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ret = master->transfer_one_message(master, master->cur_msg);
|
ret = master->transfer_one_message(master, master->cur_msg);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&master->dev,
|
dev_err(&master->dev,
|
||||||
|
@ -839,6 +1023,8 @@ void spi_finalize_current_message(struct spi_master *master)
|
||||||
queue_kthread_work(&master->kworker, &master->pump_messages);
|
queue_kthread_work(&master->kworker, &master->pump_messages);
|
||||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||||
|
|
||||||
|
spi_unmap_msg(master, mesg);
|
||||||
|
|
||||||
if (master->cur_msg_prepared && master->unprepare_message) {
|
if (master->cur_msg_prepared && master->unprepare_message) {
|
||||||
ret = master->unprepare_message(master, mesg);
|
ret = master->unprepare_message(master, mesg);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -892,7 +1078,7 @@ static int spi_stop_queue(struct spi_master *master)
|
||||||
*/
|
*/
|
||||||
while ((!list_empty(&master->queue) || master->busy) && limit--) {
|
while ((!list_empty(&master->queue) || master->busy) && limit--) {
|
||||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||||
msleep(10);
|
usleep_range(10000, 11000);
|
||||||
spin_lock_irqsave(&master->queue_lock, flags);
|
spin_lock_irqsave(&master->queue_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1372,6 +1558,8 @@ int spi_register_master(struct spi_master *master)
|
||||||
mutex_init(&master->bus_lock_mutex);
|
mutex_init(&master->bus_lock_mutex);
|
||||||
master->bus_lock_flag = 0;
|
master->bus_lock_flag = 0;
|
||||||
init_completion(&master->xfer_completion);
|
init_completion(&master->xfer_completion);
|
||||||
|
if (!master->max_dma_len)
|
||||||
|
master->max_dma_len = INT_MAX;
|
||||||
|
|
||||||
/* register the device, then userspace will see it.
|
/* register the device, then userspace will see it.
|
||||||
* registration fails if the bus ID is in use.
|
* registration fails if the bus ID is in use.
|
||||||
|
@ -1597,6 +1785,9 @@ int spi_setup(struct spi_device *spi)
|
||||||
if (!spi->bits_per_word)
|
if (!spi->bits_per_word)
|
||||||
spi->bits_per_word = 8;
|
spi->bits_per_word = 8;
|
||||||
|
|
||||||
|
if (!spi->max_speed_hz)
|
||||||
|
spi->max_speed_hz = spi->master->max_speed_hz;
|
||||||
|
|
||||||
if (spi->master->setup)
|
if (spi->master->setup)
|
||||||
status = spi->master->setup(spi);
|
status = spi->master->setup(spi);
|
||||||
|
|
||||||
|
@ -1617,11 +1808,10 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
|
||||||
{
|
{
|
||||||
struct spi_master *master = spi->master;
|
struct spi_master *master = spi->master;
|
||||||
struct spi_transfer *xfer;
|
struct spi_transfer *xfer;
|
||||||
|
int w_size;
|
||||||
|
|
||||||
if (list_empty(&message->transfers))
|
if (list_empty(&message->transfers))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (!message->complete)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/* Half-duplex links include original MicroWire, and ones with
|
/* Half-duplex links include original MicroWire, and ones with
|
||||||
* only one data pin like SPI_3WIRE (switches direction) or where
|
* only one data pin like SPI_3WIRE (switches direction) or where
|
||||||
|
@ -1652,12 +1842,13 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
|
||||||
message->frame_length += xfer->len;
|
message->frame_length += xfer->len;
|
||||||
if (!xfer->bits_per_word)
|
if (!xfer->bits_per_word)
|
||||||
xfer->bits_per_word = spi->bits_per_word;
|
xfer->bits_per_word = spi->bits_per_word;
|
||||||
if (!xfer->speed_hz) {
|
|
||||||
|
if (!xfer->speed_hz)
|
||||||
xfer->speed_hz = spi->max_speed_hz;
|
xfer->speed_hz = spi->max_speed_hz;
|
||||||
if (master->max_speed_hz &&
|
|
||||||
xfer->speed_hz > master->max_speed_hz)
|
if (master->max_speed_hz &&
|
||||||
xfer->speed_hz = master->max_speed_hz;
|
xfer->speed_hz > master->max_speed_hz)
|
||||||
}
|
xfer->speed_hz = master->max_speed_hz;
|
||||||
|
|
||||||
if (master->bits_per_word_mask) {
|
if (master->bits_per_word_mask) {
|
||||||
/* Only 32 bits fit in the mask */
|
/* Only 32 bits fit in the mask */
|
||||||
|
@ -1668,12 +1859,24 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SPI transfer length should be multiple of SPI word size
|
||||||
|
* where SPI word size should be power-of-two multiple
|
||||||
|
*/
|
||||||
|
if (xfer->bits_per_word <= 8)
|
||||||
|
w_size = 1;
|
||||||
|
else if (xfer->bits_per_word <= 16)
|
||||||
|
w_size = 2;
|
||||||
|
else
|
||||||
|
w_size = 4;
|
||||||
|
|
||||||
|
/* No partial transfers accepted */
|
||||||
|
if (xfer->len % w_size)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (xfer->speed_hz && master->min_speed_hz &&
|
if (xfer->speed_hz && master->min_speed_hz &&
|
||||||
xfer->speed_hz < master->min_speed_hz)
|
xfer->speed_hz < master->min_speed_hz)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (xfer->speed_hz && master->max_speed_hz &&
|
|
||||||
xfer->speed_hz > master->max_speed_hz)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (xfer->tx_buf && !xfer->tx_nbits)
|
if (xfer->tx_buf && !xfer->tx_nbits)
|
||||||
xfer->tx_nbits = SPI_NBITS_SINGLE;
|
xfer->tx_nbits = SPI_NBITS_SINGLE;
|
||||||
|
|
|
@ -73,7 +73,8 @@ static DECLARE_BITMAP(minors, N_SPI_MINORS);
|
||||||
*/
|
*/
|
||||||
#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
|
#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
|
||||||
| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
|
| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
|
||||||
| SPI_NO_CS | SPI_READY)
|
| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \
|
||||||
|
| SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)
|
||||||
|
|
||||||
struct spidev_data {
|
struct spidev_data {
|
||||||
dev_t devt;
|
dev_t devt;
|
||||||
|
@ -265,6 +266,8 @@ static int spidev_message(struct spidev_data *spidev,
|
||||||
buf += k_tmp->len;
|
buf += k_tmp->len;
|
||||||
|
|
||||||
k_tmp->cs_change = !!u_tmp->cs_change;
|
k_tmp->cs_change = !!u_tmp->cs_change;
|
||||||
|
k_tmp->tx_nbits = u_tmp->tx_nbits;
|
||||||
|
k_tmp->rx_nbits = u_tmp->rx_nbits;
|
||||||
k_tmp->bits_per_word = u_tmp->bits_per_word;
|
k_tmp->bits_per_word = u_tmp->bits_per_word;
|
||||||
k_tmp->delay_usecs = u_tmp->delay_usecs;
|
k_tmp->delay_usecs = u_tmp->delay_usecs;
|
||||||
k_tmp->speed_hz = u_tmp->speed_hz;
|
k_tmp->speed_hz = u_tmp->speed_hz;
|
||||||
|
@ -359,6 +362,10 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
retval = __put_user(spi->mode & SPI_MODE_MASK,
|
retval = __put_user(spi->mode & SPI_MODE_MASK,
|
||||||
(__u8 __user *)arg);
|
(__u8 __user *)arg);
|
||||||
break;
|
break;
|
||||||
|
case SPI_IOC_RD_MODE32:
|
||||||
|
retval = __put_user(spi->mode & SPI_MODE_MASK,
|
||||||
|
(__u32 __user *)arg);
|
||||||
|
break;
|
||||||
case SPI_IOC_RD_LSB_FIRST:
|
case SPI_IOC_RD_LSB_FIRST:
|
||||||
retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
|
retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
|
||||||
(__u8 __user *)arg);
|
(__u8 __user *)arg);
|
||||||
|
@ -372,9 +379,13 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
|
|
||||||
/* write requests */
|
/* write requests */
|
||||||
case SPI_IOC_WR_MODE:
|
case SPI_IOC_WR_MODE:
|
||||||
retval = __get_user(tmp, (u8 __user *)arg);
|
case SPI_IOC_WR_MODE32:
|
||||||
|
if (cmd == SPI_IOC_WR_MODE)
|
||||||
|
retval = __get_user(tmp, (u8 __user *)arg);
|
||||||
|
else
|
||||||
|
retval = __get_user(tmp, (u32 __user *)arg);
|
||||||
if (retval == 0) {
|
if (retval == 0) {
|
||||||
u8 save = spi->mode;
|
u32 save = spi->mode;
|
||||||
|
|
||||||
if (tmp & ~SPI_MODE_MASK) {
|
if (tmp & ~SPI_MODE_MASK) {
|
||||||
retval = -EINVAL;
|
retval = -EINVAL;
|
||||||
|
@ -382,18 +393,18 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp |= spi->mode & ~SPI_MODE_MASK;
|
tmp |= spi->mode & ~SPI_MODE_MASK;
|
||||||
spi->mode = (u8)tmp;
|
spi->mode = (u16)tmp;
|
||||||
retval = spi_setup(spi);
|
retval = spi_setup(spi);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
spi->mode = save;
|
spi->mode = save;
|
||||||
else
|
else
|
||||||
dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
|
dev_dbg(&spi->dev, "spi mode %x\n", tmp);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SPI_IOC_WR_LSB_FIRST:
|
case SPI_IOC_WR_LSB_FIRST:
|
||||||
retval = __get_user(tmp, (__u8 __user *)arg);
|
retval = __get_user(tmp, (__u8 __user *)arg);
|
||||||
if (retval == 0) {
|
if (retval == 0) {
|
||||||
u8 save = spi->mode;
|
u32 save = spi->mode;
|
||||||
|
|
||||||
if (tmp)
|
if (tmp)
|
||||||
spi->mode |= SPI_LSB_FIRST;
|
spi->mode |= SPI_LSB_FIRST;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
/* linux/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h
|
/*
|
||||||
*
|
|
||||||
* Copyright (C) 2009 Samsung Electronics Ltd.
|
* Copyright (C) 2009 Samsung Electronics Ltd.
|
||||||
* Jaswinder Singh <jassi.brar@samsung.com>
|
* Jaswinder Singh <jassi.brar@samsung.com>
|
||||||
*
|
*
|
||||||
|
@ -8,8 +7,8 @@
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __S3C64XX_PLAT_SPI_H
|
#ifndef __SPI_S3C64XX_H
|
||||||
#define __S3C64XX_PLAT_SPI_H
|
#define __SPI_S3C64XX_H
|
||||||
|
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
|
|
||||||
|
@ -68,4 +67,4 @@ extern int s3c64xx_spi2_cfg_gpio(void);
|
||||||
extern struct s3c64xx_spi_info s3c64xx_spi0_pdata;
|
extern struct s3c64xx_spi_info s3c64xx_spi0_pdata;
|
||||||
extern struct s3c64xx_spi_info s3c64xx_spi1_pdata;
|
extern struct s3c64xx_spi_info s3c64xx_spi1_pdata;
|
||||||
extern struct s3c64xx_spi_info s3c64xx_spi2_pdata;
|
extern struct s3c64xx_spi_info s3c64xx_spi2_pdata;
|
||||||
#endif /* __S3C64XX_PLAT_SPI_H */
|
#endif /*__SPI_S3C64XX_H */
|
||||||
|
|
|
@ -26,9 +26,13 @@
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
extern int pm_generic_runtime_suspend(struct device *dev);
|
extern int pm_generic_runtime_suspend(struct device *dev);
|
||||||
extern int pm_generic_runtime_resume(struct device *dev);
|
extern int pm_generic_runtime_resume(struct device *dev);
|
||||||
|
extern int pm_runtime_force_suspend(struct device *dev);
|
||||||
|
extern int pm_runtime_force_resume(struct device *dev);
|
||||||
#else
|
#else
|
||||||
static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; }
|
static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; }
|
||||||
static inline int pm_generic_runtime_resume(struct device *dev) { return 0; }
|
static inline int pm_generic_runtime_resume(struct device *dev) { return 0; }
|
||||||
|
static inline int pm_runtime_force_suspend(struct device *dev) { return 0; }
|
||||||
|
static inline int pm_runtime_force_resume(struct device *dev) { return 0; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM_RUNTIME
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
|
|
@ -24,6 +24,9 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
|
#include <linux/scatterlist.h>
|
||||||
|
|
||||||
|
struct dma_chan;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* INTERFACES between SPI master-side drivers and SPI infrastructure.
|
* INTERFACES between SPI master-side drivers and SPI infrastructure.
|
||||||
|
@ -266,6 +269,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
||||||
* @auto_runtime_pm: the core should ensure a runtime PM reference is held
|
* @auto_runtime_pm: the core should ensure a runtime PM reference is held
|
||||||
* while the hardware is prepared, using the parent
|
* while the hardware is prepared, using the parent
|
||||||
* device for the spidev
|
* device for the spidev
|
||||||
|
* @max_dma_len: Maximum length of a DMA transfer for the device.
|
||||||
* @prepare_transfer_hardware: a message will soon arrive from the queue
|
* @prepare_transfer_hardware: a message will soon arrive from the queue
|
||||||
* so the subsystem requests the driver to prepare the transfer hardware
|
* so the subsystem requests the driver to prepare the transfer hardware
|
||||||
* by issuing this call
|
* by issuing this call
|
||||||
|
@ -348,6 +352,8 @@ struct spi_master {
|
||||||
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
|
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
|
||||||
#define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */
|
#define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */
|
||||||
#define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
|
#define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
|
||||||
|
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
|
||||||
|
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
|
||||||
|
|
||||||
/* lock and mutex for SPI bus locking */
|
/* lock and mutex for SPI bus locking */
|
||||||
spinlock_t bus_lock_spinlock;
|
spinlock_t bus_lock_spinlock;
|
||||||
|
@ -389,6 +395,17 @@ struct spi_master {
|
||||||
/* called on release() to free memory provided by spi_master */
|
/* called on release() to free memory provided by spi_master */
|
||||||
void (*cleanup)(struct spi_device *spi);
|
void (*cleanup)(struct spi_device *spi);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used to enable core support for DMA handling, if can_dma()
|
||||||
|
* exists and returns true then the transfer will be mapped
|
||||||
|
* prior to transfer_one() being called. The driver should
|
||||||
|
* not modify or store xfer and dma_tx and dma_rx must be set
|
||||||
|
* while the device is prepared.
|
||||||
|
*/
|
||||||
|
bool (*can_dma)(struct spi_master *master,
|
||||||
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These hooks are for drivers that want to use the generic
|
* These hooks are for drivers that want to use the generic
|
||||||
* master transfer queueing mechanism. If these are used, the
|
* master transfer queueing mechanism. If these are used, the
|
||||||
|
@ -407,7 +424,9 @@ struct spi_master {
|
||||||
bool rt;
|
bool rt;
|
||||||
bool auto_runtime_pm;
|
bool auto_runtime_pm;
|
||||||
bool cur_msg_prepared;
|
bool cur_msg_prepared;
|
||||||
|
bool cur_msg_mapped;
|
||||||
struct completion xfer_completion;
|
struct completion xfer_completion;
|
||||||
|
size_t max_dma_len;
|
||||||
|
|
||||||
int (*prepare_transfer_hardware)(struct spi_master *master);
|
int (*prepare_transfer_hardware)(struct spi_master *master);
|
||||||
int (*transfer_one_message)(struct spi_master *master,
|
int (*transfer_one_message)(struct spi_master *master,
|
||||||
|
@ -428,6 +447,14 @@ struct spi_master {
|
||||||
|
|
||||||
/* gpio chip select */
|
/* gpio chip select */
|
||||||
int *cs_gpios;
|
int *cs_gpios;
|
||||||
|
|
||||||
|
/* DMA channels for use with core dmaengine helpers */
|
||||||
|
struct dma_chan *dma_tx;
|
||||||
|
struct dma_chan *dma_rx;
|
||||||
|
|
||||||
|
/* dummy data for full duplex devices */
|
||||||
|
void *dummy_rx;
|
||||||
|
void *dummy_tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void *spi_master_get_devdata(struct spi_master *master)
|
static inline void *spi_master_get_devdata(struct spi_master *master)
|
||||||
|
@ -512,6 +539,8 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum);
|
||||||
* (optionally) changing the chipselect status, then starting
|
* (optionally) changing the chipselect status, then starting
|
||||||
* the next transfer or completing this @spi_message.
|
* the next transfer or completing this @spi_message.
|
||||||
* @transfer_list: transfers are sequenced through @spi_message.transfers
|
* @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
|
||||||
*
|
*
|
||||||
* SPI transfers always write the same number of bytes as they read.
|
* SPI transfers always write the same number of bytes as they read.
|
||||||
* Protocol drivers should always provide @rx_buf and/or @tx_buf.
|
* Protocol drivers should always provide @rx_buf and/or @tx_buf.
|
||||||
|
@ -579,6 +608,8 @@ struct spi_transfer {
|
||||||
|
|
||||||
dma_addr_t tx_dma;
|
dma_addr_t tx_dma;
|
||||||
dma_addr_t rx_dma;
|
dma_addr_t rx_dma;
|
||||||
|
struct sg_table tx_sg;
|
||||||
|
struct sg_table rx_sg;
|
||||||
|
|
||||||
unsigned cs_change:1;
|
unsigned cs_change:1;
|
||||||
unsigned tx_nbits:3;
|
unsigned tx_nbits:3;
|
||||||
|
|
|
@ -42,6 +42,6 @@ extern int spi_bitbang_setup_transfer(struct spi_device *spi,
|
||||||
|
|
||||||
/* start or stop queue processing */
|
/* start or stop queue processing */
|
||||||
extern int spi_bitbang_start(struct spi_bitbang *spi);
|
extern int spi_bitbang_start(struct spi_bitbang *spi);
|
||||||
extern int spi_bitbang_stop(struct spi_bitbang *spi);
|
extern void spi_bitbang_stop(struct spi_bitbang *spi);
|
||||||
|
|
||||||
#endif /* __SPI_BITBANG_H */
|
#endif /* __SPI_BITBANG_H */
|
||||||
|
|
|
@ -42,6 +42,10 @@
|
||||||
#define SPI_LOOP 0x20
|
#define SPI_LOOP 0x20
|
||||||
#define SPI_NO_CS 0x40
|
#define SPI_NO_CS 0x40
|
||||||
#define SPI_READY 0x80
|
#define SPI_READY 0x80
|
||||||
|
#define SPI_TX_DUAL 0x100
|
||||||
|
#define SPI_TX_QUAD 0x200
|
||||||
|
#define SPI_RX_DUAL 0x400
|
||||||
|
#define SPI_RX_QUAD 0x800
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
@ -92,7 +96,9 @@ struct spi_ioc_transfer {
|
||||||
__u16 delay_usecs;
|
__u16 delay_usecs;
|
||||||
__u8 bits_per_word;
|
__u8 bits_per_word;
|
||||||
__u8 cs_change;
|
__u8 cs_change;
|
||||||
__u32 pad;
|
__u8 tx_nbits;
|
||||||
|
__u8 rx_nbits;
|
||||||
|
__u16 pad;
|
||||||
|
|
||||||
/* If the contents of 'struct spi_ioc_transfer' ever change
|
/* If the contents of 'struct spi_ioc_transfer' ever change
|
||||||
* incompatibly, then the ioctl number (currently 0) must change;
|
* incompatibly, then the ioctl number (currently 0) must change;
|
||||||
|
@ -110,7 +116,7 @@ struct spi_ioc_transfer {
|
||||||
#define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
|
#define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
|
||||||
|
|
||||||
|
|
||||||
/* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) */
|
/* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) (limited to 8 bits) */
|
||||||
#define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, __u8)
|
#define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, __u8)
|
||||||
#define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, __u8)
|
#define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, __u8)
|
||||||
|
|
||||||
|
@ -126,6 +132,10 @@ struct spi_ioc_transfer {
|
||||||
#define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, __u32)
|
#define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, __u32)
|
||||||
#define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, __u32)
|
#define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, __u32)
|
||||||
|
|
||||||
|
/* Read / Write of the SPI mode field */
|
||||||
|
#define SPI_IOC_RD_MODE32 _IOR(SPI_IOC_MAGIC, 5, __u32)
|
||||||
|
#define SPI_IOC_WR_MODE32 _IOW(SPI_IOC_MAGIC, 5, __u32)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* SPIDEV_H */
|
#endif /* SPIDEV_H */
|
||||||
|
|
Loading…
Reference in New Issue