spi: Updates for v3.12
Business as usual for SPI - some new drivers, lots of fixes and updates to existing drivers plus some new framework features. Notable changes are: - Support for dual and quad data lines, commonly used by flash chips to improve performance, from Wang Yuhang. - Factored out a common pattern for runtime PM implementation into the core saving a bunch of code. - A particularly nice set of updates to the ep93xx driver from H Hartley Sweeten, modernising it and reducing the code size a lot. - New drivers for Blackfin v3, EFM32, Freescale DSPI and TI QSPI. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iQIcBAABAgAGBQJSI6U6AAoJELSic+t+oim9geIQAJf9k3PONDHF9ShFMpc8C77Z HbUmMnWZ/69uCylG0r0Yt3fItzL2JdBCCS0vNEKy7O/smRjDs2tLlUiBPMQWh/m5 AyGIKfXegQzarJ7c1ccQIIuIvmL+LKUmgV1P4Uh6X9f3rJ2pJD499aWjC7wLcACD hn2CC8idRRA7qDbwInRHRP1zYzwv1wwb96MOiNpm4ObbbxD4c8IV2d8zCvxNk/xn 9r8MP4/FCLIwV0Fj0xzAQjtgwzDpAsBhxBcKwO+wNSCfXPy6QqlmbgDfqiG0fN4i 9qIPADqhjt5mtBZ4ZINVWZeVKHjMJJLAtnD4Pfu05wwn/YX6p1ZcrPBAud0nGYE4 3xJWpGmprPltwuCRCVNJ6iQeHAWu07k4FV7naE9XIIgxtaURqlI2X9Q6PXboE/WW xntuDjFCLrOloowWw7QGOC1Cqk5cC722PsNAzuMqDzr63NSmgHc6HudMhEDZDKcy No7Ha2xNXn7Wp/WVR3+jfGXHPBhHAz7SewrCMD3N8d4Mla+mXmL2DFm6cIgfjk99 JNoAMPo0ud7uT0j3Y+TSDNiEbidTD3fSPaJsqxZ4HykyZnlYc88ZGZeY77l23DEX lMxG85qkO/EHRB4zi1yQ0qtrrItawGRTH+tfVV0JMa2y74p2GzGtV82Y9rew+eaw jZlmfzII6UtpmVc0f1uV =Wmbk -----END PGP SIGNATURE----- Merge tag 'spi-v3.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "Business as usual for SPI - some new drivers, lots of fixes and updates to existing drivers plus some new framework features. Notable changes are: - Support for dual and quad data lines, commonly used by flash chips to improve performance, from Wang Yuhang. - Factored out a common pattern for runtime PM implementation into the core saving a bunch of code. - A particularly nice set of updates to the ep93xx driver from H Hartley Sweeten, modernising it and reducing the code size a lot. - New drivers for Blackfin v3, EFM32, Freescale DSPI and TI QSPI" * tag 'spi-v3.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (133 commits) spi/qspi: fix missing unlock on error in ti_qspi_start_transfer_one() spi: quad: fix the name of DT property spi: core: Fix spi_register_master error handling spi: efm32: Fix build error spi: altera: Use DIV_ROUND_UP to calculate hw->bytes_per_word spi: rspi: Add spi_master_get() call to prevent use after free spi: quad: Make DT properties optional spi: quad: Fix missing return spi: Use dev_get_drvdata at appropriate places spi: use dev_get_platdata() spi: nuc900: Fix mode_bits setting spi: simplify devm_request_mem_region/devm_ioremap spi: altera: Simplify altera_spi_txrx implementation for noirq case spi: spi-rspi: fix inconsistent spin_lock_irqsave spi/qspi: Add compatible string for am4372. spi/qspi: Fix device table entry spi/sirf: fix the misunderstanding about len of spi_transfer spi/qspi: Add dual/quad spi read support spi: sirf: fix error return code in spi_sirfsoc_probe() spi: bcm2835: Add spi_master_get() call to prevent use after free ...
This commit is contained in:
commit
5d3fed701d
|
@ -0,0 +1,34 @@
|
|||
* Energy Micro EFM32 SPI
|
||||
|
||||
Required properties:
|
||||
- #address-cells: see spi-bus.txt
|
||||
- #size-cells: see spi-bus.txt
|
||||
- compatible: should be "efm32,spi"
|
||||
- reg: Offset and length of the register set for the controller
|
||||
- interrupts: pair specifying rx and tx irq
|
||||
- clocks: phandle to the spi clock
|
||||
- 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.
|
||||
|
||||
Example:
|
||||
|
||||
spi1: spi@0x4000c400 { /* USART1 */
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "efm32,spi";
|
||||
reg = <0x4000c400 0x400>;
|
||||
interrupts = <15 16>;
|
||||
clocks = <&cmu 20>;
|
||||
cs-gpios = <&gpio 51 1>; // D3
|
||||
location = <1>;
|
||||
status = "ok";
|
||||
|
||||
ks8851@0 {
|
||||
compatible = "ks8851";
|
||||
spi-max-frequency = <6000000>;
|
||||
reg = <0>;
|
||||
interrupt-parent = <&boardfpga>;
|
||||
interrupts = <4>;
|
||||
status = "ok";
|
||||
};
|
||||
};
|
|
@ -55,6 +55,16 @@ contain the following properties.
|
|||
chip select active high
|
||||
- spi-3wire - (optional) Empty property indicating device requires
|
||||
3-wire mode.
|
||||
- spi-tx-bus-width - (optional) The bus width(number of data wires) that
|
||||
used for MOSI. Defaults to 1 if not present.
|
||||
- spi-rx-bus-width - (optional) The bus width(number of data wires) that
|
||||
used for MISO. Defaults to 1 if not present.
|
||||
|
||||
Some SPI controllers and devices support Dual and Quad SPI transfer mode.
|
||||
It allows data in SPI system transfered in 2 wires(DUAL) or 4 wires(QUAD).
|
||||
Now the value that spi-tx-bus-width and spi-rx-bus-width can receive is
|
||||
only 1(SINGLE), 2(DUAL) and 4(QUAD).
|
||||
Dual/Quad mode is not allowed when 3-wire mode is used.
|
||||
|
||||
If a gpio chipselect is used for the SPI slave the gpio number will be passed
|
||||
via the cs_gpio
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
ARM Freescale DSPI controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "fsl,vf610-dspi"
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : Should contain SPI controller interrupt
|
||||
- clocks: from common clock binding: handle to dspi clock.
|
||||
- clock-names: from common clock binding: Shall be "dspi".
|
||||
- pinctrl-0: pin control group to be used for this controller.
|
||||
- pinctrl-names: must contain a "default" entry.
|
||||
- spi-num-chipselects : the number of the chipselect signals.
|
||||
- bus-num : the slave chip chipselect signal number.
|
||||
Example:
|
||||
|
||||
dspi0@4002c000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,vf610-dspi";
|
||||
reg = <0x4002c000 0x1000>;
|
||||
interrupts = <0 67 0x04>;
|
||||
clocks = <&clks VF610_CLK_DSPI0>;
|
||||
clock-names = "dspi";
|
||||
spi-num-chipselects = <5>;
|
||||
bus-num = <0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_dspi0_1>;
|
||||
status = "okay";
|
||||
|
||||
sflash: at26df081a@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "atmel,at26df081a";
|
||||
spi-max-frequency = <16000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
reg = <0>;
|
||||
linux,modalias = "m25p80";
|
||||
modal = "at26df081a";
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
TI QSPI controller.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "ti,dra7xxx-qspi" or "ti,am4372-qspi".
|
||||
- reg: Should contain QSPI registers location and length.
|
||||
- #address-cells, #size-cells : Must be present if the device has sub-nodes
|
||||
- ti,hwmods: Name of the hwmod associated to the QSPI
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Example:
|
||||
|
||||
qspi: qspi@4b300000 {
|
||||
compatible = "ti,dra7xxx-qspi";
|
||||
reg = <0x4b300000 0x100>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
spi-max-frequency = <25000000>;
|
||||
ti,hwmods = "qspi";
|
||||
};
|
|
@ -215,7 +215,7 @@ So for example arch/.../mach-*/board-*.c files might have code like:
|
|||
/* if your mach-* infrastructure doesn't support kernels that can
|
||||
* run on multiple boards, pdata wouldn't benefit from "__init".
|
||||
*/
|
||||
static struct mysoc_spi_data __initdata pdata = { ... };
|
||||
static struct mysoc_spi_data pdata __initdata = { ... };
|
||||
|
||||
static __init board_init(void)
|
||||
{
|
||||
|
|
|
@ -70,14 +70,14 @@ config SPI_ATH79
|
|||
|
||||
config SPI_ATMEL
|
||||
tristate "Atmel SPI Controller"
|
||||
depends on (ARCH_AT91 || AVR32)
|
||||
depends on (ARCH_AT91 || AVR32 || COMPILE_TEST)
|
||||
help
|
||||
This selects a driver for the Atmel SPI Controller, present on
|
||||
many AT32 (AVR32) and AT91 (ARM) chips.
|
||||
|
||||
config SPI_BCM2835
|
||||
tristate "BCM2835 SPI controller"
|
||||
depends on ARCH_BCM2835
|
||||
depends on ARCH_BCM2835 || COMPILE_TEST
|
||||
help
|
||||
This selects a driver for the Broadcom BCM2835 SPI master.
|
||||
|
||||
|
@ -88,10 +88,17 @@ config SPI_BCM2835
|
|||
|
||||
config SPI_BFIN5XX
|
||||
tristate "SPI controller driver for ADI Blackfin5xx"
|
||||
depends on BLACKFIN
|
||||
depends on BLACKFIN && !BF60x
|
||||
help
|
||||
This is the SPI controller master driver for Blackfin 5xx processor.
|
||||
|
||||
config SPI_BFIN_V3
|
||||
tristate "SPI controller v3 for Blackfin"
|
||||
depends on BF60x
|
||||
help
|
||||
This is the SPI controller v3 master driver
|
||||
found on Blackfin 60x processor.
|
||||
|
||||
config SPI_BFIN_SPORT
|
||||
tristate "SPI bus via Blackfin SPORT"
|
||||
depends on BLACKFIN
|
||||
|
@ -151,15 +158,22 @@ config SPI_COLDFIRE_QSPI
|
|||
|
||||
config SPI_DAVINCI
|
||||
tristate "Texas Instruments DaVinci/DA8x/OMAP-L/AM1x SoC SPI controller"
|
||||
depends on ARCH_DAVINCI
|
||||
depends on ARCH_DAVINCI || ARCH_KEYSTONE
|
||||
select SPI_BITBANG
|
||||
select TI_EDMA
|
||||
help
|
||||
SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules.
|
||||
|
||||
config SPI_EFM32
|
||||
tristate "EFM32 SPI controller"
|
||||
depends on OF && ARM && (ARCH_EFM32 || COMPILE_TEST)
|
||||
select SPI_BITBANG
|
||||
help
|
||||
Driver for the spi controller found on Energy Micro's EFM32 SoCs.
|
||||
|
||||
config SPI_EP93XX
|
||||
tristate "Cirrus Logic EP93xx SPI controller"
|
||||
depends on ARCH_EP93XX
|
||||
depends on ARCH_EP93XX || COMPILE_TEST
|
||||
help
|
||||
This enables using the Cirrus EP93xx SPI controller in master
|
||||
mode.
|
||||
|
@ -191,7 +205,7 @@ config SPI_GPIO
|
|||
|
||||
config SPI_IMX
|
||||
tristate "Freescale i.MX SPI controllers"
|
||||
depends on ARCH_MXC
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
select SPI_BITBANG
|
||||
default m if IMX_HAVE_PLATFORM_SPI_IMX
|
||||
help
|
||||
|
@ -248,6 +262,13 @@ config SPI_FSL_SPI
|
|||
This also enables using the Aeroflex Gaisler GRLIB SPI controller in
|
||||
master mode.
|
||||
|
||||
config SPI_FSL_DSPI
|
||||
tristate "Freescale DSPI controller"
|
||||
select SPI_BITBANG
|
||||
help
|
||||
This enables support for the Freescale DSPI controller in master
|
||||
mode. VF610 platform uses the controller.
|
||||
|
||||
config SPI_FSL_ESPI
|
||||
bool "Freescale eSPI controller"
|
||||
depends on FSL_SOC
|
||||
|
@ -280,20 +301,28 @@ config SPI_OMAP_UWIRE
|
|||
|
||||
config SPI_OMAP24XX
|
||||
tristate "McSPI driver for OMAP"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
help
|
||||
SPI master controller for OMAP24XX and later Multichannel SPI
|
||||
(McSPI) modules.
|
||||
|
||||
config SPI_TI_QSPI
|
||||
tristate "DRA7xxx QSPI controller support"
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
help
|
||||
QSPI master controller for DRA7xxx used for flash devices.
|
||||
This device supports single, dual and quad read support, while
|
||||
it only supports single write mode.
|
||||
|
||||
config SPI_OMAP_100K
|
||||
tristate "OMAP SPI 100K"
|
||||
depends on ARCH_OMAP850 || ARCH_OMAP730
|
||||
depends on ARCH_OMAP850 || ARCH_OMAP730 || COMPILE_TEST
|
||||
help
|
||||
OMAP SPI 100K master controller for omap7xx boards.
|
||||
|
||||
config SPI_ORION
|
||||
tristate "Orion SPI master"
|
||||
depends on PLAT_ORION
|
||||
depends on PLAT_ORION || COMPILE_TEST
|
||||
help
|
||||
This enables using the SPI master controller on the Orion chips.
|
||||
|
||||
|
@ -341,7 +370,7 @@ config SPI_PXA2XX_PCI
|
|||
|
||||
config SPI_RSPI
|
||||
tristate "Renesas RSPI controller"
|
||||
depends on SUPERH
|
||||
depends on SUPERH && SH_DMAE_BASE
|
||||
help
|
||||
SPI driver for Renesas RSPI blocks.
|
||||
|
||||
|
@ -385,7 +414,7 @@ config SPI_SH_MSIOF
|
|||
|
||||
config SPI_SH
|
||||
tristate "SuperH SPI controller"
|
||||
depends on SUPERH
|
||||
depends on SUPERH || COMPILE_TEST
|
||||
help
|
||||
SPI driver for SuperH SPI blocks.
|
||||
|
||||
|
@ -398,13 +427,13 @@ config SPI_SH_SCI
|
|||
|
||||
config SPI_SH_HSPI
|
||||
tristate "SuperH HSPI controller"
|
||||
depends on ARCH_SHMOBILE
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
help
|
||||
SPI driver for SuperH HSPI blocks.
|
||||
|
||||
config SPI_SIRF
|
||||
tristate "CSR SiRFprimaII SPI controller"
|
||||
depends on ARCH_SIRF
|
||||
depends on SIRF_DMA
|
||||
select SPI_BITBANG
|
||||
help
|
||||
SPI driver for CSR SiRFprimaII SoCs
|
||||
|
@ -418,7 +447,7 @@ config SPI_MXS
|
|||
|
||||
config SPI_TEGRA114
|
||||
tristate "NVIDIA Tegra114 SPI Controller"
|
||||
depends on ARCH_TEGRA && TEGRA20_APB_DMA
|
||||
depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
|
||||
help
|
||||
SPI driver for NVIDIA Tegra114 SPI Controller interface. This controller
|
||||
is different than the older SoCs SPI controller and also register interface
|
||||
|
@ -426,7 +455,7 @@ config SPI_TEGRA114
|
|||
|
||||
config SPI_TEGRA20_SFLASH
|
||||
tristate "Nvidia Tegra20 Serial flash Controller"
|
||||
depends on ARCH_TEGRA
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
help
|
||||
SPI driver for Nvidia Tegra20 Serial flash Controller interface.
|
||||
The main usecase of this controller is to use spi flash as boot
|
||||
|
@ -434,7 +463,7 @@ config SPI_TEGRA20_SFLASH
|
|||
|
||||
config SPI_TEGRA20_SLINK
|
||||
tristate "Nvidia Tegra20/Tegra30 SLINK Controller"
|
||||
depends on ARCH_TEGRA && TEGRA20_APB_DMA
|
||||
depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
|
||||
help
|
||||
SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface.
|
||||
|
||||
|
@ -457,7 +486,7 @@ config SPI_TOPCLIFF_PCH
|
|||
|
||||
config SPI_TXX9
|
||||
tristate "Toshiba TXx9 SPI controller"
|
||||
depends on GPIOLIB && CPU_TX49XX
|
||||
depends on GPIOLIB && (CPU_TX49XX || COMPILE_TEST)
|
||||
help
|
||||
SPI driver for Toshiba TXx9 MIPS SoCs
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
|
|||
obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o
|
||||
obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
|
||||
obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o
|
||||
obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o
|
||||
obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
|
||||
obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
|
||||
obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
|
||||
|
@ -27,9 +28,11 @@ obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o
|
|||
obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o
|
||||
obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o
|
||||
spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o
|
||||
obj-$(CONFIG_SPI_EFM32) += spi-efm32.o
|
||||
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
|
||||
obj-$(CONFIG_SPI_FALCON) += spi-falcon.o
|
||||
obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o
|
||||
obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o
|
||||
obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o
|
||||
obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o
|
||||
obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
|
||||
|
@ -46,6 +49,7 @@ obj-$(CONFIG_SPI_OCTEON) += spi-octeon.o
|
|||
obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o
|
||||
obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o
|
||||
obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o
|
||||
obj-$(CONFIG_SPI_TI_QSPI) += spi-ti-qspi.o
|
||||
obj-$(CONFIG_SPI_ORION) += spi-orion.o
|
||||
obj-$(CONFIG_SPI_PL022) += spi-pl022.o
|
||||
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
|
||||
|
|
|
@ -103,16 +103,6 @@ static void altera_spi_chipsel(struct spi_device *spi, int value)
|
|||
}
|
||||
}
|
||||
|
||||
static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int altera_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned int hw_txbyte(struct altera_spi *hw, int count)
|
||||
{
|
||||
if (hw->tx) {
|
||||
|
@ -134,7 +124,7 @@ static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
|||
hw->tx = t->tx_buf;
|
||||
hw->rx = t->rx_buf;
|
||||
hw->count = 0;
|
||||
hw->bytes_per_word = t->bits_per_word / 8;
|
||||
hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8);
|
||||
hw->len = t->len / hw->bytes_per_word;
|
||||
|
||||
if (hw->irq >= 0) {
|
||||
|
@ -150,12 +140,12 @@ static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
|||
hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
} else {
|
||||
/* send the first byte */
|
||||
writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA);
|
||||
|
||||
while (1) {
|
||||
while (hw->count < hw->len) {
|
||||
unsigned int rxd;
|
||||
|
||||
writel(hw_txbyte(hw, hw->count),
|
||||
hw->base + ALTERA_SPI_TXDATA);
|
||||
|
||||
while (!(readl(hw->base + ALTERA_SPI_STATUS) &
|
||||
ALTERA_SPI_STATUS_RRDY_MSK))
|
||||
cpu_relax();
|
||||
|
@ -174,14 +164,7 @@ static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
|
|||
}
|
||||
|
||||
hw->count++;
|
||||
|
||||
if (hw->count < hw->len)
|
||||
writel(hw_txbyte(hw, hw->count),
|
||||
hw->base + ALTERA_SPI_TXDATA);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return hw->count * hw->bytes_per_word;
|
||||
|
@ -217,7 +200,7 @@ static irqreturn_t altera_spi_irq(int irq, void *dev)
|
|||
|
||||
static int altera_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct altera_spi_platform_data *platp = pdev->dev.platform_data;
|
||||
struct altera_spi_platform_data *platp = dev_get_platdata(&pdev->dev);
|
||||
struct altera_spi *hw;
|
||||
struct spi_master *master;
|
||||
struct resource *res;
|
||||
|
@ -231,7 +214,6 @@ static int altera_spi_probe(struct platform_device *pdev)
|
|||
master->bus_num = pdev->id;
|
||||
master->num_chipselect = 16;
|
||||
master->mode_bits = SPI_CS_HIGH;
|
||||
master->setup = altera_spi_setup;
|
||||
|
||||
hw = spi_master_get_devdata(master);
|
||||
platform_set_drvdata(pdev, hw);
|
||||
|
@ -240,21 +222,16 @@ static int altera_spi_probe(struct platform_device *pdev)
|
|||
hw->bitbang.master = spi_master_get(master);
|
||||
if (!hw->bitbang.master)
|
||||
return err;
|
||||
hw->bitbang.setup_transfer = altera_spi_setupxfer;
|
||||
hw->bitbang.chipselect = altera_spi_chipsel;
|
||||
hw->bitbang.txrx_bufs = altera_spi_txrx;
|
||||
|
||||
/* find and map our resources */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
goto exit_busy;
|
||||
if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
|
||||
pdev->name))
|
||||
goto exit_busy;
|
||||
hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!hw->base)
|
||||
goto exit_busy;
|
||||
hw->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hw->base)) {
|
||||
err = PTR_ERR(hw->base);
|
||||
goto exit;
|
||||
}
|
||||
/* program defaults into the registers */
|
||||
hw->imr = 0; /* disable spi interrupts */
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
|
@ -281,9 +258,6 @@ static int altera_spi_probe(struct platform_device *pdev)
|
|||
dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_busy:
|
||||
err = -EBUSY;
|
||||
exit:
|
||||
spi_master_put(master);
|
||||
return err;
|
||||
|
|
|
@ -221,7 +221,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
|
|||
sp = spi_master_get_devdata(master);
|
||||
platform_set_drvdata(pdev, sp);
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
|
||||
master->setup = ath79_spi_setup;
|
||||
|
|
|
@ -360,12 +360,12 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
|
|||
gpio_set_value(asd->npcs_pin, !active);
|
||||
}
|
||||
|
||||
static void atmel_spi_lock(struct atmel_spi *as)
|
||||
static void atmel_spi_lock(struct atmel_spi *as) __acquires(&as->lock)
|
||||
{
|
||||
spin_lock_irqsave(&as->lock, as->flags);
|
||||
}
|
||||
|
||||
static void atmel_spi_unlock(struct atmel_spi *as)
|
||||
static void atmel_spi_unlock(struct atmel_spi *as) __releases(&as->lock)
|
||||
{
|
||||
spin_unlock_irqrestore(&as->lock, as->flags);
|
||||
}
|
||||
|
@ -629,9 +629,9 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master,
|
|||
goto err_dma;
|
||||
|
||||
dev_dbg(master->dev.parent,
|
||||
" start dma xfer %p: len %u tx %p/%08x rx %p/%08x\n",
|
||||
xfer, xfer->len, xfer->tx_buf, xfer->tx_dma,
|
||||
xfer->rx_buf, xfer->rx_dma);
|
||||
" start dma xfer %p: len %u tx %p/%08llx rx %p/%08llx\n",
|
||||
xfer, xfer->len, xfer->tx_buf, (unsigned long long)xfer->tx_dma,
|
||||
xfer->rx_buf, (unsigned long long)xfer->rx_dma);
|
||||
|
||||
/* Enable relevant interrupts */
|
||||
spi_writel(as, IER, SPI_BIT(OVRES));
|
||||
|
@ -732,9 +732,10 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master,
|
|||
spi_writel(as, TCR, len);
|
||||
|
||||
dev_dbg(&msg->spi->dev,
|
||||
" start xfer %p: len %u tx %p/%08x rx %p/%08x\n",
|
||||
xfer, xfer->len, xfer->tx_buf, xfer->tx_dma,
|
||||
xfer->rx_buf, xfer->rx_dma);
|
||||
" start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n",
|
||||
xfer, xfer->len, xfer->tx_buf,
|
||||
(unsigned long long)xfer->tx_dma, xfer->rx_buf,
|
||||
(unsigned long long)xfer->rx_dma);
|
||||
} else {
|
||||
xfer = as->next_transfer;
|
||||
remaining = as->next_remaining_bytes;
|
||||
|
@ -771,9 +772,10 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master,
|
|||
spi_writel(as, TNCR, len);
|
||||
|
||||
dev_dbg(&msg->spi->dev,
|
||||
" next xfer %p: len %u tx %p/%08x rx %p/%08x\n",
|
||||
xfer, xfer->len, xfer->tx_buf, xfer->tx_dma,
|
||||
xfer->rx_buf, xfer->rx_dma);
|
||||
" next xfer %p: len %u tx %p/%08llx rx %p/%08llx\n",
|
||||
xfer, xfer->len, xfer->tx_buf,
|
||||
(unsigned long long)xfer->tx_dma, xfer->rx_buf,
|
||||
(unsigned long long)xfer->rx_dma);
|
||||
ieval = SPI_BIT(ENDRX) | SPI_BIT(OVRES);
|
||||
} else {
|
||||
spi_writel(as, RNCR, 0);
|
||||
|
@ -1579,7 +1581,9 @@ static int atmel_spi_probe(struct platform_device *pdev)
|
|||
goto out_unmap_regs;
|
||||
|
||||
/* Initialize the hardware */
|
||||
clk_enable(clk);
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
goto out_unmap_regs;
|
||||
spi_writel(as, CR, SPI_BIT(SWRST));
|
||||
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
|
||||
if (as->caps.has_wdrbt) {
|
||||
|
@ -1609,7 +1613,7 @@ out_free_dma:
|
|||
|
||||
spi_writel(as, CR, SPI_BIT(SWRST));
|
||||
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
|
||||
clk_disable(clk);
|
||||
clk_disable_unprepare(clk);
|
||||
free_irq(irq, master);
|
||||
out_unmap_regs:
|
||||
iounmap(as->regs);
|
||||
|
@ -1661,7 +1665,7 @@ static int atmel_spi_remove(struct platform_device *pdev)
|
|||
dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
|
||||
as->buffer_dma);
|
||||
|
||||
clk_disable(as->clk);
|
||||
clk_disable_unprepare(as->clk);
|
||||
clk_put(as->clk);
|
||||
free_irq(as->irq, master);
|
||||
iounmap(as->regs);
|
||||
|
@ -1678,7 +1682,7 @@ static int atmel_spi_suspend(struct platform_device *pdev, pm_message_t mesg)
|
|||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct atmel_spi *as = spi_master_get_devdata(master);
|
||||
|
||||
clk_disable(as->clk);
|
||||
clk_disable_unprepare(as->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1687,7 +1691,7 @@ static int atmel_spi_resume(struct platform_device *pdev)
|
|||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct atmel_spi *as = spi_master_get_devdata(master);
|
||||
|
||||
clk_enable(as->clk);
|
||||
return clk_prepare_enable(as->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -776,7 +776,7 @@ static int au1550_spi_probe(struct platform_device *pdev)
|
|||
hw = spi_master_get_devdata(master);
|
||||
|
||||
hw->master = spi_master_get(master);
|
||||
hw->pdata = pdev->dev.platform_data;
|
||||
hw->pdata = dev_get_platdata(&pdev->dev);
|
||||
hw->dev = &pdev->dev;
|
||||
|
||||
if (hw->pdata == NULL) {
|
||||
|
|
|
@ -314,7 +314,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, master);
|
||||
|
||||
master->mode_bits = BCM2835_SPI_MODE_BITS;
|
||||
master->bits_per_word_mask = BIT(8 - 1);
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
master->bus_num = -1;
|
||||
master->num_chipselect = 3;
|
||||
master->transfer_one_message = bcm2835_spi_transfer_one;
|
||||
|
@ -325,12 +325,6 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
|
|||
init_completion(&bs->done);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "could not get memory resource\n");
|
||||
err = -ENODEV;
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
bs->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(bs->regs)) {
|
||||
err = PTR_ERR(bs->regs);
|
||||
|
@ -383,7 +377,7 @@ out_master_put:
|
|||
|
||||
static int bcm2835_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
|
||||
free_irq(bs->irq, master);
|
||||
|
|
|
@ -231,24 +231,6 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bcm63xx_spi_prepare_transfer(struct spi_master *master)
|
||||
{
|
||||
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_get_sync(&bs->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm63xx_spi_unprepare_transfer(struct spi_master *master)
|
||||
{
|
||||
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_put(&bs->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm63xx_spi_transfer_one(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
|
@ -353,20 +335,13 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct resource *r;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data;
|
||||
struct bcm63xx_spi_pdata *pdata = dev_get_platdata(&pdev->dev);
|
||||
int irq;
|
||||
struct spi_master *master;
|
||||
struct clk *clk;
|
||||
struct bcm63xx_spi *bs;
|
||||
int ret;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
dev_err(dev, "no iomem\n");
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no irq\n");
|
||||
|
@ -393,6 +368,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, master);
|
||||
bs->pdev = pdev;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
bs->regs = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(bs->regs)) {
|
||||
ret = PTR_ERR(bs->regs);
|
||||
|
@ -412,11 +388,10 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
master->bus_num = pdata->bus_num;
|
||||
master->num_chipselect = pdata->num_chipselect;
|
||||
master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer;
|
||||
master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer;
|
||||
master->transfer_one_message = bcm63xx_spi_transfer_one;
|
||||
master->mode_bits = MODEBITS;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
master->auto_runtime_pm = true;
|
||||
bs->msg_type_shift = pdata->msg_type_shift;
|
||||
bs->msg_ctl_width = pdata->msg_ctl_width;
|
||||
bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA));
|
||||
|
@ -480,8 +455,7 @@ static int bcm63xx_spi_remove(struct platform_device *pdev)
|
|||
#ifdef CONFIG_PM
|
||||
static int bcm63xx_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
|
||||
|
||||
spi_master_suspend(master);
|
||||
|
@ -493,8 +467,7 @@ static int bcm63xx_spi_suspend(struct device *dev)
|
|||
|
||||
static int bcm63xx_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
|
||||
|
||||
clk_prepare_enable(bs->clk);
|
||||
|
|
|
@ -756,7 +756,7 @@ static int bfin_sport_spi_probe(struct platform_device *pdev)
|
|||
struct bfin_sport_spi_master_data *drv_data;
|
||||
int status;
|
||||
|
||||
platform_info = dev->platform_data;
|
||||
platform_info = dev_get_platdata(dev);
|
||||
|
||||
/* Allocate master with space for drv_data */
|
||||
master = spi_alloc_master(dev, sizeof(*master) + 16);
|
||||
|
|
|
@ -0,0 +1,965 @@
|
|||
/*
|
||||
* Analog Devices SPI3 controller driver
|
||||
*
|
||||
* Copyright (c) 2013 Analog Devices 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.
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/bfin_spi3.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/portmux.h>
|
||||
|
||||
enum bfin_spi_state {
|
||||
START_STATE,
|
||||
RUNNING_STATE,
|
||||
DONE_STATE,
|
||||
ERROR_STATE
|
||||
};
|
||||
|
||||
struct bfin_spi_master;
|
||||
|
||||
struct bfin_spi_transfer_ops {
|
||||
void (*write) (struct bfin_spi_master *);
|
||||
void (*read) (struct bfin_spi_master *);
|
||||
void (*duplex) (struct bfin_spi_master *);
|
||||
};
|
||||
|
||||
/* runtime info for spi master */
|
||||
struct bfin_spi_master {
|
||||
/* SPI framework hookup */
|
||||
struct spi_master *master;
|
||||
|
||||
/* Regs base of SPI controller */
|
||||
struct bfin_spi_regs __iomem *regs;
|
||||
|
||||
/* Pin request list */
|
||||
u16 *pin_req;
|
||||
|
||||
/* Message Transfer pump */
|
||||
struct tasklet_struct pump_transfers;
|
||||
|
||||
/* Current message transfer state info */
|
||||
struct spi_message *cur_msg;
|
||||
struct spi_transfer *cur_transfer;
|
||||
struct bfin_spi_device *cur_chip;
|
||||
unsigned transfer_len;
|
||||
|
||||
/* transfer buffer */
|
||||
void *tx;
|
||||
void *tx_end;
|
||||
void *rx;
|
||||
void *rx_end;
|
||||
|
||||
/* dma info */
|
||||
unsigned int tx_dma;
|
||||
unsigned int rx_dma;
|
||||
dma_addr_t tx_dma_addr;
|
||||
dma_addr_t rx_dma_addr;
|
||||
unsigned long dummy_buffer; /* used in unidirectional transfer */
|
||||
unsigned long tx_dma_size;
|
||||
unsigned long rx_dma_size;
|
||||
int tx_num;
|
||||
int rx_num;
|
||||
|
||||
/* store register value for suspend/resume */
|
||||
u32 control;
|
||||
u32 ssel;
|
||||
|
||||
unsigned long sclk;
|
||||
enum bfin_spi_state state;
|
||||
|
||||
const struct bfin_spi_transfer_ops *ops;
|
||||
};
|
||||
|
||||
struct bfin_spi_device {
|
||||
u32 control;
|
||||
u32 clock;
|
||||
u32 ssel;
|
||||
|
||||
u8 cs;
|
||||
u16 cs_chg_udelay; /* Some devices require > 255usec delay */
|
||||
u32 cs_gpio;
|
||||
u32 tx_dummy_val; /* tx value for rx only transfer */
|
||||
bool enable_dma;
|
||||
const struct bfin_spi_transfer_ops *ops;
|
||||
};
|
||||
|
||||
static void bfin_spi_enable(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
bfin_write_or(&drv_data->regs->control, SPI_CTL_EN);
|
||||
}
|
||||
|
||||
static void bfin_spi_disable(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
bfin_write_and(&drv_data->regs->control, ~SPI_CTL_EN);
|
||||
}
|
||||
|
||||
/* Caculate the SPI_CLOCK register value based on input HZ */
|
||||
static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz)
|
||||
{
|
||||
u32 spi_clock = sclk / speed_hz;
|
||||
|
||||
if (spi_clock)
|
||||
spi_clock--;
|
||||
return spi_clock;
|
||||
}
|
||||
|
||||
static int bfin_spi_flush(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
unsigned long limit = loops_per_jiffy << 1;
|
||||
|
||||
/* wait for stop and clear stat */
|
||||
while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_SPIF) && --limit)
|
||||
cpu_relax();
|
||||
|
||||
bfin_write(&drv_data->regs->status, 0xFFFFFFFF);
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
||||
/* Chip select operation functions for cs_change flag */
|
||||
static void bfin_spi_cs_active(struct bfin_spi_master *drv_data, struct bfin_spi_device *chip)
|
||||
{
|
||||
if (likely(chip->cs < MAX_CTRL_CS))
|
||||
bfin_write_and(&drv_data->regs->ssel, ~chip->ssel);
|
||||
else
|
||||
gpio_set_value(chip->cs_gpio, 0);
|
||||
}
|
||||
|
||||
static void bfin_spi_cs_deactive(struct bfin_spi_master *drv_data,
|
||||
struct bfin_spi_device *chip)
|
||||
{
|
||||
if (likely(chip->cs < MAX_CTRL_CS))
|
||||
bfin_write_or(&drv_data->regs->ssel, chip->ssel);
|
||||
else
|
||||
gpio_set_value(chip->cs_gpio, 1);
|
||||
|
||||
/* Move delay here for consistency */
|
||||
if (chip->cs_chg_udelay)
|
||||
udelay(chip->cs_chg_udelay);
|
||||
}
|
||||
|
||||
/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */
|
||||
static inline void bfin_spi_cs_enable(struct bfin_spi_master *drv_data,
|
||||
struct bfin_spi_device *chip)
|
||||
{
|
||||
if (chip->cs < MAX_CTRL_CS)
|
||||
bfin_write_or(&drv_data->regs->ssel, chip->ssel >> 8);
|
||||
}
|
||||
|
||||
static inline void bfin_spi_cs_disable(struct bfin_spi_master *drv_data,
|
||||
struct bfin_spi_device *chip)
|
||||
{
|
||||
if (chip->cs < MAX_CTRL_CS)
|
||||
bfin_write_and(&drv_data->regs->ssel, ~(chip->ssel >> 8));
|
||||
}
|
||||
|
||||
/* stop controller and re-config current chip*/
|
||||
static void bfin_spi_restore_state(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
struct bfin_spi_device *chip = drv_data->cur_chip;
|
||||
|
||||
/* Clear status and disable clock */
|
||||
bfin_write(&drv_data->regs->status, 0xFFFFFFFF);
|
||||
bfin_write(&drv_data->regs->rx_control, 0x0);
|
||||
bfin_write(&drv_data->regs->tx_control, 0x0);
|
||||
bfin_spi_disable(drv_data);
|
||||
|
||||
SSYNC();
|
||||
|
||||
/* Load the registers */
|
||||
bfin_write(&drv_data->regs->control, chip->control);
|
||||
bfin_write(&drv_data->regs->clock, chip->clock);
|
||||
|
||||
bfin_spi_enable(drv_data);
|
||||
drv_data->tx_num = drv_data->rx_num = 0;
|
||||
/* we always choose tx transfer initiate */
|
||||
bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN);
|
||||
bfin_write(&drv_data->regs->tx_control,
|
||||
SPI_TXCTL_TEN | SPI_TXCTL_TTI);
|
||||
bfin_spi_cs_active(drv_data, chip);
|
||||
}
|
||||
|
||||
/* discard invalid rx data and empty rfifo */
|
||||
static inline void dummy_read(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_RFE))
|
||||
bfin_read(&drv_data->regs->rfifo);
|
||||
}
|
||||
|
||||
static void bfin_spi_u8_write(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
dummy_read(drv_data);
|
||||
while (drv_data->tx < drv_data->tx_end) {
|
||||
bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++)));
|
||||
while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
|
||||
cpu_relax();
|
||||
bfin_read(&drv_data->regs->rfifo);
|
||||
}
|
||||
}
|
||||
|
||||
static void bfin_spi_u8_read(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
u32 tx_val = drv_data->cur_chip->tx_dummy_val;
|
||||
|
||||
dummy_read(drv_data);
|
||||
while (drv_data->rx < drv_data->rx_end) {
|
||||
bfin_write(&drv_data->regs->tfifo, tx_val);
|
||||
while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
|
||||
cpu_relax();
|
||||
*(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo);
|
||||
}
|
||||
}
|
||||
|
||||
static void bfin_spi_u8_duplex(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
dummy_read(drv_data);
|
||||
while (drv_data->rx < drv_data->rx_end) {
|
||||
bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++)));
|
||||
while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
|
||||
cpu_relax();
|
||||
*(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u8 = {
|
||||
.write = bfin_spi_u8_write,
|
||||
.read = bfin_spi_u8_read,
|
||||
.duplex = bfin_spi_u8_duplex,
|
||||
};
|
||||
|
||||
static void bfin_spi_u16_write(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
dummy_read(drv_data);
|
||||
while (drv_data->tx < drv_data->tx_end) {
|
||||
bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx));
|
||||
drv_data->tx += 2;
|
||||
while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
|
||||
cpu_relax();
|
||||
bfin_read(&drv_data->regs->rfifo);
|
||||
}
|
||||
}
|
||||
|
||||
static void bfin_spi_u16_read(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
u32 tx_val = drv_data->cur_chip->tx_dummy_val;
|
||||
|
||||
dummy_read(drv_data);
|
||||
while (drv_data->rx < drv_data->rx_end) {
|
||||
bfin_write(&drv_data->regs->tfifo, tx_val);
|
||||
while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
|
||||
cpu_relax();
|
||||
*(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo);
|
||||
drv_data->rx += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void bfin_spi_u16_duplex(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
dummy_read(drv_data);
|
||||
while (drv_data->rx < drv_data->rx_end) {
|
||||
bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx));
|
||||
drv_data->tx += 2;
|
||||
while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
|
||||
cpu_relax();
|
||||
*(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo);
|
||||
drv_data->rx += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u16 = {
|
||||
.write = bfin_spi_u16_write,
|
||||
.read = bfin_spi_u16_read,
|
||||
.duplex = bfin_spi_u16_duplex,
|
||||
};
|
||||
|
||||
static void bfin_spi_u32_write(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
dummy_read(drv_data);
|
||||
while (drv_data->tx < drv_data->tx_end) {
|
||||
bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx));
|
||||
drv_data->tx += 4;
|
||||
while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
|
||||
cpu_relax();
|
||||
bfin_read(&drv_data->regs->rfifo);
|
||||
}
|
||||
}
|
||||
|
||||
static void bfin_spi_u32_read(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
u32 tx_val = drv_data->cur_chip->tx_dummy_val;
|
||||
|
||||
dummy_read(drv_data);
|
||||
while (drv_data->rx < drv_data->rx_end) {
|
||||
bfin_write(&drv_data->regs->tfifo, tx_val);
|
||||
while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
|
||||
cpu_relax();
|
||||
*(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo);
|
||||
drv_data->rx += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void bfin_spi_u32_duplex(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
dummy_read(drv_data);
|
||||
while (drv_data->rx < drv_data->rx_end) {
|
||||
bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx));
|
||||
drv_data->tx += 4;
|
||||
while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
|
||||
cpu_relax();
|
||||
*(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo);
|
||||
drv_data->rx += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u32 = {
|
||||
.write = bfin_spi_u32_write,
|
||||
.read = bfin_spi_u32_read,
|
||||
.duplex = bfin_spi_u32_duplex,
|
||||
};
|
||||
|
||||
|
||||
/* test if there is more transfer to be done */
|
||||
static void bfin_spi_next_transfer(struct bfin_spi_master *drv)
|
||||
{
|
||||
struct spi_message *msg = drv->cur_msg;
|
||||
struct spi_transfer *t = drv->cur_transfer;
|
||||
|
||||
/* Move to next transfer */
|
||||
if (t->transfer_list.next != &msg->transfers) {
|
||||
drv->cur_transfer = list_entry(t->transfer_list.next,
|
||||
struct spi_transfer, transfer_list);
|
||||
drv->state = RUNNING_STATE;
|
||||
} else {
|
||||
drv->state = DONE_STATE;
|
||||
drv->cur_transfer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void bfin_spi_giveback(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
struct bfin_spi_device *chip = drv_data->cur_chip;
|
||||
|
||||
bfin_spi_cs_deactive(drv_data, chip);
|
||||
spi_finalize_current_message(drv_data->master);
|
||||
}
|
||||
|
||||
static int bfin_spi_setup_transfer(struct bfin_spi_master *drv)
|
||||
{
|
||||
struct spi_transfer *t = drv->cur_transfer;
|
||||
u32 cr, cr_width;
|
||||
|
||||
if (t->tx_buf) {
|
||||
drv->tx = (void *)t->tx_buf;
|
||||
drv->tx_end = drv->tx + t->len;
|
||||
} else {
|
||||
drv->tx = NULL;
|
||||
}
|
||||
|
||||
if (t->rx_buf) {
|
||||
drv->rx = t->rx_buf;
|
||||
drv->rx_end = drv->rx + t->len;
|
||||
} else {
|
||||
drv->rx = NULL;
|
||||
}
|
||||
|
||||
drv->transfer_len = t->len;
|
||||
|
||||
/* bits per word setup */
|
||||
switch (t->bits_per_word) {
|
||||
case 8:
|
||||
cr_width = SPI_CTL_SIZE08;
|
||||
drv->ops = &bfin_bfin_spi_transfer_ops_u8;
|
||||
break;
|
||||
case 16:
|
||||
cr_width = SPI_CTL_SIZE16;
|
||||
drv->ops = &bfin_bfin_spi_transfer_ops_u16;
|
||||
break;
|
||||
case 32:
|
||||
cr_width = SPI_CTL_SIZE32;
|
||||
drv->ops = &bfin_bfin_spi_transfer_ops_u32;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
cr = bfin_read(&drv->regs->control) & ~SPI_CTL_SIZE;
|
||||
cr |= cr_width;
|
||||
bfin_write(&drv->regs->control, cr);
|
||||
|
||||
/* speed setup */
|
||||
bfin_write(&drv->regs->clock,
|
||||
hz_to_spi_clock(drv->sclk, t->speed_hz));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bfin_spi_dma_xfer(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
struct spi_transfer *t = drv_data->cur_transfer;
|
||||
struct spi_message *msg = drv_data->cur_msg;
|
||||
struct bfin_spi_device *chip = drv_data->cur_chip;
|
||||
u32 dma_config;
|
||||
unsigned long word_count, word_size;
|
||||
void *tx_buf, *rx_buf;
|
||||
|
||||
switch (t->bits_per_word) {
|
||||
case 8:
|
||||
dma_config = WDSIZE_8 | PSIZE_8;
|
||||
word_count = drv_data->transfer_len;
|
||||
word_size = 1;
|
||||
break;
|
||||
case 16:
|
||||
dma_config = WDSIZE_16 | PSIZE_16;
|
||||
word_count = drv_data->transfer_len / 2;
|
||||
word_size = 2;
|
||||
break;
|
||||
default:
|
||||
dma_config = WDSIZE_32 | PSIZE_32;
|
||||
word_count = drv_data->transfer_len / 4;
|
||||
word_size = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!drv_data->rx) {
|
||||
tx_buf = drv_data->tx;
|
||||
rx_buf = &drv_data->dummy_buffer;
|
||||
drv_data->tx_dma_size = drv_data->transfer_len;
|
||||
drv_data->rx_dma_size = sizeof(drv_data->dummy_buffer);
|
||||
set_dma_x_modify(drv_data->tx_dma, word_size);
|
||||
set_dma_x_modify(drv_data->rx_dma, 0);
|
||||
} else if (!drv_data->tx) {
|
||||
drv_data->dummy_buffer = chip->tx_dummy_val;
|
||||
tx_buf = &drv_data->dummy_buffer;
|
||||
rx_buf = drv_data->rx;
|
||||
drv_data->tx_dma_size = sizeof(drv_data->dummy_buffer);
|
||||
drv_data->rx_dma_size = drv_data->transfer_len;
|
||||
set_dma_x_modify(drv_data->tx_dma, 0);
|
||||
set_dma_x_modify(drv_data->rx_dma, word_size);
|
||||
} else {
|
||||
tx_buf = drv_data->tx;
|
||||
rx_buf = drv_data->rx;
|
||||
drv_data->tx_dma_size = drv_data->rx_dma_size
|
||||
= drv_data->transfer_len;
|
||||
set_dma_x_modify(drv_data->tx_dma, word_size);
|
||||
set_dma_x_modify(drv_data->rx_dma, word_size);
|
||||
}
|
||||
|
||||
drv_data->tx_dma_addr = dma_map_single(&msg->spi->dev,
|
||||
(void *)tx_buf,
|
||||
drv_data->tx_dma_size,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(&msg->spi->dev,
|
||||
drv_data->tx_dma_addr))
|
||||
return -ENOMEM;
|
||||
|
||||
drv_data->rx_dma_addr = dma_map_single(&msg->spi->dev,
|
||||
(void *)rx_buf,
|
||||
drv_data->rx_dma_size,
|
||||
DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(&msg->spi->dev,
|
||||
drv_data->rx_dma_addr)) {
|
||||
dma_unmap_single(&msg->spi->dev,
|
||||
drv_data->tx_dma_addr,
|
||||
drv_data->tx_dma_size,
|
||||
DMA_TO_DEVICE);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dummy_read(drv_data);
|
||||
set_dma_x_count(drv_data->tx_dma, word_count);
|
||||
set_dma_x_count(drv_data->rx_dma, word_count);
|
||||
set_dma_start_addr(drv_data->tx_dma, drv_data->tx_dma_addr);
|
||||
set_dma_start_addr(drv_data->rx_dma, drv_data->rx_dma_addr);
|
||||
dma_config |= DMAFLOW_STOP | RESTART | DI_EN;
|
||||
set_dma_config(drv_data->tx_dma, dma_config);
|
||||
set_dma_config(drv_data->rx_dma, dma_config | WNR);
|
||||
enable_dma(drv_data->tx_dma);
|
||||
enable_dma(drv_data->rx_dma);
|
||||
SSYNC();
|
||||
|
||||
bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN | SPI_RXCTL_RDR_NE);
|
||||
SSYNC();
|
||||
bfin_write(&drv_data->regs->tx_control,
|
||||
SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bfin_spi_pio_xfer(struct bfin_spi_master *drv_data)
|
||||
{
|
||||
struct spi_message *msg = drv_data->cur_msg;
|
||||
|
||||
if (!drv_data->rx) {
|
||||
/* write only half duplex */
|
||||
drv_data->ops->write(drv_data);
|
||||
if (drv_data->tx != drv_data->tx_end)
|
||||
return -EIO;
|
||||
} else if (!drv_data->tx) {
|
||||
/* read only half duplex */
|
||||
drv_data->ops->read(drv_data);
|
||||
if (drv_data->rx != drv_data->rx_end)
|
||||
return -EIO;
|
||||
} else {
|
||||
/* full duplex mode */
|
||||
drv_data->ops->duplex(drv_data);
|
||||
if (drv_data->tx != drv_data->tx_end)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!bfin_spi_flush(drv_data))
|
||||
return -EIO;
|
||||
msg->actual_length += drv_data->transfer_len;
|
||||
tasklet_schedule(&drv_data->pump_transfers);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bfin_spi_pump_transfers(unsigned long data)
|
||||
{
|
||||
struct bfin_spi_master *drv_data = (struct bfin_spi_master *)data;
|
||||
struct spi_message *msg = NULL;
|
||||
struct spi_transfer *t = NULL;
|
||||
struct bfin_spi_device *chip = NULL;
|
||||
int ret;
|
||||
|
||||
/* Get current state information */
|
||||
msg = drv_data->cur_msg;
|
||||
t = drv_data->cur_transfer;
|
||||
chip = drv_data->cur_chip;
|
||||
|
||||
/* Handle for abort */
|
||||
if (drv_data->state == ERROR_STATE) {
|
||||
msg->status = -EIO;
|
||||
bfin_spi_giveback(drv_data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (drv_data->state == RUNNING_STATE) {
|
||||
if (t->delay_usecs)
|
||||
udelay(t->delay_usecs);
|
||||
if (t->cs_change)
|
||||
bfin_spi_cs_deactive(drv_data, chip);
|
||||
bfin_spi_next_transfer(drv_data);
|
||||
t = drv_data->cur_transfer;
|
||||
}
|
||||
/* Handle end of message */
|
||||
if (drv_data->state == DONE_STATE) {
|
||||
msg->status = 0;
|
||||
bfin_spi_giveback(drv_data);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((t->len == 0) || (t->tx_buf == NULL && t->rx_buf == NULL)) {
|
||||
/* Schedule next transfer tasklet */
|
||||
tasklet_schedule(&drv_data->pump_transfers);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = bfin_spi_setup_transfer(drv_data);
|
||||
if (ret) {
|
||||
msg->status = ret;
|
||||
bfin_spi_giveback(drv_data);
|
||||
}
|
||||
|
||||
bfin_write(&drv_data->regs->status, 0xFFFFFFFF);
|
||||
bfin_spi_cs_active(drv_data, chip);
|
||||
drv_data->state = RUNNING_STATE;
|
||||
|
||||
if (chip->enable_dma)
|
||||
ret = bfin_spi_dma_xfer(drv_data);
|
||||
else
|
||||
ret = bfin_spi_pio_xfer(drv_data);
|
||||
if (ret) {
|
||||
msg->status = ret;
|
||||
bfin_spi_giveback(drv_data);
|
||||
}
|
||||
}
|
||||
|
||||
static int bfin_spi_transfer_one_message(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct bfin_spi_master *drv_data = spi_master_get_devdata(master);
|
||||
|
||||
drv_data->cur_msg = m;
|
||||
drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
|
||||
bfin_spi_restore_state(drv_data);
|
||||
|
||||
drv_data->state = START_STATE;
|
||||
drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
|
||||
struct spi_transfer, transfer_list);
|
||||
|
||||
tasklet_schedule(&drv_data->pump_transfers);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_SPI_SSEL 7
|
||||
|
||||
static const u16 ssel[][MAX_SPI_SSEL] = {
|
||||
{P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3,
|
||||
P_SPI0_SSEL4, P_SPI0_SSEL5,
|
||||
P_SPI0_SSEL6, P_SPI0_SSEL7},
|
||||
|
||||
{P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3,
|
||||
P_SPI1_SSEL4, P_SPI1_SSEL5,
|
||||
P_SPI1_SSEL6, P_SPI1_SSEL7},
|
||||
|
||||
{P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3,
|
||||
P_SPI2_SSEL4, P_SPI2_SSEL5,
|
||||
P_SPI2_SSEL6, P_SPI2_SSEL7},
|
||||
};
|
||||
|
||||
static int bfin_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master);
|
||||
struct bfin_spi_device *chip = spi_get_ctldata(spi);
|
||||
u32 bfin_ctl_reg = SPI_CTL_ODM | SPI_CTL_PSSE;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!chip) {
|
||||
struct bfin_spi3_chip *chip_info = spi->controller_data;
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip) {
|
||||
dev_err(&spi->dev, "can not allocate chip data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (chip_info) {
|
||||
if (chip_info->control & ~bfin_ctl_reg) {
|
||||
dev_err(&spi->dev,
|
||||
"do not set bits that the SPI framework manages\n");
|
||||
goto error;
|
||||
}
|
||||
chip->control = chip_info->control;
|
||||
chip->cs_chg_udelay = chip_info->cs_chg_udelay;
|
||||
chip->tx_dummy_val = chip_info->tx_dummy_val;
|
||||
chip->enable_dma = chip_info->enable_dma;
|
||||
}
|
||||
chip->cs = spi->chip_select;
|
||||
if (chip->cs < MAX_CTRL_CS) {
|
||||
chip->ssel = (1 << chip->cs) << 8;
|
||||
ret = peripheral_request(ssel[spi->master->bus_num]
|
||||
[chip->cs-1], dev_name(&spi->dev));
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "peripheral_request() error\n");
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
chip->cs_gpio = chip->cs - MAX_CTRL_CS;
|
||||
ret = gpio_request_one(chip->cs_gpio, GPIOF_OUT_INIT_HIGH,
|
||||
dev_name(&spi->dev));
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "gpio_request_one() error\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
spi_set_ctldata(spi, chip);
|
||||
}
|
||||
|
||||
/* force a default base state */
|
||||
chip->control &= bfin_ctl_reg;
|
||||
|
||||
if (spi->mode & SPI_CPOL)
|
||||
chip->control |= SPI_CTL_CPOL;
|
||||
if (spi->mode & SPI_CPHA)
|
||||
chip->control |= SPI_CTL_CPHA;
|
||||
if (spi->mode & SPI_LSB_FIRST)
|
||||
chip->control |= SPI_CTL_LSBF;
|
||||
chip->control |= SPI_CTL_MSTR;
|
||||
/* we choose software to controll cs */
|
||||
chip->control &= ~SPI_CTL_ASSEL;
|
||||
|
||||
chip->clock = hz_to_spi_clock(drv_data->sclk, spi->max_speed_hz);
|
||||
|
||||
bfin_spi_cs_enable(drv_data, chip);
|
||||
bfin_spi_cs_deactive(drv_data, chip);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
if (chip) {
|
||||
kfree(chip);
|
||||
spi_set_ctldata(spi, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bfin_spi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
struct bfin_spi_device *chip = spi_get_ctldata(spi);
|
||||
struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master);
|
||||
|
||||
if (!chip)
|
||||
return;
|
||||
|
||||
if (chip->cs < MAX_CTRL_CS) {
|
||||
peripheral_free(ssel[spi->master->bus_num]
|
||||
[chip->cs-1]);
|
||||
bfin_spi_cs_disable(drv_data, chip);
|
||||
} else {
|
||||
gpio_free(chip->cs_gpio);
|
||||
}
|
||||
|
||||
kfree(chip);
|
||||
spi_set_ctldata(spi, NULL);
|
||||
}
|
||||
|
||||
static irqreturn_t bfin_spi_tx_dma_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct bfin_spi_master *drv_data = dev_id;
|
||||
u32 dma_stat = get_dma_curr_irqstat(drv_data->tx_dma);
|
||||
|
||||
clear_dma_irqstat(drv_data->tx_dma);
|
||||
if (dma_stat & DMA_DONE) {
|
||||
drv_data->tx_num++;
|
||||
} else {
|
||||
dev_err(&drv_data->master->dev,
|
||||
"spi tx dma error: %d\n", dma_stat);
|
||||
if (drv_data->tx)
|
||||
drv_data->state = ERROR_STATE;
|
||||
}
|
||||
bfin_write_and(&drv_data->regs->tx_control, ~SPI_TXCTL_TDR_NF);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t bfin_spi_rx_dma_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct bfin_spi_master *drv_data = dev_id;
|
||||
struct spi_message *msg = drv_data->cur_msg;
|
||||
u32 dma_stat = get_dma_curr_irqstat(drv_data->rx_dma);
|
||||
|
||||
clear_dma_irqstat(drv_data->rx_dma);
|
||||
if (dma_stat & DMA_DONE) {
|
||||
drv_data->rx_num++;
|
||||
/* we may fail on tx dma */
|
||||
if (drv_data->state != ERROR_STATE)
|
||||
msg->actual_length += drv_data->transfer_len;
|
||||
} else {
|
||||
drv_data->state = ERROR_STATE;
|
||||
dev_err(&drv_data->master->dev,
|
||||
"spi rx dma error: %d\n", dma_stat);
|
||||
}
|
||||
bfin_write(&drv_data->regs->tx_control, 0);
|
||||
bfin_write(&drv_data->regs->rx_control, 0);
|
||||
if (drv_data->rx_num != drv_data->tx_num)
|
||||
dev_dbg(&drv_data->master->dev,
|
||||
"dma interrupt missing: tx=%d,rx=%d\n",
|
||||
drv_data->tx_num, drv_data->rx_num);
|
||||
tasklet_schedule(&drv_data->pump_transfers);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bfin_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct bfin_spi3_master *info = dev_get_platdata(dev);
|
||||
struct spi_master *master;
|
||||
struct bfin_spi_master *drv_data;
|
||||
struct resource *mem, *res;
|
||||
unsigned int tx_dma, rx_dma;
|
||||
unsigned long sclk;
|
||||
int ret;
|
||||
|
||||
if (!info) {
|
||||
dev_err(dev, "platform data missing!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sclk = get_sclk1();
|
||||
if (!sclk) {
|
||||
dev_err(dev, "can not get sclk1\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "can not get tx dma resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
tx_dma = res->start;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!res) {
|
||||
dev_err(dev, "can not get rx dma resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
rx_dma = res->start;
|
||||
|
||||
/* allocate master with space for drv_data */
|
||||
master = spi_alloc_master(dev, sizeof(*drv_data));
|
||||
if (!master) {
|
||||
dev_err(dev, "can not alloc spi_master\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
/* the mode bits supported by this driver */
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
|
||||
|
||||
master->bus_num = pdev->id;
|
||||
master->num_chipselect = info->num_chipselect;
|
||||
master->cleanup = bfin_spi_cleanup;
|
||||
master->setup = bfin_spi_setup;
|
||||
master->transfer_one_message = bfin_spi_transfer_one_message;
|
||||
master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1);
|
||||
|
||||
drv_data = spi_master_get_devdata(master);
|
||||
drv_data->master = master;
|
||||
drv_data->tx_dma = tx_dma;
|
||||
drv_data->rx_dma = rx_dma;
|
||||
drv_data->pin_req = info->pin_req;
|
||||
drv_data->sclk = sclk;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
drv_data->regs = devm_ioremap_resource(dev, mem);
|
||||
if (IS_ERR(drv_data->regs)) {
|
||||
ret = PTR_ERR(drv_data->regs);
|
||||
goto err_put_master;
|
||||
}
|
||||
|
||||
/* request tx and rx dma */
|
||||
ret = request_dma(tx_dma, "SPI_TX_DMA");
|
||||
if (ret) {
|
||||
dev_err(dev, "can not request SPI TX DMA channel\n");
|
||||
goto err_put_master;
|
||||
}
|
||||
set_dma_callback(tx_dma, bfin_spi_tx_dma_isr, drv_data);
|
||||
|
||||
ret = request_dma(rx_dma, "SPI_RX_DMA");
|
||||
if (ret) {
|
||||
dev_err(dev, "can not request SPI RX DMA channel\n");
|
||||
goto err_free_tx_dma;
|
||||
}
|
||||
set_dma_callback(drv_data->rx_dma, bfin_spi_rx_dma_isr, drv_data);
|
||||
|
||||
/* request CLK, MOSI and MISO */
|
||||
ret = peripheral_request_list(drv_data->pin_req, "bfin-spi3");
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can not request spi pins\n");
|
||||
goto err_free_rx_dma;
|
||||
}
|
||||
|
||||
bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA);
|
||||
bfin_write(&drv_data->regs->ssel, 0x0000FE00);
|
||||
bfin_write(&drv_data->regs->delay, 0x0);
|
||||
|
||||
tasklet_init(&drv_data->pump_transfers,
|
||||
bfin_spi_pump_transfers, (unsigned long)drv_data);
|
||||
/* register with the SPI framework */
|
||||
ret = spi_register_master(master);
|
||||
if (ret) {
|
||||
dev_err(dev, "can not register spi master\n");
|
||||
goto err_free_peripheral;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_free_peripheral:
|
||||
peripheral_free_list(drv_data->pin_req);
|
||||
err_free_rx_dma:
|
||||
free_dma(rx_dma);
|
||||
err_free_tx_dma:
|
||||
free_dma(tx_dma);
|
||||
err_put_master:
|
||||
spi_master_put(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bfin_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct bfin_spi_master *drv_data = spi_master_get_devdata(master);
|
||||
|
||||
bfin_spi_disable(drv_data);
|
||||
|
||||
peripheral_free_list(drv_data->pin_req);
|
||||
free_dma(drv_data->rx_dma);
|
||||
free_dma(drv_data->tx_dma);
|
||||
|
||||
spi_unregister_master(drv_data->master);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int bfin_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct bfin_spi_master *drv_data = spi_master_get_devdata(master);
|
||||
|
||||
spi_master_suspend(master);
|
||||
|
||||
drv_data->control = bfin_read(&drv_data->regs->control);
|
||||
drv_data->ssel = bfin_read(&drv_data->regs->ssel);
|
||||
|
||||
bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA);
|
||||
bfin_write(&drv_data->regs->ssel, 0x0000FE00);
|
||||
dma_disable_irq(drv_data->rx_dma);
|
||||
dma_disable_irq(drv_data->tx_dma);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bfin_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct bfin_spi_master *drv_data = spi_master_get_devdata(master);
|
||||
int ret = 0;
|
||||
|
||||
/* bootrom may modify spi and dma status when resume in spi boot mode */
|
||||
disable_dma(drv_data->rx_dma);
|
||||
|
||||
dma_enable_irq(drv_data->rx_dma);
|
||||
dma_enable_irq(drv_data->tx_dma);
|
||||
bfin_write(&drv_data->regs->control, drv_data->control);
|
||||
bfin_write(&drv_data->regs->ssel, drv_data->ssel);
|
||||
|
||||
ret = spi_master_resume(master);
|
||||
if (ret) {
|
||||
free_dma(drv_data->rx_dma);
|
||||
free_dma(drv_data->tx_dma);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
static const struct dev_pm_ops bfin_spi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(bfin_spi_suspend, bfin_spi_resume)
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:bfin-spi3");
|
||||
static struct platform_driver bfin_spi_driver = {
|
||||
.driver = {
|
||||
.name = "bfin-spi3",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &bfin_spi_pm_ops,
|
||||
},
|
||||
.remove = bfin_spi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver_probe(bfin_spi_driver, bfin_spi_probe);
|
||||
|
||||
MODULE_DESCRIPTION("Analog Devices SPI3 controller driver");
|
||||
MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1271,7 +1271,7 @@ static int bfin_spi_probe(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
int status = 0;
|
||||
|
||||
platform_info = dev->platform_data;
|
||||
platform_info = dev_get_platdata(dev);
|
||||
|
||||
/* Allocate master with space for drv_data */
|
||||
master = spi_alloc_master(dev, sizeof(*drv_data));
|
||||
|
|
|
@ -255,150 +255,140 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
|
|||
* Drivers can provide word-at-a-time i/o primitives, or provide
|
||||
* transfer-at-a-time ones to leverage dma or fifo hardware.
|
||||
*/
|
||||
static void bitbang_work(struct work_struct *work)
|
||||
|
||||
static int spi_bitbang_prepare_hardware(struct spi_master *spi)
|
||||
{
|
||||
struct spi_bitbang *bitbang =
|
||||
container_of(work, struct spi_bitbang, work);
|
||||
struct spi_bitbang *bitbang;
|
||||
unsigned long flags;
|
||||
struct spi_message *m, *_m;
|
||||
|
||||
bitbang = spi_master_get_devdata(spi);
|
||||
|
||||
spin_lock_irqsave(&bitbang->lock, flags);
|
||||
bitbang->busy = 1;
|
||||
list_for_each_entry_safe(m, _m, &bitbang->queue, queue) {
|
||||
struct spi_device *spi;
|
||||
unsigned nsecs;
|
||||
struct spi_transfer *t = NULL;
|
||||
unsigned tmp;
|
||||
unsigned cs_change;
|
||||
int status;
|
||||
int do_setup = -1;
|
||||
spin_unlock_irqrestore(&bitbang->lock, flags);
|
||||
|
||||
list_del(&m->queue);
|
||||
spin_unlock_irqrestore(&bitbang->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME this is made-up ... the correct value is known to
|
||||
* word-at-a-time bitbang code, and presumably chipselect()
|
||||
* should enforce these requirements too?
|
||||
*/
|
||||
nsecs = 100;
|
||||
static int spi_bitbang_transfer_one(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct spi_bitbang *bitbang;
|
||||
unsigned nsecs;
|
||||
struct spi_transfer *t = NULL;
|
||||
unsigned cs_change;
|
||||
int status;
|
||||
int do_setup = -1;
|
||||
struct spi_device *spi = m->spi;
|
||||
|
||||
spi = m->spi;
|
||||
tmp = 0;
|
||||
cs_change = 1;
|
||||
status = 0;
|
||||
bitbang = spi_master_get_devdata(master);
|
||||
|
||||
list_for_each_entry (t, &m->transfers, transfer_list) {
|
||||
/* FIXME this is made-up ... the correct value is known to
|
||||
* word-at-a-time bitbang code, and presumably chipselect()
|
||||
* should enforce these requirements too?
|
||||
*/
|
||||
nsecs = 100;
|
||||
|
||||
/* override speed or wordsize? */
|
||||
if (t->speed_hz || t->bits_per_word)
|
||||
do_setup = 1;
|
||||
cs_change = 1;
|
||||
status = 0;
|
||||
|
||||
/* init (-1) or override (1) transfer params */
|
||||
if (do_setup != 0) {
|
||||
status = bitbang->setup_transfer(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
if (do_setup == -1)
|
||||
do_setup = 0;
|
||||
}
|
||||
list_for_each_entry (t, &m->transfers, transfer_list) {
|
||||
|
||||
/* set up default clock polarity, and activate chip;
|
||||
* this implicitly updates clock and spi modes as
|
||||
* previously recorded for this device via setup().
|
||||
* (and also deselects any other chip that might be
|
||||
* selected ...)
|
||||
*/
|
||||
if (cs_change) {
|
||||
bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
|
||||
ndelay(nsecs);
|
||||
}
|
||||
cs_change = t->cs_change;
|
||||
if (!t->tx_buf && !t->rx_buf && t->len) {
|
||||
status = -EINVAL;
|
||||
/* override speed or wordsize? */
|
||||
if (t->speed_hz || t->bits_per_word)
|
||||
do_setup = 1;
|
||||
|
||||
/* init (-1) or override (1) transfer params */
|
||||
if (do_setup != 0) {
|
||||
status = bitbang->setup_transfer(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* transfer data. the lower level code handles any
|
||||
* new dma mappings it needs. our caller always gave
|
||||
* us dma-safe buffers.
|
||||
*/
|
||||
if (t->len) {
|
||||
/* REVISIT dma API still needs a designated
|
||||
* DMA_ADDR_INVALID; ~0 might be better.
|
||||
*/
|
||||
if (!m->is_dma_mapped)
|
||||
t->rx_dma = t->tx_dma = 0;
|
||||
status = bitbang->txrx_bufs(spi, t);
|
||||
}
|
||||
if (status > 0)
|
||||
m->actual_length += status;
|
||||
if (status != t->len) {
|
||||
/* always report some kind of error */
|
||||
if (status >= 0)
|
||||
status = -EREMOTEIO;
|
||||
break;
|
||||
}
|
||||
status = 0;
|
||||
|
||||
/* protocol tweaks before next transfer */
|
||||
if (t->delay_usecs)
|
||||
udelay(t->delay_usecs);
|
||||
|
||||
if (cs_change && !list_is_last(&t->transfer_list, &m->transfers)) {
|
||||
/* sometimes a short mid-message deselect of the chip
|
||||
* may be needed to terminate a mode or command
|
||||
*/
|
||||
ndelay(nsecs);
|
||||
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
|
||||
ndelay(nsecs);
|
||||
}
|
||||
if (do_setup == -1)
|
||||
do_setup = 0;
|
||||
}
|
||||
|
||||
m->status = status;
|
||||
m->complete(m->context);
|
||||
|
||||
/* normally deactivate chipselect ... unless no error and
|
||||
* cs_change has hinted that the next message will probably
|
||||
* be for this chip too.
|
||||
/* set up default clock polarity, and activate chip;
|
||||
* this implicitly updates clock and spi modes as
|
||||
* previously recorded for this device via setup().
|
||||
* (and also deselects any other chip that might be
|
||||
* selected ...)
|
||||
*/
|
||||
if (!(status == 0 && cs_change)) {
|
||||
if (cs_change) {
|
||||
bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
|
||||
ndelay(nsecs);
|
||||
}
|
||||
cs_change = t->cs_change;
|
||||
if (!t->tx_buf && !t->rx_buf && t->len) {
|
||||
status = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* transfer data. the lower level code handles any
|
||||
* new dma mappings it needs. our caller always gave
|
||||
* us dma-safe buffers.
|
||||
*/
|
||||
if (t->len) {
|
||||
/* REVISIT dma API still needs a designated
|
||||
* DMA_ADDR_INVALID; ~0 might be better.
|
||||
*/
|
||||
if (!m->is_dma_mapped)
|
||||
t->rx_dma = t->tx_dma = 0;
|
||||
status = bitbang->txrx_bufs(spi, t);
|
||||
}
|
||||
if (status > 0)
|
||||
m->actual_length += status;
|
||||
if (status != t->len) {
|
||||
/* always report some kind of error */
|
||||
if (status >= 0)
|
||||
status = -EREMOTEIO;
|
||||
break;
|
||||
}
|
||||
status = 0;
|
||||
|
||||
/* protocol tweaks before next transfer */
|
||||
if (t->delay_usecs)
|
||||
udelay(t->delay_usecs);
|
||||
|
||||
if (cs_change && !list_is_last(&t->transfer_list, &m->transfers)) {
|
||||
/* sometimes a short mid-message deselect of the chip
|
||||
* may be needed to terminate a mode or command
|
||||
*/
|
||||
ndelay(nsecs);
|
||||
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
|
||||
ndelay(nsecs);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&bitbang->lock, flags);
|
||||
}
|
||||
bitbang->busy = 0;
|
||||
spin_unlock_irqrestore(&bitbang->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_bitbang_transfer - default submit to transfer queue
|
||||
*/
|
||||
int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
|
||||
{
|
||||
struct spi_bitbang *bitbang;
|
||||
unsigned long flags;
|
||||
int status = 0;
|
||||
m->status = status;
|
||||
|
||||
m->actual_length = 0;
|
||||
m->status = -EINPROGRESS;
|
||||
|
||||
bitbang = spi_master_get_devdata(spi->master);
|
||||
|
||||
spin_lock_irqsave(&bitbang->lock, flags);
|
||||
if (!spi->max_speed_hz)
|
||||
status = -ENETDOWN;
|
||||
else {
|
||||
list_add_tail(&m->queue, &bitbang->queue);
|
||||
queue_work(bitbang->workqueue, &bitbang->work);
|
||||
/* normally deactivate chipselect ... unless no error and
|
||||
* cs_change has hinted that the next message will probably
|
||||
* be for this chip too.
|
||||
*/
|
||||
if (!(status == 0 && cs_change)) {
|
||||
ndelay(nsecs);
|
||||
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
|
||||
ndelay(nsecs);
|
||||
}
|
||||
spin_unlock_irqrestore(&bitbang->lock, flags);
|
||||
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_bitbang_transfer);
|
||||
|
||||
static int spi_bitbang_unprepare_hardware(struct spi_master *spi)
|
||||
{
|
||||
struct spi_bitbang *bitbang;
|
||||
unsigned long flags;
|
||||
|
||||
bitbang = spi_master_get_devdata(spi);
|
||||
|
||||
spin_lock_irqsave(&bitbang->lock, flags);
|
||||
bitbang->busy = 0;
|
||||
spin_unlock_irqrestore(&bitbang->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
|
@ -428,20 +418,22 @@ EXPORT_SYMBOL_GPL(spi_bitbang_transfer);
|
|||
int spi_bitbang_start(struct spi_bitbang *bitbang)
|
||||
{
|
||||
struct spi_master *master = bitbang->master;
|
||||
int status;
|
||||
|
||||
if (!master || !bitbang->chipselect)
|
||||
return -EINVAL;
|
||||
|
||||
INIT_WORK(&bitbang->work, bitbang_work);
|
||||
spin_lock_init(&bitbang->lock);
|
||||
INIT_LIST_HEAD(&bitbang->queue);
|
||||
|
||||
if (!master->mode_bits)
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
|
||||
|
||||
if (!master->transfer)
|
||||
master->transfer = spi_bitbang_transfer;
|
||||
if (master->transfer || master->transfer_one_message)
|
||||
return -EINVAL;
|
||||
|
||||
master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;
|
||||
master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;
|
||||
master->transfer_one_message = spi_bitbang_transfer_one;
|
||||
|
||||
if (!bitbang->txrx_bufs) {
|
||||
bitbang->use_dma = 0;
|
||||
bitbang->txrx_bufs = spi_bitbang_bufs;
|
||||
|
@ -452,34 +444,12 @@ int spi_bitbang_start(struct spi_bitbang *bitbang)
|
|||
master->setup = spi_bitbang_setup;
|
||||
master->cleanup = spi_bitbang_cleanup;
|
||||
}
|
||||
} else if (!master->setup)
|
||||
return -EINVAL;
|
||||
if (master->transfer == spi_bitbang_transfer &&
|
||||
!bitbang->setup_transfer)
|
||||
return -EINVAL;
|
||||
|
||||
/* this task is the only thing to touch the SPI bits */
|
||||
bitbang->busy = 0;
|
||||
bitbang->workqueue = create_singlethread_workqueue(
|
||||
dev_name(master->dev.parent));
|
||||
if (bitbang->workqueue == NULL) {
|
||||
status = -EBUSY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
/* driver may get busy before register() returns, especially
|
||||
* if someone registered boardinfo for devices
|
||||
*/
|
||||
status = spi_register_master(master);
|
||||
if (status < 0)
|
||||
goto err2;
|
||||
|
||||
return status;
|
||||
|
||||
err2:
|
||||
destroy_workqueue(bitbang->workqueue);
|
||||
err1:
|
||||
return status;
|
||||
return spi_register_master(master);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_bitbang_start);
|
||||
|
||||
|
@ -490,10 +460,6 @@ int spi_bitbang_stop(struct spi_bitbang *bitbang)
|
|||
{
|
||||
spi_unregister_master(bitbang->master);
|
||||
|
||||
WARN_ON(!list_empty(&bitbang->queue));
|
||||
|
||||
destroy_workqueue(bitbang->workqueue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_bitbang_stop);
|
||||
|
|
|
@ -239,11 +239,8 @@ static int spi_clps711x_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
dev_err(&pdev->dev, "Failed to register master\n");
|
||||
devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw);
|
||||
|
||||
clk_out:
|
||||
devm_clk_put(&pdev->dev, hw->spi_clk);
|
||||
|
||||
err_out:
|
||||
while (--i >= 0)
|
||||
if (gpio_is_valid(hw->chipselect[i]))
|
||||
|
@ -261,13 +258,10 @@ static int spi_clps711x_remove(struct platform_device *pdev)
|
|||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
||||
|
||||
devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw);
|
||||
|
||||
for (i = 0; i < master->num_chipselect; i++)
|
||||
if (gpio_is_valid(hw->chipselect[i]))
|
||||
gpio_free(hw->chipselect[i]);
|
||||
|
||||
devm_clk_put(&pdev->dev, hw->spi_clk);
|
||||
spi_unregister_master(master);
|
||||
kfree(master);
|
||||
|
||||
|
|
|
@ -354,24 +354,6 @@ static int mcfqspi_transfer_one_message(struct spi_master *master,
|
|||
|
||||
}
|
||||
|
||||
static int mcfqspi_prepare_transfer_hw(struct spi_master *master)
|
||||
{
|
||||
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_get_sync(mcfqspi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcfqspi_unprepare_transfer_hw(struct spi_master *master)
|
||||
{
|
||||
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_put_sync(mcfqspi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcfqspi_setup(struct spi_device *spi)
|
||||
{
|
||||
if (spi->chip_select >= spi->master->num_chipselect) {
|
||||
|
@ -400,7 +382,7 @@ static int mcfqspi_probe(struct platform_device *pdev)
|
|||
struct mcfqspi_platform_data *pdata;
|
||||
int status;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (!pdata) {
|
||||
dev_dbg(&pdev->dev, "platform data is missing\n");
|
||||
return -ENOENT;
|
||||
|
@ -473,8 +455,7 @@ static int mcfqspi_probe(struct platform_device *pdev)
|
|||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16);
|
||||
master->setup = mcfqspi_setup;
|
||||
master->transfer_one_message = mcfqspi_transfer_one_message;
|
||||
master->prepare_transfer_hardware = mcfqspi_prepare_transfer_hw;
|
||||
master->unprepare_transfer_hardware = mcfqspi_unprepare_transfer_hw;
|
||||
master->auto_runtime_pm = true;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
|
@ -558,7 +539,7 @@ static int mcfqspi_resume(struct device *dev)
|
|||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int mcfqspi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
|
||||
struct mcfqspi *mcfqspi = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(mcfqspi->clk);
|
||||
|
||||
|
@ -567,7 +548,7 @@ static int mcfqspi_runtime_suspend(struct device *dev)
|
|||
|
||||
static int mcfqspi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
|
||||
struct mcfqspi *mcfqspi = dev_get_drvdata(dev);
|
||||
|
||||
clk_enable(mcfqspi->clk);
|
||||
|
||||
|
|
|
@ -872,8 +872,8 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
|||
goto free_master;
|
||||
}
|
||||
|
||||
if (pdev->dev.platform_data) {
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (dev_get_platdata(&pdev->dev)) {
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
dspi->pdata = *pdata;
|
||||
} else {
|
||||
/* update dspi pdata with that from the DT */
|
||||
|
|
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2013 Uwe Kleine-Koenig for Pengutronix
|
||||
*
|
||||
* 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/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_data/efm32-spi.h>
|
||||
|
||||
#define DRIVER_NAME "efm32-spi"
|
||||
|
||||
#define MASK_VAL(mask, val) ((val << __ffs(mask)) & mask)
|
||||
|
||||
#define REG_CTRL 0x00
|
||||
#define REG_CTRL_SYNC 0x0001
|
||||
#define REG_CTRL_CLKPOL 0x0100
|
||||
#define REG_CTRL_CLKPHA 0x0200
|
||||
#define REG_CTRL_MSBF 0x0400
|
||||
#define REG_CTRL_TXBIL 0x1000
|
||||
|
||||
#define REG_FRAME 0x04
|
||||
#define REG_FRAME_DATABITS__MASK 0x000f
|
||||
#define REG_FRAME_DATABITS(n) ((n) - 3)
|
||||
|
||||
#define REG_CMD 0x0c
|
||||
#define REG_CMD_RXEN 0x0001
|
||||
#define REG_CMD_RXDIS 0x0002
|
||||
#define REG_CMD_TXEN 0x0004
|
||||
#define REG_CMD_TXDIS 0x0008
|
||||
#define REG_CMD_MASTEREN 0x0010
|
||||
|
||||
#define REG_STATUS 0x10
|
||||
#define REG_STATUS_TXENS 0x0002
|
||||
#define REG_STATUS_TXC 0x0020
|
||||
#define REG_STATUS_TXBL 0x0040
|
||||
#define REG_STATUS_RXDATAV 0x0080
|
||||
|
||||
#define REG_CLKDIV 0x14
|
||||
|
||||
#define REG_RXDATAX 0x18
|
||||
#define REG_RXDATAX_RXDATA__MASK 0x01ff
|
||||
#define REG_RXDATAX_PERR 0x4000
|
||||
#define REG_RXDATAX_FERR 0x8000
|
||||
|
||||
#define REG_TXDATA 0x34
|
||||
|
||||
#define REG_IF 0x40
|
||||
#define REG_IF_TXBL 0x0002
|
||||
#define REG_IF_RXDATAV 0x0004
|
||||
|
||||
#define REG_IFS 0x44
|
||||
#define REG_IFC 0x48
|
||||
#define REG_IEN 0x4c
|
||||
|
||||
#define REG_ROUTE 0x54
|
||||
#define REG_ROUTE_RXPEN 0x0001
|
||||
#define REG_ROUTE_TXPEN 0x0002
|
||||
#define REG_ROUTE_CLKPEN 0x0008
|
||||
#define REG_ROUTE_LOCATION__MASK 0x0700
|
||||
#define REG_ROUTE_LOCATION(n) MASK_VAL(REG_ROUTE_LOCATION__MASK, (n))
|
||||
|
||||
struct efm32_spi_ddata {
|
||||
struct spi_bitbang bitbang;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
unsigned int rxirq, txirq;
|
||||
struct efm32_spi_pdata pdata;
|
||||
|
||||
/* irq data */
|
||||
struct completion done;
|
||||
const u8 *tx_buf;
|
||||
u8 *rx_buf;
|
||||
unsigned tx_len, rx_len;
|
||||
|
||||
/* chip selects */
|
||||
unsigned csgpio[];
|
||||
};
|
||||
|
||||
#define ddata_to_dev(ddata) (&(ddata->bitbang.master->dev))
|
||||
#define efm32_spi_vdbg(ddata, format, arg...) \
|
||||
dev_vdbg(ddata_to_dev(ddata), format, ##arg)
|
||||
|
||||
static void efm32_spi_write32(struct efm32_spi_ddata *ddata,
|
||||
u32 value, unsigned offset)
|
||||
{
|
||||
writel_relaxed(value, ddata->base + offset);
|
||||
}
|
||||
|
||||
static u32 efm32_spi_read32(struct efm32_spi_ddata *ddata, unsigned offset)
|
||||
{
|
||||
return readl_relaxed(ddata->base + offset);
|
||||
}
|
||||
|
||||
static void efm32_spi_chipselect(struct spi_device *spi, int is_on)
|
||||
{
|
||||
struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master);
|
||||
int value = !(spi->mode & SPI_CS_HIGH) == !(is_on == BITBANG_CS_ACTIVE);
|
||||
|
||||
gpio_set_value(ddata->csgpio[spi->chip_select], value);
|
||||
}
|
||||
|
||||
static int efm32_spi_setup_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master);
|
||||
|
||||
unsigned bpw = t->bits_per_word ?: spi->bits_per_word;
|
||||
unsigned speed = t->speed_hz ?: spi->max_speed_hz;
|
||||
unsigned long clkfreq = clk_get_rate(ddata->clk);
|
||||
u32 clkdiv;
|
||||
|
||||
efm32_spi_write32(ddata, REG_CTRL_SYNC | REG_CTRL_MSBF |
|
||||
(spi->mode & SPI_CPHA ? REG_CTRL_CLKPHA : 0) |
|
||||
(spi->mode & SPI_CPOL ? REG_CTRL_CLKPOL : 0), REG_CTRL);
|
||||
|
||||
efm32_spi_write32(ddata,
|
||||
REG_FRAME_DATABITS(bpw), REG_FRAME);
|
||||
|
||||
if (2 * speed >= clkfreq)
|
||||
clkdiv = 0;
|
||||
else
|
||||
clkdiv = 64 * (DIV_ROUND_UP(2 * clkfreq, speed) - 4);
|
||||
|
||||
if (clkdiv > (1U << 21))
|
||||
return -EINVAL;
|
||||
|
||||
efm32_spi_write32(ddata, clkdiv, REG_CLKDIV);
|
||||
efm32_spi_write32(ddata, REG_CMD_MASTEREN, REG_CMD);
|
||||
efm32_spi_write32(ddata, REG_CMD_RXEN | REG_CMD_TXEN, REG_CMD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void efm32_spi_tx_u8(struct efm32_spi_ddata *ddata)
|
||||
{
|
||||
u8 val = 0;
|
||||
|
||||
if (ddata->tx_buf) {
|
||||
val = *ddata->tx_buf;
|
||||
ddata->tx_buf++;
|
||||
}
|
||||
|
||||
ddata->tx_len--;
|
||||
efm32_spi_write32(ddata, val, REG_TXDATA);
|
||||
efm32_spi_vdbg(ddata, "%s: tx 0x%x\n", __func__, val);
|
||||
}
|
||||
|
||||
static void efm32_spi_rx_u8(struct efm32_spi_ddata *ddata)
|
||||
{
|
||||
u32 rxdata = efm32_spi_read32(ddata, REG_RXDATAX);
|
||||
efm32_spi_vdbg(ddata, "%s: rx 0x%x\n", __func__, rxdata);
|
||||
|
||||
if (ddata->rx_buf) {
|
||||
*ddata->rx_buf = rxdata;
|
||||
ddata->rx_buf++;
|
||||
}
|
||||
|
||||
ddata->rx_len--;
|
||||
}
|
||||
|
||||
static void efm32_spi_filltx(struct efm32_spi_ddata *ddata)
|
||||
{
|
||||
while (ddata->tx_len &&
|
||||
ddata->tx_len + 2 > ddata->rx_len &&
|
||||
efm32_spi_read32(ddata, REG_STATUS) & REG_STATUS_TXBL) {
|
||||
efm32_spi_tx_u8(ddata);
|
||||
}
|
||||
}
|
||||
|
||||
static int efm32_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master);
|
||||
int ret = -EBUSY;
|
||||
|
||||
spin_lock_irq(&ddata->lock);
|
||||
|
||||
if (ddata->tx_buf || ddata->rx_buf)
|
||||
goto out_unlock;
|
||||
|
||||
ddata->tx_buf = t->tx_buf;
|
||||
ddata->rx_buf = t->rx_buf;
|
||||
ddata->tx_len = ddata->rx_len =
|
||||
t->len * DIV_ROUND_UP(t->bits_per_word, 8);
|
||||
|
||||
efm32_spi_filltx(ddata);
|
||||
|
||||
init_completion(&ddata->done);
|
||||
|
||||
efm32_spi_write32(ddata, REG_IF_TXBL | REG_IF_RXDATAV, REG_IEN);
|
||||
|
||||
spin_unlock_irq(&ddata->lock);
|
||||
|
||||
wait_for_completion(&ddata->done);
|
||||
|
||||
spin_lock_irq(&ddata->lock);
|
||||
|
||||
ret = t->len - max(ddata->tx_len, ddata->rx_len);
|
||||
|
||||
efm32_spi_write32(ddata, 0, REG_IEN);
|
||||
ddata->tx_buf = ddata->rx_buf = NULL;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irq(&ddata->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t efm32_spi_rxirq(int irq, void *data)
|
||||
{
|
||||
struct efm32_spi_ddata *ddata = data;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
spin_lock(&ddata->lock);
|
||||
|
||||
while (ddata->rx_len > 0 &&
|
||||
efm32_spi_read32(ddata, REG_STATUS) &
|
||||
REG_STATUS_RXDATAV) {
|
||||
efm32_spi_rx_u8(ddata);
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (!ddata->rx_len) {
|
||||
u32 ien = efm32_spi_read32(ddata, REG_IEN);
|
||||
|
||||
ien &= ~REG_IF_RXDATAV;
|
||||
|
||||
efm32_spi_write32(ddata, ien, REG_IEN);
|
||||
|
||||
complete(&ddata->done);
|
||||
}
|
||||
|
||||
spin_unlock(&ddata->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t efm32_spi_txirq(int irq, void *data)
|
||||
{
|
||||
struct efm32_spi_ddata *ddata = data;
|
||||
|
||||
efm32_spi_vdbg(ddata,
|
||||
"%s: txlen = %u, rxlen = %u, if=0x%08x, stat=0x%08x\n",
|
||||
__func__, ddata->tx_len, ddata->rx_len,
|
||||
efm32_spi_read32(ddata, REG_IF),
|
||||
efm32_spi_read32(ddata, REG_STATUS));
|
||||
|
||||
spin_lock(&ddata->lock);
|
||||
|
||||
efm32_spi_filltx(ddata);
|
||||
|
||||
efm32_spi_vdbg(ddata, "%s: txlen = %u, rxlen = %u\n",
|
||||
__func__, ddata->tx_len, ddata->rx_len);
|
||||
|
||||
if (!ddata->tx_len) {
|
||||
u32 ien = efm32_spi_read32(ddata, REG_IEN);
|
||||
|
||||
ien &= ~REG_IF_TXBL;
|
||||
|
||||
efm32_spi_write32(ddata, ien, REG_IEN);
|
||||
efm32_spi_vdbg(ddata, "disable TXBL\n");
|
||||
}
|
||||
|
||||
spin_unlock(&ddata->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct efm32_spi_pdata efm32_spi_pdata_default = {
|
||||
.location = 1,
|
||||
};
|
||||
|
||||
static u32 efm32_spi_get_configured_location(struct efm32_spi_ddata *ddata)
|
||||
{
|
||||
u32 reg = efm32_spi_read32(ddata, REG_ROUTE);
|
||||
|
||||
return (reg & REG_ROUTE_LOCATION__MASK) >> __ffs(REG_ROUTE_LOCATION__MASK);
|
||||
}
|
||||
|
||||
static int efm32_spi_probe_dt(struct platform_device *pdev,
|
||||
struct spi_master *master, struct efm32_spi_ddata *ddata)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
u32 location;
|
||||
int ret;
|
||||
|
||||
if (!np)
|
||||
return 1;
|
||||
|
||||
ret = of_property_read_u32(np, "location", &location);
|
||||
if (!ret) {
|
||||
dev_dbg(&pdev->dev, "using location %u\n", location);
|
||||
} else {
|
||||
/* default to location configured in hardware */
|
||||
location = efm32_spi_get_configured_location(ddata);
|
||||
|
||||
dev_info(&pdev->dev, "fall back to location %u\n", 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)
|
||||
{
|
||||
struct efm32_spi_ddata *ddata;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
struct spi_master *master;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
unsigned int num_cs, i;
|
||||
|
||||
num_cs = of_gpio_named_count(np, "cs-gpios");
|
||||
|
||||
master = spi_alloc_master(&pdev->dev,
|
||||
sizeof(*ddata) + num_cs * sizeof(unsigned));
|
||||
if (!master) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"failed to allocate spi master controller\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
master->num_chipselect = num_cs;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
|
||||
|
||||
ddata = spi_master_get_devdata(master);
|
||||
|
||||
ddata->bitbang.master = spi_master_get(master);
|
||||
ddata->bitbang.chipselect = efm32_spi_chipselect;
|
||||
ddata->bitbang.setup_transfer = efm32_spi_setup_transfer;
|
||||
ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs;
|
||||
|
||||
spin_lock_init(&ddata->lock);
|
||||
|
||||
ddata->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(ddata->clk)) {
|
||||
ret = PTR_ERR(ddata->clk);
|
||||
dev_err(&pdev->dev, "failed to get clock: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_cs; ++i) {
|
||||
ret = of_get_named_gpio(np, "cs-gpios", i);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to get csgpio#%u (%d)\n",
|
||||
i, ret);
|
||||
goto err;
|
||||
}
|
||||
ddata->csgpio[i] = ret;
|
||||
dev_dbg(&pdev->dev, "csgpio#%u = %u\n", i, ddata->csgpio[i]);
|
||||
ret = devm_gpio_request_one(&pdev->dev, ddata->csgpio[i],
|
||||
GPIOF_OUT_INIT_LOW, DRIVER_NAME);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to configure csgpio#%u (%d)\n",
|
||||
i, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
ret = -ENODEV;
|
||||
dev_err(&pdev->dev, "failed to determine base address\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (resource_size(res) < 60) {
|
||||
ret = -EINVAL;
|
||||
dev_err(&pdev->dev, "memory resource too small\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ddata->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ddata->base)) {
|
||||
ret = PTR_ERR(ddata->base);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret <= 0) {
|
||||
dev_err(&pdev->dev, "failed to get rx irq (%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ddata->rxirq = ret;
|
||||
|
||||
ret = platform_get_irq(pdev, 1);
|
||||
if (ret <= 0)
|
||||
ret = ddata->rxirq + 1;
|
||||
|
||||
ddata->txirq = ret;
|
||||
|
||||
ret = clk_prepare_enable(ddata->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = 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, REG_ROUTE_TXPEN | REG_ROUTE_RXPEN |
|
||||
REG_ROUTE_CLKPEN |
|
||||
REG_ROUTE_LOCATION(ddata->pdata.location), REG_ROUTE);
|
||||
|
||||
ret = request_irq(ddata->rxirq, efm32_spi_rxirq,
|
||||
0, DRIVER_NAME " rx", ddata);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register rxirq (%d)\n", ret);
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
ret = request_irq(ddata->txirq, efm32_spi_txirq,
|
||||
0, DRIVER_NAME " tx", ddata);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register txirq (%d)\n", ret);
|
||||
goto err_free_rx_irq;
|
||||
}
|
||||
|
||||
ret = spi_bitbang_start(&ddata->bitbang);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "spi_bitbang_start failed (%d)\n", ret);
|
||||
|
||||
free_irq(ddata->txirq, ddata);
|
||||
err_free_rx_irq:
|
||||
free_irq(ddata->rxirq, ddata);
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(ddata->clk);
|
||||
err:
|
||||
spi_master_put(master);
|
||||
kfree(master);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int efm32_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct efm32_spi_ddata *ddata = spi_master_get_devdata(master);
|
||||
|
||||
efm32_spi_write32(ddata, 0, REG_IEN);
|
||||
|
||||
free_irq(ddata->txirq, ddata);
|
||||
free_irq(ddata->rxirq, ddata);
|
||||
clk_disable_unprepare(ddata->clk);
|
||||
spi_master_put(master);
|
||||
kfree(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id efm32_spi_dt_ids[] = {
|
||||
{
|
||||
.compatible = "efm32,spi",
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, efm32_spi_dt_ids);
|
||||
|
||||
static struct platform_driver efm32_spi_driver = {
|
||||
.probe = efm32_spi_probe,
|
||||
.remove = efm32_spi_remove,
|
||||
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = efm32_spi_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(efm32_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
|
||||
MODULE_DESCRIPTION("EFM32 SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
|
@ -26,7 +26,6 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
@ -70,19 +69,13 @@
|
|||
|
||||
/**
|
||||
* struct ep93xx_spi - EP93xx SPI controller structure
|
||||
* @lock: spinlock that protects concurrent accesses to fields @running,
|
||||
* @current_msg and @msg_queue
|
||||
* @pdev: pointer to platform device
|
||||
* @clk: clock for the controller
|
||||
* @regs_base: pointer to ioremap()'d registers
|
||||
* @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
|
||||
* @running: is the queue running
|
||||
* @wq: workqueue used by the driver
|
||||
* @msg_work: work that is queued for the driver
|
||||
* @wait: wait here until given transfer is completed
|
||||
* @msg_queue: queue for the messages
|
||||
* @current_msg: message that is currently processed (or %NULL if none)
|
||||
* @tx: current byte in transfer to transmit
|
||||
* @rx: current byte in transfer to receive
|
||||
|
@ -96,30 +89,15 @@
|
|||
* @tx_sgt: sg table for TX transfers
|
||||
* @zeropage: dummy page used as RX buffer when only TX buffer is passed in by
|
||||
* the client
|
||||
*
|
||||
* This structure holds EP93xx SPI controller specific information. When
|
||||
* @running is %true, driver accepts transfer requests from protocol drivers.
|
||||
* @current_msg is used to hold pointer to the message that is currently
|
||||
* processed. If @current_msg is %NULL, it means that no processing is going
|
||||
* on.
|
||||
*
|
||||
* Most of the fields are only written once and they can be accessed without
|
||||
* taking the @lock. Fields that are accessed concurrently are: @current_msg,
|
||||
* @running, and @msg_queue.
|
||||
*/
|
||||
struct ep93xx_spi {
|
||||
spinlock_t lock;
|
||||
const struct platform_device *pdev;
|
||||
struct clk *clk;
|
||||
void __iomem *regs_base;
|
||||
unsigned long sspdr_phys;
|
||||
unsigned long min_rate;
|
||||
unsigned long max_rate;
|
||||
bool running;
|
||||
struct workqueue_struct *wq;
|
||||
struct work_struct msg_work;
|
||||
struct completion wait;
|
||||
struct list_head msg_queue;
|
||||
struct spi_message *current_msg;
|
||||
size_t tx;
|
||||
size_t rx;
|
||||
|
@ -136,50 +114,36 @@ struct ep93xx_spi {
|
|||
/**
|
||||
* struct ep93xx_spi_chip - SPI device hardware settings
|
||||
* @spi: back pointer to the SPI device
|
||||
* @rate: max rate in hz this chip supports
|
||||
* @div_cpsr: cpsr (pre-scaler) divider
|
||||
* @div_scr: scr divider
|
||||
* @dss: bits per word (4 - 16 bits)
|
||||
* @ops: private chip operations
|
||||
*
|
||||
* This structure is used to store hardware register specific settings for each
|
||||
* SPI device. Settings are written to hardware by function
|
||||
* ep93xx_spi_chip_setup().
|
||||
*/
|
||||
struct ep93xx_spi_chip {
|
||||
const struct spi_device *spi;
|
||||
unsigned long rate;
|
||||
u8 div_cpsr;
|
||||
u8 div_scr;
|
||||
u8 dss;
|
||||
struct ep93xx_spi_chip_ops *ops;
|
||||
};
|
||||
|
||||
/* converts bits per word to CR0.DSS value */
|
||||
#define bits_per_word_to_dss(bpw) ((bpw) - 1)
|
||||
|
||||
static inline void
|
||||
ep93xx_spi_write_u8(const struct ep93xx_spi *espi, u16 reg, u8 value)
|
||||
static void ep93xx_spi_write_u8(const struct ep93xx_spi *espi,
|
||||
u16 reg, u8 value)
|
||||
{
|
||||
__raw_writeb(value, espi->regs_base + reg);
|
||||
writeb(value, espi->regs_base + reg);
|
||||
}
|
||||
|
||||
static inline u8
|
||||
ep93xx_spi_read_u8(const struct ep93xx_spi *spi, u16 reg)
|
||||
static u8 ep93xx_spi_read_u8(const struct ep93xx_spi *spi, u16 reg)
|
||||
{
|
||||
return __raw_readb(spi->regs_base + reg);
|
||||
return readb(spi->regs_base + reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ep93xx_spi_write_u16(const struct ep93xx_spi *espi, u16 reg, u16 value)
|
||||
static void ep93xx_spi_write_u16(const struct ep93xx_spi *espi,
|
||||
u16 reg, u16 value)
|
||||
{
|
||||
__raw_writew(value, espi->regs_base + reg);
|
||||
writew(value, espi->regs_base + reg);
|
||||
}
|
||||
|
||||
static inline u16
|
||||
ep93xx_spi_read_u16(const struct ep93xx_spi *spi, u16 reg)
|
||||
static u16 ep93xx_spi_read_u16(const struct ep93xx_spi *spi, u16 reg)
|
||||
{
|
||||
return __raw_readw(spi->regs_base + reg);
|
||||
return readw(spi->regs_base + reg);
|
||||
}
|
||||
|
||||
static int ep93xx_spi_enable(const struct ep93xx_spi *espi)
|
||||
|
@ -230,17 +194,13 @@ static void ep93xx_spi_disable_interrupts(const struct ep93xx_spi *espi)
|
|||
/**
|
||||
* ep93xx_spi_calc_divisors() - calculates SPI clock divisors
|
||||
* @espi: ep93xx SPI controller struct
|
||||
* @chip: divisors are calculated for this chip
|
||||
* @rate: desired SPI output clock rate
|
||||
*
|
||||
* Function calculates cpsr (clock pre-scaler) and scr divisors based on
|
||||
* given @rate and places them to @chip->div_cpsr and @chip->div_scr. If,
|
||||
* for some reason, divisors cannot be calculated nothing is stored and
|
||||
* %-EINVAL is returned.
|
||||
* @div_cpsr: pointer to return the cpsr (pre-scaler) divider
|
||||
* @div_scr: pointer to return the scr divider
|
||||
*/
|
||||
static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi,
|
||||
struct ep93xx_spi_chip *chip,
|
||||
unsigned long rate)
|
||||
unsigned long rate,
|
||||
u8 *div_cpsr, u8 *div_scr)
|
||||
{
|
||||
unsigned long spi_clk_rate = clk_get_rate(espi->clk);
|
||||
int cpsr, scr;
|
||||
|
@ -248,7 +208,7 @@ static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi,
|
|||
/*
|
||||
* Make sure that max value is between values supported by the
|
||||
* controller. Note that minimum value is already checked in
|
||||
* ep93xx_spi_transfer().
|
||||
* ep93xx_spi_transfer_one_message().
|
||||
*/
|
||||
rate = clamp(rate, espi->min_rate, espi->max_rate);
|
||||
|
||||
|
@ -263,8 +223,8 @@ static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi,
|
|||
for (cpsr = 2; cpsr <= 254; cpsr += 2) {
|
||||
for (scr = 0; scr <= 255; scr++) {
|
||||
if ((spi_clk_rate / (cpsr * (scr + 1))) <= rate) {
|
||||
chip->div_scr = (u8)scr;
|
||||
chip->div_cpsr = (u8)cpsr;
|
||||
*div_scr = (u8)scr;
|
||||
*div_cpsr = (u8)cpsr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -319,72 +279,10 @@ static int ep93xx_spi_setup(struct spi_device *spi)
|
|||
spi_set_ctldata(spi, chip);
|
||||
}
|
||||
|
||||
if (spi->max_speed_hz != chip->rate) {
|
||||
int err;
|
||||
|
||||
err = ep93xx_spi_calc_divisors(espi, chip, spi->max_speed_hz);
|
||||
if (err != 0) {
|
||||
spi_set_ctldata(spi, NULL);
|
||||
kfree(chip);
|
||||
return err;
|
||||
}
|
||||
chip->rate = spi->max_speed_hz;
|
||||
}
|
||||
|
||||
chip->dss = bits_per_word_to_dss(spi->bits_per_word);
|
||||
|
||||
ep93xx_spi_cs_control(spi, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ep93xx_spi_transfer() - queue message to be transferred
|
||||
* @spi: target SPI device
|
||||
* @msg: message to be transferred
|
||||
*
|
||||
* This function is called by SPI device drivers when they are going to transfer
|
||||
* a new message. It simply puts the message in the queue and schedules
|
||||
* workqueue to perform the actual transfer later on.
|
||||
*
|
||||
* Returns %0 on success and negative error in case of failure.
|
||||
*/
|
||||
static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *msg)
|
||||
{
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(spi->master);
|
||||
struct spi_transfer *t;
|
||||
unsigned long flags;
|
||||
|
||||
if (!msg || !msg->complete)
|
||||
return -EINVAL;
|
||||
|
||||
/* first validate each transfer */
|
||||
list_for_each_entry(t, &msg->transfers, transfer_list) {
|
||||
if (t->speed_hz && t->speed_hz < espi->min_rate)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that we own the message, let's initialize it so that it is
|
||||
* suitable for us. We use @msg->status to signal whether there was
|
||||
* error in transfer and @msg->state is used to hold pointer to the
|
||||
* current transfer (or %NULL if no active current transfer).
|
||||
*/
|
||||
msg->state = NULL;
|
||||
msg->status = 0;
|
||||
msg->actual_length = 0;
|
||||
|
||||
spin_lock_irqsave(&espi->lock, flags);
|
||||
if (!espi->running) {
|
||||
spin_unlock_irqrestore(&espi->lock, flags);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
list_add_tail(&msg->queue, &espi->msg_queue);
|
||||
queue_work(espi->wq, &espi->msg_work);
|
||||
spin_unlock_irqrestore(&espi->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ep93xx_spi_cleanup() - cleans up master controller specific state
|
||||
* @spi: SPI device to cleanup
|
||||
|
@ -409,39 +307,40 @@ static void ep93xx_spi_cleanup(struct spi_device *spi)
|
|||
* ep93xx_spi_chip_setup() - configures hardware according to given @chip
|
||||
* @espi: ep93xx SPI controller struct
|
||||
* @chip: chip specific settings
|
||||
*
|
||||
* This function sets up the actual hardware registers with settings given in
|
||||
* @chip. Note that no validation is done so make sure that callers validate
|
||||
* settings before calling this.
|
||||
* @speed_hz: transfer speed
|
||||
* @bits_per_word: transfer bits_per_word
|
||||
*/
|
||||
static void ep93xx_spi_chip_setup(const struct ep93xx_spi *espi,
|
||||
const struct ep93xx_spi_chip *chip)
|
||||
static int ep93xx_spi_chip_setup(const struct ep93xx_spi *espi,
|
||||
const struct ep93xx_spi_chip *chip,
|
||||
u32 speed_hz, u8 bits_per_word)
|
||||
{
|
||||
u8 dss = bits_per_word_to_dss(bits_per_word);
|
||||
u8 div_cpsr = 0;
|
||||
u8 div_scr = 0;
|
||||
u16 cr0;
|
||||
int err;
|
||||
|
||||
cr0 = chip->div_scr << SSPCR0_SCR_SHIFT;
|
||||
err = ep93xx_spi_calc_divisors(espi, speed_hz, &div_cpsr, &div_scr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cr0 = div_scr << SSPCR0_SCR_SHIFT;
|
||||
cr0 |= (chip->spi->mode & (SPI_CPHA|SPI_CPOL)) << SSPCR0_MODE_SHIFT;
|
||||
cr0 |= chip->dss;
|
||||
cr0 |= dss;
|
||||
|
||||
dev_dbg(&espi->pdev->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n",
|
||||
chip->spi->mode, chip->div_cpsr, chip->div_scr, chip->dss);
|
||||
chip->spi->mode, div_cpsr, div_scr, dss);
|
||||
dev_dbg(&espi->pdev->dev, "setup: cr0 %#x", cr0);
|
||||
|
||||
ep93xx_spi_write_u8(espi, SSPCPSR, chip->div_cpsr);
|
||||
ep93xx_spi_write_u8(espi, SSPCPSR, div_cpsr);
|
||||
ep93xx_spi_write_u16(espi, SSPCR0, cr0);
|
||||
}
|
||||
|
||||
static inline int bits_per_word(const struct ep93xx_spi *espi)
|
||||
{
|
||||
struct spi_message *msg = espi->current_msg;
|
||||
struct spi_transfer *t = msg->state;
|
||||
|
||||
return t->bits_per_word;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t)
|
||||
{
|
||||
if (bits_per_word(espi) > 8) {
|
||||
if (t->bits_per_word > 8) {
|
||||
u16 tx_val = 0;
|
||||
|
||||
if (t->tx_buf)
|
||||
|
@ -460,7 +359,7 @@ static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t)
|
|||
|
||||
static void ep93xx_do_read(struct ep93xx_spi *espi, struct spi_transfer *t)
|
||||
{
|
||||
if (bits_per_word(espi) > 8) {
|
||||
if (t->bits_per_word > 8) {
|
||||
u16 rx_val;
|
||||
|
||||
rx_val = ep93xx_spi_read_u16(espi, SSPDR);
|
||||
|
@ -546,7 +445,7 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_transfer_direction dir)
|
|||
size_t len = t->len;
|
||||
int i, ret, nents;
|
||||
|
||||
if (bits_per_word(espi) > 8)
|
||||
if (t->bits_per_word > 8)
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
else
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
|
@ -610,7 +509,7 @@ ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_transfer_direction dir)
|
|||
}
|
||||
|
||||
if (WARN_ON(len)) {
|
||||
dev_warn(&espi->pdev->dev, "len = %d expected 0!", len);
|
||||
dev_warn(&espi->pdev->dev, "len = %zu expected 0!", len);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
|
@ -708,37 +607,16 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi,
|
|||
struct spi_transfer *t)
|
||||
{
|
||||
struct ep93xx_spi_chip *chip = spi_get_ctldata(msg->spi);
|
||||
int err;
|
||||
|
||||
msg->state = t;
|
||||
|
||||
/*
|
||||
* Handle any transfer specific settings if needed. We use
|
||||
* temporary chip settings here and restore original later when
|
||||
* the transfer is finished.
|
||||
*/
|
||||
if (t->speed_hz || t->bits_per_word) {
|
||||
struct ep93xx_spi_chip tmp_chip = *chip;
|
||||
|
||||
if (t->speed_hz) {
|
||||
int err;
|
||||
|
||||
err = ep93xx_spi_calc_divisors(espi, &tmp_chip,
|
||||
t->speed_hz);
|
||||
if (err) {
|
||||
dev_err(&espi->pdev->dev,
|
||||
"failed to adjust speed\n");
|
||||
msg->status = err;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (t->bits_per_word)
|
||||
tmp_chip.dss = bits_per_word_to_dss(t->bits_per_word);
|
||||
|
||||
/*
|
||||
* Set up temporary new hw settings for this transfer.
|
||||
*/
|
||||
ep93xx_spi_chip_setup(espi, &tmp_chip);
|
||||
err = ep93xx_spi_chip_setup(espi, chip, t->speed_hz, t->bits_per_word);
|
||||
if (err) {
|
||||
dev_err(&espi->pdev->dev,
|
||||
"failed to setup chip for transfer\n");
|
||||
msg->status = err;
|
||||
return;
|
||||
}
|
||||
|
||||
espi->rx = 0;
|
||||
|
@ -783,9 +661,6 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi,
|
|||
ep93xx_spi_cs_control(msg->spi, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (t->speed_hz || t->bits_per_word)
|
||||
ep93xx_spi_chip_setup(espi, chip);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -838,10 +713,8 @@ static void ep93xx_spi_process_message(struct ep93xx_spi *espi,
|
|||
espi->fifo_level = 0;
|
||||
|
||||
/*
|
||||
* Update SPI controller registers according to spi device and assert
|
||||
* the chipselect.
|
||||
* Assert the chipselect.
|
||||
*/
|
||||
ep93xx_spi_chip_setup(espi, spi_get_ctldata(msg->spi));
|
||||
ep93xx_spi_cs_control(msg->spi, true);
|
||||
|
||||
list_for_each_entry(t, &msg->transfers, transfer_list) {
|
||||
|
@ -858,50 +731,29 @@ static void ep93xx_spi_process_message(struct ep93xx_spi *espi,
|
|||
ep93xx_spi_disable(espi);
|
||||
}
|
||||
|
||||
#define work_to_espi(work) (container_of((work), struct ep93xx_spi, msg_work))
|
||||
|
||||
/**
|
||||
* ep93xx_spi_work() - EP93xx SPI workqueue worker function
|
||||
* @work: work struct
|
||||
*
|
||||
* Workqueue worker function. This function is called when there are new
|
||||
* SPI messages to be processed. Message is taken out from the queue and then
|
||||
* passed to ep93xx_spi_process_message().
|
||||
*
|
||||
* After message is transferred, protocol driver is notified by calling
|
||||
* @msg->complete(). In case of error, @msg->status is set to negative error
|
||||
* number, otherwise it contains zero (and @msg->actual_length is updated).
|
||||
*/
|
||||
static void ep93xx_spi_work(struct work_struct *work)
|
||||
static int ep93xx_spi_transfer_one_message(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct ep93xx_spi *espi = work_to_espi(work);
|
||||
struct spi_message *msg;
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
struct spi_transfer *t;
|
||||
|
||||
spin_lock_irq(&espi->lock);
|
||||
if (!espi->running || espi->current_msg ||
|
||||
list_empty(&espi->msg_queue)) {
|
||||
spin_unlock_irq(&espi->lock);
|
||||
return;
|
||||
/* first validate each transfer */
|
||||
list_for_each_entry(t, &msg->transfers, transfer_list) {
|
||||
if (t->speed_hz < espi->min_rate)
|
||||
return -EINVAL;
|
||||
}
|
||||
msg = list_first_entry(&espi->msg_queue, struct spi_message, queue);
|
||||
list_del_init(&msg->queue);
|
||||
|
||||
msg->state = NULL;
|
||||
msg->status = 0;
|
||||
msg->actual_length = 0;
|
||||
|
||||
espi->current_msg = msg;
|
||||
spin_unlock_irq(&espi->lock);
|
||||
|
||||
ep93xx_spi_process_message(espi, msg);
|
||||
|
||||
/*
|
||||
* Update the current message and re-schedule ourselves if there are
|
||||
* more messages in the queue.
|
||||
*/
|
||||
spin_lock_irq(&espi->lock);
|
||||
espi->current_msg = NULL;
|
||||
if (espi->running && !list_empty(&espi->msg_queue))
|
||||
queue_work(espi->wq, &espi->msg_work);
|
||||
spin_unlock_irq(&espi->lock);
|
||||
|
||||
/* notify the protocol driver that we are done with this message */
|
||||
msg->complete(msg->context);
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id)
|
||||
|
@ -1022,16 +874,26 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
|
|||
int irq;
|
||||
int error;
|
||||
|
||||
info = pdev->dev.platform_data;
|
||||
info = dev_get_platdata(&pdev->dev);
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*espi));
|
||||
if (!master) {
|
||||
dev_err(&pdev->dev, "failed to allocate spi master\n");
|
||||
return -ENOMEM;
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get irq resources\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "unable to get iomem resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*espi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
master->setup = ep93xx_spi_setup;
|
||||
master->transfer = ep93xx_spi_transfer;
|
||||
master->transfer_one_message = ep93xx_spi_transfer_one_message;
|
||||
master->cleanup = ep93xx_spi_cleanup;
|
||||
master->bus_num = pdev->id;
|
||||
master->num_chipselect = info->num_chipselect;
|
||||
|
@ -1042,14 +904,13 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
espi = spi_master_get_devdata(master);
|
||||
|
||||
espi->clk = clk_get(&pdev->dev, NULL);
|
||||
espi->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(espi->clk)) {
|
||||
dev_err(&pdev->dev, "unable to get spi clock\n");
|
||||
error = PTR_ERR(espi->clk);
|
||||
goto fail_release_master;
|
||||
}
|
||||
|
||||
spin_lock_init(&espi->lock);
|
||||
init_completion(&espi->wait);
|
||||
|
||||
/*
|
||||
|
@ -1060,55 +921,31 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
|
|||
espi->min_rate = clk_get_rate(espi->clk) / (254 * 256);
|
||||
espi->pdev = pdev;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
error = -EBUSY;
|
||||
dev_err(&pdev->dev, "failed to get irq resources\n");
|
||||
goto fail_put_clock;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "unable to get iomem resource\n");
|
||||
error = -ENODEV;
|
||||
goto fail_put_clock;
|
||||
}
|
||||
|
||||
espi->sspdr_phys = res->start + SSPDR;
|
||||
|
||||
espi->regs_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(espi->regs_base)) {
|
||||
error = PTR_ERR(espi->regs_base);
|
||||
goto fail_put_clock;
|
||||
goto fail_release_master;
|
||||
}
|
||||
|
||||
error = devm_request_irq(&pdev->dev, irq, ep93xx_spi_interrupt,
|
||||
0, "ep93xx-spi", espi);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to request irq\n");
|
||||
goto fail_put_clock;
|
||||
goto fail_release_master;
|
||||
}
|
||||
|
||||
if (info->use_dma && ep93xx_spi_setup_dma(espi))
|
||||
dev_warn(&pdev->dev, "DMA setup failed. Falling back to PIO\n");
|
||||
|
||||
espi->wq = create_singlethread_workqueue("ep93xx_spid");
|
||||
if (!espi->wq) {
|
||||
dev_err(&pdev->dev, "unable to create workqueue\n");
|
||||
error = -ENOMEM;
|
||||
goto fail_free_dma;
|
||||
}
|
||||
INIT_WORK(&espi->msg_work, ep93xx_spi_work);
|
||||
INIT_LIST_HEAD(&espi->msg_queue);
|
||||
espi->running = true;
|
||||
|
||||
/* make sure that the hardware is disabled */
|
||||
ep93xx_spi_write_u8(espi, SSPCR1, 0);
|
||||
|
||||
error = spi_register_master(master);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to register SPI master\n");
|
||||
goto fail_free_queue;
|
||||
goto fail_free_dma;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "EP93xx SPI Controller at 0x%08lx irq %d\n",
|
||||
|
@ -1116,12 +953,8 @@ static int ep93xx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
fail_free_queue:
|
||||
destroy_workqueue(espi->wq);
|
||||
fail_free_dma:
|
||||
ep93xx_spi_release_dma(espi);
|
||||
fail_put_clock:
|
||||
clk_put(espi->clk);
|
||||
fail_release_master:
|
||||
spi_master_put(master);
|
||||
|
||||
|
@ -1133,31 +966,7 @@ static int ep93xx_spi_remove(struct platform_device *pdev)
|
|||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct ep93xx_spi *espi = spi_master_get_devdata(master);
|
||||
|
||||
spin_lock_irq(&espi->lock);
|
||||
espi->running = false;
|
||||
spin_unlock_irq(&espi->lock);
|
||||
|
||||
destroy_workqueue(espi->wq);
|
||||
|
||||
/*
|
||||
* Complete remaining messages with %-ESHUTDOWN status.
|
||||
*/
|
||||
spin_lock_irq(&espi->lock);
|
||||
while (!list_empty(&espi->msg_queue)) {
|
||||
struct spi_message *msg;
|
||||
|
||||
msg = list_first_entry(&espi->msg_queue,
|
||||
struct spi_message, queue);
|
||||
list_del_init(&msg->queue);
|
||||
msg->status = -ESHUTDOWN;
|
||||
spin_unlock_irq(&espi->lock);
|
||||
msg->complete(msg->context);
|
||||
spin_lock_irq(&espi->lock);
|
||||
}
|
||||
spin_unlock_irq(&espi->lock);
|
||||
|
||||
ep93xx_spi_release_dma(espi);
|
||||
clk_put(espi->clk);
|
||||
|
||||
spi_unregister_master(master);
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,557 @@
|
|||
/*
|
||||
* drivers/spi/spi-fsl-dspi.c
|
||||
*
|
||||
* Copyright 2013 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Freescale DSPI driver
|
||||
* This file contains a driver for the Freescale DSPI
|
||||
*
|
||||
* 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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define DRIVER_NAME "fsl-dspi"
|
||||
|
||||
#define TRAN_STATE_RX_VOID 0x01
|
||||
#define TRAN_STATE_TX_VOID 0x02
|
||||
#define TRAN_STATE_WORD_ODD_NUM 0x04
|
||||
|
||||
#define DSPI_FIFO_SIZE 4
|
||||
|
||||
#define SPI_MCR 0x00
|
||||
#define SPI_MCR_MASTER (1 << 31)
|
||||
#define SPI_MCR_PCSIS (0x3F << 16)
|
||||
#define SPI_MCR_CLR_TXF (1 << 11)
|
||||
#define SPI_MCR_CLR_RXF (1 << 10)
|
||||
|
||||
#define SPI_TCR 0x08
|
||||
|
||||
#define SPI_CTAR(x) (0x0c + (x * 4))
|
||||
#define SPI_CTAR_FMSZ(x) (((x) & 0x0000000f) << 27)
|
||||
#define SPI_CTAR_CPOL(x) ((x) << 26)
|
||||
#define SPI_CTAR_CPHA(x) ((x) << 25)
|
||||
#define SPI_CTAR_LSBFE(x) ((x) << 24)
|
||||
#define SPI_CTAR_PCSSCR(x) (((x) & 0x00000003) << 22)
|
||||
#define SPI_CTAR_PASC(x) (((x) & 0x00000003) << 20)
|
||||
#define SPI_CTAR_PDT(x) (((x) & 0x00000003) << 18)
|
||||
#define SPI_CTAR_PBR(x) (((x) & 0x00000003) << 16)
|
||||
#define SPI_CTAR_CSSCK(x) (((x) & 0x0000000f) << 12)
|
||||
#define SPI_CTAR_ASC(x) (((x) & 0x0000000f) << 8)
|
||||
#define SPI_CTAR_DT(x) (((x) & 0x0000000f) << 4)
|
||||
#define SPI_CTAR_BR(x) ((x) & 0x0000000f)
|
||||
|
||||
#define SPI_CTAR0_SLAVE 0x0c
|
||||
|
||||
#define SPI_SR 0x2c
|
||||
#define SPI_SR_EOQF 0x10000000
|
||||
|
||||
#define SPI_RSER 0x30
|
||||
#define SPI_RSER_EOQFE 0x10000000
|
||||
|
||||
#define SPI_PUSHR 0x34
|
||||
#define SPI_PUSHR_CONT (1 << 31)
|
||||
#define SPI_PUSHR_CTAS(x) (((x) & 0x00000007) << 28)
|
||||
#define SPI_PUSHR_EOQ (1 << 27)
|
||||
#define SPI_PUSHR_CTCNT (1 << 26)
|
||||
#define SPI_PUSHR_PCS(x) (((1 << x) & 0x0000003f) << 16)
|
||||
#define SPI_PUSHR_TXDATA(x) ((x) & 0x0000ffff)
|
||||
|
||||
#define SPI_PUSHR_SLAVE 0x34
|
||||
|
||||
#define SPI_POPR 0x38
|
||||
#define SPI_POPR_RXDATA(x) ((x) & 0x0000ffff)
|
||||
|
||||
#define SPI_TXFR0 0x3c
|
||||
#define SPI_TXFR1 0x40
|
||||
#define SPI_TXFR2 0x44
|
||||
#define SPI_TXFR3 0x48
|
||||
#define SPI_RXFR0 0x7c
|
||||
#define SPI_RXFR1 0x80
|
||||
#define SPI_RXFR2 0x84
|
||||
#define SPI_RXFR3 0x88
|
||||
|
||||
#define SPI_FRAME_BITS(bits) SPI_CTAR_FMSZ((bits) - 1)
|
||||
#define SPI_FRAME_BITS_MASK SPI_CTAR_FMSZ(0xf)
|
||||
#define SPI_FRAME_BITS_16 SPI_CTAR_FMSZ(0xf)
|
||||
#define SPI_FRAME_BITS_8 SPI_CTAR_FMSZ(0x7)
|
||||
|
||||
#define SPI_CS_INIT 0x01
|
||||
#define SPI_CS_ASSERT 0x02
|
||||
#define SPI_CS_DROP 0x04
|
||||
|
||||
struct chip_data {
|
||||
u32 mcr_val;
|
||||
u32 ctar_val;
|
||||
u16 void_write_data;
|
||||
};
|
||||
|
||||
struct fsl_dspi {
|
||||
struct spi_bitbang bitbang;
|
||||
struct platform_device *pdev;
|
||||
|
||||
void *base;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
|
||||
struct spi_transfer *cur_transfer;
|
||||
struct chip_data *cur_chip;
|
||||
size_t len;
|
||||
void *tx;
|
||||
void *tx_end;
|
||||
void *rx;
|
||||
void *rx_end;
|
||||
char dataflags;
|
||||
u8 cs;
|
||||
u16 void_write_data;
|
||||
|
||||
wait_queue_head_t waitq;
|
||||
u32 waitflags;
|
||||
};
|
||||
|
||||
static inline int is_double_byte_mode(struct fsl_dspi *dspi)
|
||||
{
|
||||
return ((readl(dspi->base + SPI_CTAR(dspi->cs)) & SPI_FRAME_BITS_MASK)
|
||||
== SPI_FRAME_BITS(8)) ? 0 : 1;
|
||||
}
|
||||
|
||||
static void set_bit_mode(struct fsl_dspi *dspi, unsigned char bits)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = readl(dspi->base + SPI_CTAR(dspi->cs));
|
||||
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,
|
||||
unsigned long clkrate)
|
||||
{
|
||||
/* Valid baud rate pre-scaler values */
|
||||
int pbr_tbl[4] = {2, 3, 5, 7};
|
||||
int brs[16] = { 2, 4, 6, 8,
|
||||
16, 32, 64, 128,
|
||||
256, 512, 1024, 2048,
|
||||
4096, 8192, 16384, 32768 };
|
||||
int temp, i = 0, j = 0;
|
||||
|
||||
temp = clkrate / 2 / speed_hz;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pbr_tbl); i++)
|
||||
for (j = 0; j < ARRAY_SIZE(brs); j++) {
|
||||
if (pbr_tbl[i] * brs[j] >= temp) {
|
||||
*pbr = i;
|
||||
*br = j;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pr_warn("Can not find valid buad rate,speed_hz is %d,clkrate is %ld\
|
||||
,we use the max prescaler value.\n", speed_hz, clkrate);
|
||||
*pbr = ARRAY_SIZE(pbr_tbl) - 1;
|
||||
*br = ARRAY_SIZE(brs) - 1;
|
||||
}
|
||||
|
||||
static int dspi_transfer_write(struct fsl_dspi *dspi)
|
||||
{
|
||||
int tx_count = 0;
|
||||
int tx_word;
|
||||
u16 d16;
|
||||
u8 d8;
|
||||
u32 dspi_pushr = 0;
|
||||
int first = 1;
|
||||
|
||||
tx_word = is_double_byte_mode(dspi);
|
||||
|
||||
/* If we are in word mode, but only have a single byte to transfer
|
||||
* then switch to byte mode temporarily. Will switch back at the
|
||||
* end of the transfer.
|
||||
*/
|
||||
if (tx_word && (dspi->len == 1)) {
|
||||
dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
|
||||
set_bit_mode(dspi, 8);
|
||||
tx_word = 0;
|
||||
}
|
||||
|
||||
while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) {
|
||||
if (tx_word) {
|
||||
if (dspi->len == 1)
|
||||
break;
|
||||
|
||||
if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) {
|
||||
d16 = *(u16 *)dspi->tx;
|
||||
dspi->tx += 2;
|
||||
} else {
|
||||
d16 = dspi->void_write_data;
|
||||
}
|
||||
|
||||
dspi_pushr = SPI_PUSHR_TXDATA(d16) |
|
||||
SPI_PUSHR_PCS(dspi->cs) |
|
||||
SPI_PUSHR_CTAS(dspi->cs) |
|
||||
SPI_PUSHR_CONT;
|
||||
|
||||
dspi->len -= 2;
|
||||
} else {
|
||||
if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) {
|
||||
|
||||
d8 = *(u8 *)dspi->tx;
|
||||
dspi->tx++;
|
||||
} else {
|
||||
d8 = (u8)dspi->void_write_data;
|
||||
}
|
||||
|
||||
dspi_pushr = SPI_PUSHR_TXDATA(d8) |
|
||||
SPI_PUSHR_PCS(dspi->cs) |
|
||||
SPI_PUSHR_CTAS(dspi->cs) |
|
||||
SPI_PUSHR_CONT;
|
||||
|
||||
dspi->len--;
|
||||
}
|
||||
|
||||
if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) {
|
||||
/* last transfer in the transfer */
|
||||
dspi_pushr |= SPI_PUSHR_EOQ;
|
||||
} else if (tx_word && (dspi->len == 1))
|
||||
dspi_pushr |= SPI_PUSHR_EOQ;
|
||||
|
||||
if (first) {
|
||||
first = 0;
|
||||
dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */
|
||||
}
|
||||
|
||||
writel(dspi_pushr, dspi->base + SPI_PUSHR);
|
||||
tx_count++;
|
||||
}
|
||||
|
||||
return tx_count * (tx_word + 1);
|
||||
}
|
||||
|
||||
static int dspi_transfer_read(struct fsl_dspi *dspi)
|
||||
{
|
||||
int rx_count = 0;
|
||||
int rx_word = is_double_byte_mode(dspi);
|
||||
u16 d;
|
||||
while ((dspi->rx < dspi->rx_end)
|
||||
&& (rx_count < DSPI_FIFO_SIZE)) {
|
||||
if (rx_word) {
|
||||
if ((dspi->rx_end - dspi->rx) == 1)
|
||||
break;
|
||||
|
||||
d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR));
|
||||
|
||||
if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
|
||||
*(u16 *)dspi->rx = d;
|
||||
dspi->rx += 2;
|
||||
|
||||
} else {
|
||||
d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR));
|
||||
if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
|
||||
*(u8 *)dspi->rx = d;
|
||||
dspi->rx++;
|
||||
}
|
||||
rx_count++;
|
||||
}
|
||||
|
||||
return rx_count;
|
||||
}
|
||||
|
||||
static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
struct fsl_dspi *dspi = spi_master_get_devdata(spi->master);
|
||||
dspi->cur_transfer = t;
|
||||
dspi->cur_chip = spi_get_ctldata(spi);
|
||||
dspi->cs = spi->chip_select;
|
||||
dspi->void_write_data = dspi->cur_chip->void_write_data;
|
||||
|
||||
dspi->dataflags = 0;
|
||||
dspi->tx = (void *)t->tx_buf;
|
||||
dspi->tx_end = dspi->tx + t->len;
|
||||
dspi->rx = t->rx_buf;
|
||||
dspi->rx_end = dspi->rx + t->len;
|
||||
dspi->len = t->len;
|
||||
|
||||
if (!dspi->rx)
|
||||
dspi->dataflags |= TRAN_STATE_RX_VOID;
|
||||
|
||||
if (!dspi->tx)
|
||||
dspi->dataflags |= TRAN_STATE_TX_VOID;
|
||||
|
||||
writel(dspi->cur_chip->mcr_val, dspi->base + SPI_MCR);
|
||||
writel(dspi->cur_chip->ctar_val, dspi->base + SPI_CTAR(dspi->cs));
|
||||
writel(SPI_RSER_EOQFE, dspi->base + SPI_RSER);
|
||||
|
||||
if (t->speed_hz)
|
||||
writel(dspi->cur_chip->ctar_val,
|
||||
dspi->base + SPI_CTAR(dspi->cs));
|
||||
|
||||
dspi_transfer_write(dspi);
|
||||
|
||||
if (wait_event_interruptible(dspi->waitq, dspi->waitflags))
|
||||
dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n");
|
||||
dspi->waitflags = 0;
|
||||
|
||||
return t->len - dspi->len;
|
||||
}
|
||||
|
||||
static void dspi_chipselect(struct spi_device *spi, int value)
|
||||
{
|
||||
struct fsl_dspi *dspi = spi_master_get_devdata(spi->master);
|
||||
u32 pushr = readl(dspi->base + SPI_PUSHR);
|
||||
|
||||
switch (value) {
|
||||
case BITBANG_CS_ACTIVE:
|
||||
pushr |= SPI_PUSHR_CONT;
|
||||
case BITBANG_CS_INACTIVE:
|
||||
pushr &= ~SPI_PUSHR_CONT;
|
||||
}
|
||||
|
||||
writel(pushr, dspi->base + SPI_PUSHR);
|
||||
}
|
||||
|
||||
static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
struct chip_data *chip;
|
||||
struct fsl_dspi *dspi = spi_master_get_devdata(spi->master);
|
||||
unsigned char br = 0, pbr = 0, fmsz = 0;
|
||||
|
||||
/* Only alloc on first setup */
|
||||
chip = spi_get_ctldata(spi);
|
||||
if (chip == NULL) {
|
||||
chip = kcalloc(1, sizeof(struct chip_data), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
chip->mcr_val = SPI_MCR_MASTER | SPI_MCR_PCSIS |
|
||||
SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF;
|
||||
if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) {
|
||||
fmsz = spi->bits_per_word - 1;
|
||||
} else {
|
||||
pr_err("Invalid wordsize\n");
|
||||
kfree(chip);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chip->void_write_data = 0;
|
||||
|
||||
hz_to_spi_baud(&pbr, &br,
|
||||
spi->max_speed_hz, clk_get_rate(dspi->clk));
|
||||
|
||||
chip->ctar_val = SPI_CTAR_FMSZ(fmsz)
|
||||
| SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0)
|
||||
| SPI_CTAR_CPHA(spi->mode & SPI_CPHA ? 1 : 0)
|
||||
| SPI_CTAR_LSBFE(spi->mode & SPI_LSB_FIRST ? 1 : 0)
|
||||
| SPI_CTAR_PBR(pbr)
|
||||
| SPI_CTAR_BR(br);
|
||||
|
||||
spi_set_ctldata(spi, chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dspi_setup(struct spi_device *spi)
|
||||
{
|
||||
if (!spi->max_speed_hz)
|
||||
return -EINVAL;
|
||||
|
||||
if (!spi->bits_per_word)
|
||||
spi->bits_per_word = 8;
|
||||
|
||||
return dspi_setup_transfer(spi, NULL);
|
||||
}
|
||||
|
||||
static irqreturn_t dspi_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id;
|
||||
|
||||
writel(SPI_SR_EOQF, dspi->base + SPI_SR);
|
||||
|
||||
dspi_transfer_read(dspi);
|
||||
|
||||
if (!dspi->len) {
|
||||
if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM)
|
||||
set_bit_mode(dspi, 16);
|
||||
dspi->waitflags = 1;
|
||||
wake_up_interruptible(&dspi->waitq);
|
||||
} else {
|
||||
dspi_transfer_write(dspi);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct of_device_id fsl_dspi_dt_ids[] = {
|
||||
{ .compatible = "fsl,vf610-dspi", .data = NULL, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dspi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct fsl_dspi *dspi = spi_master_get_devdata(master);
|
||||
|
||||
spi_master_suspend(master);
|
||||
clk_disable_unprepare(dspi->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dspi_resume(struct device *dev)
|
||||
{
|
||||
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct fsl_dspi *dspi = spi_master_get_devdata(master);
|
||||
|
||||
clk_prepare_enable(dspi->clk);
|
||||
spi_master_resume(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops dspi_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dspi_suspend, dspi_resume)
|
||||
};
|
||||
|
||||
static int dspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct spi_master *master;
|
||||
struct fsl_dspi *dspi;
|
||||
struct resource *res;
|
||||
int ret = 0, cs_num, bus_num;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
dspi = spi_master_get_devdata(master);
|
||||
dspi->pdev = pdev;
|
||||
dspi->bitbang.master = spi_master_get(master);
|
||||
dspi->bitbang.chipselect = dspi_chipselect;
|
||||
dspi->bitbang.setup_transfer = dspi_setup_transfer;
|
||||
dspi->bitbang.txrx_bufs = dspi_txrx_transfer;
|
||||
dspi->bitbang.master->setup = dspi_setup;
|
||||
dspi->bitbang.master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(4) | SPI_BPW_MASK(8) |
|
||||
SPI_BPW_MASK(16);
|
||||
|
||||
ret = of_property_read_u32(np, "spi-num-chipselects", &cs_num);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "can't get spi-num-chipselects\n");
|
||||
goto out_master_put;
|
||||
}
|
||||
master->num_chipselect = cs_num;
|
||||
|
||||
ret = of_property_read_u32(np, "bus-num", &bus_num);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "can't get bus-num\n");
|
||||
goto out_master_put;
|
||||
}
|
||||
master->bus_num = bus_num;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't get platform resource\n");
|
||||
ret = -EINVAL;
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
dspi->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (!dspi->base) {
|
||||
ret = -EINVAL;
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
dspi->irq = platform_get_irq(pdev, 0);
|
||||
if (dspi->irq < 0) {
|
||||
dev_err(&pdev->dev, "can't get platform irq\n");
|
||||
ret = dspi->irq;
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, 0,
|
||||
pdev->name, dspi);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n");
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
dspi->clk = devm_clk_get(&pdev->dev, "dspi");
|
||||
if (IS_ERR(dspi->clk)) {
|
||||
ret = PTR_ERR(dspi->clk);
|
||||
dev_err(&pdev->dev, "unable to get clock\n");
|
||||
goto out_master_put;
|
||||
}
|
||||
clk_prepare_enable(dspi->clk);
|
||||
|
||||
init_waitqueue_head(&dspi->waitq);
|
||||
platform_set_drvdata(pdev, dspi);
|
||||
|
||||
ret = spi_bitbang_start(&dspi->bitbang);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "Problem registering DSPI master\n");
|
||||
goto out_clk_put;
|
||||
}
|
||||
|
||||
pr_info(KERN_INFO "Freescale DSPI master initialized\n");
|
||||
return ret;
|
||||
|
||||
out_clk_put:
|
||||
clk_disable_unprepare(dspi->clk);
|
||||
out_master_put:
|
||||
spi_master_put(master);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dspi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_dspi *dspi = platform_get_drvdata(pdev);
|
||||
|
||||
/* Disconnect from the SPI framework */
|
||||
spi_bitbang_stop(&dspi->bitbang);
|
||||
spi_master_put(dspi->bitbang.master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver fsl_dspi_driver = {
|
||||
.driver.name = DRIVER_NAME,
|
||||
.driver.of_match_table = fsl_dspi_dt_ids,
|
||||
.driver.owner = THIS_MODULE,
|
||||
.driver.pm = &dspi_pm,
|
||||
.probe = dspi_probe,
|
||||
.remove = dspi_remove,
|
||||
};
|
||||
module_platform_driver(fsl_dspi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Freescale DSPI Controller Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
|
@ -584,7 +584,7 @@ static void fsl_espi_remove(struct mpc8xxx_spi *mspi)
|
|||
static struct spi_master * fsl_espi_probe(struct device *dev,
|
||||
struct resource *mem, unsigned int irq)
|
||||
{
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct spi_master *master;
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
struct fsl_espi_reg *reg_base;
|
||||
|
@ -665,7 +665,7 @@ err:
|
|||
static int of_fsl_espi_get_chipselects(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
const u32 *prop;
|
||||
int len;
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ const char *mpc8xxx_spi_strmode(unsigned int flags)
|
|||
int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
|
||||
unsigned int irq)
|
||||
{
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct spi_master *master;
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
int ret = 0;
|
||||
|
|
|
@ -574,7 +574,7 @@ static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on)
|
|||
|
||||
static void fsl_spi_grlib_probe(struct device *dev)
|
||||
{
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
struct fsl_spi_reg *reg_base = mpc8xxx_spi->reg_base;
|
||||
|
@ -600,7 +600,7 @@ static void fsl_spi_grlib_probe(struct device *dev)
|
|||
static struct spi_master * fsl_spi_probe(struct device *dev,
|
||||
struct resource *mem, unsigned int irq)
|
||||
{
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct spi_master *master;
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
struct fsl_spi_reg *reg_base;
|
||||
|
@ -700,7 +700,8 @@ err:
|
|||
static void fsl_spi_cs_control(struct spi_device *spi, bool on)
|
||||
{
|
||||
struct device *dev = spi->dev.parent->parent;
|
||||
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data);
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
|
||||
u16 cs = spi->chip_select;
|
||||
int gpio = pinfo->gpios[cs];
|
||||
bool alow = pinfo->alow_flags[cs];
|
||||
|
@ -711,7 +712,7 @@ static void fsl_spi_cs_control(struct spi_device *spi, bool on)
|
|||
static int of_fsl_spi_get_chipselects(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
|
||||
int ngpios;
|
||||
int i = 0;
|
||||
|
@ -790,7 +791,7 @@ err_alloc_flags:
|
|||
|
||||
static int of_fsl_spi_free_chipselects(struct device *dev)
|
||||
{
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
|
||||
int i;
|
||||
|
||||
|
@ -889,7 +890,7 @@ static int plat_mpc8xxx_spi_probe(struct platform_device *pdev)
|
|||
int irq;
|
||||
struct spi_master *master;
|
||||
|
||||
if (!pdev->dev.platform_data)
|
||||
if (!dev_get_platdata(&pdev->dev))
|
||||
return -EINVAL;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
|
|
@ -420,7 +420,7 @@ static int spi_gpio_probe(struct platform_device *pdev)
|
|||
if (status > 0)
|
||||
use_of = 1;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
#ifdef GENERIC_BITBANG
|
||||
if (!pdata || !pdata->num_chipselect)
|
||||
return -ENODEV;
|
||||
|
@ -506,7 +506,7 @@ static int spi_gpio_remove(struct platform_device *pdev)
|
|||
int status;
|
||||
|
||||
spi_gpio = platform_get_drvdata(pdev);
|
||||
pdata = pdev->dev.platform_data;
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
/* stop() unregisters child devices too */
|
||||
status = spi_bitbang_stop(&spi_gpio->bitbang);
|
||||
|
|
|
@ -619,6 +619,7 @@ static const struct of_device_id spi_imx_dt_ids[] = {
|
|||
{ .compatible = "fsl,imx51-ecspi", .data = &imx51_ecspi_devtype_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spi_imx_dt_ids);
|
||||
|
||||
static void spi_imx_chipselect(struct spi_device *spi, int is_active)
|
||||
{
|
||||
|
@ -796,10 +797,11 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|||
if (!gpio_is_valid(cs_gpio))
|
||||
continue;
|
||||
|
||||
ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
|
||||
ret = devm_gpio_request(&pdev->dev, spi_imx->chipselect[i],
|
||||
DRIVER_NAME);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't get cs gpios\n");
|
||||
goto out_gpio_free;
|
||||
goto out_master_put;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -816,50 +818,44 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|||
(struct spi_imx_devtype_data *) pdev->id_entry->driver_data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't get platform resource\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_gpio_free;
|
||||
}
|
||||
|
||||
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
|
||||
dev_err(&pdev->dev, "request_mem_region failed\n");
|
||||
ret = -EBUSY;
|
||||
goto out_gpio_free;
|
||||
}
|
||||
|
||||
spi_imx->base = ioremap(res->start, resource_size(res));
|
||||
if (!spi_imx->base) {
|
||||
ret = -EINVAL;
|
||||
goto out_release_mem;
|
||||
spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(spi_imx->base)) {
|
||||
ret = PTR_ERR(spi_imx->base);
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
spi_imx->irq = platform_get_irq(pdev, 0);
|
||||
if (spi_imx->irq < 0) {
|
||||
ret = -EINVAL;
|
||||
goto out_iounmap;
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
ret = request_irq(spi_imx->irq, spi_imx_isr, 0, DRIVER_NAME, spi_imx);
|
||||
ret = devm_request_irq(&pdev->dev, spi_imx->irq, spi_imx_isr, 0,
|
||||
DRIVER_NAME, spi_imx);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret);
|
||||
goto out_iounmap;
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
spi_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
|
||||
if (IS_ERR(spi_imx->clk_ipg)) {
|
||||
ret = PTR_ERR(spi_imx->clk_ipg);
|
||||
goto out_free_irq;
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
spi_imx->clk_per = devm_clk_get(&pdev->dev, "per");
|
||||
if (IS_ERR(spi_imx->clk_per)) {
|
||||
ret = PTR_ERR(spi_imx->clk_per);
|
||||
goto out_free_irq;
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
clk_prepare_enable(spi_imx->clk_per);
|
||||
clk_prepare_enable(spi_imx->clk_ipg);
|
||||
ret = clk_prepare_enable(spi_imx->clk_per);
|
||||
if (ret)
|
||||
goto out_master_put;
|
||||
|
||||
ret = clk_prepare_enable(spi_imx->clk_ipg);
|
||||
if (ret)
|
||||
goto out_put_per;
|
||||
|
||||
spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per);
|
||||
|
||||
|
@ -879,47 +875,27 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
|
||||
out_clk_put:
|
||||
clk_disable_unprepare(spi_imx->clk_per);
|
||||
clk_disable_unprepare(spi_imx->clk_ipg);
|
||||
out_free_irq:
|
||||
free_irq(spi_imx->irq, spi_imx);
|
||||
out_iounmap:
|
||||
iounmap(spi_imx->base);
|
||||
out_release_mem:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
out_gpio_free:
|
||||
while (--i >= 0) {
|
||||
if (gpio_is_valid(spi_imx->chipselect[i]))
|
||||
gpio_free(spi_imx->chipselect[i]);
|
||||
}
|
||||
out_put_per:
|
||||
clk_disable_unprepare(spi_imx->clk_per);
|
||||
out_master_put:
|
||||
spi_master_put(master);
|
||||
kfree(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_imx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
|
||||
int i;
|
||||
|
||||
spi_bitbang_stop(&spi_imx->bitbang);
|
||||
|
||||
writel(0, spi_imx->base + MXC_CSPICTRL);
|
||||
clk_disable_unprepare(spi_imx->clk_per);
|
||||
clk_disable_unprepare(spi_imx->clk_ipg);
|
||||
free_irq(spi_imx->irq, spi_imx);
|
||||
iounmap(spi_imx->base);
|
||||
|
||||
for (i = 0; i < master->num_chipselect; i++)
|
||||
if (gpio_is_valid(spi_imx->chipselect[i]))
|
||||
gpio_free(spi_imx->chipselect[i]);
|
||||
|
||||
clk_disable_unprepare(spi_imx->clk_per);
|
||||
spi_master_put(master);
|
||||
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,8 @@ struct mpc512x_psc_spi {
|
|||
struct mpc512x_psc_fifo __iomem *fifo;
|
||||
unsigned int irq;
|
||||
u8 bits_per_word;
|
||||
u32 mclk;
|
||||
struct clk *clk_mclk;
|
||||
u32 mclk_rate;
|
||||
|
||||
struct completion txisrdone;
|
||||
};
|
||||
|
@ -72,6 +73,7 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi)
|
|||
struct mpc52xx_psc __iomem *psc = mps->psc;
|
||||
u32 sicr;
|
||||
u32 ccr;
|
||||
int speed;
|
||||
u16 bclkdiv;
|
||||
|
||||
sicr = in_be32(&psc->sicr);
|
||||
|
@ -95,10 +97,10 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi)
|
|||
|
||||
ccr = in_be32(&psc->ccr);
|
||||
ccr &= 0xFF000000;
|
||||
if (cs->speed_hz)
|
||||
bclkdiv = (mps->mclk / cs->speed_hz) - 1;
|
||||
else
|
||||
bclkdiv = (mps->mclk / 1000000) - 1; /* default 1MHz */
|
||||
speed = cs->speed_hz;
|
||||
if (!speed)
|
||||
speed = 1000000; /* default 1MHz */
|
||||
bclkdiv = (mps->mclk_rate / speed) - 1;
|
||||
|
||||
ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8));
|
||||
out_be32(&psc->ccr, ccr);
|
||||
|
@ -386,19 +388,11 @@ static int mpc512x_psc_spi_port_config(struct spi_master *master,
|
|||
{
|
||||
struct mpc52xx_psc __iomem *psc = mps->psc;
|
||||
struct mpc512x_psc_fifo __iomem *fifo = mps->fifo;
|
||||
struct clk *spiclk;
|
||||
int ret = 0;
|
||||
char name[32];
|
||||
u32 sicr;
|
||||
u32 ccr;
|
||||
int speed;
|
||||
u16 bclkdiv;
|
||||
|
||||
sprintf(name, "psc%d_mclk", master->bus_num);
|
||||
spiclk = clk_get(&master->dev, name);
|
||||
clk_enable(spiclk);
|
||||
mps->mclk = clk_get_rate(spiclk);
|
||||
clk_put(spiclk);
|
||||
|
||||
/* Reset the PSC into a known state */
|
||||
out_8(&psc->command, MPC52xx_PSC_RST_RX);
|
||||
out_8(&psc->command, MPC52xx_PSC_RST_TX);
|
||||
|
@ -425,7 +419,8 @@ static int mpc512x_psc_spi_port_config(struct spi_master *master,
|
|||
|
||||
ccr = in_be32(&psc->ccr);
|
||||
ccr &= 0xFF000000;
|
||||
bclkdiv = (mps->mclk / 1000000) - 1; /* default 1MHz */
|
||||
speed = 1000000; /* default 1MHz */
|
||||
bclkdiv = (mps->mclk_rate / speed) - 1;
|
||||
ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8));
|
||||
out_be32(&psc->ccr, ccr);
|
||||
|
||||
|
@ -445,7 +440,7 @@ static int mpc512x_psc_spi_port_config(struct spi_master *master,
|
|||
|
||||
mps->bits_per_word = 8;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id)
|
||||
|
@ -474,11 +469,14 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
|
|||
u32 size, unsigned int irq,
|
||||
s16 bus_num)
|
||||
{
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct mpc512x_psc_spi *mps;
|
||||
struct spi_master *master;
|
||||
int ret;
|
||||
void *tempp;
|
||||
int psc_num;
|
||||
char clk_name[16];
|
||||
struct clk *clk;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof *mps);
|
||||
if (master == NULL)
|
||||
|
@ -521,16 +519,29 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
|
|||
goto free_master;
|
||||
init_completion(&mps->txisrdone);
|
||||
|
||||
psc_num = master->bus_num;
|
||||
snprintf(clk_name, sizeof(clk_name), "psc%d_mclk", psc_num);
|
||||
clk = devm_clk_get(dev, clk_name);
|
||||
if (IS_ERR(clk))
|
||||
goto free_irq;
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
goto free_irq;
|
||||
mps->clk_mclk = clk;
|
||||
mps->mclk_rate = clk_get_rate(clk);
|
||||
|
||||
ret = mpc512x_psc_spi_port_config(master, mps);
|
||||
if (ret < 0)
|
||||
goto free_irq;
|
||||
goto free_clock;
|
||||
|
||||
ret = spi_register_master(master);
|
||||
if (ret < 0)
|
||||
goto free_irq;
|
||||
goto free_clock;
|
||||
|
||||
return ret;
|
||||
|
||||
free_clock:
|
||||
clk_disable_unprepare(mps->clk_mclk);
|
||||
free_irq:
|
||||
free_irq(mps->irq, mps);
|
||||
free_master:
|
||||
|
@ -547,6 +558,7 @@ static int mpc512x_psc_spi_do_remove(struct device *dev)
|
|||
struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
|
||||
|
||||
spi_unregister_master(master);
|
||||
clk_disable_unprepare(mps->clk_mclk);
|
||||
free_irq(mps->irq, mps);
|
||||
if (mps->psc)
|
||||
iounmap(mps->psc);
|
||||
|
|
|
@ -366,7 +366,7 @@ static irqreturn_t mpc52xx_psc_spi_isr(int irq, void *dev_id)
|
|||
static int mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr,
|
||||
u32 size, unsigned int irq, s16 bus_num)
|
||||
{
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct mpc52xx_psc_spi *mps;
|
||||
struct spi_master *master;
|
||||
int ret;
|
||||
|
|
|
@ -67,13 +67,8 @@ static int mxs_spi_setup_transfer(struct spi_device *dev,
|
|||
{
|
||||
struct mxs_spi *spi = spi_master_get_devdata(dev->master);
|
||||
struct mxs_ssp *ssp = &spi->ssp;
|
||||
uint8_t bits_per_word;
|
||||
uint32_t hz = 0;
|
||||
|
||||
bits_per_word = dev->bits_per_word;
|
||||
if (t && t->bits_per_word)
|
||||
bits_per_word = t->bits_per_word;
|
||||
|
||||
hz = dev->max_speed_hz;
|
||||
if (t && t->speed_hz)
|
||||
hz = min(hz, t->speed_hz);
|
||||
|
@ -513,7 +508,7 @@ static int mxs_spi_probe(struct platform_device *pdev)
|
|||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq_err = platform_get_irq(pdev, 0);
|
||||
if (!iores || irq_err < 0)
|
||||
if (irq_err < 0)
|
||||
return -EINVAL;
|
||||
|
||||
base = devm_ioremap_resource(&pdev->dev, iores);
|
||||
|
@ -563,25 +558,31 @@ static int mxs_spi_probe(struct platform_device *pdev)
|
|||
goto out_master_free;
|
||||
}
|
||||
|
||||
clk_prepare_enable(ssp->clk);
|
||||
ret = clk_prepare_enable(ssp->clk);
|
||||
if (ret)
|
||||
goto out_dma_release;
|
||||
|
||||
clk_set_rate(ssp->clk, clk_freq);
|
||||
ssp->clk_rate = clk_get_rate(ssp->clk) / 1000;
|
||||
|
||||
stmp_reset_block(ssp->base);
|
||||
ret = stmp_reset_block(ssp->base);
|
||||
if (ret)
|
||||
goto out_disable_clk;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
ret = spi_register_master(master);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret);
|
||||
goto out_free_dma;
|
||||
goto out_disable_clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_dma:
|
||||
dma_release_channel(ssp->dmach);
|
||||
out_disable_clk:
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
out_dma_release:
|
||||
dma_release_channel(ssp->dmach);
|
||||
out_master_free:
|
||||
spi_master_put(master);
|
||||
return ret;
|
||||
|
@ -598,11 +599,8 @@ static int mxs_spi_remove(struct platform_device *pdev)
|
|||
ssp = &spi->ssp;
|
||||
|
||||
spi_unregister_master(master);
|
||||
|
||||
dma_release_channel(ssp->dmach);
|
||||
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
|
||||
dma_release_channel(ssp->dmach);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -174,17 +174,6 @@ static void nuc900_spi_gobusy(struct nuc900_spi *hw)
|
|||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
}
|
||||
|
||||
static int nuc900_spi_setupxfer(struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nuc900_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count)
|
||||
{
|
||||
return hw->tx ? hw->tx[count] : 0;
|
||||
|
@ -361,7 +350,7 @@ static int nuc900_spi_probe(struct platform_device *pdev)
|
|||
|
||||
hw = spi_master_get_devdata(master);
|
||||
hw->master = spi_master_get(master);
|
||||
hw->pdata = pdev->dev.platform_data;
|
||||
hw->pdata = dev_get_platdata(&pdev->dev);
|
||||
hw->dev = &pdev->dev;
|
||||
|
||||
if (hw->pdata == NULL) {
|
||||
|
@ -373,14 +362,12 @@ static int nuc900_spi_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, hw);
|
||||
init_completion(&hw->done);
|
||||
|
||||
master->mode_bits = SPI_MODE_0;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
master->num_chipselect = hw->pdata->num_cs;
|
||||
master->bus_num = hw->pdata->bus_num;
|
||||
hw->bitbang.master = hw->master;
|
||||
hw->bitbang.setup_transfer = nuc900_spi_setupxfer;
|
||||
hw->bitbang.chipselect = nuc900_spi_chipsel;
|
||||
hw->bitbang.txrx_bufs = nuc900_spi_txrx;
|
||||
hw->bitbang.master->setup = nuc900_spi_setup;
|
||||
|
||||
hw->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (hw->res == NULL) {
|
||||
|
|
|
@ -285,7 +285,7 @@ static int tiny_spi_of_probe(struct platform_device *pdev)
|
|||
|
||||
static int tiny_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tiny_spi_platform_data *platp = pdev->dev.platform_data;
|
||||
struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev);
|
||||
struct tiny_spi *hw;
|
||||
struct spi_master *master;
|
||||
struct resource *res;
|
||||
|
@ -315,15 +315,11 @@ static int tiny_spi_probe(struct platform_device *pdev)
|
|||
|
||||
/* find and map our resources */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
goto exit_busy;
|
||||
if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
|
||||
pdev->name))
|
||||
goto exit_busy;
|
||||
hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!hw->base)
|
||||
goto exit_busy;
|
||||
hw->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hw->base)) {
|
||||
err = PTR_ERR(hw->base);
|
||||
goto exit;
|
||||
}
|
||||
/* irq is optional */
|
||||
hw->irq = platform_get_irq(pdev, 0);
|
||||
if (hw->irq >= 0) {
|
||||
|
@ -337,8 +333,10 @@ static int tiny_spi_probe(struct platform_device *pdev)
|
|||
if (platp) {
|
||||
hw->gpio_cs_count = platp->gpio_cs_count;
|
||||
hw->gpio_cs = platp->gpio_cs;
|
||||
if (platp->gpio_cs_count && !platp->gpio_cs)
|
||||
goto exit_busy;
|
||||
if (platp->gpio_cs_count && !platp->gpio_cs) {
|
||||
err = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
hw->freq = platp->freq;
|
||||
hw->baudwidth = platp->baudwidth;
|
||||
} else {
|
||||
|
@ -365,8 +363,6 @@ static int tiny_spi_probe(struct platform_device *pdev)
|
|||
exit_gpio:
|
||||
while (i-- > 0)
|
||||
gpio_free(hw->gpio_cs[i]);
|
||||
exit_busy:
|
||||
err = -EBUSY;
|
||||
exit:
|
||||
spi_master_put(master);
|
||||
return err;
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#define OCTEON_SPI_MAX_CLOCK_HZ 16000000
|
||||
|
||||
struct octeon_spi {
|
||||
struct spi_master *my_master;
|
||||
u64 register_base;
|
||||
u64 last_cfg;
|
||||
u64 cs_enax;
|
||||
|
@ -64,7 +63,6 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
|
|||
unsigned int speed_hz;
|
||||
int mode;
|
||||
bool cpha, cpol;
|
||||
int bits_per_word;
|
||||
const u8 *tx_buf;
|
||||
u8 *rx_buf;
|
||||
int len;
|
||||
|
@ -76,12 +74,9 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
|
|||
mode = msg_setup->mode;
|
||||
cpha = mode & SPI_CPHA;
|
||||
cpol = mode & SPI_CPOL;
|
||||
bits_per_word = msg_setup->bits_per_word;
|
||||
|
||||
if (xfer->speed_hz)
|
||||
speed_hz = xfer->speed_hz;
|
||||
if (xfer->bits_per_word)
|
||||
bits_per_word = xfer->bits_per_word;
|
||||
|
||||
if (speed_hz > OCTEON_SPI_MAX_CLOCK_HZ)
|
||||
speed_hz = OCTEON_SPI_MAX_CLOCK_HZ;
|
||||
|
@ -166,19 +161,6 @@ static int octeon_spi_do_transfer(struct octeon_spi *p,
|
|||
return xfer->len;
|
||||
}
|
||||
|
||||
static int octeon_spi_validate_bpw(struct spi_device *spi, u32 speed)
|
||||
{
|
||||
switch (speed) {
|
||||
case 8:
|
||||
break;
|
||||
default:
|
||||
dev_err(&spi->dev, "Error: %d bits per word not supported\n",
|
||||
speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int octeon_spi_transfer_one_message(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
|
@ -196,15 +178,6 @@ static int octeon_spi_transfer_one_message(struct spi_master *master,
|
|||
goto err;
|
||||
}
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
if (xfer->bits_per_word) {
|
||||
status = octeon_spi_validate_bpw(msg->spi,
|
||||
xfer->bits_per_word);
|
||||
if (status)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
bool last_xfer = &xfer->transfer_list == msg->transfers.prev;
|
||||
int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer);
|
||||
|
@ -236,14 +209,9 @@ static struct octeon_spi_setup *octeon_spi_new_setup(struct spi_device *spi)
|
|||
|
||||
static int octeon_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
int r;
|
||||
struct octeon_spi_setup *new_setup;
|
||||
struct octeon_spi_setup *old_setup = spi_get_ctldata(spi);
|
||||
|
||||
r = octeon_spi_validate_bpw(spi, spi->bits_per_word);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
new_setup = octeon_spi_new_setup(spi);
|
||||
if (!new_setup)
|
||||
return -ENOMEM;
|
||||
|
@ -261,14 +229,8 @@ static void octeon_spi_cleanup(struct spi_device *spi)
|
|||
kfree(old_setup);
|
||||
}
|
||||
|
||||
static int octeon_spi_nop_transfer_hardware(struct spi_master *master)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int octeon_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
||||
struct resource *res_mem;
|
||||
struct spi_master *master;
|
||||
struct octeon_spi *p;
|
||||
|
@ -278,8 +240,7 @@ static int octeon_spi_probe(struct platform_device *pdev)
|
|||
if (!master)
|
||||
return -ENOMEM;
|
||||
p = spi_master_get_devdata(master);
|
||||
platform_set_drvdata(pdev, p);
|
||||
p->my_master = master;
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
|
@ -307,9 +268,8 @@ static int octeon_spi_probe(struct platform_device *pdev)
|
|||
|
||||
master->setup = octeon_spi_setup;
|
||||
master->cleanup = octeon_spi_cleanup;
|
||||
master->prepare_transfer_hardware = octeon_spi_nop_transfer_hardware;
|
||||
master->transfer_one_message = octeon_spi_transfer_one_message;
|
||||
master->unprepare_transfer_hardware = octeon_spi_nop_transfer_hardware;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
err = spi_register_master(master);
|
||||
|
@ -328,10 +288,11 @@ fail:
|
|||
|
||||
static int octeon_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct octeon_spi *p = platform_get_drvdata(pdev);
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct octeon_spi *p = spi_master_get_devdata(master);
|
||||
u64 register_base = p->register_base;
|
||||
|
||||
spi_unregister_master(p->my_master);
|
||||
spi_unregister_master(master);
|
||||
|
||||
/* Clear the CSENA* and put everything in a known state. */
|
||||
cvmx_write_csr(register_base + OCTEON_SPI_CFG, 0);
|
||||
|
|
|
@ -83,11 +83,6 @@
|
|||
#define SPI_SHUTDOWN 1
|
||||
|
||||
struct omap1_spi100k {
|
||||
struct work_struct work;
|
||||
|
||||
/* lock protects queue and registers */
|
||||
spinlock_t lock;
|
||||
struct list_head msg_queue;
|
||||
struct spi_master *master;
|
||||
struct clk *ick;
|
||||
struct clk *fck;
|
||||
|
@ -104,8 +99,6 @@ struct omap1_spi100k_cs {
|
|||
int word_len;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *omap1_spi100k_wq;
|
||||
|
||||
#define MOD_REG_BIT(val, mask, set) do { \
|
||||
if (set) \
|
||||
val |= mask; \
|
||||
|
@ -310,170 +303,102 @@ static int omap1_spi100k_setup(struct spi_device *spi)
|
|||
|
||||
spi100k_open(spi->master);
|
||||
|
||||
clk_enable(spi100k->ick);
|
||||
clk_enable(spi100k->fck);
|
||||
clk_prepare_enable(spi100k->ick);
|
||||
clk_prepare_enable(spi100k->fck);
|
||||
|
||||
ret = omap1_spi100k_setup_transfer(spi, NULL);
|
||||
|
||||
clk_disable(spi100k->ick);
|
||||
clk_disable(spi100k->fck);
|
||||
clk_disable_unprepare(spi100k->ick);
|
||||
clk_disable_unprepare(spi100k->fck);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void omap1_spi100k_work(struct work_struct *work)
|
||||
static int omap1_spi100k_prepare_hardware(struct spi_master *master)
|
||||
{
|
||||
struct omap1_spi100k *spi100k;
|
||||
int status = 0;
|
||||
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
||||
|
||||
spi100k = container_of(work, struct omap1_spi100k, work);
|
||||
spin_lock_irq(&spi100k->lock);
|
||||
|
||||
clk_enable(spi100k->ick);
|
||||
clk_enable(spi100k->fck);
|
||||
|
||||
/* We only enable one channel at a time -- the one whose message is
|
||||
* at the head of the queue -- although this controller would gladly
|
||||
* arbitrate among multiple channels. This corresponds to "single
|
||||
* channel" master mode. As a side effect, we need to manage the
|
||||
* chipselect with the FORCE bit ... CS != channel enable.
|
||||
*/
|
||||
while (!list_empty(&spi100k->msg_queue)) {
|
||||
struct spi_message *m;
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
int cs_active = 0;
|
||||
struct omap1_spi100k_cs *cs;
|
||||
int par_override = 0;
|
||||
|
||||
m = container_of(spi100k->msg_queue.next, struct spi_message,
|
||||
queue);
|
||||
|
||||
list_del_init(&m->queue);
|
||||
spin_unlock_irq(&spi100k->lock);
|
||||
|
||||
spi = m->spi;
|
||||
cs = spi->controller_state;
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
|
||||
status = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (par_override || t->speed_hz || t->bits_per_word) {
|
||||
par_override = 1;
|
||||
status = omap1_spi100k_setup_transfer(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
if (!t->speed_hz && !t->bits_per_word)
|
||||
par_override = 0;
|
||||
}
|
||||
|
||||
if (!cs_active) {
|
||||
omap1_spi100k_force_cs(spi100k, 1);
|
||||
cs_active = 1;
|
||||
}
|
||||
|
||||
if (t->len) {
|
||||
unsigned count;
|
||||
|
||||
count = omap1_spi100k_txrx_pio(spi, t);
|
||||
m->actual_length += count;
|
||||
|
||||
if (count != t->len) {
|
||||
status = -EIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (t->delay_usecs)
|
||||
udelay(t->delay_usecs);
|
||||
|
||||
/* ignore the "leave it on after last xfer" hint */
|
||||
|
||||
if (t->cs_change) {
|
||||
omap1_spi100k_force_cs(spi100k, 0);
|
||||
cs_active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore defaults if they were overriden */
|
||||
if (par_override) {
|
||||
par_override = 0;
|
||||
status = omap1_spi100k_setup_transfer(spi, NULL);
|
||||
}
|
||||
|
||||
if (cs_active)
|
||||
omap1_spi100k_force_cs(spi100k, 0);
|
||||
|
||||
m->status = status;
|
||||
m->complete(m->context);
|
||||
|
||||
spin_lock_irq(&spi100k->lock);
|
||||
}
|
||||
|
||||
clk_disable(spi100k->ick);
|
||||
clk_disable(spi100k->fck);
|
||||
spin_unlock_irq(&spi100k->lock);
|
||||
|
||||
if (status < 0)
|
||||
printk(KERN_WARNING "spi transfer failed with %d\n", status);
|
||||
}
|
||||
|
||||
static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m)
|
||||
{
|
||||
struct omap1_spi100k *spi100k;
|
||||
unsigned long flags;
|
||||
struct spi_transfer *t;
|
||||
|
||||
m->actual_length = 0;
|
||||
m->status = -EINPROGRESS;
|
||||
|
||||
spi100k = spi_master_get_devdata(spi->master);
|
||||
|
||||
/* Don't accept new work if we're shutting down */
|
||||
if (spi100k->state == SPI_SHUTDOWN)
|
||||
return -ESHUTDOWN;
|
||||
|
||||
/* reject invalid messages and transfers */
|
||||
if (list_empty(&m->transfers) || !m->complete)
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
const void *tx_buf = t->tx_buf;
|
||||
void *rx_buf = t->rx_buf;
|
||||
unsigned len = t->len;
|
||||
|
||||
if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ
|
||||
|| (len && !(rx_buf || tx_buf))) {
|
||||
dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
|
||||
t->speed_hz,
|
||||
len,
|
||||
tx_buf ? "tx" : "",
|
||||
rx_buf ? "rx" : "",
|
||||
t->bits_per_word);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) {
|
||||
dev_dbg(&spi->dev, "%d Hz max exceeds %d\n",
|
||||
t->speed_hz,
|
||||
OMAP1_SPI100K_MAX_FREQ/(1<<16));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&spi100k->lock, flags);
|
||||
list_add_tail(&m->queue, &spi100k->msg_queue);
|
||||
queue_work(omap1_spi100k_wq, &spi100k->work);
|
||||
spin_unlock_irqrestore(&spi100k->lock, flags);
|
||||
clk_prepare_enable(spi100k->ick);
|
||||
clk_prepare_enable(spi100k->fck);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap1_spi100k_reset(struct omap1_spi100k *spi100k)
|
||||
static int omap1_spi100k_transfer_one_message(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
||||
struct spi_device *spi = m->spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
int cs_active = 0;
|
||||
int par_override = 0;
|
||||
int status = 0;
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
|
||||
status = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (par_override || t->speed_hz || t->bits_per_word) {
|
||||
par_override = 1;
|
||||
status = omap1_spi100k_setup_transfer(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
if (!t->speed_hz && !t->bits_per_word)
|
||||
par_override = 0;
|
||||
}
|
||||
|
||||
if (!cs_active) {
|
||||
omap1_spi100k_force_cs(spi100k, 1);
|
||||
cs_active = 1;
|
||||
}
|
||||
|
||||
if (t->len) {
|
||||
unsigned count;
|
||||
|
||||
count = omap1_spi100k_txrx_pio(spi, t);
|
||||
m->actual_length += count;
|
||||
|
||||
if (count != t->len) {
|
||||
status = -EIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (t->delay_usecs)
|
||||
udelay(t->delay_usecs);
|
||||
|
||||
/* ignore the "leave it on after last xfer" hint */
|
||||
|
||||
if (t->cs_change) {
|
||||
omap1_spi100k_force_cs(spi100k, 0);
|
||||
cs_active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore defaults if they were overriden */
|
||||
if (par_override) {
|
||||
par_override = 0;
|
||||
status = omap1_spi100k_setup_transfer(spi, NULL);
|
||||
}
|
||||
|
||||
if (cs_active)
|
||||
omap1_spi100k_force_cs(spi100k, 0);
|
||||
|
||||
m->status = status;
|
||||
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int omap1_spi100k_unprepare_hardware(struct spi_master *master)
|
||||
{
|
||||
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
||||
|
||||
clk_disable_unprepare(spi100k->ick);
|
||||
clk_disable_unprepare(spi100k->fck);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -496,11 +421,15 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
|||
master->bus_num = pdev->id;
|
||||
|
||||
master->setup = omap1_spi100k_setup;
|
||||
master->transfer = omap1_spi100k_transfer;
|
||||
master->transfer_one_message = omap1_spi100k_transfer_one_message;
|
||||
master->prepare_transfer_hardware = omap1_spi100k_prepare_hardware;
|
||||
master->unprepare_transfer_hardware = omap1_spi100k_unprepare_hardware;
|
||||
master->cleanup = NULL;
|
||||
master->num_chipselect = 2;
|
||||
master->mode_bits = MODEBITS;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||
master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16);
|
||||
master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
|
@ -512,42 +441,31 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
|||
* You should allocate this with ioremap() before initializing
|
||||
* the SPI.
|
||||
*/
|
||||
spi100k->base = (void __iomem *) pdev->dev.platform_data;
|
||||
spi100k->base = (void __iomem *)dev_get_platdata(&pdev->dev);
|
||||
|
||||
INIT_WORK(&spi100k->work, omap1_spi100k_work);
|
||||
|
||||
spin_lock_init(&spi100k->lock);
|
||||
INIT_LIST_HEAD(&spi100k->msg_queue);
|
||||
spi100k->ick = clk_get(&pdev->dev, "ick");
|
||||
spi100k->ick = devm_clk_get(&pdev->dev, "ick");
|
||||
if (IS_ERR(spi100k->ick)) {
|
||||
dev_dbg(&pdev->dev, "can't get spi100k_ick\n");
|
||||
status = PTR_ERR(spi100k->ick);
|
||||
goto err1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
spi100k->fck = clk_get(&pdev->dev, "fck");
|
||||
spi100k->fck = devm_clk_get(&pdev->dev, "fck");
|
||||
if (IS_ERR(spi100k->fck)) {
|
||||
dev_dbg(&pdev->dev, "can't get spi100k_fck\n");
|
||||
status = PTR_ERR(spi100k->fck);
|
||||
goto err2;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (omap1_spi100k_reset(spi100k) < 0)
|
||||
goto err3;
|
||||
|
||||
status = spi_register_master(master);
|
||||
if (status < 0)
|
||||
goto err3;
|
||||
goto err;
|
||||
|
||||
spi100k->state = SPI_RUNNING;
|
||||
|
||||
return status;
|
||||
|
||||
err3:
|
||||
clk_put(spi100k->fck);
|
||||
err2:
|
||||
clk_put(spi100k->ick);
|
||||
err1:
|
||||
err:
|
||||
spi_master_put(master);
|
||||
return status;
|
||||
}
|
||||
|
@ -557,33 +475,14 @@ static int omap1_spi100k_remove(struct platform_device *pdev)
|
|||
struct spi_master *master;
|
||||
struct omap1_spi100k *spi100k;
|
||||
struct resource *r;
|
||||
unsigned limit = 500;
|
||||
unsigned long flags;
|
||||
int status = 0;
|
||||
|
||||
master = platform_get_drvdata(pdev);
|
||||
spi100k = spi_master_get_devdata(master);
|
||||
|
||||
spin_lock_irqsave(&spi100k->lock, flags);
|
||||
|
||||
spi100k->state = SPI_SHUTDOWN;
|
||||
while (!list_empty(&spi100k->msg_queue) && limit--) {
|
||||
spin_unlock_irqrestore(&spi100k->lock, flags);
|
||||
msleep(10);
|
||||
spin_lock_irqsave(&spi100k->lock, flags);
|
||||
}
|
||||
|
||||
if (!list_empty(&spi100k->msg_queue))
|
||||
status = -EBUSY;
|
||||
|
||||
spin_unlock_irqrestore(&spi100k->lock, flags);
|
||||
|
||||
if (status != 0)
|
||||
return status;
|
||||
|
||||
clk_put(spi100k->fck);
|
||||
clk_put(spi100k->ick);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
spi_unregister_master(master);
|
||||
|
@ -596,30 +495,11 @@ static struct platform_driver omap1_spi100k_driver = {
|
|||
.name = "omap1_spi100k",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = omap1_spi100k_probe,
|
||||
.remove = omap1_spi100k_remove,
|
||||
};
|
||||
|
||||
|
||||
static int __init omap1_spi100k_init(void)
|
||||
{
|
||||
omap1_spi100k_wq = create_singlethread_workqueue(
|
||||
omap1_spi100k_driver.driver.name);
|
||||
|
||||
if (omap1_spi100k_wq == NULL)
|
||||
return -1;
|
||||
|
||||
return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe);
|
||||
}
|
||||
|
||||
static void __exit omap1_spi100k_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap1_spi100k_driver);
|
||||
|
||||
destroy_workqueue(omap1_spi100k_wq);
|
||||
}
|
||||
|
||||
module_init(omap1_spi100k_init);
|
||||
module_exit(omap1_spi100k_exit);
|
||||
module_platform_driver(omap1_spi100k_driver);
|
||||
|
||||
MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver");
|
||||
MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>");
|
||||
|
|
|
@ -335,23 +335,6 @@ static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi)
|
|||
__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
|
||||
}
|
||||
|
||||
static int omap2_prepare_transfer(struct spi_master *master)
|
||||
{
|
||||
struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_get_sync(mcspi->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap2_unprepare_transfer(struct spi_master *master)
|
||||
{
|
||||
struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_mark_last_busy(mcspi->dev);
|
||||
pm_runtime_put_autosuspend(mcspi->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
@ -1318,8 +1301,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
|||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||
master->setup = omap2_mcspi_setup;
|
||||
master->prepare_transfer_hardware = omap2_prepare_transfer;
|
||||
master->unprepare_transfer_hardware = omap2_unprepare_transfer;
|
||||
master->auto_runtime_pm = true;
|
||||
master->transfer_one_message = omap2_mcspi_transfer_one_message;
|
||||
master->cleanup = omap2_mcspi_cleanup;
|
||||
master->dev.of_node = node;
|
||||
|
@ -1340,7 +1322,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
|||
if (of_get_property(node, "ti,pindir-d0-out-d1-in", NULL))
|
||||
mcspi->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN;
|
||||
} else {
|
||||
pdata = pdev->dev.platform_data;
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
master->num_chipselect = pdata->num_cs;
|
||||
if (pdev->id != -1)
|
||||
master->bus_num = pdev->id;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define DRIVER_NAME "orion_spi"
|
||||
|
@ -446,30 +447,22 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||
spi->min_speed = DIV_ROUND_UP(tclk_hz, 30);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (r == NULL) {
|
||||
status = -ENODEV;
|
||||
spi->base = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(spi->base)) {
|
||||
status = PTR_ERR(spi->base);
|
||||
goto out_rel_clk;
|
||||
}
|
||||
|
||||
if (!request_mem_region(r->start, resource_size(r),
|
||||
dev_name(&pdev->dev))) {
|
||||
status = -EBUSY;
|
||||
goto out_rel_clk;
|
||||
}
|
||||
spi->base = ioremap(r->start, SZ_1K);
|
||||
|
||||
if (orion_spi_reset(spi) < 0)
|
||||
goto out_rel_mem;
|
||||
goto out_rel_clk;
|
||||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
status = spi_register_master(master);
|
||||
if (status < 0)
|
||||
goto out_rel_mem;
|
||||
goto out_rel_clk;
|
||||
|
||||
return status;
|
||||
|
||||
out_rel_mem:
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
out_rel_clk:
|
||||
clk_disable_unprepare(spi->clk);
|
||||
clk_put(spi->clk);
|
||||
|
@ -482,7 +475,6 @@ out:
|
|||
static int orion_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct resource *r;
|
||||
struct orion_spi *spi;
|
||||
|
||||
master = platform_get_drvdata(pdev);
|
||||
|
@ -491,9 +483,6 @@ static int orion_spi_remove(struct platform_device *pdev)
|
|||
clk_disable_unprepare(spi->clk);
|
||||
clk_put(spi->clk);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(r->start, resource_size(r));
|
||||
|
||||
spi_unregister_master(master);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1555,18 +1555,6 @@ static int pl022_transfer_one_message(struct spi_master *master,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pl022_prepare_transfer_hardware(struct spi_master *master)
|
||||
{
|
||||
struct pl022 *pl022 = spi_master_get_devdata(master);
|
||||
|
||||
/*
|
||||
* Just make sure we have all we need to run the transfer by syncing
|
||||
* with the runtime PM framework.
|
||||
*/
|
||||
pm_runtime_get_sync(&pl022->adev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pl022_unprepare_transfer_hardware(struct spi_master *master)
|
||||
{
|
||||
struct pl022 *pl022 = spi_master_get_devdata(master);
|
||||
|
@ -1575,13 +1563,6 @@ static int pl022_unprepare_transfer_hardware(struct spi_master *master)
|
|||
writew((readw(SSP_CR1(pl022->virtbase)) &
|
||||
(~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
|
||||
|
||||
if (pl022->master_info->autosuspend_delay > 0) {
|
||||
pm_runtime_mark_last_busy(&pl022->adev->dev);
|
||||
pm_runtime_put_autosuspend(&pl022->adev->dev);
|
||||
} else {
|
||||
pm_runtime_put(&pl022->adev->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2091,7 +2072,8 @@ pl022_platform_data_dt_get(struct device *dev)
|
|||
static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
struct device *dev = &adev->dev;
|
||||
struct pl022_ssp_controller *platform_info = adev->dev.platform_data;
|
||||
struct pl022_ssp_controller *platform_info =
|
||||
dev_get_platdata(&adev->dev);
|
||||
struct spi_master *master;
|
||||
struct pl022 *pl022 = NULL; /*Data for this driver */
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
|
@ -2139,7 +2121,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
master->num_chipselect = num_cs;
|
||||
master->cleanup = pl022_cleanup;
|
||||
master->setup = pl022_setup;
|
||||
master->prepare_transfer_hardware = pl022_prepare_transfer_hardware;
|
||||
master->auto_runtime_pm = true;
|
||||
master->transfer_one_message = pl022_transfer_one_message;
|
||||
master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
|
||||
master->rt = platform_info->rt;
|
||||
|
@ -2193,8 +2175,8 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
status = -ENOMEM;
|
||||
goto err_no_ioremap;
|
||||
}
|
||||
printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
|
||||
adev->res.start, pl022->virtbase);
|
||||
printk(KERN_INFO "pl022: mapped registers from %pa to %p\n",
|
||||
&adev->res.start, pl022->virtbase);
|
||||
|
||||
pl022->clk = devm_clk_get(&adev->dev, NULL);
|
||||
if (IS_ERR(pl022->clk)) {
|
||||
|
|
|
@ -69,6 +69,8 @@ MODULE_ALIAS("platform:pxa2xx-spi");
|
|||
#define LPSS_TX_HITHRESH_DFLT 224
|
||||
|
||||
/* Offset from drv_data->lpss_base */
|
||||
#define GENERAL_REG 0x08
|
||||
#define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
|
||||
#define SSP_REG 0x0c
|
||||
#define SPI_CS_CONTROL 0x18
|
||||
#define SPI_CS_CONTROL_SW_MODE BIT(0)
|
||||
|
@ -142,8 +144,13 @@ detection_done:
|
|||
__lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value);
|
||||
|
||||
/* Enable multiblock DMA transfers */
|
||||
if (drv_data->master_info->enable_dma)
|
||||
if (drv_data->master_info->enable_dma) {
|
||||
__lpss_ssp_write_priv(drv_data, SSP_REG, 1);
|
||||
|
||||
value = __lpss_ssp_read_priv(drv_data, GENERAL_REG);
|
||||
value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE;
|
||||
__lpss_ssp_write_priv(drv_data, GENERAL_REG, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
|
||||
|
@ -804,14 +811,6 @@ static int pxa2xx_spi_transfer_one_message(struct spi_master *master,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pxa2xx_spi_prepare_transfer(struct spi_master *master)
|
||||
{
|
||||
struct driver_data *drv_data = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_get_sync(&drv_data->pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa2xx_spi_unprepare_transfer(struct spi_master *master)
|
||||
{
|
||||
struct driver_data *drv_data = spi_master_get_devdata(master);
|
||||
|
@ -820,8 +819,6 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_master *master)
|
|||
write_SSCR0(read_SSCR0(drv_data->ioaddr) & ~SSCR0_SSE,
|
||||
drv_data->ioaddr);
|
||||
|
||||
pm_runtime_mark_last_busy(&drv_data->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&drv_data->pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1134,8 +1131,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||
master->cleanup = cleanup;
|
||||
master->setup = setup;
|
||||
master->transfer_one_message = pxa2xx_spi_transfer_one_message;
|
||||
master->prepare_transfer_hardware = pxa2xx_spi_prepare_transfer;
|
||||
master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer;
|
||||
master->auto_runtime_pm = true;
|
||||
|
||||
drv_data->ssp_type = ssp->type;
|
||||
drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT);
|
||||
|
|
|
@ -564,8 +564,12 @@ static void rspi_work(struct work_struct *work)
|
|||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&rspi->lock, flags);
|
||||
while (!list_empty(&rspi->queue)) {
|
||||
while (1) {
|
||||
spin_lock_irqsave(&rspi->lock, flags);
|
||||
if (list_empty(&rspi->queue)) {
|
||||
spin_unlock_irqrestore(&rspi->lock, flags);
|
||||
break;
|
||||
}
|
||||
mesg = list_entry(rspi->queue.next, struct spi_message, queue);
|
||||
list_del_init(&mesg->queue);
|
||||
spin_unlock_irqrestore(&rspi->lock, flags);
|
||||
|
@ -595,8 +599,6 @@ static void rspi_work(struct work_struct *work)
|
|||
|
||||
mesg->status = 0;
|
||||
mesg->complete(mesg->context);
|
||||
|
||||
spin_lock_irqsave(&rspi->lock, flags);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -664,12 +666,13 @@ static irqreturn_t rspi_irq(int irq, void *_sr)
|
|||
static int rspi_request_dma(struct rspi_data *rspi,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct rspi_plat_data *rspi_pd = pdev->dev.platform_data;
|
||||
struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dma_cap_mask_t mask;
|
||||
struct dma_slave_config cfg;
|
||||
int ret;
|
||||
|
||||
if (!rspi_pd)
|
||||
if (!res || !rspi_pd)
|
||||
return 0; /* The driver assumes no error. */
|
||||
|
||||
rspi->dma_width_16bit = rspi_pd->dma_width_16bit;
|
||||
|
@ -683,6 +686,8 @@ static int rspi_request_dma(struct rspi_data *rspi,
|
|||
if (rspi->chan_rx) {
|
||||
cfg.slave_id = rspi_pd->dma_rx_id;
|
||||
cfg.direction = DMA_DEV_TO_MEM;
|
||||
cfg.dst_addr = 0;
|
||||
cfg.src_addr = res->start + RSPI_SPDR;
|
||||
ret = dmaengine_slave_config(rspi->chan_rx, &cfg);
|
||||
if (!ret)
|
||||
dev_info(&pdev->dev, "Use DMA when rx.\n");
|
||||
|
@ -698,6 +703,8 @@ static int rspi_request_dma(struct rspi_data *rspi,
|
|||
if (rspi->chan_tx) {
|
||||
cfg.slave_id = rspi_pd->dma_tx_id;
|
||||
cfg.direction = DMA_MEM_TO_DEV;
|
||||
cfg.dst_addr = res->start + RSPI_SPDR;
|
||||
cfg.src_addr = 0;
|
||||
ret = dmaengine_slave_config(rspi->chan_tx, &cfg);
|
||||
if (!ret)
|
||||
dev_info(&pdev->dev, "Use DMA when tx\n");
|
||||
|
@ -719,7 +726,7 @@ static void rspi_release_dma(struct rspi_data *rspi)
|
|||
|
||||
static int rspi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rspi_data *rspi = platform_get_drvdata(pdev);
|
||||
struct rspi_data *rspi = spi_master_get(platform_get_drvdata(pdev));
|
||||
|
||||
spi_unregister_master(rspi->master);
|
||||
rspi_release_dma(rspi);
|
||||
|
|
|
@ -525,7 +525,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev)
|
|||
memset(hw, 0, sizeof(struct s3c24xx_spi));
|
||||
|
||||
hw->master = spi_master_get(master);
|
||||
hw->pdata = pdata = pdev->dev.platform_data;
|
||||
hw->pdata = pdata = dev_get_platdata(&pdev->dev);
|
||||
hw->dev = &pdev->dev;
|
||||
|
||||
if (pdata == NULL) {
|
||||
|
@ -690,7 +690,7 @@ static int s3c24xx_spi_remove(struct platform_device *dev)
|
|||
|
||||
static int s3c24xx_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev));
|
||||
struct s3c24xx_spi *hw = dev_get_drvdata(dev);
|
||||
|
||||
if (hw->pdata && hw->pdata->gpio_setup)
|
||||
hw->pdata->gpio_setup(hw->pdata, 0);
|
||||
|
@ -701,7 +701,7 @@ static int s3c24xx_spi_suspend(struct device *dev)
|
|||
|
||||
static int s3c24xx_spi_resume(struct device *dev)
|
||||
{
|
||||
struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev));
|
||||
struct s3c24xx_spi *hw = dev_get_drvdata(dev);
|
||||
|
||||
s3c24xx_spi_initialsetup(hw);
|
||||
return 0;
|
||||
|
|
|
@ -172,7 +172,6 @@ struct s3c64xx_spi_port_config {
|
|||
* @master: Pointer to the SPI Protocol master.
|
||||
* @cntrlr_info: Platform specific data for the controller this driver manages.
|
||||
* @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint.
|
||||
* @queue: To log SPI xfer requests.
|
||||
* @lock: Controller specific lock.
|
||||
* @state: Set of FLAGS to indicate status.
|
||||
* @rx_dmach: Controller's DMA channel for Rx.
|
||||
|
@ -193,7 +192,6 @@ struct s3c64xx_spi_driver_data {
|
|||
struct spi_master *master;
|
||||
struct s3c64xx_spi_info *cntrlr_info;
|
||||
struct spi_device *tgl_spi;
|
||||
struct list_head queue;
|
||||
spinlock_t lock;
|
||||
unsigned long sfr_start;
|
||||
struct completion xfer_completion;
|
||||
|
@ -338,8 +336,10 @@ static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
|
|||
req.cap = DMA_SLAVE;
|
||||
req.client = &s3c64xx_spi_dma_client;
|
||||
|
||||
sdd->rx_dma.ch = (void *)sdd->ops->request(sdd->rx_dma.dmach, &req, dev, "rx");
|
||||
sdd->tx_dma.ch = (void *)sdd->ops->request(sdd->tx_dma.dmach, &req, dev, "tx");
|
||||
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;
|
||||
}
|
||||
|
@ -356,8 +356,6 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
|||
while (!is_polling(sdd) && !acquire_dma(sdd))
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
pm_runtime_get_sync(&sdd->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -372,7 +370,6 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
|
|||
sdd->ops->release((enum dma_ch)sdd->tx_dma.ch,
|
||||
&s3c64xx_spi_dma_client);
|
||||
}
|
||||
pm_runtime_put(&sdd->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -389,9 +386,10 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
|||
{
|
||||
struct s3c64xx_spi_driver_data *sdd;
|
||||
struct dma_slave_config config;
|
||||
struct scatterlist sg;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
|
||||
if (dma->direction == DMA_DEV_TO_MEM) {
|
||||
sdd = container_of((void *)dma,
|
||||
struct s3c64xx_spi_driver_data, rx_dma);
|
||||
|
@ -410,14 +408,8 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
|||
dmaengine_slave_config(dma->ch, &config);
|
||||
}
|
||||
|
||||
sg_init_table(&sg, 1);
|
||||
sg_dma_len(&sg) = len;
|
||||
sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf)),
|
||||
len, offset_in_page(buf));
|
||||
sg_dma_address(&sg) = buf;
|
||||
|
||||
desc = dmaengine_prep_slave_sg(dma->ch,
|
||||
&sg, 1, dma->direction, DMA_PREP_INTERRUPT);
|
||||
desc = dmaengine_prep_slave_single(dma->ch, buf, len,
|
||||
dma->direction, DMA_PREP_INTERRUPT);
|
||||
|
||||
desc->callback = s3c64xx_spi_dmacb;
|
||||
desc->callback_param = dma;
|
||||
|
@ -434,27 +426,26 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
|||
dma_cap_mask_t mask;
|
||||
int ret;
|
||||
|
||||
if (is_polling(sdd))
|
||||
return 0;
|
||||
if (!is_polling(sdd)) {
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
/* Acquire DMA channels */
|
||||
sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
||||
(void *)sdd->rx_dma.dmach, dev, "rx");
|
||||
if (!sdd->rx_dma.ch) {
|
||||
dev_err(dev, "Failed to get RX DMA channel\n");
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Acquire DMA channels */
|
||||
sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
||||
(void*)sdd->rx_dma.dmach, dev, "rx");
|
||||
if (!sdd->rx_dma.ch) {
|
||||
dev_err(dev, "Failed to get RX DMA channel\n");
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
||||
(void*)sdd->tx_dma.dmach, dev, "tx");
|
||||
if (!sdd->tx_dma.ch) {
|
||||
dev_err(dev, "Failed to get TX DMA channel\n");
|
||||
ret = -EBUSY;
|
||||
goto out_rx;
|
||||
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
||||
(void *)sdd->tx_dma.dmach, dev, "tx");
|
||||
if (!sdd->tx_dma.ch) {
|
||||
dev_err(dev, "Failed to get TX DMA channel\n");
|
||||
ret = -EBUSY;
|
||||
goto out_rx;
|
||||
}
|
||||
}
|
||||
|
||||
ret = pm_runtime_get_sync(&sdd->pdev->dev);
|
||||
|
@ -1056,8 +1047,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
|||
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
|
||||
struct s3c64xx_spi_driver_data *sdd;
|
||||
struct s3c64xx_spi_info *sci;
|
||||
struct spi_message *msg;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
sdd = spi_master_get_devdata(spi->master);
|
||||
|
@ -1071,38 +1060,24 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Request gpio only if cs line is asserted by gpio pins */
|
||||
if (sdd->cs_gpio) {
|
||||
err = gpio_request_one(cs->line, GPIOF_OUT_INIT_HIGH,
|
||||
dev_name(&spi->dev));
|
||||
if (err) {
|
||||
dev_err(&spi->dev,
|
||||
"Failed to get /CS gpio [%d]: %d\n",
|
||||
cs->line, err);
|
||||
goto err_gpio_req;
|
||||
if (!spi_get_ctldata(spi)) {
|
||||
/* Request gpio only if cs line is asserted by gpio pins */
|
||||
if (sdd->cs_gpio) {
|
||||
err = gpio_request_one(cs->line, GPIOF_OUT_INIT_HIGH,
|
||||
dev_name(&spi->dev));
|
||||
if (err) {
|
||||
dev_err(&spi->dev,
|
||||
"Failed to get /CS gpio [%d]: %d\n",
|
||||
cs->line, err);
|
||||
goto err_gpio_req;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!spi_get_ctldata(spi))
|
||||
spi_set_ctldata(spi, cs);
|
||||
}
|
||||
|
||||
sci = sdd->cntrlr_info;
|
||||
|
||||
spin_lock_irqsave(&sdd->lock, flags);
|
||||
|
||||
list_for_each_entry(msg, &sdd->queue, queue) {
|
||||
/* Is some mssg is already queued for this device */
|
||||
if (msg->spi == spi) {
|
||||
dev_err(&spi->dev,
|
||||
"setup: attempt while mssg in queue!\n");
|
||||
spin_unlock_irqrestore(&sdd->lock, flags);
|
||||
err = -EBUSY;
|
||||
goto err_msgq;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&sdd->lock, flags);
|
||||
|
||||
pm_runtime_get_sync(&sdd->pdev->dev);
|
||||
|
||||
/* Check if we can provide the requested rate */
|
||||
|
@ -1149,7 +1124,6 @@ setup_exit:
|
|||
/* setup() returns with device de-selected */
|
||||
disable_cs(sdd, spi);
|
||||
|
||||
err_msgq:
|
||||
gpio_free(cs->line);
|
||||
spi_set_ctldata(spi, NULL);
|
||||
|
||||
|
@ -1275,7 +1249,7 @@ static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev)
|
|||
#else
|
||||
static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev)
|
||||
{
|
||||
return dev->platform_data;
|
||||
return dev_get_platdata(dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1300,7 +1274,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
|||
struct resource *mem_res;
|
||||
struct resource *res;
|
||||
struct s3c64xx_spi_driver_data *sdd;
|
||||
struct s3c64xx_spi_info *sci = pdev->dev.platform_data;
|
||||
struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev);
|
||||
struct spi_master *master;
|
||||
int ret, irq;
|
||||
char clk_name[16];
|
||||
|
@ -1364,16 +1338,14 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
|||
if (!sdd->pdev->dev.of_node) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!res) {
|
||||
dev_warn(&pdev->dev, "Unable to get SPI tx dma "
|
||||
"resource. Switching to poll mode\n");
|
||||
dev_warn(&pdev->dev, "Unable to get SPI tx dma resource. Switching to poll mode\n");
|
||||
sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
|
||||
} else
|
||||
sdd->tx_dma.dmach = res->start;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!res) {
|
||||
dev_warn(&pdev->dev, "Unable to get SPI rx dma "
|
||||
"resource. Switching to poll mode\n");
|
||||
dev_warn(&pdev->dev, "Unable to get SPI rx dma resource. Switching to poll mode\n");
|
||||
sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
|
||||
} else
|
||||
sdd->rx_dma.dmach = res->start;
|
||||
|
@ -1395,6 +1367,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
|||
SPI_BPW_MASK(8);
|
||||
/* the spi->mode bits understood by this driver: */
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
master->auto_runtime_pm = true;
|
||||
|
||||
sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res);
|
||||
if (IS_ERR(sdd->regs)) {
|
||||
|
@ -1442,7 +1415,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
spin_lock_init(&sdd->lock);
|
||||
init_completion(&sdd->xfer_completion);
|
||||
INIT_LIST_HEAD(&sdd->queue);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, s3c64xx_spi_irq, 0,
|
||||
"spi-s3c64xx", sdd);
|
||||
|
@ -1464,8 +1436,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n",
|
||||
sdd->port_id, master->num_chipselect);
|
||||
dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
|
||||
mem_res->end, mem_res->start,
|
||||
dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tDMA=[Rx-%d, Tx-%d]\n",
|
||||
mem_res,
|
||||
sdd->rx_dma.dmach, sdd->tx_dma.dmach);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
|
|
@ -99,21 +99,6 @@ static int hspi_status_check_timeout(struct hspi_priv *hspi, u32 mask, u32 val)
|
|||
/*
|
||||
* spi master function
|
||||
*/
|
||||
static int hspi_prepare_transfer(struct spi_master *master)
|
||||
{
|
||||
struct hspi_priv *hspi = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_get_sync(hspi->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hspi_unprepare_transfer(struct spi_master *master)
|
||||
{
|
||||
struct hspi_priv *hspi = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_put_sync(hspi->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define hspi_hw_cs_enable(hspi) hspi_hw_cs_ctrl(hspi, 0)
|
||||
#define hspi_hw_cs_disable(hspi) hspi_hw_cs_ctrl(hspi, 1)
|
||||
|
@ -316,9 +301,8 @@ static int hspi_probe(struct platform_device *pdev)
|
|||
master->setup = hspi_setup;
|
||||
master->cleanup = hspi_cleanup;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
master->prepare_transfer_hardware = hspi_prepare_transfer;
|
||||
master->auto_runtime_pm = true;
|
||||
master->transfer_one_message = hspi_transfer_one_message;
|
||||
master->unprepare_transfer_hardware = hspi_unprepare_transfer;
|
||||
ret = spi_register_master(master);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "spi_register_master error.\n");
|
||||
|
@ -327,8 +311,6 @@ static int hspi_probe(struct platform_device *pdev)
|
|||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
dev_info(&pdev->dev, "probed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
|
|
|
@ -645,7 +645,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
|||
if (pdev->dev.of_node)
|
||||
p->info = sh_msiof_spi_parse_dt(&pdev->dev);
|
||||
else
|
||||
p->info = pdev->dev.platform_data;
|
||||
p->info = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (!p->info) {
|
||||
dev_err(&pdev->dev, "failed to obtain device info\n");
|
||||
|
@ -745,18 +745,6 @@ static int sh_msiof_spi_remove(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int sh_msiof_spi_runtime_nop(struct device *dev)
|
||||
{
|
||||
/* Runtime PM callback shared between ->runtime_suspend()
|
||||
* and ->runtime_resume(). Simply returns success.
|
||||
*
|
||||
* This driver re-initializes all registers after
|
||||
* pm_runtime_get_sync() anyway so there is no need
|
||||
* to save and restore registers here.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id sh_msiof_match[] = {
|
||||
{ .compatible = "renesas,sh-msiof", },
|
||||
|
@ -766,18 +754,12 @@ static const struct of_device_id sh_msiof_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, sh_msiof_match);
|
||||
#endif
|
||||
|
||||
static struct dev_pm_ops sh_msiof_spi_dev_pm_ops = {
|
||||
.runtime_suspend = sh_msiof_spi_runtime_nop,
|
||||
.runtime_resume = sh_msiof_spi_runtime_nop,
|
||||
};
|
||||
|
||||
static struct platform_driver sh_msiof_spi_drv = {
|
||||
.probe = sh_msiof_spi_probe,
|
||||
.remove = sh_msiof_spi_remove,
|
||||
.driver = {
|
||||
.name = "spi_sh_msiof",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &sh_msiof_spi_dev_pm_ops,
|
||||
.of_match_table = of_match_ptr(sh_msiof_match),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -130,7 +130,7 @@ static int sh_sci_spi_probe(struct platform_device *dev)
|
|||
sp = spi_master_get_devdata(master);
|
||||
|
||||
platform_set_drvdata(dev, sp);
|
||||
sp->info = dev->dev.platform_data;
|
||||
sp->info = dev_get_platdata(&dev->dev);
|
||||
|
||||
/* setup spi bitbang adaptor */
|
||||
sp->bitbang.master = spi_master_get(master);
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
#include <linux/of_gpio.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/sirfsoc_dma.h>
|
||||
|
||||
#define DRIVER_NAME "sirfsoc_spi"
|
||||
|
||||
|
@ -119,9 +123,19 @@
|
|||
#define SIRFSOC_SPI_FIFO_HC(x) (((x) & 0x3F) << 20)
|
||||
#define SIRFSOC_SPI_FIFO_THD(x) (((x) & 0xFF) << 2)
|
||||
|
||||
/*
|
||||
* only if the rx/tx buffer and transfer size are 4-bytes aligned, we use dma
|
||||
* due to the limitation of dma controller
|
||||
*/
|
||||
|
||||
#define ALIGNED(x) (!((u32)x & 0x3))
|
||||
#define IS_DMA_VALID(x) (x && ALIGNED(x->tx_buf) && ALIGNED(x->rx_buf) && \
|
||||
ALIGNED(x->len) && (x->len < 2 * PAGE_SIZE))
|
||||
|
||||
struct sirfsoc_spi {
|
||||
struct spi_bitbang bitbang;
|
||||
struct completion done;
|
||||
struct completion rx_done;
|
||||
struct completion tx_done;
|
||||
|
||||
void __iomem *base;
|
||||
u32 ctrl_freq; /* SPI controller clock speed */
|
||||
|
@ -137,8 +151,16 @@ struct sirfsoc_spi {
|
|||
void (*tx_word) (struct sirfsoc_spi *);
|
||||
|
||||
/* number of words left to be tranmitted/received */
|
||||
unsigned int left_tx_cnt;
|
||||
unsigned int left_rx_cnt;
|
||||
unsigned int left_tx_word;
|
||||
unsigned int left_rx_word;
|
||||
|
||||
/* rx & tx DMA channels */
|
||||
struct dma_chan *rx_chan;
|
||||
struct dma_chan *tx_chan;
|
||||
dma_addr_t src_start;
|
||||
dma_addr_t dst_start;
|
||||
void *dummypage;
|
||||
int word_width; /* in bytes */
|
||||
|
||||
int chipselect[0];
|
||||
};
|
||||
|
@ -155,7 +177,7 @@ static void spi_sirfsoc_rx_word_u8(struct sirfsoc_spi *sspi)
|
|||
sspi->rx = rx;
|
||||
}
|
||||
|
||||
sspi->left_rx_cnt--;
|
||||
sspi->left_rx_word--;
|
||||
}
|
||||
|
||||
static void spi_sirfsoc_tx_word_u8(struct sirfsoc_spi *sspi)
|
||||
|
@ -169,7 +191,7 @@ static void spi_sirfsoc_tx_word_u8(struct sirfsoc_spi *sspi)
|
|||
}
|
||||
|
||||
writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
|
||||
sspi->left_tx_cnt--;
|
||||
sspi->left_tx_word--;
|
||||
}
|
||||
|
||||
static void spi_sirfsoc_rx_word_u16(struct sirfsoc_spi *sspi)
|
||||
|
@ -184,7 +206,7 @@ static void spi_sirfsoc_rx_word_u16(struct sirfsoc_spi *sspi)
|
|||
sspi->rx = rx;
|
||||
}
|
||||
|
||||
sspi->left_rx_cnt--;
|
||||
sspi->left_rx_word--;
|
||||
}
|
||||
|
||||
static void spi_sirfsoc_tx_word_u16(struct sirfsoc_spi *sspi)
|
||||
|
@ -198,7 +220,7 @@ static void spi_sirfsoc_tx_word_u16(struct sirfsoc_spi *sspi)
|
|||
}
|
||||
|
||||
writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
|
||||
sspi->left_tx_cnt--;
|
||||
sspi->left_tx_word--;
|
||||
}
|
||||
|
||||
static void spi_sirfsoc_rx_word_u32(struct sirfsoc_spi *sspi)
|
||||
|
@ -213,7 +235,7 @@ static void spi_sirfsoc_rx_word_u32(struct sirfsoc_spi *sspi)
|
|||
sspi->rx = rx;
|
||||
}
|
||||
|
||||
sspi->left_rx_cnt--;
|
||||
sspi->left_rx_word--;
|
||||
|
||||
}
|
||||
|
||||
|
@ -228,7 +250,7 @@ static void spi_sirfsoc_tx_word_u32(struct sirfsoc_spi *sspi)
|
|||
}
|
||||
|
||||
writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
|
||||
sspi->left_tx_cnt--;
|
||||
sspi->left_tx_word--;
|
||||
}
|
||||
|
||||
static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id)
|
||||
|
@ -241,7 +263,7 @@ static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id)
|
|||
/* Error Conditions */
|
||||
if (spi_stat & SIRFSOC_SPI_RX_OFLOW ||
|
||||
spi_stat & SIRFSOC_SPI_TX_UFLOW) {
|
||||
complete(&sspi->done);
|
||||
complete(&sspi->rx_done);
|
||||
writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
|
||||
}
|
||||
|
||||
|
@ -249,50 +271,61 @@ static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id)
|
|||
| SIRFSOC_SPI_RXFIFO_THD_REACH))
|
||||
while (!((readl(sspi->base + SIRFSOC_SPI_RXFIFO_STATUS)
|
||||
& SIRFSOC_SPI_FIFO_EMPTY)) &&
|
||||
sspi->left_rx_cnt)
|
||||
sspi->left_rx_word)
|
||||
sspi->rx_word(sspi);
|
||||
|
||||
if (spi_stat & (SIRFSOC_SPI_FIFO_EMPTY
|
||||
| SIRFSOC_SPI_TXFIFO_THD_REACH))
|
||||
while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS)
|
||||
& SIRFSOC_SPI_FIFO_FULL)) &&
|
||||
sspi->left_tx_cnt)
|
||||
sspi->left_tx_word)
|
||||
sspi->tx_word(sspi);
|
||||
|
||||
/* Received all words */
|
||||
if ((sspi->left_rx_cnt == 0) && (sspi->left_tx_cnt == 0)) {
|
||||
complete(&sspi->done);
|
||||
if ((sspi->left_rx_word == 0) && (sspi->left_tx_word == 0)) {
|
||||
complete(&sspi->rx_done);
|
||||
writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void spi_sirfsoc_dma_fini_callback(void *data)
|
||||
{
|
||||
struct completion *dma_complete = data;
|
||||
|
||||
complete(dma_complete);
|
||||
}
|
||||
|
||||
static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
struct sirfsoc_spi *sspi;
|
||||
int timeout = t->len * 10;
|
||||
sspi = spi_master_get_devdata(spi->master);
|
||||
|
||||
sspi->tx = t->tx_buf;
|
||||
sspi->rx = t->rx_buf;
|
||||
sspi->left_tx_cnt = sspi->left_rx_cnt = t->len;
|
||||
INIT_COMPLETION(sspi->done);
|
||||
sspi->tx = t->tx_buf ? t->tx_buf : sspi->dummypage;
|
||||
sspi->rx = t->rx_buf ? t->rx_buf : sspi->dummypage;
|
||||
sspi->left_tx_word = sspi->left_rx_word = t->len / sspi->word_width;
|
||||
INIT_COMPLETION(sspi->rx_done);
|
||||
INIT_COMPLETION(sspi->tx_done);
|
||||
|
||||
writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS);
|
||||
|
||||
if (t->len == 1) {
|
||||
if (sspi->left_tx_word == 1) {
|
||||
writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
|
||||
SIRFSOC_SPI_ENA_AUTO_CLR,
|
||||
sspi->base + SIRFSOC_SPI_CTRL);
|
||||
writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
|
||||
writel(0, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
|
||||
} else if ((t->len > 1) && (t->len < SIRFSOC_SPI_DAT_FRM_LEN_MAX)) {
|
||||
} else if ((sspi->left_tx_word > 1) && (sspi->left_tx_word <
|
||||
SIRFSOC_SPI_DAT_FRM_LEN_MAX)) {
|
||||
writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
|
||||
SIRFSOC_SPI_MUL_DAT_MODE |
|
||||
SIRFSOC_SPI_ENA_AUTO_CLR,
|
||||
sspi->base + SIRFSOC_SPI_CTRL);
|
||||
writel(t->len - 1, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
|
||||
writel(t->len - 1, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
|
||||
writel(sspi->left_tx_word - 1,
|
||||
sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
|
||||
writel(sspi->left_tx_word - 1,
|
||||
sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
|
||||
} else {
|
||||
writel(readl(sspi->base + SIRFSOC_SPI_CTRL),
|
||||
sspi->base + SIRFSOC_SPI_CTRL);
|
||||
|
@ -305,17 +338,64 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
|
||||
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
|
||||
|
||||
/* Send the first word to trigger the whole tx/rx process */
|
||||
sspi->tx_word(sspi);
|
||||
if (IS_DMA_VALID(t)) {
|
||||
struct dma_async_tx_descriptor *rx_desc, *tx_desc;
|
||||
|
||||
sspi->dst_start = dma_map_single(&spi->dev, sspi->rx, t->len, DMA_FROM_DEVICE);
|
||||
rx_desc = dmaengine_prep_slave_single(sspi->rx_chan,
|
||||
sspi->dst_start, t->len, DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
rx_desc->callback = spi_sirfsoc_dma_fini_callback;
|
||||
rx_desc->callback_param = &sspi->rx_done;
|
||||
|
||||
sspi->src_start = dma_map_single(&spi->dev, (void *)sspi->tx, t->len, DMA_TO_DEVICE);
|
||||
tx_desc = dmaengine_prep_slave_single(sspi->tx_chan,
|
||||
sspi->src_start, t->len, DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
tx_desc->callback = spi_sirfsoc_dma_fini_callback;
|
||||
tx_desc->callback_param = &sspi->tx_done;
|
||||
|
||||
dmaengine_submit(tx_desc);
|
||||
dmaengine_submit(rx_desc);
|
||||
dma_async_issue_pending(sspi->tx_chan);
|
||||
dma_async_issue_pending(sspi->rx_chan);
|
||||
} else {
|
||||
/* Send the first word to trigger the whole tx/rx process */
|
||||
sspi->tx_word(sspi);
|
||||
|
||||
writel(SIRFSOC_SPI_RX_OFLOW_INT_EN | SIRFSOC_SPI_TX_UFLOW_INT_EN |
|
||||
SIRFSOC_SPI_RXFIFO_THD_INT_EN | SIRFSOC_SPI_TXFIFO_THD_INT_EN |
|
||||
SIRFSOC_SPI_FRM_END_INT_EN | SIRFSOC_SPI_RXFIFO_FULL_INT_EN |
|
||||
SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN, sspi->base + SIRFSOC_SPI_INT_EN);
|
||||
}
|
||||
|
||||
writel(SIRFSOC_SPI_RX_OFLOW_INT_EN | SIRFSOC_SPI_TX_UFLOW_INT_EN |
|
||||
SIRFSOC_SPI_RXFIFO_THD_INT_EN | SIRFSOC_SPI_TXFIFO_THD_INT_EN |
|
||||
SIRFSOC_SPI_FRM_END_INT_EN | SIRFSOC_SPI_RXFIFO_FULL_INT_EN |
|
||||
SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN, sspi->base + SIRFSOC_SPI_INT_EN);
|
||||
writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, sspi->base + SIRFSOC_SPI_TX_RX_EN);
|
||||
|
||||
if (wait_for_completion_timeout(&sspi->done, timeout) == 0)
|
||||
if (!IS_DMA_VALID(t)) { /* for PIO */
|
||||
if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0)
|
||||
dev_err(&spi->dev, "transfer timeout\n");
|
||||
} else if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) {
|
||||
dev_err(&spi->dev, "transfer timeout\n");
|
||||
dmaengine_terminate_all(sspi->rx_chan);
|
||||
} else
|
||||
sspi->left_rx_word = 0;
|
||||
|
||||
/*
|
||||
* we only wait tx-done event if transferring by DMA. for PIO,
|
||||
* we get rx data by writing tx data, so if rx is done, tx has
|
||||
* done earlier
|
||||
*/
|
||||
if (IS_DMA_VALID(t)) {
|
||||
if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) {
|
||||
dev_err(&spi->dev, "transfer timeout\n");
|
||||
dmaengine_terminate_all(sspi->tx_chan);
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_DMA_VALID(t)) {
|
||||
dma_unmap_single(&spi->dev, sspi->src_start, t->len, DMA_TO_DEVICE);
|
||||
dma_unmap_single(&spi->dev, sspi->dst_start, t->len, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
/* TX, RX FIFO stop */
|
||||
writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
|
||||
|
@ -323,7 +403,7 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
writel(0, sspi->base + SIRFSOC_SPI_TX_RX_EN);
|
||||
writel(0, sspi->base + SIRFSOC_SPI_INT_EN);
|
||||
|
||||
return t->len - sspi->left_rx_cnt;
|
||||
return t->len - sspi->left_rx_word * sspi->word_width;
|
||||
}
|
||||
|
||||
static void spi_sirfsoc_chipselect(struct spi_device *spi, int value)
|
||||
|
@ -332,7 +412,6 @@ static void spi_sirfsoc_chipselect(struct spi_device *spi, int value)
|
|||
|
||||
if (sspi->chipselect[spi->chip_select] == 0) {
|
||||
u32 regval = readl(sspi->base + SIRFSOC_SPI_CTRL);
|
||||
regval |= SIRFSOC_SPI_CS_IO_OUT;
|
||||
switch (value) {
|
||||
case BITBANG_CS_ACTIVE:
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
|
@ -369,11 +448,7 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
|
||||
hz = t && t->speed_hz ? t->speed_hz : spi->max_speed_hz;
|
||||
|
||||
/* Enable IO mode for RX, TX */
|
||||
writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL);
|
||||
writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL);
|
||||
regval = (sspi->ctrl_freq / (2 * hz)) - 1;
|
||||
|
||||
if (regval > 0xFFFF || regval < 0) {
|
||||
dev_err(&spi->dev, "Speed %d not supported\n", hz);
|
||||
return -EINVAL;
|
||||
|
@ -388,6 +463,7 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
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;
|
||||
case 12:
|
||||
case 16:
|
||||
|
@ -399,6 +475,7 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
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;
|
||||
case 32:
|
||||
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_32;
|
||||
|
@ -408,6 +485,7 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
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;
|
||||
default:
|
||||
BUG();
|
||||
|
@ -442,6 +520,17 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL);
|
||||
|
||||
writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
|
||||
|
||||
if (IS_DMA_VALID(t)) {
|
||||
/* Enable DMA mode for RX, TX */
|
||||
writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL);
|
||||
writel(SIRFSOC_SPI_RX_DMA_FLUSH, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL);
|
||||
} else {
|
||||
/* Enable IO mode for RX, TX */
|
||||
writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL);
|
||||
writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -466,6 +555,8 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||
struct spi_master *master;
|
||||
struct resource *mem_res;
|
||||
int num_cs, cs_gpio, irq;
|
||||
u32 rx_dma_ch, tx_dma_ch;
|
||||
dma_cap_mask_t dma_cap_mask;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
|
@ -476,6 +567,20 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||
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);
|
||||
if (!master) {
|
||||
dev_err(&pdev->dev, "Unable to allocate SPI master\n");
|
||||
|
@ -484,12 +589,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, master);
|
||||
sspi = spi_master_get_devdata(master);
|
||||
|
||||
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem_res) {
|
||||
dev_err(&pdev->dev, "Unable to get IO resource\n");
|
||||
ret = -ENODEV;
|
||||
goto free_master;
|
||||
}
|
||||
master->num_chipselect = num_cs;
|
||||
|
||||
for (i = 0; i < master->num_chipselect; i++) {
|
||||
|
@ -516,6 +615,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
sspi->base = devm_ioremap_resource(&pdev->dev, mem_res);
|
||||
if (IS_ERR(sspi->base)) {
|
||||
ret = PTR_ERR(sspi->base);
|
||||
|
@ -538,19 +638,40 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||
sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer;
|
||||
sspi->bitbang.master->setup = spi_sirfsoc_setup;
|
||||
master->bus_num = pdev->id;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(12) |
|
||||
SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
|
||||
sspi->bitbang.master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
/* request DMA channels */
|
||||
dma_cap_zero(dma_cap_mask);
|
||||
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) {
|
||||
dev_err(&pdev->dev, "can not allocate rx dma channel\n");
|
||||
ret = -ENODEV;
|
||||
goto free_master;
|
||||
}
|
||||
sspi->tx_chan = dma_request_channel(dma_cap_mask, (dma_filter_fn)sirfsoc_dma_filter_id,
|
||||
(void *)tx_dma_ch);
|
||||
if (!sspi->tx_chan) {
|
||||
dev_err(&pdev->dev, "can not allocate tx dma channel\n");
|
||||
ret = -ENODEV;
|
||||
goto free_rx_dma;
|
||||
}
|
||||
|
||||
sspi->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(sspi->clk)) {
|
||||
ret = -EINVAL;
|
||||
goto free_master;
|
||||
ret = PTR_ERR(sspi->clk);
|
||||
goto free_tx_dma;
|
||||
}
|
||||
clk_prepare_enable(sspi->clk);
|
||||
sspi->ctrl_freq = clk_get_rate(sspi->clk);
|
||||
|
||||
init_completion(&sspi->done);
|
||||
init_completion(&sspi->rx_done);
|
||||
init_completion(&sspi->tx_done);
|
||||
|
||||
writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
|
||||
writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
|
||||
|
@ -559,17 +680,28 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||
/* We are not using dummy delay between command and data */
|
||||
writel(0, sspi->base + SIRFSOC_SPI_DUMMY_DELAY_CTL);
|
||||
|
||||
sspi->dummypage = kmalloc(2 * PAGE_SIZE, GFP_KERNEL);
|
||||
if (!sspi->dummypage) {
|
||||
ret = -ENOMEM;
|
||||
goto free_clk;
|
||||
}
|
||||
|
||||
ret = spi_bitbang_start(&sspi->bitbang);
|
||||
if (ret)
|
||||
goto free_clk;
|
||||
goto free_dummypage;
|
||||
|
||||
dev_info(&pdev->dev, "registerred, bus number = %d\n", master->bus_num);
|
||||
|
||||
return 0;
|
||||
|
||||
free_dummypage:
|
||||
kfree(sspi->dummypage);
|
||||
free_clk:
|
||||
clk_disable_unprepare(sspi->clk);
|
||||
clk_put(sspi->clk);
|
||||
free_tx_dma:
|
||||
dma_release_channel(sspi->tx_chan);
|
||||
free_rx_dma:
|
||||
dma_release_channel(sspi->rx_chan);
|
||||
free_master:
|
||||
spi_master_put(master);
|
||||
err_cs:
|
||||
|
@ -590,8 +722,11 @@ static int spi_sirfsoc_remove(struct platform_device *pdev)
|
|||
if (sspi->chipselect[i] > 0)
|
||||
gpio_free(sspi->chipselect[i]);
|
||||
}
|
||||
kfree(sspi->dummypage);
|
||||
clk_disable_unprepare(sspi->clk);
|
||||
clk_put(sspi->clk);
|
||||
dma_release_channel(sspi->rx_chan);
|
||||
dma_release_channel(sspi->tx_chan);
|
||||
spi_master_put(master);
|
||||
return 0;
|
||||
}
|
||||
|
@ -599,8 +734,7 @@ static int spi_sirfsoc_remove(struct platform_device *pdev)
|
|||
#ifdef CONFIG_PM
|
||||
static int spi_sirfsoc_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
|
||||
|
||||
clk_disable(sspi->clk);
|
||||
|
@ -609,8 +743,7 @@ static int spi_sirfsoc_suspend(struct device *dev)
|
|||
|
||||
static int spi_sirfsoc_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
|
||||
|
||||
clk_enable(sspi->clk);
|
||||
|
|
|
@ -816,14 +816,6 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
|
|||
msg->status = 0;
|
||||
msg->actual_length = 0;
|
||||
|
||||
ret = pm_runtime_get_sync(tspi->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(tspi->dev, "runtime PM get failed: %d\n", ret);
|
||||
msg->status = ret;
|
||||
spi_finalize_current_message(master);
|
||||
return ret;
|
||||
}
|
||||
|
||||
single_xfer = list_is_singular(&msg->transfers);
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
INIT_COMPLETION(tspi->xfer_completion);
|
||||
|
@ -859,7 +851,6 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
|
|||
ret = 0;
|
||||
exit:
|
||||
tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1);
|
||||
pm_runtime_put(tspi->dev);
|
||||
msg->status = ret;
|
||||
spi_finalize_current_message(master);
|
||||
return ret;
|
||||
|
@ -1053,24 +1044,19 @@ static int tegra_spi_probe(struct platform_device *pdev)
|
|||
master->transfer_one_message = tegra_spi_transfer_one_message;
|
||||
master->num_chipselect = MAX_CHIP_SELECT;
|
||||
master->bus_num = -1;
|
||||
master->auto_runtime_pm = true;
|
||||
|
||||
tspi->master = master;
|
||||
tspi->dev = &pdev->dev;
|
||||
spin_lock_init(&tspi->lock);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "No IO memory resource\n");
|
||||
ret = -ENODEV;
|
||||
goto exit_free_master;
|
||||
}
|
||||
tspi->phys = r->start;
|
||||
tspi->base = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(tspi->base)) {
|
||||
ret = PTR_ERR(tspi->base);
|
||||
dev_err(&pdev->dev, "ioremap failed: err = %d\n", ret);
|
||||
goto exit_free_master;
|
||||
}
|
||||
tspi->phys = r->start;
|
||||
|
||||
spi_irq = platform_get_irq(pdev, 0);
|
||||
tspi->irq = spi_irq;
|
||||
|
|
|
@ -335,12 +335,6 @@ static int tegra_sflash_transfer_one_message(struct spi_master *master,
|
|||
struct spi_device *spi = msg->spi;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(tsd->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(tsd->dev, "pm_runtime_get() failed, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msg->status = 0;
|
||||
msg->actual_length = 0;
|
||||
single_xfer = list_is_singular(&msg->transfers);
|
||||
|
@ -380,7 +374,6 @@ exit:
|
|||
tegra_sflash_writel(tsd, tsd->def_command_reg, SPI_COMMAND);
|
||||
msg->status = ret;
|
||||
spi_finalize_current_message(master);
|
||||
pm_runtime_put(tsd->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -477,6 +470,7 @@ static int tegra_sflash_probe(struct platform_device *pdev)
|
|||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
master->setup = tegra_sflash_setup;
|
||||
master->transfer_one_message = tegra_sflash_transfer_one_message;
|
||||
master->auto_runtime_pm = true;
|
||||
master->num_chipselect = MAX_CHIP_SELECT;
|
||||
master->bus_num = -1;
|
||||
|
||||
|
|
|
@ -836,11 +836,6 @@ static int tegra_slink_transfer_one_message(struct spi_master *master,
|
|||
|
||||
msg->status = 0;
|
||||
msg->actual_length = 0;
|
||||
ret = pm_runtime_get_sync(tspi->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(tspi->dev, "runtime get failed: %d\n", ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
single_xfer = list_is_singular(&msg->transfers);
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
|
@ -878,8 +873,6 @@ static int tegra_slink_transfer_one_message(struct spi_master *master,
|
|||
exit:
|
||||
tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND);
|
||||
tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2);
|
||||
pm_runtime_put(tspi->dev);
|
||||
done:
|
||||
msg->status = ret;
|
||||
spi_finalize_current_message(master);
|
||||
return ret;
|
||||
|
@ -1086,6 +1079,7 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
|||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
master->setup = tegra_slink_setup;
|
||||
master->transfer_one_message = tegra_slink_transfer_one_message;
|
||||
master->auto_runtime_pm = true;
|
||||
master->num_chipselect = MAX_CHIP_SELECT;
|
||||
master->bus_num = -1;
|
||||
|
||||
|
|
|
@ -0,0 +1,574 @@
|
|||
/*
|
||||
* TI QSPI driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
|
||||
* Author: Sourav Poddar <sourav.poddar@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GPLv2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR /PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/omap-dma.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
struct ti_qspi_regs {
|
||||
u32 clkctrl;
|
||||
};
|
||||
|
||||
struct ti_qspi {
|
||||
struct completion transfer_complete;
|
||||
|
||||
/* IRQ synchronization */
|
||||
spinlock_t lock;
|
||||
|
||||
/* list synchronization */
|
||||
struct mutex list_lock;
|
||||
|
||||
struct spi_master *master;
|
||||
void __iomem *base;
|
||||
struct clk *fclk;
|
||||
struct device *dev;
|
||||
|
||||
struct ti_qspi_regs ctx_reg;
|
||||
|
||||
u32 spi_max_frequency;
|
||||
u32 cmd;
|
||||
u32 dc;
|
||||
u32 stat;
|
||||
};
|
||||
|
||||
#define QSPI_PID (0x0)
|
||||
#define QSPI_SYSCONFIG (0x10)
|
||||
#define QSPI_INTR_STATUS_RAW_SET (0x20)
|
||||
#define QSPI_INTR_STATUS_ENABLED_CLEAR (0x24)
|
||||
#define QSPI_INTR_ENABLE_SET_REG (0x28)
|
||||
#define QSPI_INTR_ENABLE_CLEAR_REG (0x2c)
|
||||
#define QSPI_SPI_CLOCK_CNTRL_REG (0x40)
|
||||
#define QSPI_SPI_DC_REG (0x44)
|
||||
#define QSPI_SPI_CMD_REG (0x48)
|
||||
#define QSPI_SPI_STATUS_REG (0x4c)
|
||||
#define QSPI_SPI_DATA_REG (0x50)
|
||||
#define QSPI_SPI_SETUP0_REG (0x54)
|
||||
#define QSPI_SPI_SWITCH_REG (0x64)
|
||||
#define QSPI_SPI_SETUP1_REG (0x58)
|
||||
#define QSPI_SPI_SETUP2_REG (0x5c)
|
||||
#define QSPI_SPI_SETUP3_REG (0x60)
|
||||
#define QSPI_SPI_DATA_REG_1 (0x68)
|
||||
#define QSPI_SPI_DATA_REG_2 (0x6c)
|
||||
#define QSPI_SPI_DATA_REG_3 (0x70)
|
||||
|
||||
#define QSPI_COMPLETION_TIMEOUT msecs_to_jiffies(2000)
|
||||
|
||||
#define QSPI_FCLK 192000000
|
||||
|
||||
/* Clock Control */
|
||||
#define QSPI_CLK_EN (1 << 31)
|
||||
#define QSPI_CLK_DIV_MAX 0xffff
|
||||
|
||||
/* Command */
|
||||
#define QSPI_EN_CS(n) (n << 28)
|
||||
#define QSPI_WLEN(n) ((n - 1) << 19)
|
||||
#define QSPI_3_PIN (1 << 18)
|
||||
#define QSPI_RD_SNGL (1 << 16)
|
||||
#define QSPI_WR_SNGL (2 << 16)
|
||||
#define QSPI_RD_DUAL (3 << 16)
|
||||
#define QSPI_RD_QUAD (7 << 16)
|
||||
#define QSPI_INVAL (4 << 16)
|
||||
#define QSPI_WC_CMD_INT_EN (1 << 14)
|
||||
#define QSPI_FLEN(n) ((n - 1) << 0)
|
||||
|
||||
/* STATUS REGISTER */
|
||||
#define WC 0x02
|
||||
|
||||
/* INTERRUPT REGISTER */
|
||||
#define QSPI_WC_INT_EN (1 << 1)
|
||||
#define QSPI_WC_INT_DISABLE (1 << 1)
|
||||
|
||||
/* Device Control */
|
||||
#define QSPI_DD(m, n) (m << (3 + n * 8))
|
||||
#define QSPI_CKPHA(n) (1 << (2 + n * 8))
|
||||
#define QSPI_CSPOL(n) (1 << (1 + n * 8))
|
||||
#define QSPI_CKPOL(n) (1 << (n * 8))
|
||||
|
||||
#define QSPI_FRAME 4096
|
||||
|
||||
#define QSPI_AUTOSUSPEND_TIMEOUT 2000
|
||||
|
||||
static inline unsigned long ti_qspi_read(struct ti_qspi *qspi,
|
||||
unsigned long reg)
|
||||
{
|
||||
return readl(qspi->base + reg);
|
||||
}
|
||||
|
||||
static inline void ti_qspi_write(struct ti_qspi *qspi,
|
||||
unsigned long val, unsigned long reg)
|
||||
{
|
||||
writel(val, qspi->base + reg);
|
||||
}
|
||||
|
||||
static int ti_qspi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct ti_qspi *qspi = spi_master_get_devdata(spi->master);
|
||||
struct ti_qspi_regs *ctx_reg = &qspi->ctx_reg;
|
||||
int clk_div = 0, ret;
|
||||
u32 clk_ctrl_reg, clk_rate, clk_mask;
|
||||
|
||||
if (spi->master->busy) {
|
||||
dev_dbg(qspi->dev, "master busy doing other trasnfers\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!qspi->spi_max_frequency) {
|
||||
dev_err(qspi->dev, "spi max frequency not defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clk_rate = clk_get_rate(qspi->fclk);
|
||||
|
||||
clk_div = DIV_ROUND_UP(clk_rate, qspi->spi_max_frequency) - 1;
|
||||
|
||||
if (clk_div < 0) {
|
||||
dev_dbg(qspi->dev, "clock divider < 0, using /1 divider\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (clk_div > QSPI_CLK_DIV_MAX) {
|
||||
dev_dbg(qspi->dev, "clock divider >%d , using /%d divider\n",
|
||||
QSPI_CLK_DIV_MAX, QSPI_CLK_DIV_MAX + 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(qspi->dev, "hz: %d, clock divider %d\n",
|
||||
qspi->spi_max_frequency, clk_div);
|
||||
|
||||
ret = pm_runtime_get_sync(qspi->dev);
|
||||
if (ret) {
|
||||
dev_err(qspi->dev, "pm_runtime_get_sync() failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_ctrl_reg = ti_qspi_read(qspi, QSPI_SPI_CLOCK_CNTRL_REG);
|
||||
|
||||
clk_ctrl_reg &= ~QSPI_CLK_EN;
|
||||
|
||||
/* disable SCLK */
|
||||
ti_qspi_write(qspi, clk_ctrl_reg, QSPI_SPI_CLOCK_CNTRL_REG);
|
||||
|
||||
/* enable SCLK */
|
||||
clk_mask = QSPI_CLK_EN | clk_div;
|
||||
ti_qspi_write(qspi, clk_mask, QSPI_SPI_CLOCK_CNTRL_REG);
|
||||
ctx_reg->clkctrl = clk_mask;
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
ret = pm_runtime_put_autosuspend(qspi->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(qspi->dev, "pm_runtime_put_autosuspend() failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ti_qspi_restore_ctx(struct ti_qspi *qspi)
|
||||
{
|
||||
struct ti_qspi_regs *ctx_reg = &qspi->ctx_reg;
|
||||
|
||||
ti_qspi_write(qspi, ctx_reg->clkctrl, QSPI_SPI_CLOCK_CNTRL_REG);
|
||||
}
|
||||
|
||||
static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
|
||||
{
|
||||
int wlen, count, ret;
|
||||
unsigned int cmd;
|
||||
const u8 *txbuf;
|
||||
|
||||
txbuf = t->tx_buf;
|
||||
cmd = qspi->cmd | QSPI_WR_SNGL;
|
||||
count = t->len;
|
||||
wlen = t->bits_per_word;
|
||||
|
||||
while (count) {
|
||||
switch (wlen) {
|
||||
case 8:
|
||||
dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %02x\n",
|
||||
cmd, qspi->dc, *txbuf);
|
||||
writeb(*txbuf, qspi->base + QSPI_SPI_DATA_REG);
|
||||
ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG);
|
||||
ret = wait_for_completion_timeout(&qspi->transfer_complete,
|
||||
QSPI_COMPLETION_TIMEOUT);
|
||||
if (ret == 0) {
|
||||
dev_err(qspi->dev, "write timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
txbuf += 1;
|
||||
count -= 1;
|
||||
break;
|
||||
case 16:
|
||||
dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %04x\n",
|
||||
cmd, qspi->dc, *txbuf);
|
||||
writew(*((u16 *)txbuf), qspi->base + QSPI_SPI_DATA_REG);
|
||||
ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG);
|
||||
ret = wait_for_completion_timeout(&qspi->transfer_complete,
|
||||
QSPI_COMPLETION_TIMEOUT);
|
||||
if (ret == 0) {
|
||||
dev_err(qspi->dev, "write timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
txbuf += 2;
|
||||
count -= 2;
|
||||
break;
|
||||
case 32:
|
||||
dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %08x\n",
|
||||
cmd, qspi->dc, *txbuf);
|
||||
writel(*((u32 *)txbuf), qspi->base + QSPI_SPI_DATA_REG);
|
||||
ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG);
|
||||
ret = wait_for_completion_timeout(&qspi->transfer_complete,
|
||||
QSPI_COMPLETION_TIMEOUT);
|
||||
if (ret == 0) {
|
||||
dev_err(qspi->dev, "write timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
txbuf += 4;
|
||||
count -= 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t)
|
||||
{
|
||||
int wlen, count, ret;
|
||||
unsigned int cmd;
|
||||
u8 *rxbuf;
|
||||
|
||||
rxbuf = t->rx_buf;
|
||||
cmd = qspi->cmd;
|
||||
switch (t->rx_nbits) {
|
||||
case SPI_NBITS_DUAL:
|
||||
cmd |= QSPI_RD_DUAL;
|
||||
break;
|
||||
case SPI_NBITS_QUAD:
|
||||
cmd |= QSPI_RD_QUAD;
|
||||
break;
|
||||
default:
|
||||
cmd |= QSPI_RD_SNGL;
|
||||
break;
|
||||
}
|
||||
count = t->len;
|
||||
wlen = t->bits_per_word;
|
||||
|
||||
while (count) {
|
||||
dev_dbg(qspi->dev, "rx cmd %08x dc %08x\n", cmd, qspi->dc);
|
||||
ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG);
|
||||
ret = wait_for_completion_timeout(&qspi->transfer_complete,
|
||||
QSPI_COMPLETION_TIMEOUT);
|
||||
if (ret == 0) {
|
||||
dev_err(qspi->dev, "read timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
switch (wlen) {
|
||||
case 8:
|
||||
*rxbuf = readb(qspi->base + QSPI_SPI_DATA_REG);
|
||||
rxbuf += 1;
|
||||
count -= 1;
|
||||
break;
|
||||
case 16:
|
||||
*((u16 *)rxbuf) = readw(qspi->base + QSPI_SPI_DATA_REG);
|
||||
rxbuf += 2;
|
||||
count -= 2;
|
||||
break;
|
||||
case 32:
|
||||
*((u32 *)rxbuf) = readl(qspi->base + QSPI_SPI_DATA_REG);
|
||||
rxbuf += 4;
|
||||
count -= 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (t->tx_buf) {
|
||||
ret = qspi_write_msg(qspi, t);
|
||||
if (ret) {
|
||||
dev_dbg(qspi->dev, "Error while writing\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (t->rx_buf) {
|
||||
ret = qspi_read_msg(qspi, t);
|
||||
if (ret) {
|
||||
dev_dbg(qspi->dev, "Error while reading\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_qspi_start_transfer_one(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct ti_qspi *qspi = spi_master_get_devdata(master);
|
||||
struct spi_device *spi = m->spi;
|
||||
struct spi_transfer *t;
|
||||
int status = 0, ret;
|
||||
int frame_length;
|
||||
|
||||
/* setup device control reg */
|
||||
qspi->dc = 0;
|
||||
|
||||
if (spi->mode & SPI_CPHA)
|
||||
qspi->dc |= QSPI_CKPHA(spi->chip_select);
|
||||
if (spi->mode & SPI_CPOL)
|
||||
qspi->dc |= QSPI_CKPOL(spi->chip_select);
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
qspi->dc |= QSPI_CSPOL(spi->chip_select);
|
||||
|
||||
frame_length = (m->frame_length << 3) / spi->bits_per_word;
|
||||
|
||||
frame_length = clamp(frame_length, 0, QSPI_FRAME);
|
||||
|
||||
/* setup command reg */
|
||||
qspi->cmd = 0;
|
||||
qspi->cmd |= QSPI_EN_CS(spi->chip_select);
|
||||
qspi->cmd |= QSPI_FLEN(frame_length);
|
||||
qspi->cmd |= QSPI_WC_CMD_INT_EN;
|
||||
|
||||
ti_qspi_write(qspi, QSPI_WC_INT_EN, QSPI_INTR_ENABLE_SET_REG);
|
||||
ti_qspi_write(qspi, qspi->dc, QSPI_SPI_DC_REG);
|
||||
|
||||
mutex_lock(&qspi->list_lock);
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
qspi->cmd |= QSPI_WLEN(t->bits_per_word);
|
||||
|
||||
ret = qspi_transfer_msg(qspi, t);
|
||||
if (ret) {
|
||||
dev_dbg(qspi->dev, "transfer message failed\n");
|
||||
mutex_unlock(&qspi->list_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
m->actual_length += t->len;
|
||||
}
|
||||
|
||||
mutex_unlock(&qspi->list_lock);
|
||||
|
||||
m->status = status;
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
ti_qspi_write(qspi, qspi->cmd | QSPI_INVAL, QSPI_SPI_CMD_REG);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static irqreturn_t ti_qspi_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct ti_qspi *qspi = dev_id;
|
||||
u16 int_stat;
|
||||
|
||||
irqreturn_t ret = IRQ_HANDLED;
|
||||
|
||||
spin_lock(&qspi->lock);
|
||||
|
||||
int_stat = ti_qspi_read(qspi, QSPI_INTR_STATUS_ENABLED_CLEAR);
|
||||
qspi->stat = ti_qspi_read(qspi, QSPI_SPI_STATUS_REG);
|
||||
|
||||
if (!int_stat) {
|
||||
dev_dbg(qspi->dev, "No IRQ triggered\n");
|
||||
ret = IRQ_NONE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
|
||||
ti_qspi_write(qspi, QSPI_WC_INT_DISABLE, QSPI_INTR_ENABLE_CLEAR_REG);
|
||||
ti_qspi_write(qspi, QSPI_WC_INT_DISABLE,
|
||||
QSPI_INTR_STATUS_ENABLED_CLEAR);
|
||||
|
||||
out:
|
||||
spin_unlock(&qspi->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t ti_qspi_threaded_isr(int this_irq, void *dev_id)
|
||||
{
|
||||
struct ti_qspi *qspi = dev_id;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&qspi->lock, flags);
|
||||
|
||||
if (qspi->stat & WC)
|
||||
complete(&qspi->transfer_complete);
|
||||
|
||||
spin_unlock_irqrestore(&qspi->lock, flags);
|
||||
|
||||
ti_qspi_write(qspi, QSPI_WC_INT_EN, QSPI_INTR_ENABLE_SET_REG);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ti_qspi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct ti_qspi *qspi;
|
||||
struct spi_master *master;
|
||||
|
||||
master = dev_get_drvdata(dev);
|
||||
qspi = spi_master_get_devdata(master);
|
||||
ti_qspi_restore_ctx(qspi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ti_qspi_match[] = {
|
||||
{.compatible = "ti,dra7xxx-qspi" },
|
||||
{.compatible = "ti,am4372-qspi" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_qspi_match);
|
||||
|
||||
static int ti_qspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_qspi *qspi;
|
||||
struct spi_master *master;
|
||||
struct resource *r;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
u32 max_freq;
|
||||
int ret = 0, num_cs, irq;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*qspi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
|
||||
master->bus_num = -1;
|
||||
master->flags = SPI_MASTER_HALF_DUPLEX;
|
||||
master->setup = ti_qspi_setup;
|
||||
master->auto_runtime_pm = true;
|
||||
master->transfer_one_message = ti_qspi_start_transfer_one;
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1);
|
||||
|
||||
if (!of_property_read_u32(np, "num-cs", &num_cs))
|
||||
master->num_chipselect = num_cs;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
qspi = spi_master_get_devdata(master);
|
||||
qspi->master = master;
|
||||
qspi->dev = &pdev->dev;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
spin_lock_init(&qspi->lock);
|
||||
mutex_init(&qspi->list_lock);
|
||||
|
||||
qspi->base = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(qspi->base)) {
|
||||
ret = PTR_ERR(qspi->base);
|
||||
goto free_master;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, ti_qspi_isr,
|
||||
ti_qspi_threaded_isr, 0,
|
||||
dev_name(&pdev->dev), qspi);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
|
||||
irq);
|
||||
goto free_master;
|
||||
}
|
||||
|
||||
qspi->fclk = devm_clk_get(&pdev->dev, "fck");
|
||||
if (IS_ERR(qspi->fclk)) {
|
||||
ret = PTR_ERR(qspi->fclk);
|
||||
dev_err(&pdev->dev, "could not get clk: %d\n", ret);
|
||||
}
|
||||
|
||||
init_completion(&qspi->transfer_complete);
|
||||
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, QSPI_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
if (!of_property_read_u32(np, "spi-max-frequency", &max_freq))
|
||||
qspi->spi_max_frequency = max_freq;
|
||||
|
||||
ret = spi_register_master(master);
|
||||
if (ret)
|
||||
goto free_master;
|
||||
|
||||
return 0;
|
||||
|
||||
free_master:
|
||||
spi_master_put(master);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_qspi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_qspi *qspi = platform_get_drvdata(pdev);
|
||||
|
||||
spi_unregister_master(qspi->master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ti_qspi_pm_ops = {
|
||||
.runtime_resume = ti_qspi_runtime_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver ti_qspi_driver = {
|
||||
.probe = ti_qspi_probe,
|
||||
.remove = ti_qspi_remove,
|
||||
.driver = {
|
||||
.name = "ti,dra7xxx-qspi",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ti_qspi_pm_ops,
|
||||
.of_match_table = ti_qspi_match,
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(ti_qspi_driver);
|
||||
|
||||
MODULE_AUTHOR("Sourav Poddar <sourav.poddar@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("TI QSPI controller driver");
|
|
@ -283,7 +283,7 @@ static int ti_ssp_spi_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
int error = 0;
|
||||
|
||||
pdata = dev->platform_data;
|
||||
pdata = dev_get_platdata(dev);
|
||||
if (!pdata) {
|
||||
dev_err(dev, "platform data not found\n");
|
||||
return -EINVAL;
|
||||
|
|
|
@ -52,8 +52,7 @@ static inline int tle62x0_write(struct tle62x0_state *st)
|
|||
buff[1] = gpio_state;
|
||||
}
|
||||
|
||||
dev_dbg(&st->us->dev, "buff %02x,%02x,%02x\n",
|
||||
buff[0], buff[1], buff[2]);
|
||||
dev_dbg(&st->us->dev, "buff %3ph\n", buff);
|
||||
|
||||
return spi_write(st->us, buff, (st->nr_gpio == 16) ? 3 : 2);
|
||||
}
|
||||
|
@ -247,7 +246,7 @@ static int tle62x0_probe(struct spi_device *spi)
|
|||
int ptr;
|
||||
int ret;
|
||||
|
||||
pdata = spi->dev.platform_data;
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
if (pdata == NULL) {
|
||||
dev_err(&spi->dev, "no device data specified\n");
|
||||
return -EINVAL;
|
||||
|
|
|
@ -1797,3 +1797,5 @@ MODULE_PARM_DESC(use_dma,
|
|||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semiconductor ML7xxx IOH SPI Driver");
|
||||
MODULE_DEVICE_TABLE(pci, pch_spi_pcidev_id);
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
|
||||
#define SPI_FIFO_SIZE 4
|
||||
|
|
|
@ -80,10 +80,9 @@ struct xilinx_spi {
|
|||
/* bitbang has to be first */
|
||||
struct spi_bitbang bitbang;
|
||||
struct completion done;
|
||||
struct resource mem; /* phys mem */
|
||||
void __iomem *regs; /* virt. address of the control registers */
|
||||
|
||||
u32 irq;
|
||||
int irq;
|
||||
|
||||
u8 *rx_ptr; /* pointer in the Tx buffer */
|
||||
const u8 *tx_ptr; /* pointer in the Rx buffer */
|
||||
|
@ -233,21 +232,6 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
/* always return 0, we can not check the number of bits.
|
||||
* There are cases when SPI setup is called before any driver is
|
||||
* there, in that case the SPI core defaults to 8 bits, which we
|
||||
* do not support in some cases. But if we return an error, the
|
||||
* SPI device would not be registered and no driver can get hold of it
|
||||
* When the driver is there, it will call SPI setup again with the
|
||||
* correct number of bits per transfer.
|
||||
* If a driver setups with the wrong bit number, it will fail when
|
||||
* it tries to do a transfer
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi)
|
||||
{
|
||||
u8 sr;
|
||||
|
@ -355,17 +339,34 @@ static const struct of_device_id xilinx_spi_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, xilinx_spi_of_match);
|
||||
|
||||
struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
|
||||
u32 irq, s16 bus_num, int num_cs, int bits_per_word)
|
||||
static int xilinx_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct xilinx_spi *xspi;
|
||||
int ret;
|
||||
struct xspi_platform_data *pdata;
|
||||
struct resource *res;
|
||||
int ret, num_cs = 0, bits_per_word = 8;
|
||||
struct spi_master *master;
|
||||
u32 tmp;
|
||||
u8 i;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof(struct xilinx_spi));
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (pdata) {
|
||||
num_cs = pdata->num_chipselect;
|
||||
bits_per_word = pdata->bits_per_word;
|
||||
} else {
|
||||
of_property_read_u32(pdev->dev.of_node, "xlnx,num-ss-bits",
|
||||
&num_cs);
|
||||
}
|
||||
|
||||
if (!num_cs) {
|
||||
dev_err(&pdev->dev,
|
||||
"Missing slave select configuration data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct xilinx_spi));
|
||||
if (!master)
|
||||
return NULL;
|
||||
return -ENODEV;
|
||||
|
||||
/* the spi->mode bits understood by this driver: */
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
|
@ -375,25 +376,18 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
|
|||
xspi->bitbang.chipselect = xilinx_spi_chipselect;
|
||||
xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer;
|
||||
xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs;
|
||||
xspi->bitbang.master->setup = xilinx_spi_setup;
|
||||
init_completion(&xspi->done);
|
||||
|
||||
if (!request_mem_region(mem->start, resource_size(mem),
|
||||
XILINX_SPI_NAME))
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
xspi->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(xspi->regs)) {
|
||||
ret = PTR_ERR(xspi->regs);
|
||||
goto put_master;
|
||||
|
||||
xspi->regs = ioremap(mem->start, resource_size(mem));
|
||||
if (xspi->regs == NULL) {
|
||||
dev_warn(dev, "ioremap failure\n");
|
||||
goto map_failed;
|
||||
}
|
||||
|
||||
master->bus_num = bus_num;
|
||||
master->bus_num = pdev->dev.id;
|
||||
master->num_chipselect = num_cs;
|
||||
master->dev.of_node = dev->of_node;
|
||||
|
||||
xspi->mem = *mem;
|
||||
xspi->irq = irq;
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
/*
|
||||
* Detect endianess on the IP via loop bit in CR. Detection
|
||||
|
@ -423,113 +417,63 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
|
|||
} else if (xspi->bits_per_word == 32) {
|
||||
xspi->tx_fn = xspi_tx32;
|
||||
xspi->rx_fn = xspi_rx32;
|
||||
} else
|
||||
goto unmap_io;
|
||||
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto put_master;
|
||||
}
|
||||
|
||||
/* SPI controller initializations */
|
||||
xspi_init_hw(xspi);
|
||||
|
||||
xspi->irq = platform_get_irq(pdev, 0);
|
||||
if (xspi->irq < 0) {
|
||||
ret = xspi->irq;
|
||||
goto put_master;
|
||||
}
|
||||
|
||||
/* Register for SPI Interrupt */
|
||||
ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
|
||||
ret = devm_request_irq(&pdev->dev, xspi->irq, xilinx_spi_irq, 0,
|
||||
dev_name(&pdev->dev), xspi);
|
||||
if (ret)
|
||||
goto unmap_io;
|
||||
goto put_master;
|
||||
|
||||
ret = spi_bitbang_start(&xspi->bitbang);
|
||||
if (ret) {
|
||||
dev_err(dev, "spi_bitbang_start FAILED\n");
|
||||
goto free_irq;
|
||||
dev_err(&pdev->dev, "spi_bitbang_start FAILED\n");
|
||||
goto put_master;
|
||||
}
|
||||
|
||||
dev_info(dev, "at 0x%08llX mapped to 0x%p, irq=%d\n",
|
||||
(unsigned long long)mem->start, xspi->regs, xspi->irq);
|
||||
return master;
|
||||
|
||||
free_irq:
|
||||
free_irq(xspi->irq, xspi);
|
||||
unmap_io:
|
||||
iounmap(xspi->regs);
|
||||
map_failed:
|
||||
release_mem_region(mem->start, resource_size(mem));
|
||||
put_master:
|
||||
spi_master_put(master);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(xilinx_spi_init);
|
||||
|
||||
void xilinx_spi_deinit(struct spi_master *master)
|
||||
{
|
||||
struct xilinx_spi *xspi;
|
||||
|
||||
xspi = spi_master_get_devdata(master);
|
||||
|
||||
spi_bitbang_stop(&xspi->bitbang);
|
||||
free_irq(xspi->irq, xspi);
|
||||
iounmap(xspi->regs);
|
||||
|
||||
release_mem_region(xspi->mem.start, resource_size(&xspi->mem));
|
||||
spi_master_put(xspi->bitbang.master);
|
||||
}
|
||||
EXPORT_SYMBOL(xilinx_spi_deinit);
|
||||
|
||||
static int xilinx_spi_probe(struct platform_device *dev)
|
||||
{
|
||||
struct xspi_platform_data *pdata;
|
||||
struct resource *r;
|
||||
int irq, num_cs = 0, bits_per_word = 8;
|
||||
struct spi_master *master;
|
||||
u8 i;
|
||||
|
||||
pdata = dev->dev.platform_data;
|
||||
if (pdata) {
|
||||
num_cs = pdata->num_chipselect;
|
||||
bits_per_word = pdata->bits_per_word;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
if (dev->dev.of_node) {
|
||||
const __be32 *prop;
|
||||
int len;
|
||||
|
||||
/* number of slave select bits is required */
|
||||
prop = of_get_property(dev->dev.of_node, "xlnx,num-ss-bits",
|
||||
&len);
|
||||
if (prop && len >= sizeof(*prop))
|
||||
num_cs = __be32_to_cpup(prop);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!num_cs) {
|
||||
dev_err(&dev->dev, "Missing slave select configuration data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
if (!r)
|
||||
return -ENODEV;
|
||||
|
||||
irq = platform_get_irq(dev, 0);
|
||||
if (irq < 0)
|
||||
return -ENXIO;
|
||||
|
||||
master = xilinx_spi_init(&dev->dev, r, irq, dev->id, num_cs,
|
||||
bits_per_word);
|
||||
if (!master)
|
||||
return -ENODEV;
|
||||
dev_info(&pdev->dev, "at 0x%08llX mapped to 0x%p, irq=%d\n",
|
||||
(unsigned long long)res->start, xspi->regs, xspi->irq);
|
||||
|
||||
if (pdata) {
|
||||
for (i = 0; i < pdata->num_devices; i++)
|
||||
spi_new_device(master, pdata->devices + i);
|
||||
}
|
||||
|
||||
platform_set_drvdata(dev, master);
|
||||
platform_set_drvdata(pdev, master);
|
||||
return 0;
|
||||
|
||||
put_master:
|
||||
spi_master_put(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xilinx_spi_remove(struct platform_device *dev)
|
||||
static int xilinx_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
xilinx_spi_deinit(platform_get_drvdata(dev));
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct xilinx_spi *xspi = spi_master_get_devdata(master);
|
||||
void __iomem *regs_base = xspi->regs;
|
||||
|
||||
spi_bitbang_stop(&xspi->bitbang);
|
||||
|
||||
/* Disable all the interrupts just in case */
|
||||
xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET);
|
||||
/* Disable the global IPIF interrupt */
|
||||
xspi->write_fn(0, regs_base + XIPIF_V123B_DGIER_OFFSET);
|
||||
|
||||
spi_master_put(xspi->bitbang.master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -553,6 +553,10 @@ static void spi_pump_messages(struct kthread_work *work)
|
|||
master->unprepare_transfer_hardware(master))
|
||||
dev_err(&master->dev,
|
||||
"failed to unprepare transfer hardware\n");
|
||||
if (master->auto_runtime_pm) {
|
||||
pm_runtime_mark_last_busy(master->dev.parent);
|
||||
pm_runtime_put_autosuspend(master->dev.parent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -572,11 +576,23 @@ static void spi_pump_messages(struct kthread_work *work)
|
|||
master->busy = true;
|
||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
|
||||
if (!was_busy && master->auto_runtime_pm) {
|
||||
ret = pm_runtime_get_sync(master->dev.parent);
|
||||
if (ret < 0) {
|
||||
dev_err(&master->dev, "Failed to power device: %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!was_busy && master->prepare_transfer_hardware) {
|
||||
ret = master->prepare_transfer_hardware(master);
|
||||
if (ret) {
|
||||
dev_err(&master->dev,
|
||||
"failed to prepare transfer hardware\n");
|
||||
|
||||
if (master->auto_runtime_pm)
|
||||
pm_runtime_put(master->dev.parent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -774,7 +790,7 @@ static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
|
|||
msg->status = -EINPROGRESS;
|
||||
|
||||
list_add_tail(&msg->queue, &master->queue);
|
||||
if (master->running && !master->busy)
|
||||
if (!master->busy)
|
||||
queue_kthread_work(&master->kworker, &master->pump_messages);
|
||||
|
||||
spin_unlock_irqrestore(&master->queue_lock, flags);
|
||||
|
@ -869,6 +885,47 @@ static void of_register_spi_devices(struct spi_master *master)
|
|||
if (of_find_property(nc, "spi-3wire", NULL))
|
||||
spi->mode |= SPI_3WIRE;
|
||||
|
||||
/* Device DUAL/QUAD mode */
|
||||
prop = of_get_property(nc, "spi-tx-bus-width", &len);
|
||||
if (prop && len == sizeof(*prop)) {
|
||||
switch (be32_to_cpup(prop)) {
|
||||
case SPI_NBITS_SINGLE:
|
||||
break;
|
||||
case SPI_NBITS_DUAL:
|
||||
spi->mode |= SPI_TX_DUAL;
|
||||
break;
|
||||
case SPI_NBITS_QUAD:
|
||||
spi->mode |= SPI_TX_QUAD;
|
||||
break;
|
||||
default:
|
||||
dev_err(&master->dev,
|
||||
"spi-tx-bus-width %d not supported\n",
|
||||
be32_to_cpup(prop));
|
||||
spi_dev_put(spi);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
prop = of_get_property(nc, "spi-rx-bus-width", &len);
|
||||
if (prop && len == sizeof(*prop)) {
|
||||
switch (be32_to_cpup(prop)) {
|
||||
case SPI_NBITS_SINGLE:
|
||||
break;
|
||||
case SPI_NBITS_DUAL:
|
||||
spi->mode |= SPI_RX_DUAL;
|
||||
break;
|
||||
case SPI_NBITS_QUAD:
|
||||
spi->mode |= SPI_RX_QUAD;
|
||||
break;
|
||||
default:
|
||||
dev_err(&master->dev,
|
||||
"spi-rx-bus-width %d not supported\n",
|
||||
be32_to_cpup(prop));
|
||||
spi_dev_put(spi);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Device speed */
|
||||
prop = of_get_property(nc, "spi-max-frequency", &len);
|
||||
if (!prop || len < sizeof(*prop)) {
|
||||
|
@ -1169,7 +1226,7 @@ int spi_register_master(struct spi_master *master)
|
|||
else {
|
||||
status = spi_master_initialize_queue(master);
|
||||
if (status) {
|
||||
device_unregister(&master->dev);
|
||||
device_del(&master->dev);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
@ -1316,6 +1373,19 @@ int spi_setup(struct spi_device *spi)
|
|||
unsigned bad_bits;
|
||||
int status = 0;
|
||||
|
||||
/* check mode to prevent that DUAL and QUAD set at the same time
|
||||
*/
|
||||
if (((spi->mode & SPI_TX_DUAL) && (spi->mode & SPI_TX_QUAD)) ||
|
||||
((spi->mode & SPI_RX_DUAL) && (spi->mode & SPI_RX_QUAD))) {
|
||||
dev_err(&spi->dev,
|
||||
"setup: can not select dual and quad at the same time\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden
|
||||
*/
|
||||
if ((spi->mode & SPI_3WIRE) && (spi->mode &
|
||||
(SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)))
|
||||
return -EINVAL;
|
||||
/* help drivers fail *cleanly* when they need options
|
||||
* that aren't supported with their current master
|
||||
*/
|
||||
|
@ -1351,6 +1421,11 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
|
|||
struct spi_master *master = spi->master;
|
||||
struct spi_transfer *xfer;
|
||||
|
||||
if (list_empty(&message->transfers))
|
||||
return -EINVAL;
|
||||
if (!message->complete)
|
||||
return -EINVAL;
|
||||
|
||||
/* Half-duplex links include original MicroWire, and ones with
|
||||
* only one data pin like SPI_3WIRE (switches direction) or where
|
||||
* either MOSI or MISO is missing. They can also be caused by
|
||||
|
@ -1373,12 +1448,20 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
|
|||
/**
|
||||
* Set transfer bits_per_word and max speed as spi device default if
|
||||
* it is not set for this transfer.
|
||||
* Set transfer tx_nbits and rx_nbits as single transfer default
|
||||
* (SPI_NBITS_SINGLE) if it is not set for this transfer.
|
||||
*/
|
||||
list_for_each_entry(xfer, &message->transfers, transfer_list) {
|
||||
message->frame_length += xfer->len;
|
||||
if (!xfer->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;
|
||||
if (master->max_speed_hz &&
|
||||
xfer->speed_hz > master->max_speed_hz)
|
||||
xfer->speed_hz = master->max_speed_hz;
|
||||
}
|
||||
|
||||
if (master->bits_per_word_mask) {
|
||||
/* Only 32 bits fit in the mask */
|
||||
if (xfer->bits_per_word > 32)
|
||||
|
@ -1387,6 +1470,54 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
|
|||
BIT(xfer->bits_per_word - 1)))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (xfer->speed_hz && master->min_speed_hz &&
|
||||
xfer->speed_hz < master->min_speed_hz)
|
||||
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)
|
||||
xfer->tx_nbits = SPI_NBITS_SINGLE;
|
||||
if (xfer->rx_buf && !xfer->rx_nbits)
|
||||
xfer->rx_nbits = SPI_NBITS_SINGLE;
|
||||
/* check transfer tx/rx_nbits:
|
||||
* 1. keep the value is not out of single, dual and quad
|
||||
* 2. keep tx/rx_nbits is contained by mode in spi_device
|
||||
* 3. if SPI_3WIRE, tx/rx_nbits should be in single
|
||||
*/
|
||||
if (xfer->tx_buf) {
|
||||
if (xfer->tx_nbits != SPI_NBITS_SINGLE &&
|
||||
xfer->tx_nbits != SPI_NBITS_DUAL &&
|
||||
xfer->tx_nbits != SPI_NBITS_QUAD)
|
||||
return -EINVAL;
|
||||
if ((xfer->tx_nbits == SPI_NBITS_DUAL) &&
|
||||
!(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD)))
|
||||
return -EINVAL;
|
||||
if ((xfer->tx_nbits == SPI_NBITS_QUAD) &&
|
||||
!(spi->mode & SPI_TX_QUAD))
|
||||
return -EINVAL;
|
||||
if ((spi->mode & SPI_3WIRE) &&
|
||||
(xfer->tx_nbits != SPI_NBITS_SINGLE))
|
||||
return -EINVAL;
|
||||
}
|
||||
/* check transfer rx_nbits */
|
||||
if (xfer->rx_buf) {
|
||||
if (xfer->rx_nbits != SPI_NBITS_SINGLE &&
|
||||
xfer->rx_nbits != SPI_NBITS_DUAL &&
|
||||
xfer->rx_nbits != SPI_NBITS_QUAD)
|
||||
return -EINVAL;
|
||||
if ((xfer->rx_nbits == SPI_NBITS_DUAL) &&
|
||||
!(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD)))
|
||||
return -EINVAL;
|
||||
if ((xfer->rx_nbits == SPI_NBITS_QUAD) &&
|
||||
!(spi->mode & SPI_RX_QUAD))
|
||||
return -EINVAL;
|
||||
if ((spi->mode & SPI_3WIRE) &&
|
||||
(xfer->rx_nbits != SPI_NBITS_SINGLE))
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
message->spi = spi;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef __LINUX_PLATFORM_DATA_EFM32_SPI_H__
|
||||
#define __LINUX_PLATFORM_DATA_EFM32_SPI_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* struct efm32_spi_pdata
|
||||
* @location: pinmux location for the I/O pins (to be written to the ROUTE
|
||||
* register)
|
||||
*/
|
||||
struct efm32_spi_pdata {
|
||||
u8 location;
|
||||
};
|
||||
#endif /* ifndef __LINUX_PLATFORM_DATA_EFM32_SPI_H__ */
|
|
@ -74,7 +74,7 @@ struct spi_device {
|
|||
struct spi_master *master;
|
||||
u32 max_speed_hz;
|
||||
u8 chip_select;
|
||||
u8 mode;
|
||||
u16 mode;
|
||||
#define SPI_CPHA 0x01 /* clock phase */
|
||||
#define SPI_CPOL 0x02 /* clock polarity */
|
||||
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
|
||||
|
@ -87,6 +87,10 @@ struct spi_device {
|
|||
#define SPI_LOOP 0x20 /* loopback mode */
|
||||
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
|
||||
#define SPI_READY 0x80 /* slave pulls low to pause */
|
||||
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
|
||||
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
|
||||
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
|
||||
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
|
||||
u8 bits_per_word;
|
||||
int irq;
|
||||
void *controller_state;
|
||||
|
@ -233,6 +237,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
|||
* suported. If set, the SPI core will reject any transfer with an
|
||||
* unsupported bits_per_word. If not set, this value is simply ignored,
|
||||
* and it's up to the individual driver to perform any validation.
|
||||
* @min_speed_hz: Lowest supported transfer speed
|
||||
* @max_speed_hz: Highest supported transfer speed
|
||||
* @flags: other constraints relevant to this driver
|
||||
* @bus_lock_spinlock: spinlock for SPI bus locking
|
||||
* @bus_lock_mutex: mutex for SPI bus locking
|
||||
|
@ -254,6 +260,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
|||
* @busy: message pump is busy
|
||||
* @running: message pump is running
|
||||
* @rt: whether this queue is set to run as a realtime task
|
||||
* @auto_runtime_pm: the core should ensure a runtime PM reference is held
|
||||
* while the hardware is prepared, using the parent
|
||||
* device for the spidev
|
||||
* @prepare_transfer_hardware: a message will soon arrive from the queue
|
||||
* so the subsystem requests the driver to prepare the transfer hardware
|
||||
* by issuing this call
|
||||
|
@ -309,9 +318,13 @@ struct spi_master {
|
|||
/* bitmask of supported bits_per_word for transfers */
|
||||
u32 bits_per_word_mask;
|
||||
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
|
||||
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0UL : (BIT(bits) - 1))
|
||||
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
|
||||
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
|
||||
|
||||
/* limits on transfer speed */
|
||||
u32 min_speed_hz;
|
||||
u32 max_speed_hz;
|
||||
|
||||
/* other constraints relevant to this driver */
|
||||
u16 flags;
|
||||
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
|
||||
|
@ -374,11 +387,13 @@ struct spi_master {
|
|||
bool busy;
|
||||
bool running;
|
||||
bool rt;
|
||||
bool auto_runtime_pm;
|
||||
|
||||
int (*prepare_transfer_hardware)(struct spi_master *master);
|
||||
int (*transfer_one_message)(struct spi_master *master,
|
||||
struct spi_message *mesg);
|
||||
int (*unprepare_transfer_hardware)(struct spi_master *master);
|
||||
|
||||
/* gpio chip select */
|
||||
int *cs_gpios;
|
||||
};
|
||||
|
@ -448,6 +463,10 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum);
|
|||
* @rx_buf: data to be read (dma-safe memory), or NULL
|
||||
* @tx_dma: DMA address of tx_buf, if @spi_message.is_dma_mapped
|
||||
* @rx_dma: DMA address of rx_buf, if @spi_message.is_dma_mapped
|
||||
* @tx_nbits: number of bits used for writting. If 0 the default
|
||||
* (SPI_NBITS_SINGLE) is used.
|
||||
* @rx_nbits: number of bits used for reading. If 0 the default
|
||||
* (SPI_NBITS_SINGLE) is used.
|
||||
* @len: size of rx and tx buffers (in bytes)
|
||||
* @speed_hz: Select a speed other than the device default for this
|
||||
* transfer. If 0 the default (from @spi_device) is used.
|
||||
|
@ -502,6 +521,11 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum);
|
|||
* by the results of previous messages and where the whole transaction
|
||||
* ends when the chipselect goes intactive.
|
||||
*
|
||||
* When SPI can transfer in 1x,2x or 4x. It can get this tranfer information
|
||||
* from device through @tx_nbits and @rx_nbits. In Bi-direction, these
|
||||
* two should both be set. User can set transfer mode with SPI_NBITS_SINGLE(1x)
|
||||
* SPI_NBITS_DUAL(2x) and SPI_NBITS_QUAD(4x) to support these three transfer.
|
||||
*
|
||||
* The code that submits an spi_message (and its spi_transfers)
|
||||
* to the lower layers is responsible for managing its memory.
|
||||
* Zero-initialize every field you don't set up explicitly, to
|
||||
|
@ -522,6 +546,11 @@ struct spi_transfer {
|
|||
dma_addr_t rx_dma;
|
||||
|
||||
unsigned cs_change:1;
|
||||
u8 tx_nbits;
|
||||
u8 rx_nbits;
|
||||
#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
|
||||
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
|
||||
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
|
||||
u8 bits_per_word;
|
||||
u16 delay_usecs;
|
||||
u32 speed_hz;
|
||||
|
@ -578,6 +607,7 @@ struct spi_message {
|
|||
/* completion is reported through a callback */
|
||||
void (*complete)(void *context);
|
||||
void *context;
|
||||
unsigned frame_length;
|
||||
unsigned actual_length;
|
||||
int status;
|
||||
|
||||
|
@ -869,7 +899,7 @@ struct spi_board_info {
|
|||
/* mode becomes spi_device.mode, and is essential for chips
|
||||
* where the default of SPI_CS_HIGH = 0 is wrong.
|
||||
*/
|
||||
u8 mode;
|
||||
u16 mode;
|
||||
|
||||
/* ... may need additional spi_device chip config data here.
|
||||
* avoid stuff protocol drivers can set; but include stuff
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
#include <linux/workqueue.h>
|
||||
|
||||
struct spi_bitbang {
|
||||
struct workqueue_struct *workqueue;
|
||||
struct work_struct work;
|
||||
|
||||
spinlock_t lock;
|
||||
struct list_head queue;
|
||||
u8 busy;
|
||||
u8 use_dma;
|
||||
u8 flags; /* extra spi->mode support */
|
||||
|
@ -41,7 +37,6 @@ struct spi_bitbang {
|
|||
*/
|
||||
extern int spi_bitbang_setup(struct spi_device *spi);
|
||||
extern void spi_bitbang_cleanup(struct spi_device *spi);
|
||||
extern int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m);
|
||||
extern int spi_bitbang_setup_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *t);
|
||||
|
||||
|
|
Loading…
Reference in New Issue