spi: Updates for v4.2

No framework updates for the SPI API this time around aside from one
 small fix, just driver improvments.  Some highlights include:
 
  - New driver support for CSR USP, Mikrotik RB4xx and Zynq GQSPI
    controllers.
  - Modernisation of the OMAP McSPI controller driver, moving it to
    current APIs to enable support for a wider range of client drivers.
  - DMA support for the bcm2835 controller.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJViTdyAAoJECTWi3JdVIfQxY0H/2dWW+6SIt+To4WHDQDkKluA
 QsT2n+t05LMukdWkD+fiaXMJ+4yqY1Dg8oNRUJeGCltRTJWmcjZep44hRpMv3EJd
 oswPwKcfBMQ5jP3iwoiZvZiJxhTVBm2xJerOtL8ZyALovmU4BPN1VUpq+t1+yLnF
 O53DnuIN29PEm2/VGK1+HlWNRebMEAsG/wlPvkFBT3TyIVwLAoMuYwkRMeK9XVnN
 J9sgdsxQwKwKk6Vnh1oZ2uht83PFrMpZIyX2pKYLGRLxJ8ZYBOPUnAdqbWfDXA9t
 rexsQdNWjJrcyIRJh1NqRnnIA43q6AgdPsCgg8eaI+1Jx3F2Bp+SpRjwK91KW+I=
 =0yyj
 -----END PGP SIGNATURE-----

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

Pull spi updates from Mark Brown:
 "No framework updates for the SPI API this time around aside from one
  small fix, just driver improvments.  Some highlights include:

   - New driver support for CSR USP, Mikrotik RB4xx and Zynq GQSPI
     controllers.

   - Modernisation of the OMAP McSPI controller driver, moving it to
     current APIs to enable support for a wider range of client drivers.

   - DMA support for the bcm2835 controller"

* tag 'spi-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (60 commits)
  spi: zynq: Remove execute bit
  spi: atmel: add support to FIFOs
  spi: atmel: update DT bindings documentation
  spi: spi-fsl-dspi: Update DT binding documentation
  spi: pxa2xx: Constify ACPI device ids
  spi: Add support for Zynq Ultrascale+ MPSoC GQSPI controller
  spi: zynq: Add DT bindings documentation for Zynq Ultrascale+ MPSoC GQSPI controller
  spi: fsl-dspi: Use pinctrl PM helpers
  spi: davinci: change the lower limit of pre-scale divider to 1
  spi: spi-fsl-dspi: Change the way of increasing spi_message->actual_length
  spi: spi-fsl-dspi: Enable TCF interrupt mode support
  spi: atmel: add support for the internal chip-select of the spi controller
  spi: spi-pxa2xx: remove legacy PXA DMA bits
  spi: pxa2xx: Make LPSS SPI general register optional
  spi: pxa2xx: Prepare for new Intel LPSS SPI type
  spi: pxa2xx: Differentiate Intel LPSS types
  spi: restore rx/tx_buf in case of unset CONFIG_HAS_DMA
  spi: rspi: Re-do the returning value of qspi_transfer_out_in
  spi: rspi: modify the name of "qspi_trigger_transfer_out_int" function
  spi: orion: Fix extended baud rates for each Armada SoCs
  ...
This commit is contained in:
Linus Torvalds 2015-06-23 17:27:09 -07:00
commit 5a602e157a
32 changed files with 3292 additions and 1198 deletions

View File

@ -0,0 +1,24 @@
Binding for Qualcomm Atheros AR7xxx/AR9xxx SPI controller
Required properties:
- compatible: has to be "qca,<soc-type>-spi", "qca,ar7100-spi" as fallback.
- reg: Base address and size of the controllers memory area
- clocks: phandle to the AHB clock.
- clock-names: has to be "ahb".
- #address-cells: <1>, as required by generic SPI binding.
- #size-cells: <0>, also as required by generic SPI binding.
Child nodes as per the generic SPI binding.
Example:
spi@1F000000 {
compatible = "qca,ar9132-spi", "qca,ar7100-spi";
reg = <0x1F000000 0x10>;
clocks = <&pll 2>;
clock-names = "ahb";
#address-cells = <1>;
#size-cells = <0>;
};

View File

@ -1,7 +1,7 @@
ARM Freescale DSPI controller ARM Freescale DSPI controller
Required properties: Required properties:
- compatible : "fsl,vf610-dspi" - compatible : "fsl,vf610-dspi", "fsl,ls1021a-v1.0-dspi", "fsl,ls2085a-dspi"
- reg : Offset and length of the register set for the device - reg : Offset and length of the register set for the device
- interrupts : Should contain SPI controller interrupt - interrupts : Should contain SPI controller interrupt
- clocks: from common clock binding: handle to dspi clock. - clocks: from common clock binding: handle to dspi clock.

View File

@ -1,7 +1,13 @@
Marvell Orion SPI device Marvell Orion SPI device
Required properties: Required properties:
- compatible : should be "marvell,orion-spi" or "marvell,armada-370-spi". - compatible : should be on of the following:
- "marvell,orion-spi" for the Orion, mv78x00, Kirkwood and Dove SoCs
- "marvell,armada-370-spi", for the Armada 370 SoCs
- "marvell,armada-375-spi", for the Armada 375 SoCs
- "marvell,armada-380-spi", for the Armada 38x SoCs
- "marvell,armada-390-spi", for the Armada 39x SoCs
- "marvell,armada-xp-spi", for the Armada XP SoCs
- reg : offset and length of the register set for the device - reg : offset and length of the register set for the device
- cell-index : Which of multiple SPI controllers is this. - cell-index : Which of multiple SPI controllers is this.
Optional properties: Optional properties:

View File

@ -1,7 +1,8 @@
* CSR SiRFprimaII Serial Peripheral Interface * CSR SiRFprimaII Serial Peripheral Interface
Required properties: Required properties:
- compatible : Should be "sirf,prima2-spi" - compatible : Should be "sirf,prima2-spi", "sirf,prima2-usp"
or "sirf,atlas7-usp"
- reg : Offset and length of the register set for the device - reg : Offset and length of the register set for the device
- interrupts : Should contain SPI interrupt - interrupts : Should contain SPI interrupt
- resets: phandle to the reset controller asserting this device in - resets: phandle to the reset controller asserting this device in

View File

@ -0,0 +1,26 @@
Xilinx Zynq UltraScale+ MPSoC GQSPI controller Device Tree Bindings
-------------------------------------------------------------------
Required properties:
- compatible : Should be "xlnx,zynqmp-qspi-1.0".
- reg : Physical base address and size of GQSPI registers map.
- interrupts : Property with a value describing the interrupt
number.
- interrupt-parent : Must be core interrupt controller.
- clock-names : List of input clock names - "ref_clk", "pclk"
(See clock bindings for details).
- clocks : Clock phandles (see clock bindings for details).
Optional properties:
- num-cs : Number of chip selects used.
Example:
qspi: spi@ff0f0000 {
compatible = "xlnx,zynqmp-qspi-1.0";
clock-names = "ref_clk", "pclk";
clocks = <&misc_clk &misc_clk>;
interrupts = <0 15 4>;
interrupt-parent = <&gic>;
num-cs = <1>;
reg = <0x0 0xff0f0000 0x1000>,<0x0 0xc0000000 0x8000000>;
};

View File

@ -4,11 +4,16 @@ Required properties:
- compatible : should be "atmel,at91rm9200-spi". - compatible : should be "atmel,at91rm9200-spi".
- reg: Address and length of the register set for the device - reg: Address and length of the register set for the device
- interrupts: Should contain spi interrupt - interrupts: Should contain spi interrupt
- cs-gpios: chipselects - cs-gpios: chipselects (optional for SPI controller version >= 2 with the
Chip Select Active After Transfer feature).
- clock-names: tuple listing input clock names. - clock-names: tuple listing input clock names.
Required elements: "spi_clk" Required elements: "spi_clk"
- clocks: phandles to input clocks. - clocks: phandles to input clocks.
Optional properties:
- atmel,fifo-size: maximum number of data the RX and TX FIFOs can store for FIFO
capable SPI controllers.
Example: Example:
spi1: spi@fffcc000 { spi1: spi@fffcc000 {
@ -20,6 +25,7 @@ spi1: spi@fffcc000 {
clocks = <&spi1_clk>; clocks = <&spi1_clk>;
clock-names = "spi_clk"; clock-names = "spi_clk";
cs-gpios = <&pioB 3 0>; cs-gpios = <&pioB 3 0>;
atmel,fifo-size = <32>;
status = "okay"; status = "okay";
mmc-slot@0 { mmc-slot@0 {

View File

@ -4,9 +4,9 @@ Required properties:
- compatible : "arm,pl022", "arm,primecell" - compatible : "arm,pl022", "arm,primecell"
- reg : Offset and length of the register set for the device - reg : Offset and length of the register set for the device
- interrupts : Should contain SPI controller interrupt - interrupts : Should contain SPI controller interrupt
- num-cs : total number of chipselects
Optional properties: Optional properties:
- num-cs : total number of chipselects
- cs-gpios : should specify GPIOs used for chipselects. - cs-gpios : should specify GPIOs used for chipselects.
The gpios will be referred to as reg = <index> in the SPI child nodes. The gpios will be referred to as reg = <index> in the SPI child nodes.
If unspecified, a single SPI device without a chip select can be used. If unspecified, a single SPI device without a chip select can be used.

View File

@ -16,8 +16,4 @@ struct ath79_spi_platform_data {
unsigned num_chipselect; unsigned num_chipselect;
}; };
struct ath79_spi_controller_data {
unsigned gpio;
};
#endif /* _ATH79_SPI_PLATFORM_H */ #endif /* _ATH79_SPI_PLATFORM_H */

View File

@ -77,6 +77,7 @@ config SPI_ATMEL
config SPI_BCM2835 config SPI_BCM2835
tristate "BCM2835 SPI controller" tristate "BCM2835 SPI controller"
depends on GPIOLIB
depends on ARCH_BCM2835 || COMPILE_TEST depends on ARCH_BCM2835 || COMPILE_TEST
depends on GPIOLIB depends on GPIOLIB
help help
@ -221,7 +222,7 @@ config SPI_FALCON
config SPI_GPIO config SPI_GPIO
tristate "GPIO-based bitbanging SPI Master" tristate "GPIO-based bitbanging SPI Master"
depends on GPIOLIB depends on GPIOLIB || COMPILE_TEST
select SPI_BITBANG select SPI_BITBANG
help help
This simple GPIO bitbanging SPI master uses the arch-neutral GPIO This simple GPIO bitbanging SPI master uses the arch-neutral GPIO
@ -327,7 +328,7 @@ config SPI_MESON_SPIFC
config SPI_OC_TINY config SPI_OC_TINY
tristate "OpenCores tiny SPI" tristate "OpenCores tiny SPI"
depends on GPIOLIB depends on GPIOLIB || COMPILE_TEST
select SPI_BITBANG select SPI_BITBANG
help help
This is the driver for OpenCores tiny SPI master controller. This is the driver for OpenCores tiny SPI master controller.
@ -394,16 +395,9 @@ config SPI_PPC4xx
help help
This selects a driver for the PPC4xx SPI Controller. This selects a driver for the PPC4xx SPI Controller.
config SPI_PXA2XX_PXADMA
bool "PXA2xx SSP legacy PXA DMA API support"
depends on SPI_PXA2XX && ARCH_PXA
help
Enable PXA private legacy DMA API support. Note that this is
deprecated in favor of generic DMA engine API.
config SPI_PXA2XX_DMA config SPI_PXA2XX_DMA
def_bool y def_bool y
depends on SPI_PXA2XX && !SPI_PXA2XX_PXADMA depends on SPI_PXA2XX
config SPI_PXA2XX config SPI_PXA2XX
tristate "PXA2xx SSP SPI master" tristate "PXA2xx SSP SPI master"
@ -429,6 +423,12 @@ config SPI_ROCKCHIP
The main usecase of this controller is to use spi flash as boot The main usecase of this controller is to use spi flash as boot
device. device.
config SPI_RB4XX
tristate "Mikrotik RB4XX SPI master"
depends on SPI_MASTER && ATH79
help
SPI controller driver for the Mikrotik RB4xx series boards.
config SPI_RSPI config SPI_RSPI
tristate "Renesas RSPI/QSPI controller" tristate "Renesas RSPI/QSPI controller"
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
@ -610,6 +610,12 @@ config SPI_XTENSA_XTFPGA
16 bit words in SPI mode 0, automatically asserting CS on transfer 16 bit words in SPI mode 0, automatically asserting CS on transfer
start and deasserting on end. start and deasserting on end.
config SPI_ZYNQMP_GQSPI
tristate "Xilinx ZynqMP GQSPI controller"
depends on SPI_MASTER
help
Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC.
config SPI_NUC900 config SPI_NUC900
tristate "Nuvoton NUC900 series SPI" tristate "Nuvoton NUC900 series SPI"
depends on ARCH_W90X900 depends on ARCH_W90X900

View File

@ -60,12 +60,12 @@ obj-$(CONFIG_SPI_ORION) += spi-orion.o
obj-$(CONFIG_SPI_PL022) += spi-pl022.o obj-$(CONFIG_SPI_PL022) += spi-pl022.o
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
spi-pxa2xx-platform-objs := spi-pxa2xx.o spi-pxa2xx-platform-objs := spi-pxa2xx.o
spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o
spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
obj-$(CONFIG_SPI_QUP) += spi-qup.o obj-$(CONFIG_SPI_QUP) += spi-qup.o
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
spi-s3c24xx-hw-y := spi-s3c24xx.o spi-s3c24xx-hw-y := spi-s3c24xx.o
@ -89,3 +89,4 @@ obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o

View File

@ -79,10 +79,8 @@ static void ath79_spi_chipselect(struct spi_device *spi, int is_active)
} }
if (spi->chip_select) { if (spi->chip_select) {
struct ath79_spi_controller_data *cdata = spi->controller_data;
/* SPI is normally active-low */ /* SPI is normally active-low */
gpio_set_value(cdata->gpio, cs_high); gpio_set_value(spi->cs_gpio, cs_high);
} else { } else {
if (cs_high) if (cs_high)
sp->ioc_base |= AR71XX_SPI_IOC_CS0; sp->ioc_base |= AR71XX_SPI_IOC_CS0;
@ -117,11 +115,10 @@ static void ath79_spi_disable(struct ath79_spi *sp)
static int ath79_spi_setup_cs(struct spi_device *spi) static int ath79_spi_setup_cs(struct spi_device *spi)
{ {
struct ath79_spi_controller_data *cdata; struct ath79_spi *sp = ath79_spidev_to_sp(spi);
int status; int status;
cdata = spi->controller_data; if (spi->chip_select && !gpio_is_valid(spi->cs_gpio))
if (spi->chip_select && !cdata)
return -EINVAL; return -EINVAL;
status = 0; status = 0;
@ -134,8 +131,15 @@ static int ath79_spi_setup_cs(struct spi_device *spi)
else else
flags |= GPIOF_INIT_HIGH; flags |= GPIOF_INIT_HIGH;
status = gpio_request_one(cdata->gpio, flags, status = gpio_request_one(spi->cs_gpio, flags,
dev_name(&spi->dev)); dev_name(&spi->dev));
} else {
if (spi->mode & SPI_CS_HIGH)
sp->ioc_base &= ~AR71XX_SPI_IOC_CS0;
else
sp->ioc_base |= AR71XX_SPI_IOC_CS0;
ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
} }
return status; return status;
@ -144,8 +148,7 @@ static int ath79_spi_setup_cs(struct spi_device *spi)
static void ath79_spi_cleanup_cs(struct spi_device *spi) static void ath79_spi_cleanup_cs(struct spi_device *spi)
{ {
if (spi->chip_select) { if (spi->chip_select) {
struct ath79_spi_controller_data *cdata = spi->controller_data; gpio_free(spi->cs_gpio);
gpio_free(cdata->gpio);
} }
} }
@ -217,6 +220,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
} }
sp = spi_master_get_devdata(master); sp = spi_master_get_devdata(master);
master->dev.of_node = pdev->dev.of_node;
platform_set_drvdata(pdev, sp); platform_set_drvdata(pdev, sp);
pdata = dev_get_platdata(&pdev->dev); pdata = dev_get_platdata(&pdev->dev);
@ -253,7 +257,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
goto err_put_master; goto err_put_master;
} }
ret = clk_enable(sp->clk); ret = clk_prepare_enable(sp->clk);
if (ret) if (ret)
goto err_put_master; goto err_put_master;
@ -277,7 +281,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
err_disable: err_disable:
ath79_spi_disable(sp); ath79_spi_disable(sp);
err_clk_disable: err_clk_disable:
clk_disable(sp->clk); clk_disable_unprepare(sp->clk);
err_put_master: err_put_master:
spi_master_put(sp->bitbang.master); spi_master_put(sp->bitbang.master);
@ -290,7 +294,7 @@ static int ath79_spi_remove(struct platform_device *pdev)
spi_bitbang_stop(&sp->bitbang); spi_bitbang_stop(&sp->bitbang);
ath79_spi_disable(sp); ath79_spi_disable(sp);
clk_disable(sp->clk); clk_disable_unprepare(sp->clk);
spi_master_put(sp->bitbang.master); spi_master_put(sp->bitbang.master);
return 0; return 0;
@ -301,12 +305,18 @@ static void ath79_spi_shutdown(struct platform_device *pdev)
ath79_spi_remove(pdev); ath79_spi_remove(pdev);
} }
static const struct of_device_id ath79_spi_of_match[] = {
{ .compatible = "qca,ar7100-spi", },
{ },
};
static struct platform_driver ath79_spi_driver = { static struct platform_driver ath79_spi_driver = {
.probe = ath79_spi_probe, .probe = ath79_spi_probe,
.remove = ath79_spi_remove, .remove = ath79_spi_remove,
.shutdown = ath79_spi_shutdown, .shutdown = ath79_spi_shutdown,
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
.of_match_table = ath79_spi_of_match,
}, },
}; };
module_platform_driver(ath79_spi_driver); module_platform_driver(ath79_spi_driver);

View File

@ -41,6 +41,8 @@
#define SPI_CSR1 0x0034 #define SPI_CSR1 0x0034
#define SPI_CSR2 0x0038 #define SPI_CSR2 0x0038
#define SPI_CSR3 0x003c #define SPI_CSR3 0x003c
#define SPI_FMR 0x0040
#define SPI_FLR 0x0044
#define SPI_VERSION 0x00fc #define SPI_VERSION 0x00fc
#define SPI_RPR 0x0100 #define SPI_RPR 0x0100
#define SPI_RCR 0x0104 #define SPI_RCR 0x0104
@ -62,6 +64,14 @@
#define SPI_SWRST_SIZE 1 #define SPI_SWRST_SIZE 1
#define SPI_LASTXFER_OFFSET 24 #define SPI_LASTXFER_OFFSET 24
#define SPI_LASTXFER_SIZE 1 #define SPI_LASTXFER_SIZE 1
#define SPI_TXFCLR_OFFSET 16
#define SPI_TXFCLR_SIZE 1
#define SPI_RXFCLR_OFFSET 17
#define SPI_RXFCLR_SIZE 1
#define SPI_FIFOEN_OFFSET 30
#define SPI_FIFOEN_SIZE 1
#define SPI_FIFODIS_OFFSET 31
#define SPI_FIFODIS_SIZE 1
/* Bitfields in MR */ /* Bitfields in MR */
#define SPI_MSTR_OFFSET 0 #define SPI_MSTR_OFFSET 0
@ -114,6 +124,22 @@
#define SPI_TXEMPTY_SIZE 1 #define SPI_TXEMPTY_SIZE 1
#define SPI_SPIENS_OFFSET 16 #define SPI_SPIENS_OFFSET 16
#define SPI_SPIENS_SIZE 1 #define SPI_SPIENS_SIZE 1
#define SPI_TXFEF_OFFSET 24
#define SPI_TXFEF_SIZE 1
#define SPI_TXFFF_OFFSET 25
#define SPI_TXFFF_SIZE 1
#define SPI_TXFTHF_OFFSET 26
#define SPI_TXFTHF_SIZE 1
#define SPI_RXFEF_OFFSET 27
#define SPI_RXFEF_SIZE 1
#define SPI_RXFFF_OFFSET 28
#define SPI_RXFFF_SIZE 1
#define SPI_RXFTHF_OFFSET 29
#define SPI_RXFTHF_SIZE 1
#define SPI_TXFPTEF_OFFSET 30
#define SPI_TXFPTEF_SIZE 1
#define SPI_RXFPTEF_OFFSET 31
#define SPI_RXFPTEF_SIZE 1
/* Bitfields in CSR0 */ /* Bitfields in CSR0 */
#define SPI_CPOL_OFFSET 0 #define SPI_CPOL_OFFSET 0
@ -157,6 +183,22 @@
#define SPI_TXTDIS_OFFSET 9 #define SPI_TXTDIS_OFFSET 9
#define SPI_TXTDIS_SIZE 1 #define SPI_TXTDIS_SIZE 1
/* Bitfields in FMR */
#define SPI_TXRDYM_OFFSET 0
#define SPI_TXRDYM_SIZE 2
#define SPI_RXRDYM_OFFSET 4
#define SPI_RXRDYM_SIZE 2
#define SPI_TXFTHRES_OFFSET 16
#define SPI_TXFTHRES_SIZE 6
#define SPI_RXFTHRES_OFFSET 24
#define SPI_RXFTHRES_SIZE 6
/* Bitfields in FLR */
#define SPI_TXFL_OFFSET 0
#define SPI_TXFL_SIZE 6
#define SPI_RXFL_OFFSET 16
#define SPI_RXFL_SIZE 6
/* Constants for BITS */ /* Constants for BITS */
#define SPI_BITS_8_BPT 0 #define SPI_BITS_8_BPT 0
#define SPI_BITS_9_BPT 1 #define SPI_BITS_9_BPT 1
@ -167,6 +209,9 @@
#define SPI_BITS_14_BPT 6 #define SPI_BITS_14_BPT 6
#define SPI_BITS_15_BPT 7 #define SPI_BITS_15_BPT 7
#define SPI_BITS_16_BPT 8 #define SPI_BITS_16_BPT 8
#define SPI_ONE_DATA 0
#define SPI_TWO_DATA 1
#define SPI_FOUR_DATA 2
/* Bit manipulation macros */ /* Bit manipulation macros */
#define SPI_BIT(name) \ #define SPI_BIT(name) \
@ -185,11 +230,31 @@
__raw_readl((port)->regs + SPI_##reg) __raw_readl((port)->regs + SPI_##reg)
#define spi_writel(port, reg, value) \ #define spi_writel(port, reg, value) \
__raw_writel((value), (port)->regs + SPI_##reg) __raw_writel((value), (port)->regs + SPI_##reg)
#define spi_readw(port, reg) \
__raw_readw((port)->regs + SPI_##reg)
#define spi_writew(port, reg, value) \
__raw_writew((value), (port)->regs + SPI_##reg)
#define spi_readb(port, reg) \
__raw_readb((port)->regs + SPI_##reg)
#define spi_writeb(port, reg, value) \
__raw_writeb((value), (port)->regs + SPI_##reg)
#else #else
#define spi_readl(port, reg) \ #define spi_readl(port, reg) \
readl_relaxed((port)->regs + SPI_##reg) readl_relaxed((port)->regs + SPI_##reg)
#define spi_writel(port, reg, value) \ #define spi_writel(port, reg, value) \
writel_relaxed((value), (port)->regs + SPI_##reg) writel_relaxed((value), (port)->regs + SPI_##reg)
#define spi_readw(port, reg) \
readw_relaxed((port)->regs + SPI_##reg)
#define spi_writew(port, reg, value) \
writew_relaxed((value), (port)->regs + SPI_##reg)
#define spi_readb(port, reg) \
readb_relaxed((port)->regs + SPI_##reg)
#define spi_writeb(port, reg, value) \
writeb_relaxed((value), (port)->regs + SPI_##reg)
#endif #endif
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and /* use PIO for small transfers, avoiding DMA setup/teardown overhead and
* cache operations; better heuristics consider wordsize and bitrate. * cache operations; better heuristics consider wordsize and bitrate.
@ -246,11 +311,14 @@ struct atmel_spi {
bool use_dma; bool use_dma;
bool use_pdc; bool use_pdc;
bool use_cs_gpios;
/* dmaengine data */ /* dmaengine data */
struct atmel_spi_dma dma; struct atmel_spi_dma dma;
bool keep_cs; bool keep_cs;
bool cs_active; bool cs_active;
u32 fifo_size;
}; };
/* Controller-specific per-slave state */ /* Controller-specific per-slave state */
@ -321,7 +389,8 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
} }
mr = spi_readl(as, MR); mr = spi_readl(as, MR);
gpio_set_value(asd->npcs_pin, active); if (as->use_cs_gpios)
gpio_set_value(asd->npcs_pin, active);
} else { } else {
u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0; u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
int i; int i;
@ -337,7 +406,7 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
mr = spi_readl(as, MR); mr = spi_readl(as, MR);
mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr); mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
if (spi->chip_select != 0) if (as->use_cs_gpios && spi->chip_select != 0)
gpio_set_value(asd->npcs_pin, active); gpio_set_value(asd->npcs_pin, active);
spi_writel(as, MR, mr); spi_writel(as, MR, mr);
} }
@ -366,7 +435,9 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
asd->npcs_pin, active ? " (low)" : "", asd->npcs_pin, active ? " (low)" : "",
mr); mr);
if (atmel_spi_is_v2(as) || spi->chip_select != 0) if (!as->use_cs_gpios)
spi_writel(as, CR, SPI_BIT(LASTXFER));
else if (atmel_spi_is_v2(as) || spi->chip_select != 0)
gpio_set_value(asd->npcs_pin, !active); gpio_set_value(asd->npcs_pin, !active);
} }
@ -406,6 +477,20 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as,
slave_config->dst_maxburst = 1; slave_config->dst_maxburst = 1;
slave_config->device_fc = false; slave_config->device_fc = false;
/*
* This driver uses fixed peripheral select mode (PS bit set to '0' in
* the Mode Register).
* So according to the datasheet, when FIFOs are available (and
* enabled), the Transmit FIFO operates in Multiple Data Mode.
* In this mode, up to 2 data, not 4, can be written into the Transmit
* Data Register in a single access.
* However, the first data has to be written into the lowest 16 bits and
* the second data into the highest 16 bits of the Transmit
* Data Register. For 8bit data (the most frequent case), it would
* require to rework tx_buf so each data would actualy fit 16 bits.
* So we'd rather write only one data at the time. Hence the transmit
* path works the same whether FIFOs are available (and enabled) or not.
*/
slave_config->direction = DMA_MEM_TO_DEV; slave_config->direction = DMA_MEM_TO_DEV;
if (dmaengine_slave_config(as->dma.chan_tx, slave_config)) { if (dmaengine_slave_config(as->dma.chan_tx, slave_config)) {
dev_err(&as->pdev->dev, dev_err(&as->pdev->dev,
@ -413,6 +498,14 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as,
err = -EINVAL; err = -EINVAL;
} }
/*
* This driver configures the spi controller for master mode (MSTR bit
* set to '1' in the Mode Register).
* So according to the datasheet, when FIFOs are available (and
* enabled), the Receive FIFO operates in Single Data Mode.
* So the receive path works the same whether FIFOs are available (and
* enabled) or not.
*/
slave_config->direction = DMA_DEV_TO_MEM; slave_config->direction = DMA_DEV_TO_MEM;
if (dmaengine_slave_config(as->dma.chan_rx, slave_config)) { if (dmaengine_slave_config(as->dma.chan_rx, slave_config)) {
dev_err(&as->pdev->dev, dev_err(&as->pdev->dev,
@ -502,10 +595,10 @@ static void dma_callback(void *data)
} }
/* /*
* Next transfer using PIO. * Next transfer using PIO without FIFO.
*/ */
static void atmel_spi_next_xfer_pio(struct spi_master *master, static void atmel_spi_next_xfer_single(struct spi_master *master,
struct spi_transfer *xfer) struct spi_transfer *xfer)
{ {
struct atmel_spi *as = spi_master_get_devdata(master); struct atmel_spi *as = spi_master_get_devdata(master);
unsigned long xfer_pos = xfer->len - as->current_remaining_bytes; unsigned long xfer_pos = xfer->len - as->current_remaining_bytes;
@ -537,6 +630,99 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master,
spi_writel(as, IER, SPI_BIT(RDRF) | SPI_BIT(OVRES)); spi_writel(as, IER, SPI_BIT(RDRF) | SPI_BIT(OVRES));
} }
/*
* Next transfer using PIO with FIFO.
*/
static void atmel_spi_next_xfer_fifo(struct spi_master *master,
struct spi_transfer *xfer)
{
struct atmel_spi *as = spi_master_get_devdata(master);
u32 current_remaining_data, num_data;
u32 offset = xfer->len - as->current_remaining_bytes;
const u16 *words = (const u16 *)((u8 *)xfer->tx_buf + offset);
const u8 *bytes = (const u8 *)((u8 *)xfer->tx_buf + offset);
u16 td0, td1;
u32 fifomr;
dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_fifo\n");
/* Compute the number of data to transfer in the current iteration */
current_remaining_data = ((xfer->bits_per_word > 8) ?
((u32)as->current_remaining_bytes >> 1) :
(u32)as->current_remaining_bytes);
num_data = min(current_remaining_data, as->fifo_size);
/* Flush RX and TX FIFOs */
spi_writel(as, CR, SPI_BIT(RXFCLR) | SPI_BIT(TXFCLR));
while (spi_readl(as, FLR))
cpu_relax();
/* Set RX FIFO Threshold to the number of data to transfer */
fifomr = spi_readl(as, FMR);
spi_writel(as, FMR, SPI_BFINS(RXFTHRES, num_data, fifomr));
/* Clear FIFO flags in the Status Register, especially RXFTHF */
(void)spi_readl(as, SR);
/* Fill TX FIFO */
while (num_data >= 2) {
if (xfer->tx_buf) {
if (xfer->bits_per_word > 8) {
td0 = *words++;
td1 = *words++;
} else {
td0 = *bytes++;
td1 = *bytes++;
}
} else {
td0 = 0;
td1 = 0;
}
spi_writel(as, TDR, (td1 << 16) | td0);
num_data -= 2;
}
if (num_data) {
if (xfer->tx_buf) {
if (xfer->bits_per_word > 8)
td0 = *words++;
else
td0 = *bytes++;
} else {
td0 = 0;
}
spi_writew(as, TDR, td0);
num_data--;
}
dev_dbg(master->dev.parent,
" start fifo xfer %p: len %u tx %p rx %p bitpw %d\n",
xfer, xfer->len, xfer->tx_buf, xfer->rx_buf,
xfer->bits_per_word);
/*
* Enable RX FIFO Threshold Flag interrupt to be notified about
* transfer completion.
*/
spi_writel(as, IER, SPI_BIT(RXFTHF) | SPI_BIT(OVRES));
}
/*
* Next transfer using PIO.
*/
static void atmel_spi_next_xfer_pio(struct spi_master *master,
struct spi_transfer *xfer)
{
struct atmel_spi *as = spi_master_get_devdata(master);
if (as->fifo_size)
atmel_spi_next_xfer_fifo(master, xfer);
else
atmel_spi_next_xfer_single(master, xfer);
}
/* /*
* Submit next transfer for DMA. * Submit next transfer for DMA.
*/ */
@ -839,13 +1025,8 @@ static void atmel_spi_disable_pdc_transfer(struct atmel_spi *as)
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
} }
/* Called from IRQ
*
* Must update "current_remaining_bytes" to keep track of data
* to transfer.
*/
static void static void
atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer) atmel_spi_pump_single_data(struct atmel_spi *as, struct spi_transfer *xfer)
{ {
u8 *rxp; u8 *rxp;
u16 *rxp16; u16 *rxp16;
@ -872,6 +1053,57 @@ atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
} }
} }
static void
atmel_spi_pump_fifo_data(struct atmel_spi *as, struct spi_transfer *xfer)
{
u32 fifolr = spi_readl(as, FLR);
u32 num_bytes, num_data = SPI_BFEXT(RXFL, fifolr);
u32 offset = xfer->len - as->current_remaining_bytes;
u16 *words = (u16 *)((u8 *)xfer->rx_buf + offset);
u8 *bytes = (u8 *)((u8 *)xfer->rx_buf + offset);
u16 rd; /* RD field is the lowest 16 bits of RDR */
/* Update the number of remaining bytes to transfer */
num_bytes = ((xfer->bits_per_word > 8) ?
(num_data << 1) :
num_data);
if (as->current_remaining_bytes > num_bytes)
as->current_remaining_bytes -= num_bytes;
else
as->current_remaining_bytes = 0;
/* Handle odd number of bytes when data are more than 8bit width */
if (xfer->bits_per_word > 8)
as->current_remaining_bytes &= ~0x1;
/* Read data */
while (num_data) {
rd = spi_readl(as, RDR);
if (xfer->rx_buf) {
if (xfer->bits_per_word > 8)
*words++ = rd;
else
*bytes++ = rd;
}
num_data--;
}
}
/* Called from IRQ
*
* Must update "current_remaining_bytes" to keep track of data
* to transfer.
*/
static void
atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
{
if (as->fifo_size)
atmel_spi_pump_fifo_data(as, xfer);
else
atmel_spi_pump_single_data(as, xfer);
}
/* Interrupt /* Interrupt
* *
* No need for locking in this Interrupt handler: done_status is the * No need for locking in this Interrupt handler: done_status is the
@ -912,7 +1144,7 @@ atmel_spi_pio_interrupt(int irq, void *dev_id)
complete(&as->xfer_completion); complete(&as->xfer_completion);
} else if (pending & SPI_BIT(RDRF)) { } else if (pending & (SPI_BIT(RDRF) | SPI_BIT(RXFTHF))) {
atmel_spi_lock(as); atmel_spi_lock(as);
if (as->current_remaining_bytes) { if (as->current_remaining_bytes) {
@ -996,6 +1228,8 @@ static int atmel_spi_setup(struct spi_device *spi)
csr |= SPI_BIT(CPOL); csr |= SPI_BIT(CPOL);
if (!(spi->mode & SPI_CPHA)) if (!(spi->mode & SPI_CPHA))
csr |= SPI_BIT(NCPHA); csr |= SPI_BIT(NCPHA);
if (!as->use_cs_gpios)
csr |= SPI_BIT(CSAAT);
/* DLYBS is mostly irrelevant since we manage chipselect using GPIOs. /* DLYBS is mostly irrelevant since we manage chipselect using GPIOs.
* *
@ -1009,7 +1243,9 @@ static int atmel_spi_setup(struct spi_device *spi)
/* chipselect must have been muxed as GPIO (e.g. in board setup) */ /* chipselect must have been muxed as GPIO (e.g. in board setup) */
npcs_pin = (unsigned long)spi->controller_data; npcs_pin = (unsigned long)spi->controller_data;
if (gpio_is_valid(spi->cs_gpio)) if (!as->use_cs_gpios)
npcs_pin = spi->chip_select;
else if (gpio_is_valid(spi->cs_gpio))
npcs_pin = spi->cs_gpio; npcs_pin = spi->cs_gpio;
asd = spi->controller_state; asd = spi->controller_state;
@ -1018,15 +1254,19 @@ static int atmel_spi_setup(struct spi_device *spi)
if (!asd) if (!asd)
return -ENOMEM; return -ENOMEM;
ret = gpio_request(npcs_pin, dev_name(&spi->dev)); if (as->use_cs_gpios) {
if (ret) { ret = gpio_request(npcs_pin, dev_name(&spi->dev));
kfree(asd); if (ret) {
return ret; kfree(asd);
return ret;
}
gpio_direction_output(npcs_pin,
!(spi->mode & SPI_CS_HIGH));
} }
asd->npcs_pin = npcs_pin; asd->npcs_pin = npcs_pin;
spi->controller_state = asd; spi->controller_state = asd;
gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH));
} }
asd->csr = csr; asd->csr = csr;
@ -1338,6 +1578,13 @@ static int atmel_spi_probe(struct platform_device *pdev)
atmel_get_caps(as); atmel_get_caps(as);
as->use_cs_gpios = true;
if (atmel_spi_is_v2(as) &&
!of_get_property(pdev->dev.of_node, "cs-gpios", NULL)) {
as->use_cs_gpios = false;
master->num_chipselect = 4;
}
as->use_dma = false; as->use_dma = false;
as->use_pdc = false; as->use_pdc = false;
if (as->caps.has_dma_support) { if (as->caps.has_dma_support) {
@ -1380,6 +1627,13 @@ static int atmel_spi_probe(struct platform_device *pdev)
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
spi_writel(as, CR, SPI_BIT(SPIEN)); spi_writel(as, CR, SPI_BIT(SPIEN));
as->fifo_size = 0;
if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
&as->fifo_size)) {
dev_info(&pdev->dev, "Using FIFO (%u data)\n", as->fifo_size);
spi_writel(as, CR, SPI_BIT(FIFOEN));
}
/* go! */ /* go! */
dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n", dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n",
(unsigned long)regs->start, irq); (unsigned long)regs->start, irq);

View File

@ -20,18 +20,22 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include <asm/page.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_irq.h> #include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
/* SPI register offsets */ /* SPI register offsets */
@ -69,7 +73,8 @@
#define BCM2835_SPI_CS_CS_01 0x00000001 #define BCM2835_SPI_CS_CS_01 0x00000001
#define BCM2835_SPI_POLLING_LIMIT_US 30 #define BCM2835_SPI_POLLING_LIMIT_US 30
#define BCM2835_SPI_TIMEOUT_MS 30000 #define BCM2835_SPI_POLLING_JIFFIES 2
#define BCM2835_SPI_DMA_MIN_LENGTH 96
#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \ #define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
| SPI_NO_CS | SPI_3WIRE) | SPI_NO_CS | SPI_3WIRE)
@ -83,6 +88,7 @@ struct bcm2835_spi {
u8 *rx_buf; u8 *rx_buf;
int tx_len; int tx_len;
int rx_len; int rx_len;
bool dma_pending;
}; };
static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg) static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg)
@ -128,12 +134,15 @@ static void bcm2835_spi_reset_hw(struct spi_master *master)
/* Disable SPI interrupts and transfer */ /* Disable SPI interrupts and transfer */
cs &= ~(BCM2835_SPI_CS_INTR | cs &= ~(BCM2835_SPI_CS_INTR |
BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_INTD |
BCM2835_SPI_CS_DMAEN |
BCM2835_SPI_CS_TA); BCM2835_SPI_CS_TA);
/* and reset RX/TX FIFOS */ /* and reset RX/TX FIFOS */
cs |= BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX; cs |= BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX;
/* and reset the SPI_HW */ /* and reset the SPI_HW */
bcm2835_wr(bs, BCM2835_SPI_CS, cs); bcm2835_wr(bs, BCM2835_SPI_CS, cs);
/* as well as DLEN */
bcm2835_wr(bs, BCM2835_SPI_DLEN, 0);
} }
static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
@ -157,42 +166,6 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *tfr,
u32 cs,
unsigned long xfer_time_us)
{
struct bcm2835_spi *bs = spi_master_get_devdata(master);
/* set timeout to 1 second of maximum polling */
unsigned long timeout = jiffies + HZ;
/* enable HW block without interrupts */
bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA);
/* loop until finished the transfer */
while (bs->rx_len) {
/* read from fifo as much as possible */
bcm2835_rd_fifo(bs);
/* fill in tx fifo as much as possible */
bcm2835_wr_fifo(bs);
/* if we still expect some data after the read,
* check for a possible timeout
*/
if (bs->rx_len && time_after(jiffies, timeout)) {
/* Transfer complete - reset SPI HW */
bcm2835_spi_reset_hw(master);
/* and return timeout */
return -ETIMEDOUT;
}
}
/* Transfer complete - reset SPI HW */
bcm2835_spi_reset_hw(master);
/* and return without waiting for completion */
return 0;
}
static int bcm2835_spi_transfer_one_irq(struct spi_master *master, static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
struct spi_device *spi, struct spi_device *spi,
struct spi_transfer *tfr, struct spi_transfer *tfr,
@ -229,6 +202,329 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
return 1; return 1;
} }
/*
* DMA support
*
* this implementation has currently a few issues in so far as it does
* not work arrount limitations of the HW.
*
* the main one being that DMA transfers are limited to 16 bit
* (so 0 to 65535 bytes) by the SPI HW due to BCM2835_SPI_DLEN
*
* also we currently assume that the scatter-gather fragments are
* all multiple of 4 (except the last) - otherwise we would need
* to reset the FIFO before subsequent transfers...
* this also means that tx/rx transfers sg's need to be of equal size!
*
* there may be a few more border-cases we may need to address as well
* but unfortunately this would mean splitting up the scatter-gather
* list making it slightly unpractical...
*/
static void bcm2835_spi_dma_done(void *data)
{
struct spi_master *master = data;
struct bcm2835_spi *bs = spi_master_get_devdata(master);
/* reset fifo and HW */
bcm2835_spi_reset_hw(master);
/* and terminate tx-dma as we do not have an irq for it
* because when the rx dma will terminate and this callback
* is called the tx-dma must have finished - can't get to this
* situation otherwise...
*/
dmaengine_terminate_all(master->dma_tx);
/* mark as no longer pending */
bs->dma_pending = 0;
/* and mark as completed */;
complete(&master->xfer_completion);
}
static int bcm2835_spi_prepare_sg(struct spi_master *master,
struct spi_transfer *tfr,
bool is_tx)
{
struct dma_chan *chan;
struct scatterlist *sgl;
unsigned int nents;
enum dma_transfer_direction dir;
unsigned long flags;
struct dma_async_tx_descriptor *desc;
dma_cookie_t cookie;
if (is_tx) {
dir = DMA_MEM_TO_DEV;
chan = master->dma_tx;
nents = tfr->tx_sg.nents;
sgl = tfr->tx_sg.sgl;
flags = 0 /* no tx interrupt */;
} else {
dir = DMA_DEV_TO_MEM;
chan = master->dma_rx;
nents = tfr->rx_sg.nents;
sgl = tfr->rx_sg.sgl;
flags = DMA_PREP_INTERRUPT;
}
/* prepare the channel */
desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
if (!desc)
return -EINVAL;
/* set callback for rx */
if (!is_tx) {
desc->callback = bcm2835_spi_dma_done;
desc->callback_param = master;
}
/* submit it to DMA-engine */
cookie = dmaengine_submit(desc);
return dma_submit_error(cookie);
}
static inline int bcm2835_check_sg_length(struct sg_table *sgt)
{
int i;
struct scatterlist *sgl;
/* check that the sg entries are word-sized (except for last) */
for_each_sg(sgt->sgl, sgl, (int)sgt->nents - 1, i) {
if (sg_dma_len(sgl) % 4)
return -EFAULT;
}
return 0;
}
static int bcm2835_spi_transfer_one_dma(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *tfr,
u32 cs)
{
struct bcm2835_spi *bs = spi_master_get_devdata(master);
int ret;
/* check that the scatter gather segments are all a multiple of 4 */
if (bcm2835_check_sg_length(&tfr->tx_sg) ||
bcm2835_check_sg_length(&tfr->rx_sg)) {
dev_warn_once(&spi->dev,
"scatter gather segment length is not a multiple of 4 - falling back to interrupt mode\n");
return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs);
}
/* setup tx-DMA */
ret = bcm2835_spi_prepare_sg(master, tfr, true);
if (ret)
return ret;
/* start TX early */
dma_async_issue_pending(master->dma_tx);
/* mark as dma pending */
bs->dma_pending = 1;
/* set the DMA length */
bcm2835_wr(bs, BCM2835_SPI_DLEN, tfr->len);
/* start the HW */
bcm2835_wr(bs, BCM2835_SPI_CS,
cs | BCM2835_SPI_CS_TA | BCM2835_SPI_CS_DMAEN);
/* setup rx-DMA late - to run transfers while
* mapping of the rx buffers still takes place
* this saves 10us or more.
*/
ret = bcm2835_spi_prepare_sg(master, tfr, false);
if (ret) {
/* need to reset on errors */
dmaengine_terminate_all(master->dma_tx);
bcm2835_spi_reset_hw(master);
return ret;
}
/* start rx dma late */
dma_async_issue_pending(master->dma_rx);
/* wait for wakeup in framework */
return 1;
}
static bool bcm2835_spi_can_dma(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *tfr)
{
/* only run for gpio_cs */
if (!gpio_is_valid(spi->cs_gpio))
return false;
/* we start DMA efforts only on bigger transfers */
if (tfr->len < BCM2835_SPI_DMA_MIN_LENGTH)
return false;
/* BCM2835_SPI_DLEN has defined a max transfer size as
* 16 bit, so max is 65535
* we can revisit this by using an alternative transfer
* method - ideally this would get done without any more
* interaction...
*/
if (tfr->len > 65535) {
dev_warn_once(&spi->dev,
"transfer size of %d too big for dma-transfer\n",
tfr->len);
return false;
}
/* if we run rx/tx_buf with word aligned addresses then we are OK */
if ((((size_t)tfr->rx_buf & 3) == 0) &&
(((size_t)tfr->tx_buf & 3) == 0))
return true;
/* otherwise we only allow transfers within the same page
* to avoid wasting time on dma_mapping when it is not practical
*/
if (((size_t)tfr->tx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) {
dev_warn_once(&spi->dev,
"Unaligned spi tx-transfer bridging page\n");
return false;
}
if (((size_t)tfr->rx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) {
dev_warn_once(&spi->dev,
"Unaligned spi tx-transfer bridging page\n");
return false;
}
/* return OK */
return true;
}
static void bcm2835_dma_release(struct spi_master *master)
{
if (master->dma_tx) {
dmaengine_terminate_all(master->dma_tx);
dma_release_channel(master->dma_tx);
master->dma_tx = NULL;
}
if (master->dma_rx) {
dmaengine_terminate_all(master->dma_rx);
dma_release_channel(master->dma_rx);
master->dma_rx = NULL;
}
}
static void bcm2835_dma_init(struct spi_master *master, struct device *dev)
{
struct dma_slave_config slave_config;
const __be32 *addr;
dma_addr_t dma_reg_base;
int ret;
/* base address in dma-space */
addr = of_get_address(master->dev.of_node, 0, NULL, NULL);
if (!addr) {
dev_err(dev, "could not get DMA-register address - not using dma mode\n");
goto err;
}
dma_reg_base = be32_to_cpup(addr);
/* get tx/rx dma */
master->dma_tx = dma_request_slave_channel(dev, "tx");
if (!master->dma_tx) {
dev_err(dev, "no tx-dma configuration found - not using dma mode\n");
goto err;
}
master->dma_rx = dma_request_slave_channel(dev, "rx");
if (!master->dma_rx) {
dev_err(dev, "no rx-dma configuration found - not using dma mode\n");
goto err_release;
}
/* configure DMAs */
slave_config.direction = DMA_MEM_TO_DEV;
slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
ret = dmaengine_slave_config(master->dma_tx, &slave_config);
if (ret)
goto err_config;
slave_config.direction = DMA_DEV_TO_MEM;
slave_config.src_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
ret = dmaengine_slave_config(master->dma_rx, &slave_config);
if (ret)
goto err_config;
/* all went well, so set can_dma */
master->can_dma = bcm2835_spi_can_dma;
master->max_dma_len = 65535; /* limitation by BCM2835_SPI_DLEN */
/* need to do TX AND RX DMA, so we need dummy buffers */
master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
return;
err_config:
dev_err(dev, "issue configuring dma: %d - not using DMA mode\n",
ret);
err_release:
bcm2835_dma_release(master);
err:
return;
}
static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *tfr,
u32 cs,
unsigned long xfer_time_us)
{
struct bcm2835_spi *bs = spi_master_get_devdata(master);
unsigned long timeout;
/* enable HW block without interrupts */
bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA);
/* fill in the fifo before timeout calculations
* if we are interrupted here, then the data is
* getting transferred by the HW while we are interrupted
*/
bcm2835_wr_fifo(bs);
/* set the timeout */
timeout = jiffies + BCM2835_SPI_POLLING_JIFFIES;
/* loop until finished the transfer */
while (bs->rx_len) {
/* fill in tx fifo with remaining data */
bcm2835_wr_fifo(bs);
/* read from fifo as much as possible */
bcm2835_rd_fifo(bs);
/* if there is still data pending to read
* then check the timeout
*/
if (bs->rx_len && time_after(jiffies, timeout)) {
dev_dbg_ratelimited(&spi->dev,
"timeout period reached: jiffies: %lu remaining tx/rx: %d/%d - falling back to interrupt mode\n",
jiffies - timeout,
bs->tx_len, bs->rx_len);
/* fall back to interrupt mode */
return bcm2835_spi_transfer_one_irq(master, spi,
tfr, cs);
}
}
/* Transfer complete - reset SPI HW */
bcm2835_spi_reset_hw(master);
/* and return without waiting for completion */
return 0;
}
static int bcm2835_spi_transfer_one(struct spi_master *master, static int bcm2835_spi_transfer_one(struct spi_master *master,
struct spi_device *spi, struct spi_device *spi,
struct spi_transfer *tfr) struct spi_transfer *tfr)
@ -288,12 +584,26 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
return bcm2835_spi_transfer_one_poll(master, spi, tfr, return bcm2835_spi_transfer_one_poll(master, spi, tfr,
cs, xfer_time_us); cs, xfer_time_us);
/* run in dma mode if conditions are right */
if (master->can_dma && bcm2835_spi_can_dma(master, spi, tfr))
return bcm2835_spi_transfer_one_dma(master, spi, tfr, cs);
/* run in interrupt-mode */
return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs); return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs);
} }
static void bcm2835_spi_handle_err(struct spi_master *master, static void bcm2835_spi_handle_err(struct spi_master *master,
struct spi_message *msg) struct spi_message *msg)
{ {
struct bcm2835_spi *bs = spi_master_get_devdata(master);
/* if an error occurred and we have an active dma, then terminate */
if (bs->dma_pending) {
dmaengine_terminate_all(master->dma_tx);
dmaengine_terminate_all(master->dma_rx);
bs->dma_pending = 0;
}
/* and reset */
bcm2835_spi_reset_hw(master); bcm2835_spi_reset_hw(master);
} }
@ -463,6 +773,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
goto out_clk_disable; goto out_clk_disable;
} }
bcm2835_dma_init(master, &pdev->dev);
/* initialise the hardware with the default polarities */ /* initialise the hardware with the default polarities */
bcm2835_wr(bs, BCM2835_SPI_CS, bcm2835_wr(bs, BCM2835_SPI_CS,
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
@ -493,6 +805,8 @@ static int bcm2835_spi_remove(struct platform_device *pdev)
clk_disable_unprepare(bs->clk); clk_disable_unprepare(bs->clk);
bcm2835_dma_release(master);
return 0; return 0;
} }

View File

@ -265,7 +265,7 @@ static inline int davinci_spi_get_prescale(struct davinci_spi *dspi,
ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz); ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz);
if (ret < 3 || ret > 256) if (ret < 1 || ret > 256)
return -EINVAL; return -EINVAL;
return ret - 1; return ret - 1;

View File

@ -24,6 +24,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/regmap.h> #include <linux/regmap.h>
@ -47,6 +48,7 @@
#define SPI_MCR_CLR_RXF (1 << 10) #define SPI_MCR_CLR_RXF (1 << 10)
#define SPI_TCR 0x08 #define SPI_TCR 0x08
#define SPI_TCR_GET_TCNT(x) (((x) & 0xffff0000) >> 16)
#define SPI_CTAR(x) (0x0c + (((x) & 0x3) * 4)) #define SPI_CTAR(x) (0x0c + (((x) & 0x3) * 4))
#define SPI_CTAR_FMSZ(x) (((x) & 0x0000000f) << 27) #define SPI_CTAR_FMSZ(x) (((x) & 0x0000000f) << 27)
@ -67,9 +69,11 @@
#define SPI_SR 0x2c #define SPI_SR 0x2c
#define SPI_SR_EOQF 0x10000000 #define SPI_SR_EOQF 0x10000000
#define SPI_SR_TCFQF 0x80000000
#define SPI_RSER 0x30 #define SPI_RSER 0x30
#define SPI_RSER_EOQFE 0x10000000 #define SPI_RSER_EOQFE 0x10000000
#define SPI_RSER_TCFQE 0x80000000
#define SPI_PUSHR 0x34 #define SPI_PUSHR 0x34
#define SPI_PUSHR_CONT (1 << 31) #define SPI_PUSHR_CONT (1 << 31)
@ -102,12 +106,35 @@
#define SPI_CS_ASSERT 0x02 #define SPI_CS_ASSERT 0x02
#define SPI_CS_DROP 0x04 #define SPI_CS_DROP 0x04
#define SPI_TCR_TCNT_MAX 0x10000
struct chip_data { struct chip_data {
u32 mcr_val; u32 mcr_val;
u32 ctar_val; u32 ctar_val;
u16 void_write_data; u16 void_write_data;
}; };
enum dspi_trans_mode {
DSPI_EOQ_MODE = 0,
DSPI_TCFQ_MODE,
};
struct fsl_dspi_devtype_data {
enum dspi_trans_mode trans_mode;
};
static const struct fsl_dspi_devtype_data vf610_data = {
.trans_mode = DSPI_EOQ_MODE,
};
static const struct fsl_dspi_devtype_data ls1021a_v1_data = {
.trans_mode = DSPI_TCFQ_MODE,
};
static const struct fsl_dspi_devtype_data ls2085a_data = {
.trans_mode = DSPI_TCFQ_MODE,
};
struct fsl_dspi { struct fsl_dspi {
struct spi_master *master; struct spi_master *master;
struct platform_device *pdev; struct platform_device *pdev;
@ -128,9 +155,12 @@ struct fsl_dspi {
u8 cs; u8 cs;
u16 void_write_data; u16 void_write_data;
u32 cs_change; u32 cs_change;
struct fsl_dspi_devtype_data *devtype_data;
wait_queue_head_t waitq; wait_queue_head_t waitq;
u32 waitflags; u32 waitflags;
u32 spi_tcnt;
}; };
static inline int is_double_byte_mode(struct fsl_dspi *dspi) static inline int is_double_byte_mode(struct fsl_dspi *dspi)
@ -213,63 +243,60 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns,
} }
} }
static int dspi_transfer_write(struct fsl_dspi *dspi) static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word)
{
u16 d16;
if (!(dspi->dataflags & TRAN_STATE_TX_VOID))
d16 = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx;
else
d16 = dspi->void_write_data;
dspi->tx += tx_word + 1;
dspi->len -= tx_word + 1;
return SPI_PUSHR_TXDATA(d16) |
SPI_PUSHR_PCS(dspi->cs) |
SPI_PUSHR_CTAS(dspi->cs) |
SPI_PUSHR_CONT;
}
static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word)
{
u16 d;
unsigned int val;
regmap_read(dspi->regmap, SPI_POPR, &val);
d = SPI_POPR_RXDATA(val);
if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
rx_word ? (*(u16 *)dspi->rx = d) : (*(u8 *)dspi->rx = d);
dspi->rx += rx_word + 1;
}
static int dspi_eoq_write(struct fsl_dspi *dspi)
{ {
int tx_count = 0; int tx_count = 0;
int tx_word; int tx_word;
u16 d16;
u8 d8;
u32 dspi_pushr = 0; u32 dspi_pushr = 0;
int first = 1;
tx_word = is_double_byte_mode(dspi); 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;
regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
tx_word = 0;
}
while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) { while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) {
if (tx_word) { /* If we are in word mode, only have a single byte to transfer
if (dspi->len == 1) * switch to byte mode temporarily. Will switch back at the
break; * end of the transfer.
*/
if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { if (tx_word && (dspi->len == 1)) {
d16 = *(u16 *)dspi->tx; dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
dspi->tx += 2; regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
} else { SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
d16 = dspi->void_write_data; tx_word = 0;
}
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--;
} }
dspi_pushr = dspi_data_to_pushr(dspi, tx_word);
if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) { if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) {
/* last transfer in the transfer */ /* last transfer in the transfer */
dspi_pushr |= SPI_PUSHR_EOQ; dspi_pushr |= SPI_PUSHR_EOQ;
@ -278,11 +305,6 @@ static int dspi_transfer_write(struct fsl_dspi *dspi)
} else if (tx_word && (dspi->len == 1)) } else if (tx_word && (dspi->len == 1))
dspi_pushr |= SPI_PUSHR_EOQ; dspi_pushr |= SPI_PUSHR_EOQ;
if (first) {
first = 0;
dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */
}
regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr);
tx_count++; tx_count++;
@ -291,42 +313,57 @@ static int dspi_transfer_write(struct fsl_dspi *dspi)
return tx_count * (tx_word + 1); return tx_count * (tx_word + 1);
} }
static int dspi_transfer_read(struct fsl_dspi *dspi) static int dspi_eoq_read(struct fsl_dspi *dspi)
{ {
int rx_count = 0; int rx_count = 0;
int rx_word = is_double_byte_mode(dspi); int rx_word = is_double_byte_mode(dspi);
u16 d;
while ((dspi->rx < dspi->rx_end) while ((dspi->rx < dspi->rx_end)
&& (rx_count < DSPI_FIFO_SIZE)) { && (rx_count < DSPI_FIFO_SIZE)) {
if (rx_word) { if (rx_word && (dspi->rx_end - dspi->rx) == 1)
unsigned int val; rx_word = 0;
if ((dspi->rx_end - dspi->rx) == 1) dspi_data_from_popr(dspi, rx_word);
break;
regmap_read(dspi->regmap, SPI_POPR, &val);
d = SPI_POPR_RXDATA(val);
if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
*(u16 *)dspi->rx = d;
dspi->rx += 2;
} else {
unsigned int val;
regmap_read(dspi->regmap, SPI_POPR, &val);
d = SPI_POPR_RXDATA(val);
if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
*(u8 *)dspi->rx = d;
dspi->rx++;
}
rx_count++; rx_count++;
} }
return rx_count; return rx_count;
} }
static int dspi_tcfq_write(struct fsl_dspi *dspi)
{
int tx_word;
u32 dspi_pushr = 0;
tx_word = is_double_byte_mode(dspi);
if (tx_word && (dspi->len == 1)) {
dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
tx_word = 0;
}
dspi_pushr = dspi_data_to_pushr(dspi, tx_word);
if ((dspi->cs_change) && (!dspi->len))
dspi_pushr &= ~SPI_PUSHR_CONT;
regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr);
return tx_word + 1;
}
static void dspi_tcfq_read(struct fsl_dspi *dspi)
{
int rx_word = is_double_byte_mode(dspi);
if (rx_word && (dspi->rx_end - dspi->rx) == 1)
rx_word = 0;
dspi_data_from_popr(dspi, rx_word);
}
static int dspi_transfer_one_message(struct spi_master *master, static int dspi_transfer_one_message(struct spi_master *master,
struct spi_message *message) struct spi_message *message)
{ {
@ -334,6 +371,12 @@ static int dspi_transfer_one_message(struct spi_master *master,
struct spi_device *spi = message->spi; struct spi_device *spi = message->spi;
struct spi_transfer *transfer; struct spi_transfer *transfer;
int status = 0; int status = 0;
enum dspi_trans_mode trans_mode;
u32 spi_tcr;
regmap_read(dspi->regmap, SPI_TCR, &spi_tcr);
dspi->spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr);
message->actual_length = 0; message->actual_length = 0;
list_for_each_entry(transfer, &message->transfers, transfer_list) { list_for_each_entry(transfer, &message->transfers, transfer_list) {
@ -341,10 +384,10 @@ static int dspi_transfer_one_message(struct spi_master *master,
dspi->cur_msg = message; dspi->cur_msg = message;
dspi->cur_chip = spi_get_ctldata(spi); dspi->cur_chip = spi_get_ctldata(spi);
dspi->cs = spi->chip_select; dspi->cs = spi->chip_select;
dspi->cs_change = 0;
if (dspi->cur_transfer->transfer_list.next if (dspi->cur_transfer->transfer_list.next
== &dspi->cur_msg->transfers) == &dspi->cur_msg->transfers)
transfer->cs_change = 1; dspi->cs_change = 1;
dspi->cs_change = transfer->cs_change;
dspi->void_write_data = dspi->cur_chip->void_write_data; dspi->void_write_data = dspi->cur_chip->void_write_data;
dspi->dataflags = 0; dspi->dataflags = 0;
@ -370,8 +413,22 @@ static int dspi_transfer_one_message(struct spi_master *master,
regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), regmap_write(dspi->regmap, SPI_CTAR(dspi->cs),
dspi->cur_chip->ctar_val); dspi->cur_chip->ctar_val);
regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); trans_mode = dspi->devtype_data->trans_mode;
message->actual_length += dspi_transfer_write(dspi); switch (trans_mode) {
case DSPI_EOQ_MODE:
regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE);
dspi_eoq_write(dspi);
break;
case DSPI_TCFQ_MODE:
regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE);
dspi_tcfq_write(dspi);
break;
default:
dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n",
trans_mode);
status = -EINVAL;
goto out;
}
if (wait_event_interruptible(dspi->waitq, dspi->waitflags)) if (wait_event_interruptible(dspi->waitq, dspi->waitflags))
dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n"); dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n");
@ -381,6 +438,7 @@ static int dspi_transfer_one_message(struct spi_master *master,
udelay(transfer->delay_usecs); udelay(transfer->delay_usecs);
} }
out:
message->status = status; message->status = status;
spi_finalize_current_message(master); spi_finalize_current_message(master);
@ -460,27 +518,89 @@ static void dspi_cleanup(struct spi_device *spi)
static irqreturn_t dspi_interrupt(int irq, void *dev_id) static irqreturn_t dspi_interrupt(int irq, void *dev_id)
{ {
struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id;
struct spi_message *msg = dspi->cur_msg; struct spi_message *msg = dspi->cur_msg;
enum dspi_trans_mode trans_mode;
u32 spi_sr, spi_tcr;
u32 spi_tcnt, tcnt_diff;
int tx_word;
regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF); regmap_read(dspi->regmap, SPI_SR, &spi_sr);
dspi_transfer_read(dspi); regmap_write(dspi->regmap, SPI_SR, spi_sr);
if (!dspi->len) {
if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) {
tx_word = is_double_byte_mode(dspi);
regmap_read(dspi->regmap, SPI_TCR, &spi_tcr);
spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr);
/*
* The width of SPI Transfer Counter in SPI_TCR is 16bits,
* so the max couner is 65535. When the counter reach 65535,
* it will wrap around, counter reset to zero.
* spi_tcnt my be less than dspi->spi_tcnt, it means the
* counter already wrapped around.
* SPI Transfer Counter is a counter of transmitted frames.
* The size of frame maybe two bytes.
*/
tcnt_diff = ((spi_tcnt + SPI_TCR_TCNT_MAX) - dspi->spi_tcnt)
% SPI_TCR_TCNT_MAX;
tcnt_diff *= (tx_word + 1);
if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM)
regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), tcnt_diff--;
SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16));
dspi->waitflags = 1; msg->actual_length += tcnt_diff;
wake_up_interruptible(&dspi->waitq);
} else dspi->spi_tcnt = spi_tcnt;
msg->actual_length += dspi_transfer_write(dspi);
trans_mode = dspi->devtype_data->trans_mode;
switch (trans_mode) {
case DSPI_EOQ_MODE:
dspi_eoq_read(dspi);
break;
case DSPI_TCFQ_MODE:
dspi_tcfq_read(dspi);
break;
default:
dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n",
trans_mode);
return IRQ_HANDLED;
}
if (!dspi->len) {
if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) {
regmap_update_bits(dspi->regmap,
SPI_CTAR(dspi->cs),
SPI_FRAME_BITS_MASK,
SPI_FRAME_BITS(16));
dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM;
}
dspi->waitflags = 1;
wake_up_interruptible(&dspi->waitq);
} else {
switch (trans_mode) {
case DSPI_EOQ_MODE:
dspi_eoq_write(dspi);
break;
case DSPI_TCFQ_MODE:
dspi_tcfq_write(dspi);
break;
default:
dev_err(&dspi->pdev->dev,
"unsupported trans_mode %u\n",
trans_mode);
}
}
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static const struct of_device_id fsl_dspi_dt_ids[] = { static const struct of_device_id fsl_dspi_dt_ids[] = {
{ .compatible = "fsl,vf610-dspi", .data = NULL, }, { .compatible = "fsl,vf610-dspi", .data = (void *)&vf610_data, },
{ .compatible = "fsl,ls1021a-v1.0-dspi",
.data = (void *)&ls1021a_v1_data, },
{ .compatible = "fsl,ls2085a-dspi", .data = (void *)&ls2085a_data, },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids); MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids);
@ -494,6 +614,8 @@ static int dspi_suspend(struct device *dev)
spi_master_suspend(master); spi_master_suspend(master);
clk_disable_unprepare(dspi->clk); clk_disable_unprepare(dspi->clk);
pinctrl_pm_select_sleep_state(dev);
return 0; return 0;
} }
@ -502,6 +624,8 @@ static int dspi_resume(struct device *dev)
struct spi_master *master = dev_get_drvdata(dev); struct spi_master *master = dev_get_drvdata(dev);
struct fsl_dspi *dspi = spi_master_get_devdata(master); struct fsl_dspi *dspi = spi_master_get_devdata(master);
pinctrl_pm_select_default_state(dev);
clk_prepare_enable(dspi->clk); clk_prepare_enable(dspi->clk);
spi_master_resume(master); spi_master_resume(master);
@ -526,6 +650,8 @@ static int dspi_probe(struct platform_device *pdev)
struct resource *res; struct resource *res;
void __iomem *base; void __iomem *base;
int ret = 0, cs_num, bus_num; int ret = 0, cs_num, bus_num;
const struct of_device_id *of_id =
of_match_device(fsl_dspi_dt_ids, &pdev->dev);
master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi)); master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi));
if (!master) if (!master)
@ -559,6 +685,13 @@ static int dspi_probe(struct platform_device *pdev)
} }
master->bus_num = bus_num; master->bus_num = bus_num;
dspi->devtype_data = (struct fsl_dspi_devtype_data *)of_id->data;
if (!dspi->devtype_data) {
dev_err(&pdev->dev, "can't get devtype_data\n");
ret = -EFAULT;
goto out_master_put;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res); base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base)) { if (IS_ERR(base)) {
@ -566,7 +699,7 @@ static int dspi_probe(struct platform_device *pdev)
goto out_master_put; goto out_master_put;
} }
dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base, dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
&dspi_regmap_config); &dspi_regmap_config);
if (IS_ERR(dspi->regmap)) { if (IS_ERR(dspi->regmap)) {
dev_err(&pdev->dev, "failed to init regmap: %ld\n", dev_err(&pdev->dev, "failed to init regmap: %ld\n",

View File

@ -561,9 +561,13 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
/* spin until TX is done */ /* spin until TX is done */
ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg( ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg(
&reg_base->event)) & SPIE_NF) == 0, 1000, 0); &reg_base->event)) & SPIE_NF), 1000, 0);
if (!ret) { if (!ret) {
dev_err(mspi->dev, "tired waiting for SPIE_NF\n"); dev_err(mspi->dev, "tired waiting for SPIE_NF\n");
/* Clear the SPIE bits */
mpc8xxx_spi_write_reg(&reg_base->event, events);
complete(&mspi->done);
return; return;
} }
} }

View File

@ -674,7 +674,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
.devtype = IMX51_ECSPI, .devtype = IMX51_ECSPI,
}; };
static struct platform_device_id spi_imx_devtype[] = { static const struct platform_device_id spi_imx_devtype[] = {
{ {
.name = "imx1-cspi", .name = "imx1-cspi",
.driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data, .driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data,

View File

@ -35,6 +35,7 @@
#include <linux/gcd.h> #include <linux/gcd.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/platform_data/spi-omap2-mcspi.h> #include <linux/platform_data/spi-omap2-mcspi.h>
@ -242,17 +243,27 @@ static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable)
mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0); mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0);
} }
static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable)
{ {
u32 l; u32 l;
l = mcspi_cached_chconf0(spi); /* The controller handles the inverted chip selects
if (cs_active) * using the OMAP2_MCSPI_CHCONF_EPOL bit so revert
l |= OMAP2_MCSPI_CHCONF_FORCE; * the inversion from the core spi_set_cs function.
else */
l &= ~OMAP2_MCSPI_CHCONF_FORCE; if (spi->mode & SPI_CS_HIGH)
enable = !enable;
mcspi_write_chconf0(spi, l); if (spi->controller_state) {
l = mcspi_cached_chconf0(spi);
if (enable)
l &= ~OMAP2_MCSPI_CHCONF_FORCE;
else
l |= OMAP2_MCSPI_CHCONF_FORCE;
mcspi_write_chconf0(spi, l);
}
} }
static void omap2_mcspi_set_master_mode(struct spi_master *master) static void omap2_mcspi_set_master_mode(struct spi_master *master)
@ -1011,6 +1022,15 @@ static int omap2_mcspi_setup(struct spi_device *spi)
return ret; return ret;
} }
if (gpio_is_valid(spi->cs_gpio)) {
ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev));
if (ret) {
dev_err(&spi->dev, "failed to request gpio\n");
return ret;
}
gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
}
ret = pm_runtime_get_sync(mcspi->dev); ret = pm_runtime_get_sync(mcspi->dev);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -1050,9 +1070,13 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
mcspi_dma->dma_tx = NULL; mcspi_dma->dma_tx = NULL;
} }
} }
if (gpio_is_valid(spi->cs_gpio))
gpio_free(spi->cs_gpio);
} }
static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi,
struct spi_device *spi, struct spi_transfer *t)
{ {
/* We only enable one channel at a time -- the one whose message is /* We only enable one channel at a time -- the one whose message is
@ -1062,18 +1086,14 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
* chipselect with the FORCE bit ... CS != channel enable. * chipselect with the FORCE bit ... CS != channel enable.
*/ */
struct spi_device *spi;
struct spi_transfer *t = NULL;
struct spi_master *master; struct spi_master *master;
struct omap2_mcspi_dma *mcspi_dma; struct omap2_mcspi_dma *mcspi_dma;
int cs_active = 0;
struct omap2_mcspi_cs *cs; struct omap2_mcspi_cs *cs;
struct omap2_mcspi_device_config *cd; struct omap2_mcspi_device_config *cd;
int par_override = 0; int par_override = 0;
int status = 0; int status = 0;
u32 chconf; u32 chconf;
spi = m->spi;
master = spi->master; master = spi->master;
mcspi_dma = mcspi->dma_channels + spi->chip_select; mcspi_dma = mcspi->dma_channels + spi->chip_select;
cs = spi->controller_state; cs = spi->controller_state;
@ -1090,103 +1110,84 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
par_override = 1; par_override = 1;
omap2_mcspi_set_enable(spi, 0); omap2_mcspi_set_enable(spi, 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 != spi->max_speed_hz) ||
(t->bits_per_word != spi->bits_per_word)) {
par_override = 1;
status = omap2_mcspi_setup_transfer(spi, t);
if (status < 0)
break;
if (t->speed_hz == spi->max_speed_hz &&
t->bits_per_word == spi->bits_per_word)
par_override = 0;
}
if (cd && cd->cs_per_word) {
chconf = mcspi->ctx.modulctrl;
chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE;
mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf);
mcspi->ctx.modulctrl =
mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL);
}
if (gpio_is_valid(spi->cs_gpio))
omap2_mcspi_set_cs(spi, spi->mode & SPI_CS_HIGH);
if (!cs_active) { if (par_override ||
omap2_mcspi_force_cs(spi, 1); (t->speed_hz != spi->max_speed_hz) ||
cs_active = 1; (t->bits_per_word != spi->bits_per_word)) {
} par_override = 1;
status = omap2_mcspi_setup_transfer(spi, t);
chconf = mcspi_cached_chconf0(spi); if (status < 0)
chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; goto out;
chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; if (t->speed_hz == spi->max_speed_hz &&
t->bits_per_word == spi->bits_per_word)
if (t->tx_buf == NULL) par_override = 0;
chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY;
else if (t->rx_buf == NULL)
chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY;
if (cd && cd->turbo_mode && t->tx_buf == NULL) {
/* Turbo mode is for more than one word */
if (t->len > ((cs->word_len + 7) >> 3))
chconf |= OMAP2_MCSPI_CHCONF_TURBO;
}
mcspi_write_chconf0(spi, chconf);
if (t->len) {
unsigned count;
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
(m->is_dma_mapped || t->len >= DMA_MIN_BYTES))
omap2_mcspi_set_fifo(spi, t, 1);
omap2_mcspi_set_enable(spi, 1);
/* RX_ONLY mode needs dummy data in TX reg */
if (t->tx_buf == NULL)
writel_relaxed(0, cs->base
+ OMAP2_MCSPI_TX0);
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
(m->is_dma_mapped || t->len >= DMA_MIN_BYTES))
count = omap2_mcspi_txrx_dma(spi, t);
else
count = omap2_mcspi_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) {
omap2_mcspi_force_cs(spi, 0);
cs_active = 0;
}
omap2_mcspi_set_enable(spi, 0);
if (mcspi->fifo_depth > 0)
omap2_mcspi_set_fifo(spi, t, 0);
} }
if (cd && cd->cs_per_word) {
chconf = mcspi->ctx.modulctrl;
chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE;
mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf);
mcspi->ctx.modulctrl =
mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL);
}
chconf = mcspi_cached_chconf0(spi);
chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK;
chconf &= ~OMAP2_MCSPI_CHCONF_TURBO;
if (t->tx_buf == NULL)
chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY;
else if (t->rx_buf == NULL)
chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY;
if (cd && cd->turbo_mode && t->tx_buf == NULL) {
/* Turbo mode is for more than one word */
if (t->len > ((cs->word_len + 7) >> 3))
chconf |= OMAP2_MCSPI_CHCONF_TURBO;
}
mcspi_write_chconf0(spi, chconf);
if (t->len) {
unsigned count;
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
(t->len >= DMA_MIN_BYTES))
omap2_mcspi_set_fifo(spi, t, 1);
omap2_mcspi_set_enable(spi, 1);
/* RX_ONLY mode needs dummy data in TX reg */
if (t->tx_buf == NULL)
writel_relaxed(0, cs->base
+ OMAP2_MCSPI_TX0);
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
(t->len >= DMA_MIN_BYTES))
count = omap2_mcspi_txrx_dma(spi, t);
else
count = omap2_mcspi_txrx_pio(spi, t);
if (count != t->len) {
status = -EIO;
goto out;
}
}
omap2_mcspi_set_enable(spi, 0);
if (mcspi->fifo_depth > 0)
omap2_mcspi_set_fifo(spi, t, 0);
out:
/* Restore defaults if they were overriden */ /* Restore defaults if they were overriden */
if (par_override) { if (par_override) {
par_override = 0; par_override = 0;
status = omap2_mcspi_setup_transfer(spi, NULL); status = omap2_mcspi_setup_transfer(spi, NULL);
} }
if (cs_active)
omap2_mcspi_force_cs(spi, 0);
if (cd && cd->cs_per_word) { if (cd && cd->cs_per_word) {
chconf = mcspi->ctx.modulctrl; chconf = mcspi->ctx.modulctrl;
chconf |= OMAP2_MCSPI_MODULCTRL_SINGLE; chconf |= OMAP2_MCSPI_MODULCTRL_SINGLE;
@ -1197,78 +1198,64 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
omap2_mcspi_set_enable(spi, 0); omap2_mcspi_set_enable(spi, 0);
if (gpio_is_valid(spi->cs_gpio))
omap2_mcspi_set_cs(spi, !(spi->mode & SPI_CS_HIGH));
if (mcspi->fifo_depth > 0 && t) if (mcspi->fifo_depth > 0 && t)
omap2_mcspi_set_fifo(spi, t, 0); omap2_mcspi_set_fifo(spi, t, 0);
m->status = status; return status;
} }
static int omap2_mcspi_transfer_one_message(struct spi_master *master, static int omap2_mcspi_transfer_one(struct spi_master *master,
struct spi_message *m) struct spi_device *spi, struct spi_transfer *t)
{ {
struct spi_device *spi;
struct omap2_mcspi *mcspi; struct omap2_mcspi *mcspi;
struct omap2_mcspi_dma *mcspi_dma; struct omap2_mcspi_dma *mcspi_dma;
struct spi_transfer *t; const void *tx_buf = t->tx_buf;
int status; void *rx_buf = t->rx_buf;
unsigned len = t->len;
spi = m->spi;
mcspi = spi_master_get_devdata(master); mcspi = spi_master_get_devdata(master);
mcspi_dma = mcspi->dma_channels + spi->chip_select; mcspi_dma = mcspi->dma_channels + spi->chip_select;
m->actual_length = 0;
m->status = 0;
list_for_each_entry(t, &m->transfers, transfer_list) { if ((len && !(rx_buf || tx_buf))) {
const void *tx_buf = t->tx_buf; dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
void *rx_buf = t->rx_buf; t->speed_hz,
unsigned len = t->len; len,
tx_buf ? "tx" : "",
rx_buf ? "rx" : "",
t->bits_per_word);
return -EINVAL;
}
if ((len && !(rx_buf || tx_buf))) { if (len < DMA_MIN_BYTES)
dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", goto skip_dma_map;
t->speed_hz,
len, if (mcspi_dma->dma_tx && tx_buf != NULL) {
tx_buf ? "tx" : "", t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf,
rx_buf ? "rx" : "", len, DMA_TO_DEVICE);
t->bits_per_word); if (dma_mapping_error(mcspi->dev, t->tx_dma)) {
status = -EINVAL; dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
goto out; 'T', len);
return -EINVAL;
} }
}
if (m->is_dma_mapped || len < DMA_MIN_BYTES) if (mcspi_dma->dma_rx && rx_buf != NULL) {
continue; t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len,
DMA_FROM_DEVICE);
if (mcspi_dma->dma_tx && tx_buf != NULL) { if (dma_mapping_error(mcspi->dev, t->rx_dma)) {
t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf, dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
len, DMA_TO_DEVICE); 'R', len);
if (dma_mapping_error(mcspi->dev, t->tx_dma)) { if (tx_buf != NULL)
dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", dma_unmap_single(mcspi->dev, t->tx_dma,
'T', len); len, DMA_TO_DEVICE);
status = -EINVAL; return -EINVAL;
goto out;
}
}
if (mcspi_dma->dma_rx && rx_buf != NULL) {
t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len,
DMA_FROM_DEVICE);
if (dma_mapping_error(mcspi->dev, t->rx_dma)) {
dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
'R', len);
if (tx_buf != NULL)
dma_unmap_single(mcspi->dev, t->tx_dma,
len, DMA_TO_DEVICE);
status = -EINVAL;
goto out;
}
} }
} }
omap2_mcspi_work(mcspi, m); skip_dma_map:
/* spi_finalize_current_message() changes the status inside the return omap2_mcspi_work_one(mcspi, spi, t);
* spi_message, save the status here. */
status = m->status;
out:
spi_finalize_current_message(master);
return status;
} }
static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
@ -1347,7 +1334,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
master->setup = omap2_mcspi_setup; master->setup = omap2_mcspi_setup;
master->auto_runtime_pm = true; master->auto_runtime_pm = true;
master->transfer_one_message = omap2_mcspi_transfer_one_message; master->transfer_one = omap2_mcspi_transfer_one;
master->set_cs = omap2_mcspi_set_cs;
master->cleanup = omap2_mcspi_cleanup; master->cleanup = omap2_mcspi_cleanup;
master->dev.of_node = node; master->dev.of_node = node;
master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ; master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ;

View File

@ -61,6 +61,12 @@ enum orion_spi_type {
struct orion_spi_dev { struct orion_spi_dev {
enum orion_spi_type typ; enum orion_spi_type typ;
/*
* min_divisor and max_hz should be exclusive, the only we can
* have both is for managing the armada-370-spi case with old
* device tree
*/
unsigned long max_hz;
unsigned int min_divisor; unsigned int min_divisor;
unsigned int max_divisor; unsigned int max_divisor;
u32 prescale_mask; u32 prescale_mask;
@ -385,16 +391,54 @@ static const struct orion_spi_dev orion_spi_dev_data = {
.prescale_mask = ORION_SPI_CLK_PRESCALE_MASK, .prescale_mask = ORION_SPI_CLK_PRESCALE_MASK,
}; };
static const struct orion_spi_dev armada_spi_dev_data = { static const struct orion_spi_dev armada_370_spi_dev_data = {
.typ = ARMADA_SPI, .typ = ARMADA_SPI,
.min_divisor = 1, .min_divisor = 4,
.max_divisor = 1920,
.max_hz = 50000000,
.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
};
static const struct orion_spi_dev armada_xp_spi_dev_data = {
.typ = ARMADA_SPI,
.max_hz = 50000000,
.max_divisor = 1920,
.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
};
static const struct orion_spi_dev armada_375_spi_dev_data = {
.typ = ARMADA_SPI,
.min_divisor = 15,
.max_divisor = 1920, .max_divisor = 1920,
.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK, .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
}; };
static const struct of_device_id orion_spi_of_match_table[] = { static const struct of_device_id orion_spi_of_match_table[] = {
{ .compatible = "marvell,orion-spi", .data = &orion_spi_dev_data, }, {
{ .compatible = "marvell,armada-370-spi", .data = &armada_spi_dev_data, }, .compatible = "marvell,orion-spi",
.data = &orion_spi_dev_data,
},
{
.compatible = "marvell,armada-370-spi",
.data = &armada_370_spi_dev_data,
},
{
.compatible = "marvell,armada-375-spi",
.data = &armada_375_spi_dev_data,
},
{
.compatible = "marvell,armada-380-spi",
.data = &armada_xp_spi_dev_data,
},
{
.compatible = "marvell,armada-390-spi",
.data = &armada_xp_spi_dev_data,
},
{
.compatible = "marvell,armada-xp-spi",
.data = &armada_xp_spi_dev_data,
},
{} {}
}; };
MODULE_DEVICE_TABLE(of, orion_spi_of_match_table); MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
@ -454,7 +498,23 @@ static int orion_spi_probe(struct platform_device *pdev)
goto out; goto out;
tclk_hz = clk_get_rate(spi->clk); tclk_hz = clk_get_rate(spi->clk);
master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
/*
* With old device tree, armada-370-spi could be used with
* Armada XP, however for this SoC the maximum frequency is
* 50MHz instead of tclk/4. On Armada 370, tclk cannot be
* higher than 200MHz. So, in order to be able to handle both
* SoCs, we can take the minimum of 50MHz and tclk/4.
*/
if (of_device_is_compatible(pdev->dev.of_node,
"marvell,armada-370-spi"))
master->max_speed_hz = min(devdata->max_hz,
DIV_ROUND_UP(tclk_hz, devdata->min_divisor));
else if (devdata->min_divisor)
master->max_speed_hz =
DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
else
master->max_speed_hz = devdata->max_hz;
master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor); master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); r = platform_get_resource(pdev, IORESOURCE_MEM, 0);

View File

@ -62,7 +62,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.max_clk_rate = 3686400, .max_clk_rate = 3686400,
}, },
[PORT_BYT] = { [PORT_BYT] = {
.type = LPSS_SSP, .type = LPSS_BYT_SSP,
.port_id = 0, .port_id = 0,
.num_chipselect = 1, .num_chipselect = 1,
.max_clk_rate = 50000000, .max_clk_rate = 50000000,
@ -70,7 +70,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.rx_param = &byt_rx_param, .rx_param = &byt_rx_param,
}, },
[PORT_BSW0] = { [PORT_BSW0] = {
.type = LPSS_SSP, .type = LPSS_BYT_SSP,
.port_id = 0, .port_id = 0,
.num_chipselect = 1, .num_chipselect = 1,
.max_clk_rate = 50000000, .max_clk_rate = 50000000,
@ -78,7 +78,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.rx_param = &bsw0_rx_param, .rx_param = &bsw0_rx_param,
}, },
[PORT_BSW1] = { [PORT_BSW1] = {
.type = LPSS_SSP, .type = LPSS_BYT_SSP,
.port_id = 1, .port_id = 1,
.num_chipselect = 1, .num_chipselect = 1,
.max_clk_rate = 50000000, .max_clk_rate = 50000000,
@ -86,7 +86,7 @@ static struct pxa_spi_info spi_info_configs[] = {
.rx_param = &bsw1_rx_param, .rx_param = &bsw1_rx_param,
}, },
[PORT_BSW2] = { [PORT_BSW2] = {
.type = LPSS_SSP, .type = LPSS_BYT_SSP,
.port_id = 2, .port_id = 2,
.num_chipselect = 1, .num_chipselect = 1,
.max_clk_rate = 50000000, .max_clk_rate = 50000000,

View File

@ -1,487 +0,0 @@
/*
* PXA2xx SPI private DMA support.
*
* Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/pxa2xx_ssp.h>
#include <linux/spi/spi.h>
#include <linux/spi/pxa2xx_spi.h>
#include <mach/dma.h>
#include "spi-pxa2xx.h"
#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR)
#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK)
bool pxa2xx_spi_dma_is_possible(size_t len)
{
/* Try to map dma buffer and do a dma transfer if successful, but
* only if the length is non-zero and less than MAX_DMA_LEN.
*
* Zero-length non-descriptor DMA is illegal on PXA2xx; force use
* of PIO instead. Care is needed above because the transfer may
* have have been passed with buffers that are already dma mapped.
* A zero-length transfer in PIO mode will not try to write/read
* to/from the buffers
*
* REVISIT large transfers are exactly where we most want to be
* using DMA. If this happens much, split those transfers into
* multiple DMA segments rather than forcing PIO.
*/
return len > 0 && len <= MAX_DMA_LEN;
}
int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data)
{
struct spi_message *msg = drv_data->cur_msg;
struct device *dev = &msg->spi->dev;
if (!drv_data->cur_chip->enable_dma)
return 0;
if (msg->is_dma_mapped)
return drv_data->rx_dma && drv_data->tx_dma;
if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx))
return 0;
/* Modify setup if rx buffer is null */
if (drv_data->rx == NULL) {
*drv_data->null_dma_buf = 0;
drv_data->rx = drv_data->null_dma_buf;
drv_data->rx_map_len = 4;
} else
drv_data->rx_map_len = drv_data->len;
/* Modify setup if tx buffer is null */
if (drv_data->tx == NULL) {
*drv_data->null_dma_buf = 0;
drv_data->tx = drv_data->null_dma_buf;
drv_data->tx_map_len = 4;
} else
drv_data->tx_map_len = drv_data->len;
/* Stream map the tx buffer. Always do DMA_TO_DEVICE first
* so we flush the cache *before* invalidating it, in case
* the tx and rx buffers overlap.
*/
drv_data->tx_dma = dma_map_single(dev, drv_data->tx,
drv_data->tx_map_len, DMA_TO_DEVICE);
if (dma_mapping_error(dev, drv_data->tx_dma))
return 0;
/* Stream map the rx buffer */
drv_data->rx_dma = dma_map_single(dev, drv_data->rx,
drv_data->rx_map_len, DMA_FROM_DEVICE);
if (dma_mapping_error(dev, drv_data->rx_dma)) {
dma_unmap_single(dev, drv_data->tx_dma,
drv_data->tx_map_len, DMA_TO_DEVICE);
return 0;
}
return 1;
}
static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data)
{
struct device *dev;
if (!drv_data->dma_mapped)
return;
if (!drv_data->cur_msg->is_dma_mapped) {
dev = &drv_data->cur_msg->spi->dev;
dma_unmap_single(dev, drv_data->rx_dma,
drv_data->rx_map_len, DMA_FROM_DEVICE);
dma_unmap_single(dev, drv_data->tx_dma,
drv_data->tx_map_len, DMA_TO_DEVICE);
}
drv_data->dma_mapped = 0;
}
static int wait_ssp_rx_stall(struct driver_data *drv_data)
{
unsigned long limit = loops_per_jiffy << 1;
while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY) && --limit)
cpu_relax();
return limit;
}
static int wait_dma_channel_stop(int channel)
{
unsigned long limit = loops_per_jiffy << 1;
while (!(DCSR(channel) & DCSR_STOPSTATE) && --limit)
cpu_relax();
return limit;
}
static void pxa2xx_spi_dma_error_stop(struct driver_data *drv_data,
const char *msg)
{
/* Stop and reset */
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
write_SSSR_CS(drv_data, drv_data->clear_sr);
pxa2xx_spi_write(drv_data, SSCR1,
pxa2xx_spi_read(drv_data, SSCR1)
& ~drv_data->dma_cr1);
if (!pxa25x_ssp_comp(drv_data))
pxa2xx_spi_write(drv_data, SSTO, 0);
pxa2xx_spi_flush(drv_data);
pxa2xx_spi_write(drv_data, SSCR0,
pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
pxa2xx_spi_unmap_dma_buffers(drv_data);
dev_err(&drv_data->pdev->dev, "%s\n", msg);
drv_data->cur_msg->state = ERROR_STATE;
tasklet_schedule(&drv_data->pump_transfers);
}
static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data)
{
struct spi_message *msg = drv_data->cur_msg;
/* Clear and disable interrupts on SSP and DMA channels*/
pxa2xx_spi_write(drv_data, SSCR1,
pxa2xx_spi_read(drv_data, SSCR1)
& ~drv_data->dma_cr1);
write_SSSR_CS(drv_data, drv_data->clear_sr);
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
if (wait_dma_channel_stop(drv_data->rx_channel) == 0)
dev_err(&drv_data->pdev->dev,
"dma_handler: dma rx channel stop failed\n");
if (wait_ssp_rx_stall(drv_data->ioaddr) == 0)
dev_err(&drv_data->pdev->dev,
"dma_transfer: ssp rx stall failed\n");
pxa2xx_spi_unmap_dma_buffers(drv_data);
/* update the buffer pointer for the amount completed in dma */
drv_data->rx += drv_data->len -
(DCMD(drv_data->rx_channel) & DCMD_LENGTH);
/* read trailing data from fifo, it does not matter how many
* bytes are in the fifo just read until buffer is full
* or fifo is empty, which ever occurs first */
drv_data->read(drv_data);
/* return count of what was actually read */
msg->actual_length += drv_data->len -
(drv_data->rx_end - drv_data->rx);
/* Transfer delays and chip select release are
* handled in pump_transfers or giveback
*/
/* Move to next transfer */
msg->state = pxa2xx_spi_next_transfer(drv_data);
/* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
}
void pxa2xx_spi_dma_handler(int channel, void *data)
{
struct driver_data *drv_data = data;
u32 irq_status = DCSR(channel) & DMA_INT_MASK;
if (irq_status & DCSR_BUSERR) {
if (channel == drv_data->tx_channel)
pxa2xx_spi_dma_error_stop(drv_data,
"dma_handler: bad bus address on tx channel");
else
pxa2xx_spi_dma_error_stop(drv_data,
"dma_handler: bad bus address on rx channel");
return;
}
/* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */
if ((channel == drv_data->tx_channel)
&& (irq_status & DCSR_ENDINTR)
&& (drv_data->ssp_type == PXA25x_SSP)) {
/* Wait for rx to stall */
if (wait_ssp_rx_stall(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"dma_handler: ssp rx stall failed\n");
/* finish this transfer, start the next */
pxa2xx_spi_dma_transfer_complete(drv_data);
}
}
irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data)
{
u32 irq_status;
irq_status = pxa2xx_spi_read(drv_data, SSSR) & drv_data->mask_sr;
if (irq_status & SSSR_ROR) {
pxa2xx_spi_dma_error_stop(drv_data,
"dma_transfer: fifo overrun");
return IRQ_HANDLED;
}
/* Check for false positive timeout */
if ((irq_status & SSSR_TINT)
&& (DCSR(drv_data->tx_channel) & DCSR_RUN)) {
pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT);
return IRQ_HANDLED;
}
if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) {
/* Clear and disable timeout interrupt, do the rest in
* dma_transfer_complete */
if (!pxa25x_ssp_comp(drv_data))
pxa2xx_spi_write(drv_data, SSTO, 0);
/* finish this transfer, start the next */
pxa2xx_spi_dma_transfer_complete(drv_data);
return IRQ_HANDLED;
}
/* Opps problem detected */
return IRQ_NONE;
}
int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst)
{
u32 dma_width;
switch (drv_data->n_bytes) {
case 1:
dma_width = DCMD_WIDTH1;
break;
case 2:
dma_width = DCMD_WIDTH2;
break;
default:
dma_width = DCMD_WIDTH4;
break;
}
/* Setup rx DMA Channel */
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
DSADR(drv_data->rx_channel) = drv_data->ssdr_physical;
DTADR(drv_data->rx_channel) = drv_data->rx_dma;
if (drv_data->rx == drv_data->null_dma_buf)
/* No target address increment */
DCMD(drv_data->rx_channel) = DCMD_FLOWSRC
| dma_width
| dma_burst
| drv_data->len;
else
DCMD(drv_data->rx_channel) = DCMD_INCTRGADDR
| DCMD_FLOWSRC
| dma_width
| dma_burst
| drv_data->len;
/* Setup tx DMA Channel */
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
DSADR(drv_data->tx_channel) = drv_data->tx_dma;
DTADR(drv_data->tx_channel) = drv_data->ssdr_physical;
if (drv_data->tx == drv_data->null_dma_buf)
/* No source address increment */
DCMD(drv_data->tx_channel) = DCMD_FLOWTRG
| dma_width
| dma_burst
| drv_data->len;
else
DCMD(drv_data->tx_channel) = DCMD_INCSRCADDR
| DCMD_FLOWTRG
| dma_width
| dma_burst
| drv_data->len;
/* Enable dma end irqs on SSP to detect end of transfer */
if (drv_data->ssp_type == PXA25x_SSP)
DCMD(drv_data->tx_channel) |= DCMD_ENDIRQEN;
return 0;
}
void pxa2xx_spi_dma_start(struct driver_data *drv_data)
{
DCSR(drv_data->rx_channel) |= DCSR_RUN;
DCSR(drv_data->tx_channel) |= DCSR_RUN;
}
int pxa2xx_spi_dma_setup(struct driver_data *drv_data)
{
struct device *dev = &drv_data->pdev->dev;
struct ssp_device *ssp = drv_data->ssp;
/* Get two DMA channels (rx and tx) */
drv_data->rx_channel = pxa_request_dma("pxa2xx_spi_ssp_rx",
DMA_PRIO_HIGH,
pxa2xx_spi_dma_handler,
drv_data);
if (drv_data->rx_channel < 0) {
dev_err(dev, "problem (%d) requesting rx channel\n",
drv_data->rx_channel);
return -ENODEV;
}
drv_data->tx_channel = pxa_request_dma("pxa2xx_spi_ssp_tx",
DMA_PRIO_MEDIUM,
pxa2xx_spi_dma_handler,
drv_data);
if (drv_data->tx_channel < 0) {
dev_err(dev, "problem (%d) requesting tx channel\n",
drv_data->tx_channel);
pxa_free_dma(drv_data->rx_channel);
return -ENODEV;
}
DRCMR(ssp->drcmr_rx) = DRCMR_MAPVLD | drv_data->rx_channel;
DRCMR(ssp->drcmr_tx) = DRCMR_MAPVLD | drv_data->tx_channel;
return 0;
}
void pxa2xx_spi_dma_release(struct driver_data *drv_data)
{
struct ssp_device *ssp = drv_data->ssp;
DRCMR(ssp->drcmr_rx) = 0;
DRCMR(ssp->drcmr_tx) = 0;
if (drv_data->tx_channel != 0)
pxa_free_dma(drv_data->tx_channel);
if (drv_data->rx_channel != 0)
pxa_free_dma(drv_data->rx_channel);
}
void pxa2xx_spi_dma_resume(struct driver_data *drv_data)
{
if (drv_data->rx_channel != -1)
DRCMR(drv_data->ssp->drcmr_rx) =
DRCMR_MAPVLD | drv_data->rx_channel;
if (drv_data->tx_channel != -1)
DRCMR(drv_data->ssp->drcmr_tx) =
DRCMR_MAPVLD | drv_data->tx_channel;
}
int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip,
struct spi_device *spi,
u8 bits_per_word, u32 *burst_code,
u32 *threshold)
{
struct pxa2xx_spi_chip *chip_info =
(struct pxa2xx_spi_chip *)spi->controller_data;
int bytes_per_word;
int burst_bytes;
int thresh_words;
int req_burst_size;
int retval = 0;
/* Set the threshold (in registers) to equal the same amount of data
* as represented by burst size (in bytes). The computation below
* is (burst_size rounded up to nearest 8 byte, word or long word)
* divided by (bytes/register); the tx threshold is the inverse of
* the rx, so that there will always be enough data in the rx fifo
* to satisfy a burst, and there will always be enough space in the
* tx fifo to accept a burst (a tx burst will overwrite the fifo if
* there is not enough space), there must always remain enough empty
* space in the rx fifo for any data loaded to the tx fifo.
* Whenever burst_size (in bytes) equals bits/word, the fifo threshold
* will be 8, or half the fifo;
* The threshold can only be set to 2, 4 or 8, but not 16, because
* to burst 16 to the tx fifo, the fifo would have to be empty;
* however, the minimum fifo trigger level is 1, and the tx will
* request service when the fifo is at this level, with only 15 spaces.
*/
/* find bytes/word */
if (bits_per_word <= 8)
bytes_per_word = 1;
else if (bits_per_word <= 16)
bytes_per_word = 2;
else
bytes_per_word = 4;
/* use struct pxa2xx_spi_chip->dma_burst_size if available */
if (chip_info)
req_burst_size = chip_info->dma_burst_size;
else {
switch (chip->dma_burst_size) {
default:
/* if the default burst size is not set,
* do it now */
chip->dma_burst_size = DCMD_BURST8;
case DCMD_BURST8:
req_burst_size = 8;
break;
case DCMD_BURST16:
req_burst_size = 16;
break;
case DCMD_BURST32:
req_burst_size = 32;
break;
}
}
if (req_burst_size <= 8) {
*burst_code = DCMD_BURST8;
burst_bytes = 8;
} else if (req_burst_size <= 16) {
if (bytes_per_word == 1) {
/* don't burst more than 1/2 the fifo */
*burst_code = DCMD_BURST8;
burst_bytes = 8;
retval = 1;
} else {
*burst_code = DCMD_BURST16;
burst_bytes = 16;
}
} else {
if (bytes_per_word == 1) {
/* don't burst more than 1/2 the fifo */
*burst_code = DCMD_BURST8;
burst_bytes = 8;
retval = 1;
} else if (bytes_per_word == 2) {
/* don't burst more than 1/2 the fifo */
*burst_code = DCMD_BURST16;
burst_bytes = 16;
retval = 1;
} else {
*burst_code = DCMD_BURST32;
burst_bytes = 32;
}
}
thresh_words = burst_bytes / bytes_per_word;
/* thresh_words will be between 2 and 8 */
*threshold = (SSCR1_RxTresh(thresh_words) & SSCR1_RFT)
| (SSCR1_TxTresh(16-thresh_words) & SSCR1_TFT);
return retval;
}

View File

@ -60,21 +60,60 @@ MODULE_ALIAS("platform:pxa2xx-spi");
| QUARK_X1000_SSCR1_TFT \ | QUARK_X1000_SSCR1_TFT \
| SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
#define LPSS_RX_THRESH_DFLT 64
#define LPSS_TX_LOTHRESH_DFLT 160
#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 GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
#define SSP_REG 0x0c
#define SPI_CS_CONTROL 0x18
#define SPI_CS_CONTROL_SW_MODE BIT(0) #define SPI_CS_CONTROL_SW_MODE BIT(0)
#define SPI_CS_CONTROL_CS_HIGH BIT(1) #define SPI_CS_CONTROL_CS_HIGH BIT(1)
struct lpss_config {
/* LPSS offset from drv_data->ioaddr */
unsigned offset;
/* Register offsets from drv_data->lpss_base or -1 */
int reg_general;
int reg_ssp;
int reg_cs_ctrl;
/* FIFO thresholds */
u32 rx_threshold;
u32 tx_threshold_lo;
u32 tx_threshold_hi;
};
/* Keep these sorted with enum pxa_ssp_type */
static const struct lpss_config lpss_platforms[] = {
{ /* LPSS_LPT_SSP */
.offset = 0x800,
.reg_general = 0x08,
.reg_ssp = 0x0c,
.reg_cs_ctrl = 0x18,
.rx_threshold = 64,
.tx_threshold_lo = 160,
.tx_threshold_hi = 224,
},
{ /* LPSS_BYT_SSP */
.offset = 0x400,
.reg_general = 0x08,
.reg_ssp = 0x0c,
.reg_cs_ctrl = 0x18,
.rx_threshold = 64,
.tx_threshold_lo = 160,
.tx_threshold_hi = 224,
},
};
static inline const struct lpss_config
*lpss_get_config(const struct driver_data *drv_data)
{
return &lpss_platforms[drv_data->ssp_type - LPSS_LPT_SSP];
}
static bool is_lpss_ssp(const struct driver_data *drv_data) static bool is_lpss_ssp(const struct driver_data *drv_data)
{ {
return drv_data->ssp_type == LPSS_SSP; switch (drv_data->ssp_type) {
case LPSS_LPT_SSP:
case LPSS_BYT_SSP:
return true;
default:
return false;
}
} }
static bool is_quark_x1000_ssp(const struct driver_data *drv_data) static bool is_quark_x1000_ssp(const struct driver_data *drv_data)
@ -192,63 +231,43 @@ static void __lpss_ssp_write_priv(struct driver_data *drv_data,
*/ */
static void lpss_ssp_setup(struct driver_data *drv_data) static void lpss_ssp_setup(struct driver_data *drv_data)
{ {
unsigned offset = 0x400; const struct lpss_config *config;
u32 value, orig; u32 value;
/* config = lpss_get_config(drv_data);
* Perform auto-detection of the LPSS SSP private registers. They drv_data->lpss_base = drv_data->ioaddr + config->offset;
* can be either at 1k or 2k offset from the base address.
*/
orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
/* Test SPI_CS_CONTROL_SW_MODE bit enabling */
value = orig | SPI_CS_CONTROL_SW_MODE;
writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL);
value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
if (value != (orig | SPI_CS_CONTROL_SW_MODE)) {
offset = 0x800;
goto detection_done;
}
orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
/* Test SPI_CS_CONTROL_SW_MODE bit disabling */
value = orig & ~SPI_CS_CONTROL_SW_MODE;
writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL);
value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
if (value != (orig & ~SPI_CS_CONTROL_SW_MODE)) {
offset = 0x800;
goto detection_done;
}
detection_done:
/* Now set the LPSS base */
drv_data->lpss_base = drv_data->ioaddr + offset;
/* Enable software chip select control */ /* Enable software chip select control */
value = SPI_CS_CONTROL_SW_MODE | SPI_CS_CONTROL_CS_HIGH; value = SPI_CS_CONTROL_SW_MODE | SPI_CS_CONTROL_CS_HIGH;
__lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
/* Enable multiblock DMA transfers */ /* 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); __lpss_ssp_write_priv(drv_data, config->reg_ssp, 1);
value = __lpss_ssp_read_priv(drv_data, GENERAL_REG); if (config->reg_general >= 0) {
value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE; value = __lpss_ssp_read_priv(drv_data,
__lpss_ssp_write_priv(drv_data, GENERAL_REG, value); config->reg_general);
value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE;
__lpss_ssp_write_priv(drv_data,
config->reg_general, value);
}
} }
} }
static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
{ {
const struct lpss_config *config;
u32 value; u32 value;
value = __lpss_ssp_read_priv(drv_data, SPI_CS_CONTROL); config = lpss_get_config(drv_data);
value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
if (enable) if (enable)
value &= ~SPI_CS_CONTROL_CS_HIGH; value &= ~SPI_CS_CONTROL_CS_HIGH;
else else
value |= SPI_CS_CONTROL_CS_HIGH; value |= SPI_CS_CONTROL_CS_HIGH;
__lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
} }
static void cs_assert(struct driver_data *drv_data) static void cs_assert(struct driver_data *drv_data)
@ -1075,6 +1094,7 @@ static int setup(struct spi_device *spi)
{ {
struct pxa2xx_spi_chip *chip_info = NULL; struct pxa2xx_spi_chip *chip_info = NULL;
struct chip_data *chip; struct chip_data *chip;
const struct lpss_config *config;
struct driver_data *drv_data = spi_master_get_devdata(spi->master); struct driver_data *drv_data = spi_master_get_devdata(spi->master);
unsigned int clk_div; unsigned int clk_div;
uint tx_thres, tx_hi_thres, rx_thres; uint tx_thres, tx_hi_thres, rx_thres;
@ -1085,10 +1105,12 @@ static int setup(struct spi_device *spi)
tx_hi_thres = 0; tx_hi_thres = 0;
rx_thres = RX_THRESH_QUARK_X1000_DFLT; rx_thres = RX_THRESH_QUARK_X1000_DFLT;
break; break;
case LPSS_SSP: case LPSS_LPT_SSP:
tx_thres = LPSS_TX_LOTHRESH_DFLT; case LPSS_BYT_SSP:
tx_hi_thres = LPSS_TX_HITHRESH_DFLT; config = lpss_get_config(drv_data);
rx_thres = LPSS_RX_THRESH_DFLT; tx_thres = config->tx_threshold_lo;
tx_hi_thres = config->tx_threshold_hi;
rx_thres = config->rx_threshold;
break; break;
default: default:
tx_thres = TX_THRESH_DFLT; tx_thres = TX_THRESH_DFLT;
@ -1242,6 +1264,18 @@ static void cleanup(struct spi_device *spi)
} }
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
{ "INT33C0", LPSS_LPT_SSP },
{ "INT33C1", LPSS_LPT_SSP },
{ "INT3430", LPSS_LPT_SSP },
{ "INT3431", LPSS_LPT_SSP },
{ "80860F0E", LPSS_BYT_SSP },
{ "8086228E", LPSS_BYT_SSP },
{ },
};
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
static struct pxa2xx_spi_master * static struct pxa2xx_spi_master *
pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
{ {
@ -1249,12 +1283,19 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
struct acpi_device *adev; struct acpi_device *adev;
struct ssp_device *ssp; struct ssp_device *ssp;
struct resource *res; struct resource *res;
int devid; const struct acpi_device_id *id;
int devid, type;
if (!ACPI_HANDLE(&pdev->dev) || if (!ACPI_HANDLE(&pdev->dev) ||
acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev))
return NULL; return NULL;
id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
if (id)
type = (int)id->driver_data;
else
return NULL;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) if (!pdata)
return NULL; return NULL;
@ -1272,7 +1313,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->clk = devm_clk_get(&pdev->dev, NULL);
ssp->irq = platform_get_irq(pdev, 0); ssp->irq = platform_get_irq(pdev, 0);
ssp->type = LPSS_SSP; ssp->type = type;
ssp->pdev = pdev; ssp->pdev = pdev;
ssp->port_id = -1; ssp->port_id = -1;
@ -1285,16 +1326,6 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
return pdata; return pdata;
} }
static struct acpi_device_id pxa2xx_spi_acpi_match[] = {
{ "INT33C0", 0 },
{ "INT33C1", 0 },
{ "INT3430", 0 },
{ "INT3431", 0 },
{ "80860F0E", 0 },
{ "8086228E", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
#else #else
static inline struct pxa2xx_spi_master * static inline struct pxa2xx_spi_master *
pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)

View File

@ -162,11 +162,7 @@ extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data);
/* /*
* Select the right DMA implementation. * Select the right DMA implementation.
*/ */
#if defined(CONFIG_SPI_PXA2XX_PXADMA) #if defined(CONFIG_SPI_PXA2XX_DMA)
#define SPI_PXA2XX_USE_DMA 1
#define MAX_DMA_LEN 8191
#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TINTE)
#elif defined(CONFIG_SPI_PXA2XX_DMA)
#define SPI_PXA2XX_USE_DMA 1 #define SPI_PXA2XX_USE_DMA 1
#define MAX_DMA_LEN SZ_64K #define MAX_DMA_LEN SZ_64K
#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL) #define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL)

210
drivers/spi/spi-rb4xx.c Normal file
View File

@ -0,0 +1,210 @@
/*
* SPI controller driver for the Mikrotik RB4xx boards
*
* Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2015 Bert Vermeulen <bert@biot.com>
*
* This file was based on the patches for Linux 2.6.27.39 published by
* MikroTik for their RouterBoard 4xx series devices.
*
* 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/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/spi/spi.h>
#include <asm/mach-ath79/ar71xx_regs.h>
struct rb4xx_spi {
void __iomem *base;
struct clk *clk;
};
static inline u32 rb4xx_read(struct rb4xx_spi *rbspi, u32 reg)
{
return __raw_readl(rbspi->base + reg);
}
static inline void rb4xx_write(struct rb4xx_spi *rbspi, u32 reg, u32 value)
{
__raw_writel(value, rbspi->base + reg);
}
static inline void do_spi_clk(struct rb4xx_spi *rbspi, u32 spi_ioc, int value)
{
u32 regval;
regval = spi_ioc;
if (value & BIT(0))
regval |= AR71XX_SPI_IOC_DO;
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
}
static void do_spi_byte(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
{
int i;
for (i = 7; i >= 0; i--)
do_spi_clk(rbspi, spi_ioc, byte >> i);
}
/* The CS2 pin is used to clock in a second bit per clock cycle. */
static inline void do_spi_clk_two(struct rb4xx_spi *rbspi, u32 spi_ioc,
u8 value)
{
u32 regval;
regval = spi_ioc;
if (value & BIT(1))
regval |= AR71XX_SPI_IOC_DO;
if (value & BIT(0))
regval |= AR71XX_SPI_IOC_CS2;
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
}
/* Two bits at a time, msb first */
static void do_spi_byte_two(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
{
do_spi_clk_two(rbspi, spi_ioc, byte >> 6);
do_spi_clk_two(rbspi, spi_ioc, byte >> 4);
do_spi_clk_two(rbspi, spi_ioc, byte >> 2);
do_spi_clk_two(rbspi, spi_ioc, byte >> 0);
}
static void rb4xx_set_cs(struct spi_device *spi, bool enable)
{
struct rb4xx_spi *rbspi = spi_master_get_devdata(spi->master);
/*
* Setting CS is done along with bitbanging the actual values,
* since it's all on the same hardware register. However the
* CPLD needs CS deselected after every command.
*/
if (enable)
rb4xx_write(rbspi, AR71XX_SPI_REG_IOC,
AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1);
}
static int rb4xx_transfer_one(struct spi_master *master,
struct spi_device *spi, struct spi_transfer *t)
{
struct rb4xx_spi *rbspi = spi_master_get_devdata(master);
int i;
u32 spi_ioc;
u8 *rx_buf;
const u8 *tx_buf;
/*
* Prime the SPI register with the SPI device selected. The m25p80 boot
* flash and CPLD share the CS0 pin. This works because the CPLD's
* command set was designed to almost not clash with that of the
* boot flash.
*/
if (spi->chip_select == 2)
/* MMC */
spi_ioc = AR71XX_SPI_IOC_CS0;
else
/* Boot flash and CPLD */
spi_ioc = AR71XX_SPI_IOC_CS1;
tx_buf = t->tx_buf;
rx_buf = t->rx_buf;
for (i = 0; i < t->len; ++i) {
if (t->tx_nbits == SPI_NBITS_DUAL)
/* CPLD can use two-wire transfers */
do_spi_byte_two(rbspi, spi_ioc, tx_buf[i]);
else
do_spi_byte(rbspi, spi_ioc, tx_buf[i]);
if (!rx_buf)
continue;
rx_buf[i] = rb4xx_read(rbspi, AR71XX_SPI_REG_RDS);
}
spi_finalize_current_transfer(master);
return 0;
}
static int rb4xx_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct clk *ahb_clk;
struct rb4xx_spi *rbspi;
struct resource *r;
int err;
void __iomem *spi_base;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi_base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(spi_base))
return PTR_ERR(spi_base);
master = spi_alloc_master(&pdev->dev, sizeof(*rbspi));
if (!master)
return -ENOMEM;
ahb_clk = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(ahb_clk))
return PTR_ERR(ahb_clk);
master->bus_num = 0;
master->num_chipselect = 3;
master->mode_bits = SPI_TX_DUAL;
master->bits_per_word_mask = BIT(7);
master->flags = SPI_MASTER_MUST_TX;
master->transfer_one = rb4xx_transfer_one;
master->set_cs = rb4xx_set_cs;
err = devm_spi_register_master(&pdev->dev, master);
if (err) {
dev_err(&pdev->dev, "failed to register SPI master\n");
return err;
}
err = clk_prepare_enable(ahb_clk);
if (err)
return err;
rbspi = spi_master_get_devdata(master);
rbspi->base = spi_base;
rbspi->clk = ahb_clk;
platform_set_drvdata(pdev, rbspi);
/* Enable SPI */
rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
return 0;
}
static int rb4xx_spi_remove(struct platform_device *pdev)
{
struct rb4xx_spi *rbspi = platform_get_drvdata(pdev);
clk_disable_unprepare(rbspi->clk);
return 0;
}
static struct platform_driver rb4xx_spi_drv = {
.probe = rb4xx_spi_probe,
.remove = rb4xx_spi_remove,
.driver = {
.name = "rb4xx-spi",
},
};
module_platform_driver(rb4xx_spi_drv);
MODULE_DESCRIPTION("Mikrotik RB4xx SPI controller driver");
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_AUTHOR("Bert Vermeulen <bert@biot.com>");
MODULE_LICENSE("GPL v2");

View File

@ -665,15 +665,12 @@ static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi,
static int rspi_dma_check_then_transfer(struct rspi_data *rspi, static int rspi_dma_check_then_transfer(struct rspi_data *rspi,
struct spi_transfer *xfer) struct spi_transfer *xfer)
{ {
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) { if (!rspi->master->can_dma || !__rspi_can_dma(rspi, xfer))
/* rx_buf can be NULL on RSPI on SH in TX-only Mode */ return -EAGAIN;
int ret = rspi_dma_transfer(rspi, &xfer->tx_sg,
xfer->rx_buf ? &xfer->rx_sg : NULL);
if (ret != -EAGAIN)
return 0;
}
return -EAGAIN; /* rx_buf can be NULL on RSPI on SH in TX-only Mode */
return rspi_dma_transfer(rspi, &xfer->tx_sg,
xfer->rx_buf ? &xfer->rx_sg : NULL);
} }
static int rspi_common_transfer(struct rspi_data *rspi, static int rspi_common_transfer(struct rspi_data *rspi,
@ -724,7 +721,7 @@ static int rspi_rz_transfer_one(struct spi_master *master,
return rspi_common_transfer(rspi, xfer); return rspi_common_transfer(rspi, xfer);
} }
static int qspi_trigger_transfer_out_int(struct rspi_data *rspi, const u8 *tx, static int qspi_trigger_transfer_out_in(struct rspi_data *rspi, const u8 *tx,
u8 *rx, unsigned int len) u8 *rx, unsigned int len)
{ {
int i, n, ret; int i, n, ret;
@ -771,12 +768,8 @@ static int qspi_transfer_out_in(struct rspi_data *rspi,
if (ret != -EAGAIN) if (ret != -EAGAIN)
return ret; return ret;
ret = qspi_trigger_transfer_out_int(rspi, xfer->tx_buf, return qspi_trigger_transfer_out_in(rspi, xfer->tx_buf,
xfer->rx_buf, xfer->len); xfer->rx_buf, xfer->len);
if (ret < 0)
return ret;
return 0;
} }
static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
@ -1300,7 +1293,7 @@ error1:
return ret; return ret;
} }
static struct platform_device_id spi_driver_ids[] = { static const struct platform_device_id spi_driver_ids[] = {
{ "rspi", (kernel_ulong_t)&rspi_ops }, { "rspi", (kernel_ulong_t)&rspi_ops },
{ "rspi-rz", (kernel_ulong_t)&rspi_rz_ops }, { "rspi-rz", (kernel_ulong_t)&rspi_rz_ops },
{ "qspi", (kernel_ulong_t)&qspi_ops }, { "qspi", (kernel_ulong_t)&qspi_ops },

View File

@ -1347,7 +1347,7 @@ static struct s3c64xx_spi_port_config exynos7_spi_port_config = {
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO, .quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
}; };
static struct platform_device_id s3c64xx_spi_driver_ids[] = { static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
{ {
.name = "s3c2443-spi", .name = "s3c2443-spi",
.driver_data = (kernel_ulong_t)&s3c2443_spi_port_config, .driver_data = (kernel_ulong_t)&s3c2443_spi_port_config,

View File

@ -1263,7 +1263,7 @@ static int sh_msiof_spi_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct platform_device_id spi_driver_ids[] = { static const struct platform_device_id spi_driver_ids[] = {
{ "spi_sh_msiof", (kernel_ulong_t)&sh_data }, { "spi_sh_msiof", (kernel_ulong_t)&sh_data },
{ "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data },
{ "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data },

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -571,7 +571,7 @@ static int __spi_map_msg(struct spi_master *master, struct spi_message *msg)
return 0; return 0;
} }
static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg) static int __spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
{ {
struct spi_transfer *xfer; struct spi_transfer *xfer;
struct device *tx_dev, *rx_dev; struct device *tx_dev, *rx_dev;
@ -583,15 +583,6 @@ static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
rx_dev = master->dma_rx->device->dev; rx_dev = master->dma_rx->device->dev;
list_for_each_entry(xfer, &msg->transfers, transfer_list) { list_for_each_entry(xfer, &msg->transfers, transfer_list) {
/*
* Restore the original value of tx_buf or rx_buf if they are
* NULL.
*/
if (xfer->tx_buf == master->dummy_tx)
xfer->tx_buf = NULL;
if (xfer->rx_buf == master->dummy_rx)
xfer->rx_buf = NULL;
if (!master->can_dma(master, msg->spi, xfer)) if (!master->can_dma(master, msg->spi, xfer))
continue; continue;
@ -608,13 +599,32 @@ static inline int __spi_map_msg(struct spi_master *master,
return 0; return 0;
} }
static inline int spi_unmap_msg(struct spi_master *master, static inline int __spi_unmap_msg(struct spi_master *master,
struct spi_message *msg) struct spi_message *msg)
{ {
return 0; return 0;
} }
#endif /* !CONFIG_HAS_DMA */ #endif /* !CONFIG_HAS_DMA */
static inline int spi_unmap_msg(struct spi_master *master,
struct spi_message *msg)
{
struct spi_transfer *xfer;
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
/*
* Restore the original value of tx_buf or rx_buf if they are
* NULL.
*/
if (xfer->tx_buf == master->dummy_tx)
xfer->tx_buf = NULL;
if (xfer->rx_buf == master->dummy_rx)
xfer->rx_buf = NULL;
}
return __spi_unmap_msg(master, msg);
}
static int spi_map_msg(struct spi_master *master, struct spi_message *msg) static int spi_map_msg(struct spi_master *master, struct spi_message *msg)
{ {
struct spi_transfer *xfer; struct spi_transfer *xfer;
@ -988,9 +998,6 @@ void spi_finalize_current_message(struct spi_master *master)
spin_lock_irqsave(&master->queue_lock, flags); spin_lock_irqsave(&master->queue_lock, flags);
mesg = master->cur_msg; mesg = master->cur_msg;
master->cur_msg = NULL;
queue_kthread_work(&master->kworker, &master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags); spin_unlock_irqrestore(&master->queue_lock, flags);
spi_unmap_msg(master, mesg); spi_unmap_msg(master, mesg);
@ -1003,9 +1010,13 @@ void spi_finalize_current_message(struct spi_master *master)
} }
} }
trace_spi_message_done(mesg); spin_lock_irqsave(&master->queue_lock, flags);
master->cur_msg = NULL;
master->cur_msg_prepared = false; master->cur_msg_prepared = false;
queue_kthread_work(&master->kworker, &master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags);
trace_spi_message_done(mesg);
mesg->state = NULL; mesg->state = NULL;
if (mesg->complete) if (mesg->complete)

View File

@ -95,37 +95,25 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/*
* We can't use the standard synchronous wrappers for file I/O; we
* need to protect against async removal of the underlying spi_device.
*/
static void spidev_complete(void *arg)
{
complete(arg);
}
static ssize_t static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message) spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{ {
DECLARE_COMPLETION_ONSTACK(done); DECLARE_COMPLETION_ONSTACK(done);
int status; int status;
struct spi_device *spi;
message->complete = spidev_complete;
message->context = &done;
spin_lock_irq(&spidev->spi_lock); spin_lock_irq(&spidev->spi_lock);
if (spidev->spi == NULL) spi = spidev->spi;
status = -ESHUTDOWN;
else
status = spi_async(spidev->spi, message);
spin_unlock_irq(&spidev->spi_lock); spin_unlock_irq(&spidev->spi_lock);
if (status == 0) { if (spi == NULL)
wait_for_completion(&done); status = -ESHUTDOWN;
status = message->status; else
if (status == 0) status = spi_sync(spi, message);
status = message->actual_length;
} if (status == 0)
status = message->actual_length;
return status; return status;
} }
@ -647,7 +635,6 @@ err_find_dev:
static int spidev_release(struct inode *inode, struct file *filp) static int spidev_release(struct inode *inode, struct file *filp)
{ {
struct spidev_data *spidev; struct spidev_data *spidev;
int status = 0;
mutex_lock(&device_list_lock); mutex_lock(&device_list_lock);
spidev = filp->private_data; spidev = filp->private_data;
@ -676,7 +663,7 @@ static int spidev_release(struct inode *inode, struct file *filp)
} }
mutex_unlock(&device_list_lock); mutex_unlock(&device_list_lock);
return status; return 0;
} }
static const struct file_operations spidev_fops = { static const struct file_operations spidev_fops = {

View File

@ -194,8 +194,9 @@ enum pxa_ssp_type {
PXA168_SSP, PXA168_SSP,
PXA910_SSP, PXA910_SSP,
CE4100_SSP, CE4100_SSP,
LPSS_SSP,
QUARK_X1000_SSP, QUARK_X1000_SSP,
LPSS_LPT_SSP, /* Keep LPSS types sorted with lpss_platforms[] */
LPSS_BYT_SSP,
}; };
struct ssp_device { struct ssp_device {