From 8348c259dd6a6019a8fa01b0a3443409480f7b9d Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Nov 2010 17:12:15 -0800 Subject: arm/pxa2xx: reorgazine SSP and SPI header files The PXA-SPI driver relies on some files / defines which are arm specific and are within the ARM tree. The CE4100 SoC which is x86 has also the SPI core. This patch moves the ssp and spi files from arm/mach-pxa and plat-pxa to include/linux where the CE4100 can access them. This move got verified by building the following defconfigs: cm_x2xx_defconfig corgi_defconfig em_x270_defconfig ezx_defconfig imote2_defconfig pxa3xx_defconfig spitz_defconfig zeus_defconfig raumfeld_defconfig magician_defconfig Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- include/linux/pxa2xx_ssp.h | 187 +++++++++++++++++++++++++++++++++++++++++ include/linux/spi/pxa2xx_spi.h | 49 +++++++++++ 2 files changed, 236 insertions(+) create mode 100644 include/linux/pxa2xx_ssp.h create mode 100644 include/linux/spi/pxa2xx_spi.h (limited to 'include/linux') diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h new file mode 100644 index 000000000000..84465d4a51b7 --- /dev/null +++ b/include/linux/pxa2xx_ssp.h @@ -0,0 +1,187 @@ +/* + * pxa2xx_ssp.h + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver supports the following PXA CPU/SSP ports:- + * + * PXA250 SSP + * PXA255 SSP, NSSP + * PXA26x SSP, NSSP, ASSP + * PXA27x SSP1, SSP2, SSP3 + * PXA3xx SSP1, SSP2, SSP3, SSP4 + */ + +#ifndef __LINUX_SSP_H +#define __LINUX_SSP_H + +#include +#include + +/* + * SSP Serial Port Registers + * PXA250, PXA255, PXA26x and PXA27x SSP controllers are all slightly different. + * PXA255, PXA26x and PXA27x have extra ports, registers and bits. + */ + +#define SSCR0 (0x00) /* SSP Control Register 0 */ +#define SSCR1 (0x04) /* SSP Control Register 1 */ +#define SSSR (0x08) /* SSP Status Register */ +#define SSITR (0x0C) /* SSP Interrupt Test Register */ +#define SSDR (0x10) /* SSP Data Write/Data Read Register */ + +#define SSTO (0x28) /* SSP Time Out Register */ +#define SSPSP (0x2C) /* SSP Programmable Serial Protocol */ +#define SSTSA (0x30) /* SSP Tx Timeslot Active */ +#define SSRSA (0x34) /* SSP Rx Timeslot Active */ +#define SSTSS (0x38) /* SSP Timeslot Status */ +#define SSACD (0x3C) /* SSP Audio Clock Divider */ +#define SSACDD (0x40) /* SSP Audio Clock Dither Divider */ + +/* Common PXA2xx bits first */ +#define SSCR0_DSS (0x0000000f) /* Data Size Select (mask) */ +#define SSCR0_DataSize(x) ((x) - 1) /* Data Size Select [4..16] */ +#define SSCR0_FRF (0x00000030) /* FRame Format (mask) */ +#define SSCR0_Motorola (0x0 << 4) /* Motorola's Serial Peripheral Interface (SPI) */ +#define SSCR0_TI (0x1 << 4) /* Texas Instruments' Synchronous Serial Protocol (SSP) */ +#define SSCR0_National (0x2 << 4) /* National Microwire */ +#define SSCR0_ECS (1 << 6) /* External clock select */ +#define SSCR0_SSE (1 << 7) /* Synchronous Serial Port Enable */ +#define SSCR0_SCR(x) ((x) << 8) /* Serial Clock Rate (mask) */ + +/* PXA27x, PXA3xx */ +#define SSCR0_EDSS (1 << 20) /* Extended data size select */ +#define SSCR0_NCS (1 << 21) /* Network clock select */ +#define SSCR0_RIM (1 << 22) /* Receive FIFO overrrun interrupt mask */ +#define SSCR0_TUM (1 << 23) /* Transmit FIFO underrun interrupt mask */ +#define SSCR0_FRDC (0x07000000) /* Frame rate divider control (mask) */ +#define SSCR0_SlotsPerFrm(x) (((x) - 1) << 24) /* Time slots per frame [1..8] */ +#define SSCR0_FPCKE (1 << 29) /* FIFO packing enable */ +#define SSCR0_ACS (1 << 30) /* Audio clock select */ +#define SSCR0_MOD (1 << 31) /* Mode (normal or network) */ + + +#define SSCR1_RIE (1 << 0) /* Receive FIFO Interrupt Enable */ +#define SSCR1_TIE (1 << 1) /* Transmit FIFO Interrupt Enable */ +#define SSCR1_LBM (1 << 2) /* Loop-Back Mode */ +#define SSCR1_SPO (1 << 3) /* Motorola SPI SSPSCLK polarity setting */ +#define SSCR1_SPH (1 << 4) /* Motorola SPI SSPSCLK phase setting */ +#define SSCR1_MWDS (1 << 5) /* Microwire Transmit Data Size */ +#define SSCR1_TFT (0x000003c0) /* Transmit FIFO Threshold (mask) */ +#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..16] */ +#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */ +#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */ + +#define SSSR_TNF (1 << 2) /* Transmit FIFO Not Full */ +#define SSSR_RNE (1 << 3) /* Receive FIFO Not Empty */ +#define SSSR_BSY (1 << 4) /* SSP Busy */ +#define SSSR_TFS (1 << 5) /* Transmit FIFO Service Request */ +#define SSSR_RFS (1 << 6) /* Receive FIFO Service Request */ +#define SSSR_ROR (1 << 7) /* Receive FIFO Overrun */ +#define SSSR_TFL_MASK (0xf << 8) /* Transmit FIFO Level mask */ +#define SSSR_RFL_MASK (0xf << 12) /* Receive FIFO Level mask */ + +/* extra bits in PXA255, PXA26x and PXA27x SSP ports */ +#define SSCR0_TISSP (1 << 4) /* TI Sync Serial Protocol */ +#define SSCR0_PSP (3 << 4) /* PSP - Programmable Serial Protocol */ +#define SSCR1_TTELP (1 << 31) /* TXD Tristate Enable Last Phase */ +#define SSCR1_TTE (1 << 30) /* TXD Tristate Enable */ +#define SSCR1_EBCEI (1 << 29) /* Enable Bit Count Error interrupt */ +#define SSCR1_SCFR (1 << 28) /* Slave Clock free Running */ +#define SSCR1_ECRA (1 << 27) /* Enable Clock Request A */ +#define SSCR1_ECRB (1 << 26) /* Enable Clock request B */ +#define SSCR1_SCLKDIR (1 << 25) /* Serial Bit Rate Clock Direction */ +#define SSCR1_SFRMDIR (1 << 24) /* Frame Direction */ +#define SSCR1_RWOT (1 << 23) /* Receive Without Transmit */ +#define SSCR1_TRAIL (1 << 22) /* Trailing Byte */ +#define SSCR1_TSRE (1 << 21) /* Transmit Service Request Enable */ +#define SSCR1_RSRE (1 << 20) /* Receive Service Request Enable */ +#define SSCR1_TINTE (1 << 19) /* Receiver Time-out Interrupt enable */ +#define SSCR1_PINTE (1 << 18) /* Peripheral Trailing Byte Interupt Enable */ +#define SSCR1_IFS (1 << 16) /* Invert Frame Signal */ +#define SSCR1_STRF (1 << 15) /* Select FIFO or EFWR */ +#define SSCR1_EFWR (1 << 14) /* Enable FIFO Write/Read */ + +#define SSSR_BCE (1 << 23) /* Bit Count Error */ +#define SSSR_CSS (1 << 22) /* Clock Synchronisation Status */ +#define SSSR_TUR (1 << 21) /* Transmit FIFO Under Run */ +#define SSSR_EOC (1 << 20) /* End Of Chain */ +#define SSSR_TINT (1 << 19) /* Receiver Time-out Interrupt */ +#define SSSR_PINT (1 << 18) /* Peripheral Trailing Byte Interrupt */ + + +#define SSPSP_SCMODE(x) ((x) << 0) /* Serial Bit Rate Clock Mode */ +#define SSPSP_SFRMP (1 << 2) /* Serial Frame Polarity */ +#define SSPSP_ETDS (1 << 3) /* End of Transfer data State */ +#define SSPSP_STRTDLY(x) ((x) << 4) /* Start Delay */ +#define SSPSP_DMYSTRT(x) ((x) << 7) /* Dummy Start */ +#define SSPSP_SFRMDLY(x) ((x) << 9) /* Serial Frame Delay */ +#define SSPSP_SFRMWDTH(x) ((x) << 16) /* Serial Frame Width */ +#define SSPSP_DMYSTOP(x) ((x) << 23) /* Dummy Stop */ +#define SSPSP_FSRT (1 << 25) /* Frame Sync Relative Timing */ + +/* PXA3xx */ +#define SSPSP_EDMYSTRT(x) ((x) << 26) /* Extended Dummy Start */ +#define SSPSP_EDMYSTOP(x) ((x) << 28) /* Extended Dummy Stop */ +#define SSPSP_TIMING_MASK (0x7f8001f0) + +#define SSACD_SCDB (1 << 3) /* SSPSYSCLK Divider Bypass */ +#define SSACD_ACPS(x) ((x) << 4) /* Audio clock PLL select */ +#define SSACD_ACDS(x) ((x) << 0) /* Audio clock divider select */ +#define SSACD_SCDX8 (1 << 7) /* SYSCLK division ratio select */ + +enum pxa_ssp_type { + SSP_UNDEFINED = 0, + PXA25x_SSP, /* pxa 210, 250, 255, 26x */ + PXA25x_NSSP, /* pxa 255, 26x (including ASSP) */ + PXA27x_SSP, + PXA168_SSP, +}; + +struct ssp_device { + struct platform_device *pdev; + struct list_head node; + + struct clk *clk; + void __iomem *mmio_base; + unsigned long phys_base; + + const char *label; + int port_id; + int type; + int use_count; + int irq; + int drcmr_rx; + int drcmr_tx; +}; + +/** + * pxa_ssp_write_reg - Write to a SSP register + * + * @dev: SSP device to access + * @reg: Register to write to + * @val: Value to be written. + */ +static inline void pxa_ssp_write_reg(struct ssp_device *dev, u32 reg, u32 val) +{ + __raw_writel(val, dev->mmio_base + reg); +} + +/** + * pxa_ssp_read_reg - Read from a SSP register + * + * @dev: SSP device to access + * @reg: Register to read from + */ +static inline u32 pxa_ssp_read_reg(struct ssp_device *dev, u32 reg) +{ + return __raw_readl(dev->mmio_base + reg); +} + +struct ssp_device *pxa_ssp_request(int port, const char *label); +void pxa_ssp_free(struct ssp_device *); +#endif diff --git a/include/linux/spi/pxa2xx_spi.h b/include/linux/spi/pxa2xx_spi.h new file mode 100644 index 000000000000..471ed6889113 --- /dev/null +++ b/include/linux/spi/pxa2xx_spi.h @@ -0,0 +1,49 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __linux_pxa2xx_spi_h +#define __linux_pxa2xx_spi_h + +#include +#include + +#define PXA2XX_CS_ASSERT (0x01) +#define PXA2XX_CS_DEASSERT (0x02) + +/* device.platform_data for SSP controller devices */ +struct pxa2xx_spi_master { + u32 clock_enable; + u16 num_chipselect; + u8 enable_dma; +}; + +/* spi_board_info.controller_data for SPI slave devices, + * copied to spi_device.platform_data ... mostly for dma tuning + */ +struct pxa2xx_spi_chip { + u8 tx_threshold; + u8 rx_threshold; + u8 dma_burst_size; + u32 timeout; + u8 enable_loopback; + int gpio_cs; + void (*cs_control)(u32 command); +}; + +extern void pxa2xx_set_spi_info(unsigned id, struct pxa2xx_spi_master *info); + +#endif -- cgit v1.2.3-71-gd317 From d6ea3df0d470fb9260db93883f97764cf9f0e562 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 24 Nov 2010 10:17:14 +0100 Subject: spi/pxa2xx: Add CE4100 support Sodaville's SPI controller is very much the same as in PXA25x. The difference: - The RX/TX FIFO is only 4 words deep instead of 16 - No DMA support - The SPI controller offers a CS functionality Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- drivers/spi/Kconfig | 13 ++- drivers/spi/Makefile | 1 + drivers/spi/pxa2xx_spi.c | 1 - drivers/spi/pxa2xx_spi_pci.c | 201 +++++++++++++++++++++++++++++++++++++++++ include/linux/spi/pxa2xx_spi.h | 105 ++++++++++++++++++++- 5 files changed, 314 insertions(+), 7 deletions(-) create mode 100644 drivers/spi/pxa2xx_spi_pci.c (limited to 'include/linux') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 78f9fd02c1b2..537759011d1d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -267,12 +267,15 @@ config SPI_PPC4xx config SPI_PXA2XX tristate "PXA2xx SSP SPI master" - depends on ARCH_PXA && EXPERIMENTAL - select PXA_SSP + depends on (ARCH_PXA || (X86_32 && PCI)) && EXPERIMENTAL + select PXA_SSP if ARCH_PXA help - This enables using a PXA2xx SSP port as a SPI master controller. - The driver can be configured to use any SSP port and additional - documentation can be found a Documentation/spi/pxa2xx. + This enables using a PXA2xx or Sodaville SSP port as a SPI master + controller. The driver can be configured to use any SSP port and + additional documentation can be found a Documentation/spi/pxa2xx. + +config SPI_PXA2XX_PCI + def_bool SPI_PXA2XX && X86_32 && PCI config SPI_S3C24XX tristate "Samsung S3C24XX series SPI" diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 8bc1a5abac1f..bdc4c400fd71 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_SPI_GPIO) += spi_gpio.o obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o +obj-$(CONFIG_SPI_PXA2XX_PCI) += pxa2xx_spi_pci.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 98d9c8b09182..ed212c2583ab 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/drivers/spi/pxa2xx_spi_pci.c b/drivers/spi/pxa2xx_spi_pci.c new file mode 100644 index 000000000000..351d8a375b57 --- /dev/null +++ b/drivers/spi/pxa2xx_spi_pci.c @@ -0,0 +1,201 @@ +/* + * CE4100's SPI device is more or less the same one as found on PXA + * + */ +#include +#include +#include +#include + +struct awesome_struct { + struct ssp_device ssp; + struct platform_device spi_pdev; + struct pxa2xx_spi_master spi_pdata; +}; + +static DEFINE_MUTEX(ssp_lock); +static LIST_HEAD(ssp_list); + +struct ssp_device *pxa_ssp_request(int port, const char *label) +{ + struct ssp_device *ssp = NULL; + + mutex_lock(&ssp_lock); + + list_for_each_entry(ssp, &ssp_list, node) { + if (ssp->port_id == port && ssp->use_count == 0) { + ssp->use_count++; + ssp->label = label; + break; + } + } + + mutex_unlock(&ssp_lock); + + if (&ssp->node == &ssp_list) + return NULL; + + return ssp; +} +EXPORT_SYMBOL_GPL(pxa_ssp_request); + +void pxa_ssp_free(struct ssp_device *ssp) +{ + mutex_lock(&ssp_lock); + if (ssp->use_count) { + ssp->use_count--; + ssp->label = NULL; + } else + dev_err(&ssp->pdev->dev, "device already free\n"); + mutex_unlock(&ssp_lock); +} +EXPORT_SYMBOL_GPL(pxa_ssp_free); + +static void plat_dev_release(struct device *dev) +{ + struct awesome_struct *as = container_of(dev, + struct awesome_struct, spi_pdev.dev); + + of_device_node_put(&as->spi_pdev.dev); +} + +static int __devinit ce4100_spi_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + int ret; + resource_size_t phys_beg; + resource_size_t phys_len; + struct awesome_struct *spi_info; + struct platform_device *pdev; + struct pxa2xx_spi_master *spi_pdata; + struct ssp_device *ssp; + + ret = pci_enable_device(dev); + if (ret) + return ret; + + phys_beg = pci_resource_start(dev, 0); + phys_len = pci_resource_len(dev, 0); + + if (!request_mem_region(phys_beg, phys_len, + "CE4100 SPI")) { + dev_err(&dev->dev, "Can't request register space.\n"); + ret = -EBUSY; + return ret; + } + + spi_info = kzalloc(sizeof(*spi_info), GFP_KERNEL); + if (!spi_info) { + ret = -ENOMEM; + goto err_kz; + } + ssp = &spi_info->ssp; + pdev = &spi_info->spi_pdev; + spi_pdata = &spi_info->spi_pdata; + + pdev->name = "pxa2xx-spi"; + pdev->id = dev->devfn; + pdev->dev.parent = &dev->dev; + pdev->dev.platform_data = &spi_info->spi_pdata; + +#ifdef CONFIG_OF + pdev->dev.of_node = dev->dev.of_node; +#endif + pdev->dev.release = plat_dev_release; + + spi_pdata->num_chipselect = dev->devfn; + + ssp->phys_base = pci_resource_start(dev, 0); + ssp->mmio_base = ioremap(phys_beg, phys_len); + if (!ssp->mmio_base) { + dev_err(&pdev->dev, "failed to ioremap() registers\n"); + ret = -EIO; + goto err_remap; + } + ssp->irq = dev->irq; + ssp->port_id = pdev->id; + ssp->type = PXA25x_SSP; + + mutex_lock(&ssp_lock); + list_add(&ssp->node, &ssp_list); + mutex_unlock(&ssp_lock); + + pci_set_drvdata(dev, spi_info); + + ret = platform_device_register(pdev); + if (ret) + goto err_dev_add; + + return ret; + +err_dev_add: + pci_set_drvdata(dev, NULL); + mutex_lock(&ssp_lock); + list_del(&ssp->node); + mutex_unlock(&ssp_lock); + iounmap(ssp->mmio_base); + +err_remap: + kfree(spi_info); + +err_kz: + release_mem_region(phys_beg, phys_len); + + return ret; +} + +static void __devexit ce4100_spi_remove(struct pci_dev *dev) +{ + struct awesome_struct *spi_info; + struct platform_device *pdev; + struct ssp_device *ssp; + + spi_info = pci_get_drvdata(dev); + + ssp = &spi_info->ssp; + pdev = &spi_info->spi_pdev; + + platform_device_unregister(pdev); + + iounmap(ssp->mmio_base); + release_mem_region(pci_resource_start(dev, 0), + pci_resource_len(dev, 0)); + + mutex_lock(&ssp_lock); + list_del(&ssp->node); + mutex_unlock(&ssp_lock); + + pci_set_drvdata(dev, NULL); + pci_disable_device(dev); + kfree(spi_info); +} + +static struct pci_device_id ce4100_spi_devices[] __devinitdata = { + + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2e6a) }, + { }, +}; +MODULE_DEVICE_TABLE(pci, ce4100_spi_devices); + +static struct pci_driver ce4100_spi_driver = { + .name = "ce4100_spi", + .id_table = ce4100_spi_devices, + .probe = ce4100_spi_probe, + .remove = __devexit_p(ce4100_spi_remove), +}; + +static int __init ce4100_spi_init(void) +{ + return pci_register_driver(&ce4100_spi_driver); +} +module_init(ce4100_spi_init); + +static void __exit ce4100_spi_exit(void) +{ + pci_unregister_driver(&ce4100_spi_driver); +} +module_exit(ce4100_spi_exit); + +MODULE_DESCRIPTION("CE4100 PCI-SPI glue code for PXA's driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sebastian Andrzej Siewior "); diff --git a/include/linux/spi/pxa2xx_spi.h b/include/linux/spi/pxa2xx_spi.h index 471ed6889113..d3e1075f7b60 100644 --- a/include/linux/spi/pxa2xx_spi.h +++ b/include/linux/spi/pxa2xx_spi.h @@ -19,7 +19,6 @@ #define __linux_pxa2xx_spi_h #include -#include #define PXA2XX_CS_ASSERT (0x01) #define PXA2XX_CS_DEASSERT (0x02) @@ -44,6 +43,110 @@ struct pxa2xx_spi_chip { void (*cs_control)(u32 command); }; +#ifdef CONFIG_ARCH_PXA + +#include +#include + extern void pxa2xx_set_spi_info(unsigned id, struct pxa2xx_spi_master *info); +#else +/* + * This is the implemtation for CE4100 on x86. ARM defines them in mach/ or + * plat/ include path. + * The CE4100 does not provide DMA support. This bits are here to let the driver + * compile and will never be used. Maybe we get DMA support at a later point in + * time. + */ + +#define DCSR(n) (n) +#define DSADR(n) (n) +#define DTADR(n) (n) +#define DCMD(n) (n) +#define DRCMR(n) (n) + +#define DCSR_RUN (1 << 31) /* Run Bit */ +#define DCSR_NODESC (1 << 30) /* No-Descriptor Fetch */ +#define DCSR_STOPIRQEN (1 << 29) /* Stop Interrupt Enable */ +#define DCSR_REQPEND (1 << 8) /* Request Pending (read-only) */ +#define DCSR_STOPSTATE (1 << 3) /* Stop State (read-only) */ +#define DCSR_ENDINTR (1 << 2) /* End Interrupt */ +#define DCSR_STARTINTR (1 << 1) /* Start Interrupt */ +#define DCSR_BUSERR (1 << 0) /* Bus Error Interrupt */ + +#define DCSR_EORIRQEN (1 << 28) /* End of Receive Interrupt Enable */ +#define DCSR_EORJMPEN (1 << 27) /* Jump to next descriptor on EOR */ +#define DCSR_EORSTOPEN (1 << 26) /* STOP on an EOR */ +#define DCSR_SETCMPST (1 << 25) /* Set Descriptor Compare Status */ +#define DCSR_CLRCMPST (1 << 24) /* Clear Descriptor Compare Status */ +#define DCSR_CMPST (1 << 10) /* The Descriptor Compare Status */ +#define DCSR_EORINTR (1 << 9) /* The end of Receive */ + +#define DRCMR_MAPVLD (1 << 7) /* Map Valid */ +#define DRCMR_CHLNUM 0x1f /* mask for Channel Number */ + +#define DDADR_DESCADDR 0xfffffff0 /* Address of next descriptor */ +#define DDADR_STOP (1 << 0) /* Stop */ + +#define DCMD_INCSRCADDR (1 << 31) /* Source Address Increment Setting. */ +#define DCMD_INCTRGADDR (1 << 30) /* Target Address Increment Setting. */ +#define DCMD_FLOWSRC (1 << 29) /* Flow Control by the source. */ +#define DCMD_FLOWTRG (1 << 28) /* Flow Control by the target. */ +#define DCMD_STARTIRQEN (1 << 22) /* Start Interrupt Enable */ +#define DCMD_ENDIRQEN (1 << 21) /* End Interrupt Enable */ +#define DCMD_ENDIAN (1 << 18) /* Device Endian-ness. */ +#define DCMD_BURST8 (1 << 16) /* 8 byte burst */ +#define DCMD_BURST16 (2 << 16) /* 16 byte burst */ +#define DCMD_BURST32 (3 << 16) /* 32 byte burst */ +#define DCMD_WIDTH1 (1 << 14) /* 1 byte width */ +#define DCMD_WIDTH2 (2 << 14) /* 2 byte width (HalfWord) */ +#define DCMD_WIDTH4 (3 << 14) /* 4 byte width (Word) */ +#define DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */ + +/* + * Descriptor structure for PXA's DMA engine + * Note: this structure must always be aligned to a 16-byte boundary. + */ + +typedef enum { + DMA_PRIO_HIGH = 0, + DMA_PRIO_MEDIUM = 1, + DMA_PRIO_LOW = 2 +} pxa_dma_prio; + +/* + * DMA registration + */ + +static inline int pxa_request_dma(char *name, + pxa_dma_prio prio, + void (*irq_handler)(int, void *), + void *data) +{ + return -ENODEV; +} + +static inline void pxa_free_dma(int dma_ch) +{ +} + +/* + * The CE4100 does not have the clk framework implemented and SPI clock can + * not be switched on/off or the divider changed. + */ +static inline void clk_disable(struct clk *clk) +{ +} + +static inline int clk_enable(struct clk *clk) +{ + return 0; +} + +static inline unsigned long clk_get_rate(struct clk *clk) +{ + return 3686400; +} + +#endif #endif -- cgit v1.2.3-71-gd317 From d0777f2c3eda180e3fc549e0efbe741014f17689 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Nov 2010 17:12:16 -0800 Subject: spi/pxa2xx: Consider CE4100's FIFO depth For PXA the default threshold is FIFO_DEPTH / 2. Adjust this value for CE4100. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- drivers/spi/pxa2xx_spi.c | 2 -- include/linux/pxa2xx_ssp.h | 32 ++++++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index ed212c2583ab..81cfbbc58e94 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -43,8 +43,6 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define MAX_BUSES 3 -#define RX_THRESH_DFLT 8 -#define TX_THRESH_DFLT 8 #define TIMOUT_DFLT 1000 #define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 84465d4a51b7..c3aa334cbb9b 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -71,10 +71,6 @@ #define SSCR1_SPO (1 << 3) /* Motorola SPI SSPSCLK polarity setting */ #define SSCR1_SPH (1 << 4) /* Motorola SPI SSPSCLK phase setting */ #define SSCR1_MWDS (1 << 5) /* Microwire Transmit Data Size */ -#define SSCR1_TFT (0x000003c0) /* Transmit FIFO Threshold (mask) */ -#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..16] */ -#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */ -#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */ #define SSSR_TNF (1 << 2) /* Transmit FIFO Not Full */ #define SSSR_RNE (1 << 3) /* Receive FIFO Not Empty */ @@ -82,8 +78,32 @@ #define SSSR_TFS (1 << 5) /* Transmit FIFO Service Request */ #define SSSR_RFS (1 << 6) /* Receive FIFO Service Request */ #define SSSR_ROR (1 << 7) /* Receive FIFO Overrun */ -#define SSSR_TFL_MASK (0xf << 8) /* Transmit FIFO Level mask */ -#define SSSR_RFL_MASK (0xf << 12) /* Receive FIFO Level mask */ + +#ifdef CONFIG_ARCH_PXA +#define RX_THRESH_DFLT 8 +#define TX_THRESH_DFLT 8 + +#define SSSR_TFL_MASK (0xf << 8) /* Transmit FIFO Level mask */ +#define SSSR_RFL_MASK (0xf << 12) /* Receive FIFO Level mask */ + +#define SSCR1_TFT (0x000003c0) /* Transmit FIFO Threshold (mask) */ +#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..16] */ +#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */ +#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */ + +#else + +#define RX_THRESH_DFLT 2 +#define TX_THRESH_DFLT 2 + +#define SSSR_TFL_MASK (0x3 << 8) /* Transmit FIFO Level mask */ +#define SSSR_RFL_MASK (0x3 << 12) /* Receive FIFO Level mask */ + +#define SSCR1_TFT (0x000000c0) /* Transmit FIFO Threshold (mask) */ +#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..4] */ +#define SSCR1_RFT (0x00000c00) /* Receive FIFO Threshold (mask) */ +#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..4] */ +#endif /* extra bits in PXA255, PXA26x and PXA27x SSP ports */ #define SSCR0_TISSP (1 << 4) /* TI Sync Serial Protocol */ -- cgit v1.2.3-71-gd317 From 2a8626a9e2d86d114a2d9f813a1acebf9d53dd10 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Nov 2010 17:12:17 -0800 Subject: spi/pxa2xx: Add chipselect support for Sodaville The SPI core on Sodaville supports chip selects. Its configuration moved into the SSSR register at bit 0 and 1. Thus Sodaville can be hooked up with up to 4 devices. This patch ensures that the bits which are otherwiese reserved are only touched on Sodaville and not on any other PXAs. Also it makes sure that the status register does not lose the CS information while clearing the ROR bit. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dirk Brandewie --- drivers/spi/pxa2xx_spi.c | 93 +++++++++++++++++++++++++++++++++------------- include/linux/pxa2xx_ssp.h | 2 + 2 files changed, 70 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 81cfbbc58e94..a54685bb7e53 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -163,7 +163,10 @@ struct chip_data { u8 enable_dma; u8 bits_per_word; u32 speed_hz; - int gpio_cs; + union { + int gpio_cs; + unsigned int frm; + }; int gpio_cs_inverted; int (*write)(struct driver_data *drv_data); int (*read)(struct driver_data *drv_data); @@ -176,6 +179,11 @@ static void cs_assert(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; + if (drv_data->ssp_type == CE4100_SSP) { + write_SSSR(drv_data->cur_chip->frm, drv_data->ioaddr); + return; + } + if (chip->cs_control) { chip->cs_control(PXA2XX_CS_ASSERT); return; @@ -189,6 +197,9 @@ static void cs_deassert(struct driver_data *drv_data) { struct chip_data *chip = drv_data->cur_chip; + if (drv_data->ssp_type == CE4100_SSP) + return; + if (chip->cs_control) { chip->cs_control(PXA2XX_CS_DEASSERT); return; @@ -198,6 +209,25 @@ static void cs_deassert(struct driver_data *drv_data) gpio_set_value(chip->gpio_cs, !chip->gpio_cs_inverted); } +static void write_SSSR_CS(struct driver_data *drv_data, u32 val) +{ + void __iomem *reg = drv_data->ioaddr; + + if (drv_data->ssp_type == CE4100_SSP) + val |= read_SSSR(reg) & SSSR_ALT_FRM_MASK; + + write_SSSR(val, reg); +} + +static int pxa25x_ssp_comp(struct driver_data *drv_data) +{ + if (drv_data->ssp_type == PXA25x_SSP) + return 1; + if (drv_data->ssp_type == CE4100_SSP) + return 1; + return 0; +} + static int flush(struct driver_data *drv_data) { unsigned long limit = loops_per_jiffy << 1; @@ -209,7 +239,7 @@ static int flush(struct driver_data *drv_data) read_SSDR(reg); } } while ((read_SSSR(reg) & SSSR_BSY) && --limit); - write_SSSR(SSSR_ROR, reg); + write_SSSR_CS(drv_data, SSSR_ROR); return limit; } @@ -502,9 +532,9 @@ static void 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(drv_data->clear_sr, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); flush(drv_data); write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); @@ -524,7 +554,7 @@ static void dma_transfer_complete(struct driver_data *drv_data) /* Clear and disable interrupts on SSP and DMA channels*/ write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); - write_SSSR(drv_data->clear_sr, reg); + 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; @@ -617,7 +647,7 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data) /* Clear and disable timeout interrupt, do the rest in * dma_transfer_complete */ - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); /* finish this transfer, start the next */ @@ -635,9 +665,9 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg) void __iomem *reg = drv_data->ioaddr; /* Stop and reset SSP */ - write_SSSR(drv_data->clear_sr, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); flush(drv_data); write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); @@ -653,9 +683,9 @@ static void int_transfer_complete(struct driver_data *drv_data) void __iomem *reg = drv_data->ioaddr; /* Stop SSP */ - write_SSSR(drv_data->clear_sr, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); /* Update total byte transfered return count actual bytes read */ @@ -711,7 +741,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) if (drv_data->tx == drv_data->tx_end) { write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg); /* PXA25x_SSP has no timeout, read trailing bytes */ - if (drv_data->ssp_type == PXA25x_SSP) { + if (pxa25x_ssp_comp(drv_data)) { if (!wait_ssp_rx_stall(reg)) { int_error_stop(drv_data, "interrupt_transfer: " @@ -754,9 +784,9 @@ static irqreturn_t ssp_int(int irq, void *dev_id) write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, reg); - write_SSSR(drv_data->clear_sr, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); dev_err(&drv_data->pdev->dev, "bad message state " "in interrupt handler\n"); @@ -869,7 +899,7 @@ static unsigned int ssp_get_clk_div(struct ssp_device *ssp, int rate) { unsigned long ssp_clk = clk_get_rate(ssp->clk); - if (ssp->type == PXA25x_SSP) + if (ssp->type == PXA25x_SSP || ssp->type == CE4100_SSP) return ((ssp_clk / (2 * rate) - 1) & 0xff) << 8; else return ((ssp_clk / rate - 1) & 0xfff) << 8; @@ -1095,7 +1125,7 @@ static void pump_transfers(unsigned long data) /* Clear status */ cr1 = chip->cr1 | chip->threshold | drv_data->int_cr1; - write_SSSR(drv_data->clear_sr, reg); + write_SSSR_CS(drv_data, drv_data->clear_sr); } /* see if we need to reload the config registers */ @@ -1105,7 +1135,7 @@ static void pump_transfers(unsigned long data) /* stop the SSP, and update the other bits */ write_SSCR0(cr0 & ~SSCR0_SSE, reg); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(chip->timeout, reg); /* first set CR1 without interrupt and service enables */ write_SSCR1(cr1 & SSCR1_CHANGE_MASK, reg); @@ -1113,7 +1143,7 @@ static void pump_transfers(unsigned long data) write_SSCR0(cr0, reg); } else { - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(chip->timeout, reg); } @@ -1240,14 +1270,13 @@ static int setup(struct spi_device *spi) uint tx_thres = TX_THRESH_DFLT; uint rx_thres = RX_THRESH_DFLT; - if (drv_data->ssp_type != PXA25x_SSP + if (!pxa25x_ssp_comp(drv_data) && (spi->bits_per_word < 4 || spi->bits_per_word > 32)) { dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d " "b/w not 4-32 for type non-PXA25x_SSP\n", drv_data->ssp_type, spi->bits_per_word); return -EINVAL; - } - else if (drv_data->ssp_type == PXA25x_SSP + } else if (pxa25x_ssp_comp(drv_data) && (spi->bits_per_word < 4 || spi->bits_per_word > 16)) { dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d " @@ -1266,7 +1295,17 @@ static int setup(struct spi_device *spi) return -ENOMEM; } - chip->gpio_cs = -1; + if (drv_data->ssp_type == CE4100_SSP) { + if (spi->chip_select > 4) { + dev_err(&spi->dev, "failed setup: " + "cs number must not be > 4.\n"); + kfree(chip); + return -EINVAL; + } + + chip->frm = spi->chip_select; + } else + chip->gpio_cs = -1; chip->enable_dma = 0; chip->timeout = TIMOUT_DFLT; chip->dma_burst_size = drv_data->master_info->enable_dma ? @@ -1322,7 +1361,7 @@ static int setup(struct spi_device *spi) | (((spi->mode & SPI_CPOL) != 0) ? SSCR1_SPO : 0); /* NOTE: PXA25x_SSP _could_ use external clocking ... */ - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) dev_dbg(&spi->dev, "%ld Hz actual, %s\n", clk_get_rate(ssp->clk) / (1 + ((chip->cr0 & SSCR0_SCR(0xfff)) >> 8)), @@ -1357,17 +1396,21 @@ static int setup(struct spi_device *spi) spi_set_ctldata(spi, chip); + if (drv_data->ssp_type == CE4100_SSP) + return 0; + return setup_cs(spi, chip, chip_info); } static void cleanup(struct spi_device *spi) { struct chip_data *chip = spi_get_ctldata(spi); + struct driver_data *drv_data = spi_master_get_devdata(spi->master); if (!chip) return; - if (gpio_is_valid(chip->gpio_cs)) + if (drv_data->ssp_type != CE4100_SSP && gpio_is_valid(chip->gpio_cs)) gpio_free(chip->gpio_cs); kfree(chip); @@ -1507,7 +1550,7 @@ static int __devinit pxa2xx_spi_probe(struct platform_device *pdev) drv_data->ioaddr = ssp->mmio_base; drv_data->ssdr_physical = ssp->phys_base + SSDR; - if (ssp->type == PXA25x_SSP) { + if (pxa25x_ssp_comp(drv_data)) { drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE; drv_data->dma_cr1 = 0; drv_data->clear_sr = SSSR_ROR; @@ -1569,7 +1612,7 @@ static int __devinit pxa2xx_spi_probe(struct platform_device *pdev) | SSCR0_Motorola | SSCR0_DataSize(8), drv_data->ioaddr); - if (drv_data->ssp_type != PXA25x_SSP) + if (!pxa25x_ssp_comp(drv_data)) write_SSTO(0, drv_data->ioaddr); write_SSPSP(0, drv_data->ioaddr); diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index c3aa334cbb9b..2f691e4e6222 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -72,6 +72,7 @@ #define SSCR1_SPH (1 << 4) /* Motorola SPI SSPSCLK phase setting */ #define SSCR1_MWDS (1 << 5) /* Microwire Transmit Data Size */ +#define SSSR_ALT_FRM_MASK 3 /* Masks the SFRM signal number */ #define SSSR_TNF (1 << 2) /* Transmit FIFO Not Full */ #define SSSR_RNE (1 << 3) /* Receive FIFO Not Empty */ #define SSSR_BSY (1 << 4) /* SSP Busy */ @@ -160,6 +161,7 @@ enum pxa_ssp_type { PXA25x_NSSP, /* pxa 255, 26x (including ASSP) */ PXA27x_SSP, PXA168_SSP, + CE4100_SSP, }; struct ssp_device { -- cgit v1.2.3-71-gd317 From 7063c0d942a1af2993531fbe52b4c74c1db818c4 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Fri, 24 Dec 2010 13:59:11 +0800 Subject: spi/dw_spi: add DMA support dw_spi driver in upstream only supports PIO mode, and this patch will support it to cowork with the Designware dma controller used on Intel Moorestown platform, at the same time it provides a general framework to support dw_spi core to cowork with dma controllers on other platforms It has been tested with a Option GTM501L 3G modem and Infenion 60x60 modem. To use DMA mode, DMA controller 2 of Moorestown has to be enabled Also change the dma interface suggested by Linus Walleij. Acked-by: Linus Walleij Signed-off-by: Feng Tang [Typo fix and renames to match intel_mid_dma renaming] Signed-off-by: Vinod Koul Signed-off-by: Alan Cox Signed-off-by: Grant Likely --- drivers/spi/Kconfig | 4 + drivers/spi/Makefile | 3 +- drivers/spi/dw_spi.c | 33 ++++--- drivers/spi/dw_spi_mid.c | 223 +++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/dw_spi_pci.c | 20 ++-- include/linux/spi/dw_spi.h | 24 ++++- 6 files changed, 283 insertions(+), 24 deletions(-) create mode 100644 drivers/spi/dw_spi_mid.c (limited to 'include/linux') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 665d03d4e022..caa2e84cc0a6 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -382,6 +382,10 @@ config SPI_DW_PCI tristate "PCI interface driver for DW SPI core" depends on SPI_DESIGNWARE && PCI +config SPI_DW_MID_DMA + bool "DMA support for DW SPI controller on Intel Moorestown platform" + depends on SPI_DW_PCI && INTEL_MID_DMAC + config SPI_DW_MMIO tristate "Memory-mapped io interface driver for DW SPI core" depends on SPI_DESIGNWARE && HAVE_CLK diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 02dad4ae412d..7ec375b6c0c3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -17,7 +17,8 @@ obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += coldfire_qspi.o obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o obj-$(CONFIG_SPI_DESIGNWARE) += dw_spi.o -obj-$(CONFIG_SPI_DW_PCI) += dw_spi_pci.o +obj-$(CONFIG_SPI_DW_PCI) += dw_spi_midpci.o +dw_spi_midpci-objs := dw_spi_pci.o dw_spi_mid.o obj-$(CONFIG_SPI_DW_MMIO) += dw_spi_mmio.o obj-$(CONFIG_SPI_EP93XX) += ep93xx_spi.o obj-$(CONFIG_SPI_GPIO) += spi_gpio.o diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index b50bf5ba873f..497ecb3ab83f 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -288,8 +288,10 @@ static void *next_transfer(struct dw_spi *dws) */ static int map_dma_buffers(struct dw_spi *dws) { - if (!dws->cur_msg->is_dma_mapped || !dws->dma_inited - || !dws->cur_chip->enable_dma) + if (!dws->cur_msg->is_dma_mapped + || !dws->dma_inited + || !dws->cur_chip->enable_dma + || !dws->dma_ops) return 0; if (dws->cur_transfer->tx_dma) @@ -341,7 +343,7 @@ static void int_error_stop(struct dw_spi *dws, const char *msg) tasklet_schedule(&dws->pump_transfers); } -static void transfer_complete(struct dw_spi *dws) +void dw_spi_xfer_done(struct dw_spi *dws) { /* Update total byte transfered return count actual bytes read */ dws->cur_msg->actual_length += dws->len; @@ -356,6 +358,7 @@ static void transfer_complete(struct dw_spi *dws) } else tasklet_schedule(&dws->pump_transfers); } +EXPORT_SYMBOL_GPL(dw_spi_xfer_done); static irqreturn_t interrupt_transfer(struct dw_spi *dws) { @@ -387,7 +390,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) if (dws->tx_end > dws->tx) spi_umask_intr(dws, SPI_INT_TXEI); else - transfer_complete(dws); + dw_spi_xfer_done(dws); } return IRQ_HANDLED; @@ -422,11 +425,7 @@ static void poll_transfer(struct dw_spi *dws) */ dws->read(dws); - transfer_complete(dws); -} - -static void dma_transfer(struct dw_spi *dws, int cs_change) -{ + dw_spi_xfer_done(dws); } static void pump_transfers(unsigned long data) @@ -608,7 +607,7 @@ static void pump_transfers(unsigned long data) } if (dws->dma_mapped) - dma_transfer(dws, cs_change); + dws->dma_ops->dma_transfer(dws, cs_change); if (chip->poll_mode) poll_transfer(dws); @@ -904,11 +903,17 @@ int __devinit dw_spi_add_host(struct dw_spi *dws) master->setup = dw_spi_setup; master->transfer = dw_spi_transfer; - dws->dma_inited = 0; - /* Basic HW init */ spi_hw_init(dws); + if (dws->dma_ops && dws->dma_ops->dma_init) { + ret = dws->dma_ops->dma_init(dws); + if (ret) { + dev_warn(&master->dev, "DMA init failed\n"); + dws->dma_inited = 0; + } + } + /* Initial and start queue */ ret = init_queue(dws); if (ret) { @@ -933,6 +938,8 @@ int __devinit dw_spi_add_host(struct dw_spi *dws) err_queue_alloc: destroy_queue(dws); + if (dws->dma_ops && dws->dma_ops->dma_exit) + dws->dma_ops->dma_exit(dws); err_diable_hw: spi_enable_chip(dws, 0); free_irq(dws->irq, dws); @@ -957,6 +964,8 @@ void __devexit dw_spi_remove_host(struct dw_spi *dws) dev_err(&dws->master->dev, "dw_spi_remove: workqueue will not " "complete, message memory not freed\n"); + if (dws->dma_ops && dws->dma_ops->dma_exit) + dws->dma_ops->dma_exit(dws); spi_enable_chip(dws, 0); /* Disable clk */ spi_set_clk(dws, 0); diff --git a/drivers/spi/dw_spi_mid.c b/drivers/spi/dw_spi_mid.c new file mode 100644 index 000000000000..c91c966e0717 --- /dev/null +++ b/drivers/spi/dw_spi_mid.c @@ -0,0 +1,223 @@ +/* + * dw_spi_mid.c - special handling for DW core on Intel MID platform + * + * Copyright (c) 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SPI_DW_MID_DMA +#include +#include + +struct mid_dma { + struct intel_mid_dma_slave dmas_tx; + struct intel_mid_dma_slave dmas_rx; +}; + +static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) +{ + struct dw_spi *dws = param; + + return dws->dmac && (&dws->dmac->dev == chan->device->dev); +} + +static int mid_spi_dma_init(struct dw_spi *dws) +{ + struct mid_dma *dw_dma = dws->dma_priv; + struct intel_mid_dma_slave *rxs, *txs; + dma_cap_mask_t mask; + + /* + * Get pci device for DMA controller, currently it could only + * be the DMA controller of either Moorestown or Medfield + */ + dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0813, NULL); + if (!dws->dmac) + dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL); + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* 1. Init rx channel */ + dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws); + if (!dws->rxchan) + goto err_exit; + rxs = &dw_dma->dmas_rx; + rxs->hs_mode = LNW_DMA_HW_HS; + rxs->cfg_mode = LNW_DMA_PER_TO_MEM; + dws->rxchan->private = rxs; + + /* 2. Init tx channel */ + dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws); + if (!dws->txchan) + goto free_rxchan; + txs = &dw_dma->dmas_tx; + txs->hs_mode = LNW_DMA_HW_HS; + txs->cfg_mode = LNW_DMA_MEM_TO_PER; + dws->txchan->private = txs; + + dws->dma_inited = 1; + return 0; + +free_rxchan: + dma_release_channel(dws->rxchan); +err_exit: + return -1; + +} + +static void mid_spi_dma_exit(struct dw_spi *dws) +{ + dma_release_channel(dws->txchan); + dma_release_channel(dws->rxchan); +} + +/* + * dws->dma_chan_done is cleared before the dma transfer starts, + * callback for rx/tx channel will each increment it by 1. + * Reaching 2 means the whole spi transaction is done. + */ +static void dw_spi_dma_done(void *arg) +{ + struct dw_spi *dws = arg; + + if (++dws->dma_chan_done != 2) + return; + dw_spi_xfer_done(dws); +} + +static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) +{ + struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL; + struct dma_chan *txchan, *rxchan; + struct dma_slave_config txconf, rxconf; + u16 dma_ctrl = 0; + + /* 1. setup DMA related registers */ + if (cs_change) { + spi_enable_chip(dws, 0); + dw_writew(dws, dmardlr, 0xf); + dw_writew(dws, dmatdlr, 0x10); + if (dws->tx_dma) + dma_ctrl |= 0x2; + if (dws->rx_dma) + dma_ctrl |= 0x1; + dw_writew(dws, dmacr, dma_ctrl); + spi_enable_chip(dws, 1); + } + + dws->dma_chan_done = 0; + txchan = dws->txchan; + rxchan = dws->rxchan; + + /* 2. Prepare the TX dma transfer */ + txconf.direction = DMA_TO_DEVICE; + txconf.dst_addr = dws->dma_addr; + txconf.dst_maxburst = LNW_DMA_MSIZE_16; + txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + txchan->device->device_control(txchan, DMA_SLAVE_CONFIG, + (unsigned long) &txconf); + + memset(&dws->tx_sgl, 0, sizeof(dws->tx_sgl)); + dws->tx_sgl.dma_address = dws->tx_dma; + dws->tx_sgl.length = dws->len; + + txdesc = txchan->device->device_prep_slave_sg(txchan, + &dws->tx_sgl, + 1, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP); + txdesc->callback = dw_spi_dma_done; + txdesc->callback_param = dws; + + /* 3. Prepare the RX dma transfer */ + rxconf.direction = DMA_FROM_DEVICE; + rxconf.src_addr = dws->dma_addr; + rxconf.src_maxburst = LNW_DMA_MSIZE_16; + rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG, + (unsigned long) &rxconf); + + memset(&dws->rx_sgl, 0, sizeof(dws->rx_sgl)); + dws->rx_sgl.dma_address = dws->rx_dma; + dws->rx_sgl.length = dws->len; + + rxdesc = rxchan->device->device_prep_slave_sg(rxchan, + &dws->rx_sgl, + 1, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP); + rxdesc->callback = dw_spi_dma_done; + rxdesc->callback_param = dws; + + /* rx must be started before tx due to spi instinct */ + rxdesc->tx_submit(rxdesc); + txdesc->tx_submit(txdesc); + return 0; +} + +static struct dw_spi_dma_ops mid_dma_ops = { + .dma_init = mid_spi_dma_init, + .dma_exit = mid_spi_dma_exit, + .dma_transfer = mid_spi_dma_transfer, +}; +#endif + +/* Some specific info for SPI0 controller on Moorestown */ + +/* HW info for MRST CLk Control Unit, one 32b reg */ +#define MRST_SPI_CLK_BASE 100000000 /* 100m */ +#define MRST_CLK_SPI0_REG 0xff11d86c +#define CLK_SPI_BDIV_OFFSET 0 +#define CLK_SPI_BDIV_MASK 0x00000007 +#define CLK_SPI_CDIV_OFFSET 9 +#define CLK_SPI_CDIV_MASK 0x00000e00 +#define CLK_SPI_DISABLE_OFFSET 8 + +int dw_spi_mid_init(struct dw_spi *dws) +{ + u32 *clk_reg, clk_cdiv; + + clk_reg = ioremap_nocache(MRST_CLK_SPI0_REG, 16); + if (!clk_reg) + return -ENOMEM; + + /* get SPI controller operating freq info */ + clk_cdiv = (readl(clk_reg) & CLK_SPI_CDIV_MASK) >> CLK_SPI_CDIV_OFFSET; + dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1); + iounmap(clk_reg); + + dws->num_cs = 16; + dws->fifo_len = 40; /* FIFO has 40 words buffer */ + +#ifdef CONFIG_SPI_DW_MID_DMA + dws->dma_priv = kzalloc(sizeof(struct mid_dma), GFP_KERNEL); + if (!dws->dma_priv) + return -ENOMEM; + dws->dma_ops = &mid_dma_ops; +#endif + return 0; +} diff --git a/drivers/spi/dw_spi_pci.c b/drivers/spi/dw_spi_pci.c index 1f52755dc878..49ec3aa1219f 100644 --- a/drivers/spi/dw_spi_pci.c +++ b/drivers/spi/dw_spi_pci.c @@ -1,5 +1,5 @@ /* - * mrst_spi_pci.c - PCI interface driver for DW SPI Core + * dw_spi_pci.c - PCI interface driver for DW SPI Core * * Copyright (c) 2009, Intel Corporation. * @@ -26,8 +26,8 @@ #define DRIVER_NAME "dw_spi_pci" struct dw_spi_pci { - struct pci_dev *pdev; - struct dw_spi dws; + struct pci_dev *pdev; + struct dw_spi dws; }; static int __devinit spi_pci_probe(struct pci_dev *pdev, @@ -72,9 +72,17 @@ static int __devinit spi_pci_probe(struct pci_dev *pdev, dws->parent_dev = &pdev->dev; dws->bus_num = 0; dws->num_cs = 4; - dws->max_freq = 25000000; /* for Moorestwon */ dws->irq = pdev->irq; - dws->fifo_len = 40; /* FIFO has 40 words buffer */ + + /* + * Specific handling for Intel MID paltforms, like dma setup, + * clock rate, FIFO depth. + */ + if (pdev->device == 0x0800) { + ret = dw_spi_mid_init(dws); + if (ret) + goto err_unmap; + } ret = dw_spi_add_host(dws); if (ret) @@ -140,7 +148,7 @@ static int spi_resume(struct pci_dev *pdev) #endif static const struct pci_device_id pci_ids[] __devinitdata = { - /* Intel Moorestown platform SPI controller 0 */ + /* Intel MID platform SPI controller 0 */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) }, {}, }; diff --git a/include/linux/spi/dw_spi.h b/include/linux/spi/dw_spi.h index c91302f3a257..6cd10f6ad472 100644 --- a/include/linux/spi/dw_spi.h +++ b/include/linux/spi/dw_spi.h @@ -1,5 +1,6 @@ #ifndef DW_SPI_HEADER_H #define DW_SPI_HEADER_H + #include /* Bit fields in CTRLR0 */ @@ -82,6 +83,13 @@ struct dw_spi_reg { though only low 16 bits matters */ } __packed; +struct dw_spi; +struct dw_spi_dma_ops { + int (*dma_init)(struct dw_spi *dws); + void (*dma_exit)(struct dw_spi *dws); + int (*dma_transfer)(struct dw_spi *dws, int cs_change); +}; + struct dw_spi { struct spi_master *master; struct spi_device *cur_dev; @@ -136,13 +144,15 @@ struct dw_spi { /* Dma info */ int dma_inited; struct dma_chan *txchan; + struct scatterlist tx_sgl; struct dma_chan *rxchan; - int txdma_done; - int rxdma_done; - u64 tx_param; - u64 rx_param; + struct scatterlist rx_sgl; + int dma_chan_done; struct device *dma_dev; - dma_addr_t dma_addr; + dma_addr_t dma_addr; /* phy address of the Data register */ + struct dw_spi_dma_ops *dma_ops; + void *dma_priv; /* platform relate info */ + struct pci_dev *dmac; /* Bus interface info */ void *priv; @@ -216,4 +226,8 @@ extern int dw_spi_add_host(struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws); extern int dw_spi_resume_host(struct dw_spi *dws); +extern void dw_spi_xfer_done(struct dw_spi *dws); + +/* platform related setup */ +extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */ #endif /* DW_SPI_HEADER_H */ -- cgit v1.2.3-71-gd317