diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 53 | ||||
-rw-r--r-- | drivers/spi/Makefile | 3 | ||||
-rw-r--r-- | drivers/spi/spi-altera.c | 34 | ||||
-rw-r--r-- | drivers/spi/spi-atmel.c | 36 | ||||
-rw-r--r-- | drivers/spi/spi-bcm2835.c | 10 | ||||
-rw-r--r-- | drivers/spi/spi-bfin-v3.c | 971 | ||||
-rw-r--r-- | drivers/spi/spi-bitbang.c | 260 | ||||
-rw-r--r-- | drivers/spi/spi-clps711x.c | 6 | ||||
-rw-r--r-- | drivers/spi/spi-davinci.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-efm32.c | 516 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-dspi.c | 557 | ||||
-rw-r--r-- | drivers/spi/spi-oc-tiny.c | 22 | ||||
-rw-r--r-- | drivers/spi/spi-orion.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-s3c64xx.c | 3 | ||||
-rw-r--r-- | drivers/spi/spi.c | 23 |
15 files changed, 2263 insertions, 234 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 89cbbabaff4..8aad2c1dc33 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -70,14 +70,14 @@ config SPI_ATH79 config SPI_ATMEL tristate "Atmel SPI Controller" - depends on (ARCH_AT91 || AVR32) + depends on (ARCH_AT91 || AVR32 || COMPILE_TEST) help This selects a driver for the Atmel SPI Controller, present on many AT32 (AVR32) and AT91 (ARM) chips. config SPI_BCM2835 tristate "BCM2835 SPI controller" - depends on ARCH_BCM2835 + depends on ARCH_BCM2835 || COMPILE_TEST help This selects a driver for the Broadcom BCM2835 SPI master. @@ -88,10 +88,17 @@ config SPI_BCM2835 config SPI_BFIN5XX tristate "SPI controller driver for ADI Blackfin5xx" - depends on BLACKFIN + depends on BLACKFIN && !BF60x help This is the SPI controller master driver for Blackfin 5xx processor. +config SPI_BFIN_V3 + tristate "SPI controller v3 for Blackfin" + depends on BF60x + help + This is the SPI controller v3 master driver + found on Blackfin 60x processor. + config SPI_BFIN_SPORT tristate "SPI bus via Blackfin SPORT" depends on BLACKFIN @@ -151,15 +158,22 @@ config SPI_COLDFIRE_QSPI config SPI_DAVINCI tristate "Texas Instruments DaVinci/DA8x/OMAP-L/AM1x SoC SPI controller" - depends on ARCH_DAVINCI + depends on ARCH_DAVINCI || ARCH_KEYSTONE select SPI_BITBANG select TI_EDMA help SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules. +config SPI_EFM32 + tristate "EFM32 SPI controller" + depends on OF && ARM && (ARCH_EFM32 || COMPILE_TEST) + select SPI_BITBANG + help + Driver for the spi controller found on Energy Micro's EFM32 SoCs. + config SPI_EP93XX tristate "Cirrus Logic EP93xx SPI controller" - depends on ARCH_EP93XX + depends on ARCH_EP93XX || COMPILE_TEST help This enables using the Cirrus EP93xx SPI controller in master mode. @@ -191,7 +205,7 @@ config SPI_GPIO config SPI_IMX tristate "Freescale i.MX SPI controllers" - depends on ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST select SPI_BITBANG default m if IMX_HAVE_PLATFORM_SPI_IMX help @@ -248,6 +262,13 @@ config SPI_FSL_SPI This also enables using the Aeroflex Gaisler GRLIB SPI controller in master mode. +config SPI_FSL_DSPI + tristate "Freescale DSPI controller" + select SPI_BITBANG + help + This enables support for the Freescale DSPI controller in master + mode. VF610 platform uses the controller. + config SPI_FSL_ESPI bool "Freescale eSPI controller" depends on FSL_SOC @@ -280,20 +301,20 @@ config SPI_OMAP_UWIRE config SPI_OMAP24XX tristate "McSPI driver for OMAP" - depends on ARCH_OMAP2PLUS + depends on ARCH_OMAP2PLUS || COMPILE_TEST help SPI master controller for OMAP24XX and later Multichannel SPI (McSPI) modules. config SPI_OMAP_100K tristate "OMAP SPI 100K" - depends on ARCH_OMAP850 || ARCH_OMAP730 + depends on ARCH_OMAP850 || ARCH_OMAP730 || COMPILE_TEST help OMAP SPI 100K master controller for omap7xx boards. config SPI_ORION tristate "Orion SPI master" - depends on PLAT_ORION + depends on PLAT_ORION || COMPILE_TEST help This enables using the SPI master controller on the Orion chips. @@ -341,7 +362,7 @@ config SPI_PXA2XX_PCI config SPI_RSPI tristate "Renesas RSPI controller" - depends on SUPERH + depends on SUPERH && SH_DMAE_BASE help SPI driver for Renesas RSPI blocks. @@ -385,7 +406,7 @@ config SPI_SH_MSIOF config SPI_SH tristate "SuperH SPI controller" - depends on SUPERH + depends on SUPERH || COMPILE_TEST help SPI driver for SuperH SPI blocks. @@ -398,7 +419,7 @@ config SPI_SH_SCI config SPI_SH_HSPI tristate "SuperH HSPI controller" - depends on ARCH_SHMOBILE + depends on ARCH_SHMOBILE || COMPILE_TEST help SPI driver for SuperH HSPI blocks. @@ -418,7 +439,7 @@ config SPI_MXS config SPI_TEGRA114 tristate "NVIDIA Tegra114 SPI Controller" - depends on ARCH_TEGRA && TEGRA20_APB_DMA + depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST help SPI driver for NVIDIA Tegra114 SPI Controller interface. This controller is different than the older SoCs SPI controller and also register interface @@ -426,7 +447,7 @@ config SPI_TEGRA114 config SPI_TEGRA20_SFLASH tristate "Nvidia Tegra20 Serial flash Controller" - depends on ARCH_TEGRA + depends on ARCH_TEGRA || COMPILE_TEST help SPI driver for Nvidia Tegra20 Serial flash Controller interface. The main usecase of this controller is to use spi flash as boot @@ -434,7 +455,7 @@ config SPI_TEGRA20_SFLASH config SPI_TEGRA20_SLINK tristate "Nvidia Tegra20/Tegra30 SLINK Controller" - depends on ARCH_TEGRA && TEGRA20_APB_DMA + depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST help SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface. @@ -457,7 +478,7 @@ config SPI_TOPCLIFF_PCH config SPI_TXX9 tristate "Toshiba TXx9 SPI controller" - depends on GPIOLIB && CPU_TX49XX + depends on GPIOLIB && (CPU_TX49XX || COMPILE_TEST) help SPI driver for Toshiba TXx9 MIPS SoCs diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 33f9c09561e..985d7bad793 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_SPI_AU1550) += spi-au1550.o obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o +obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o @@ -27,9 +28,11 @@ obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o +obj-$(CONFIG_SPI_EFM32) += spi-efm32.o obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o obj-$(CONFIG_SPI_FALCON) += spi-falcon.o obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o +obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index 8a6bb37910d..156eb888655 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -124,7 +124,7 @@ static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t) hw->tx = t->tx_buf; hw->rx = t->rx_buf; hw->count = 0; - hw->bytes_per_word = t->bits_per_word / 8; + hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8); hw->len = t->len / hw->bytes_per_word; if (hw->irq >= 0) { @@ -140,12 +140,12 @@ static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t) hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); } else { - /* send the first byte */ - writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA); - - while (1) { + while (hw->count < hw->len) { unsigned int rxd; + writel(hw_txbyte(hw, hw->count), + hw->base + ALTERA_SPI_TXDATA); + while (!(readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK)) cpu_relax(); @@ -164,14 +164,7 @@ static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t) } hw->count++; - - if (hw->count < hw->len) - writel(hw_txbyte(hw, hw->count), - hw->base + ALTERA_SPI_TXDATA); - else - break; } - } return hw->count * hw->bytes_per_word; @@ -234,15 +227,11 @@ static int altera_spi_probe(struct platform_device *pdev) /* find and map our resources */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - goto exit_busy; - if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), - pdev->name)) - goto exit_busy; - hw->base = devm_ioremap_nocache(&pdev->dev, res->start, - resource_size(res)); - if (!hw->base) - goto exit_busy; + hw->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hw->base)) { + err = PTR_ERR(hw->base); + goto exit; + } /* program defaults into the registers */ hw->imr = 0; /* disable spi interrupts */ writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); @@ -269,9 +258,6 @@ static int altera_spi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); return 0; - -exit_busy: - err = -EBUSY; exit: spi_master_put(master); return err; diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index ea1ec009f44..fd7cc566095 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -360,12 +360,12 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi) gpio_set_value(asd->npcs_pin, !active); } -static void atmel_spi_lock(struct atmel_spi *as) +static void atmel_spi_lock(struct atmel_spi *as) __acquires(&as->lock) { spin_lock_irqsave(&as->lock, as->flags); } -static void atmel_spi_unlock(struct atmel_spi *as) +static void atmel_spi_unlock(struct atmel_spi *as) __releases(&as->lock) { spin_unlock_irqrestore(&as->lock, as->flags); } @@ -629,9 +629,9 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, goto err_dma; dev_dbg(master->dev.parent, - " start dma xfer %p: len %u tx %p/%08x rx %p/%08x\n", - xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, - xfer->rx_buf, xfer->rx_dma); + " start dma xfer %p: len %u tx %p/%08llx rx %p/%08llx\n", + xfer, xfer->len, xfer->tx_buf, (unsigned long long)xfer->tx_dma, + xfer->rx_buf, (unsigned long long)xfer->rx_dma); /* Enable relevant interrupts */ spi_writel(as, IER, SPI_BIT(OVRES)); @@ -732,9 +732,10 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master, spi_writel(as, TCR, len); dev_dbg(&msg->spi->dev, - " start xfer %p: len %u tx %p/%08x rx %p/%08x\n", - xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, - xfer->rx_buf, xfer->rx_dma); + " start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n", + xfer, xfer->len, xfer->tx_buf, + (unsigned long long)xfer->tx_dma, xfer->rx_buf, + (unsigned long long)xfer->rx_dma); } else { xfer = as->next_transfer; remaining = as->next_remaining_bytes; @@ -771,9 +772,10 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master, spi_writel(as, TNCR, len); dev_dbg(&msg->spi->dev, - " next xfer %p: len %u tx %p/%08x rx %p/%08x\n", - xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, - xfer->rx_buf, xfer->rx_dma); + " next xfer %p: len %u tx %p/%08llx rx %p/%08llx\n", + xfer, xfer->len, xfer->tx_buf, + (unsigned long long)xfer->tx_dma, xfer->rx_buf, + (unsigned long long)xfer->rx_dma); ieval = SPI_BIT(ENDRX) | SPI_BIT(OVRES); } else { spi_writel(as, RNCR, 0); @@ -1579,7 +1581,9 @@ static int atmel_spi_probe(struct platform_device *pdev) goto out_unmap_regs; /* Initialize the hardware */ - clk_enable(clk); + ret = clk_prepare_enable(clk); + if (ret) + goto out_unmap_regs; spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ if (as->caps.has_wdrbt) { @@ -1609,7 +1613,7 @@ out_free_dma: spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ - clk_disable(clk); + clk_disable_unprepare(clk); free_irq(irq, master); out_unmap_regs: iounmap(as->regs); @@ -1661,7 +1665,7 @@ static int atmel_spi_remove(struct platform_device *pdev) dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, as->buffer_dma); - clk_disable(as->clk); + clk_disable_unprepare(as->clk); clk_put(as->clk); free_irq(as->irq, master); iounmap(as->regs); @@ -1678,7 +1682,7 @@ static int atmel_spi_suspend(struct platform_device *pdev, pm_message_t mesg) struct spi_master *master = platform_get_drvdata(pdev); struct atmel_spi *as = spi_master_get_devdata(master); - clk_disable(as->clk); + clk_disable_unprepare(as->clk); return 0; } @@ -1687,7 +1691,7 @@ static int atmel_spi_resume(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct atmel_spi *as = spi_master_get_devdata(master); - clk_enable(as->clk); + return clk_prepare_enable(as->clk); return 0; } diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index a4185e49232..52c81481c5c 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -314,7 +314,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); master->mode_bits = BCM2835_SPI_MODE_BITS; - master->bits_per_word_mask = BIT(8 - 1); + master->bits_per_word_mask = SPI_BPW_MASK(8); master->bus_num = -1; master->num_chipselect = 3; master->transfer_one_message = bcm2835_spi_transfer_one; @@ -325,12 +325,6 @@ static int bcm2835_spi_probe(struct platform_device *pdev) init_completion(&bs->done); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "could not get memory resource\n"); - err = -ENODEV; - goto out_master_put; - } - bs->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(bs->regs)) { err = PTR_ERR(bs->regs); @@ -383,7 +377,7 @@ out_master_put: static int bcm2835_spi_remove(struct platform_device *pdev) { - struct spi_master *master = platform_get_drvdata(pdev); + struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); struct bcm2835_spi *bs = spi_master_get_devdata(master); free_irq(bs->irq, master); diff --git a/drivers/spi/spi-bfin-v3.c b/drivers/spi/spi-bfin-v3.c new file mode 100644 index 00000000000..914f9fe1ec9 --- /dev/null +++ b/drivers/spi/spi-bfin-v3.c @@ -0,0 +1,971 @@ +/* + * Analog Devices SPI3 controller driver + * + * Copyright (c) 2013 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/types.h> + +#include <asm/bfin_spi3.h> +#include <asm/cacheflush.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +enum bfin_spi_state { + START_STATE, + RUNNING_STATE, + DONE_STATE, + ERROR_STATE +}; + +struct bfin_spi_master; + +struct bfin_spi_transfer_ops { + void (*write) (struct bfin_spi_master *); + void (*read) (struct bfin_spi_master *); + void (*duplex) (struct bfin_spi_master *); +}; + +/* runtime info for spi master */ +struct bfin_spi_master { + /* SPI framework hookup */ + struct spi_master *master; + + /* Regs base of SPI controller */ + struct bfin_spi_regs __iomem *regs; + + /* Pin request list */ + u16 *pin_req; + + /* Message Transfer pump */ + struct tasklet_struct pump_transfers; + + /* Current message transfer state info */ + struct spi_message *cur_msg; + struct spi_transfer *cur_transfer; + struct bfin_spi_device *cur_chip; + unsigned transfer_len; + + /* transfer buffer */ + void *tx; + void *tx_end; + void *rx; + void *rx_end; + + /* dma info */ + unsigned int tx_dma; + unsigned int rx_dma; + dma_addr_t tx_dma_addr; + dma_addr_t rx_dma_addr; + unsigned long dummy_buffer; /* used in unidirectional transfer */ + unsigned long tx_dma_size; + unsigned long rx_dma_size; + int tx_num; + int rx_num; + + /* store register value for suspend/resume */ + u32 control; + u32 ssel; + + unsigned long sclk; + enum bfin_spi_state state; + + const struct bfin_spi_transfer_ops *ops; +}; + +struct bfin_spi_device { + u32 control; + u32 clock; + u32 ssel; + + u8 cs; + u16 cs_chg_udelay; /* Some devices require > 255usec delay */ + u32 cs_gpio; + u32 tx_dummy_val; /* tx value for rx only transfer */ + bool enable_dma; + const struct bfin_spi_transfer_ops *ops; +}; + +static void bfin_spi_enable(struct bfin_spi_master *drv_data) +{ + bfin_write_or(&drv_data->regs->control, SPI_CTL_EN); +} + +static void bfin_spi_disable(struct bfin_spi_master *drv_data) +{ + bfin_write_and(&drv_data->regs->control, ~SPI_CTL_EN); +} + +/* Caculate the SPI_CLOCK register value based on input HZ */ +static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz) +{ + u32 spi_clock = sclk / speed_hz; + + if (spi_clock) + spi_clock--; + return spi_clock; +} + +static int bfin_spi_flush(struct bfin_spi_master *drv_data) +{ + unsigned long limit = loops_per_jiffy << 1; + + /* wait for stop and clear stat */ + while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_SPIF) && --limit) + cpu_relax(); + + bfin_write(&drv_data->regs->status, 0xFFFFFFFF); + + return limit; +} + +/* Chip select operation functions for cs_change flag */ +static void bfin_spi_cs_active(struct bfin_spi_master *drv_data, struct bfin_spi_device *chip) +{ + if (likely(chip->cs < MAX_CTRL_CS)) + bfin_write_and(&drv_data->regs->ssel, ~chip->ssel); + else + gpio_set_value(chip->cs_gpio, 0); +} + +static void bfin_spi_cs_deactive(struct bfin_spi_master *drv_data, + struct bfin_spi_device *chip) +{ + if (likely(chip->cs < MAX_CTRL_CS)) + bfin_write_or(&drv_data->regs->ssel, chip->ssel); + else + gpio_set_value(chip->cs_gpio, 1); + + /* Move delay here for consistency */ + if (chip->cs_chg_udelay) + udelay(chip->cs_chg_udelay); +} + +/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */ +static inline void bfin_spi_cs_enable(struct bfin_spi_master *drv_data, + struct bfin_spi_device *chip) +{ + if (chip->cs < MAX_CTRL_CS) + bfin_write_or(&drv_data->regs->ssel, chip->ssel >> 8); +} + +static inline void bfin_spi_cs_disable(struct bfin_spi_master *drv_data, + struct bfin_spi_device *chip) +{ + if (chip->cs < MAX_CTRL_CS) + bfin_write_and(&drv_data->regs->ssel, ~(chip->ssel >> 8)); +} + +/* stop controller and re-config current chip*/ +static void bfin_spi_restore_state(struct bfin_spi_master *drv_data) +{ + struct bfin_spi_device *chip = drv_data->cur_chip; + + /* Clear status and disable clock */ + bfin_write(&drv_data->regs->status, 0xFFFFFFFF); + bfin_write(&drv_data->regs->rx_control, 0x0); + bfin_write(&drv_data->regs->tx_control, 0x0); + bfin_spi_disable(drv_data); + + SSYNC(); + + /* Load the registers */ + bfin_write(&drv_data->regs->control, chip->control); + bfin_write(&drv_data->regs->clock, chip->clock); + + bfin_spi_enable(drv_data); + drv_data->tx_num = drv_data->rx_num = 0; + /* we always choose tx transfer initiate */ + bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN); + bfin_write(&drv_data->regs->tx_control, + SPI_TXCTL_TEN | SPI_TXCTL_TTI); + bfin_spi_cs_active(drv_data, chip); +} + +/* discard invalid rx data and empty rfifo */ +static inline void dummy_read(struct bfin_spi_master *drv_data) +{ + while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)) + bfin_read(&drv_data->regs->rfifo); +} + +static void bfin_spi_u8_write(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++))); + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + bfin_read(&drv_data->regs->rfifo); + } +} + +static void bfin_spi_u8_read(struct bfin_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, tx_val); + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo); + } +} + +static void bfin_spi_u8_duplex(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++))); + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo); + } +} + +static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u8 = { + .write = bfin_spi_u8_write, + .read = bfin_spi_u8_read, + .duplex = bfin_spi_u8_duplex, +}; + +static void bfin_spi_u16_write(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx)); + drv_data->tx += 2; + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + bfin_read(&drv_data->regs->rfifo); + } +} + +static void bfin_spi_u16_read(struct bfin_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, tx_val); + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); + drv_data->rx += 2; + } +} + +static void bfin_spi_u16_duplex(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx)); + drv_data->tx += 2; + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); + drv_data->rx += 2; + } +} + +static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u16 = { + .write = bfin_spi_u16_write, + .read = bfin_spi_u16_read, + .duplex = bfin_spi_u16_duplex, +}; + +static void bfin_spi_u32_write(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->tx < drv_data->tx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx)); + drv_data->tx += 4; + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + bfin_read(&drv_data->regs->rfifo); + } +} + +static void bfin_spi_u32_read(struct bfin_spi_master *drv_data) +{ + u32 tx_val = drv_data->cur_chip->tx_dummy_val; + + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, tx_val); + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); + drv_data->rx += 4; + } +} + +static void bfin_spi_u32_duplex(struct bfin_spi_master *drv_data) +{ + dummy_read(drv_data); + while (drv_data->rx < drv_data->rx_end) { + bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx)); + drv_data->tx += 4; + while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE) + cpu_relax(); + *(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo); + drv_data->rx += 4; + } +} + +static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u32 = { + .write = bfin_spi_u32_write, + .read = bfin_spi_u32_read, + .duplex = bfin_spi_u32_duplex, +}; + + +/* test if there is more transfer to be done */ +static void bfin_spi_next_transfer(struct bfin_spi_master *drv) +{ + struct spi_message *msg = drv->cur_msg; + struct spi_transfer *t = drv->cur_transfer; + + /* Move to next transfer */ + if (t->transfer_list.next != &msg->transfers) { + drv->cur_transfer = list_entry(t->transfer_list.next, + struct spi_transfer, transfer_list); + drv->state = RUNNING_STATE; + } else { + drv->state = DONE_STATE; + drv->cur_transfer = NULL; + } +} + +static void bfin_spi_giveback(struct bfin_spi_master *drv_data) +{ + struct bfin_spi_device *chip = drv_data->cur_chip; + + bfin_spi_cs_deactive(drv_data, chip); + spi_finalize_current_message(drv_data->master); +} + +static int bfin_spi_setup_transfer(struct bfin_spi_master *drv) +{ + struct spi_transfer *t = drv->cur_transfer; + u32 cr, cr_width; + + if (t->tx_buf) { + drv->tx = (void *)t->tx_buf; + drv->tx_end = drv->tx + t->len; + } else { + drv->tx = NULL; + } + + if (t->rx_buf) { + drv->rx = t->rx_buf; + drv->rx_end = drv->rx + t->len; + } else { + drv->rx = NULL; + } + + drv->transfer_len = t->len; + + /* bits per word setup */ + switch (t->bits_per_word) { + case 8: + cr_width = SPI_CTL_SIZE08; + drv->ops = &bfin_bfin_spi_transfer_ops_u8; + break; + case 16: + cr_width = SPI_CTL_SIZE16; + drv->ops = &bfin_bfin_spi_transfer_ops_u16; + break; + case 32: + cr_width = SPI_CTL_SIZE32; + drv->ops = &bfin_bfin_spi_transfer_ops_u32; + break; + default: + return -EINVAL; + } + cr = bfin_read(&drv->regs->control) & ~SPI_CTL_SIZE; + cr |= cr_width; + bfin_write(&drv->regs->control, cr); + + /* speed setup */ + bfin_write(&drv->regs->clock, + hz_to_spi_clock(drv->sclk, t->speed_hz)); + return 0; +} + +static int bfin_spi_dma_xfer(struct bfin_spi_master *drv_data) +{ + struct spi_transfer *t = drv_data->cur_transfer; + struct spi_message *msg = drv_data->cur_msg; + struct bfin_spi_device *chip = drv_data->cur_chip; + u32 dma_config; + unsigned long word_count, word_size; + void *tx_buf, *rx_buf; + + switch (t->bits_per_word) { + case 8: + dma_config = WDSIZE_8 | PSIZE_8; + word_count = drv_data->transfer_len; + word_size = 1; + break; + case 16: + dma_config = WDSIZE_16 | PSIZE_16; + word_count = drv_data->transfer_len / 2; + word_size = 2; + break; + default: + dma_config = WDSIZE_32 | PSIZE_32; + word_count = drv_data->transfer_len / 4; + word_size = 4; + break; + } + + if (!drv_data->rx) { + tx_buf = drv_data->tx; + rx_buf = &drv_data->dummy_buffer; + drv_data->tx_dma_size = drv_data->transfer_len; + drv_data->rx_dma_size = sizeof(drv_data->dummy_buffer); + set_dma_x_modify(drv_data->tx_dma, word_size); + set_dma_x_modify(drv_data->rx_dma, 0); + } else if (!drv_data->tx) { + drv_data->dummy_buffer = chip->tx_dummy_val; + tx_buf = &drv_data->dummy_buffer; + rx_buf = drv_data->rx; + drv_data->tx_dma_size = sizeof(drv_data->dummy_buffer); + drv_data->rx_dma_size = drv_data->transfer_len; + set_dma_x_modify(drv_data->tx_dma, 0); + set_dma_x_modify(drv_data->rx_dma, word_size); + } else { + tx_buf = drv_data->tx; + rx_buf = drv_data->rx; + drv_data->tx_dma_size = drv_data->rx_dma_size + = drv_data->transfer_len; + set_dma_x_modify(drv_data->tx_dma, word_size); + set_dma_x_modify(drv_data->rx_dma, word_size); + } + + drv_data->tx_dma_addr = dma_map_single(&msg->spi->dev, + (void *)tx_buf, + drv_data->tx_dma_size, + DMA_TO_DEVICE); + if (dma_mapping_error(&msg->spi->dev, + drv_data->tx_dma_addr)) + return -ENOMEM; + + drv_data->rx_dma_addr = dma_map_single(&msg->spi->dev, + (void *)rx_buf, + drv_data->rx_dma_size, + DMA_FROM_DEVICE); + if (dma_mapping_error(&msg->spi->dev, + drv_data->rx_dma_addr)) { + dma_unmap_single(&msg->spi->dev, + drv_data->tx_dma_addr, + drv_data->tx_dma_size, + DMA_TO_DEVICE); + return -ENOMEM; + } + + dummy_read(drv_data); + set_dma_x_count(drv_data->tx_dma, word_count); + set_dma_x_count(drv_data->rx_dma, word_count); + set_dma_start_addr(drv_data->tx_dma, drv_data->tx_dma_addr); + set_dma_start_addr(drv_data->rx_dma, drv_data->rx_dma_addr); + dma_config |= DMAFLOW_STOP | RESTART | DI_EN; + set_dma_config(drv_data->tx_dma, dma_config); + set_dma_config(drv_data->rx_dma, dma_config | WNR); + enable_dma(drv_data->tx_dma); + enable_dma(drv_data->rx_dma); + SSYNC(); + + bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN | SPI_RXCTL_RDR_NE); + SSYNC(); + bfin_write(&drv_data->regs->tx_control, + SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF); + + return 0; +} + +static int bfin_spi_pio_xfer(struct bfin_spi_master *drv_data) +{ + struct spi_message *msg = drv_data->cur_msg; + + if (!drv_data->rx) { + /* write only half duplex */ + drv_data->ops->write(drv_data); + if (drv_data->tx != drv_data->tx_end) + return -EIO; + } else if (!drv_data->tx) { + /* read only half duplex */ + drv_data->ops->read(drv_data); + if (drv_data->rx != drv_data->rx_end) + return -EIO; + } else { + /* full duplex mode */ + drv_data->ops->duplex(drv_data); + if (drv_data->tx != drv_data->tx_end) + return -EIO; + } + + if (!bfin_spi_flush(drv_data)) + return -EIO; + msg->actual_length += drv_data->transfer_len; + tasklet_schedule(&drv_data->pump_transfers); + return 0; +} + +static void bfin_spi_pump_transfers(unsigned long data) +{ + struct bfin_spi_master *drv_data = (struct bfin_spi_master *)data; + struct spi_message *msg = NULL; + struct spi_transfer *t = NULL; + struct bfin_spi_device *chip = NULL; + int ret; + + /* Get current state information */ + msg = drv_data->cur_msg; + t = drv_data->cur_transfer; + chip = drv_data->cur_chip; + + /* Handle for abort */ + if (drv_data->state == ERROR_STATE) { + msg->status = -EIO; + bfin_spi_giveback(drv_data); + return; + } + + if (drv_data->state == RUNNING_STATE) { + if (t->delay_usecs) + udelay(t->delay_usecs); + if (t->cs_change) + bfin_spi_cs_deactive(drv_data, chip); + bfin_spi_next_transfer(drv_data); + t = drv_data->cur_transfer; + } + /* Handle end of message */ + if (drv_data->state == DONE_STATE) { + msg->status = 0; + bfin_spi_giveback(drv_data); + return; + } + + if ((t->len == 0) || (t->tx_buf == NULL && t->rx_buf == NULL)) { + /* Schedule next transfer tasklet */ + tasklet_schedule(&drv_data->pump_transfers); + return; + } + + ret = bfin_spi_setup_transfer(drv_data); + if (ret) { + msg->status = ret; + bfin_spi_giveback(drv_data); + } + + bfin_write(&drv_data->regs->status, 0xFFFFFFFF); + bfin_spi_cs_active(drv_data, chip); + drv_data->state = RUNNING_STATE; + + if (chip->enable_dma) + ret = bfin_spi_dma_xfer(drv_data); + else + ret = bfin_spi_pio_xfer(drv_data); + if (ret) { + msg->status = ret; + bfin_spi_giveback(drv_data); + } +} + +static int bfin_spi_transfer_one_message(struct spi_master *master, + struct spi_message *m) +{ + struct bfin_spi_master *drv_data = spi_master_get_devdata(master); + + drv_data->cur_msg = m; + drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); + bfin_spi_restore_state(drv_data); + + drv_data->state = START_STATE; + drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, + struct spi_transfer, transfer_list); + + tasklet_schedule(&drv_data->pump_transfers); + return 0; +} + +#define MAX_SPI_SSEL 7 + +static const u16 ssel[][MAX_SPI_SSEL] = { + {P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3, + P_SPI0_SSEL4, P_SPI0_SSEL5, + P_SPI0_SSEL6, P_SPI0_SSEL7}, + + {P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3, + P_SPI1_SSEL4, P_SPI1_SSEL5, + P_SPI1_SSEL6, P_SPI1_SSEL7}, + + {P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3, + P_SPI2_SSEL4, P_SPI2_SSEL5, + P_SPI2_SSEL6, P_SPI2_SSEL7}, +}; + +static int bfin_spi_setup(struct spi_device *spi) +{ + struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master); + struct bfin_spi_device *chip = spi_get_ctldata(spi); + u32 bfin_ctl_reg = SPI_CTL_ODM | SPI_CTL_PSSE; + int ret = -EINVAL; + + if (!chip) { + struct bfin_spi3_chip *chip_info = spi->controller_data; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + dev_err(&spi->dev, "can not allocate chip data\n"); + return -ENOMEM; + } + if (chip_info) { + if (chip_info->control & ~bfin_ctl_reg) { + dev_err(&spi->dev, + "do not set bits that the SPI framework manages\n"); + goto error; + } + chip->control = chip_info->control; + chip->cs_chg_udelay = chip_info->cs_chg_udelay; + chip->tx_dummy_val = chip_info->tx_dummy_val; + chip->enable_dma = chip_info->enable_dma; + } + chip->cs = spi->chip_select; + if (chip->cs < MAX_CTRL_CS) { + chip->ssel = (1 << chip->cs) << 8; + ret = peripheral_request(ssel[spi->master->bus_num] + [chip->cs-1], dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "peripheral_request() error\n"); + goto error; + } + } else { + chip->cs_gpio = chip->cs - MAX_CTRL_CS; + ret = gpio_request_one(chip->cs_gpio, GPIOF_OUT_INIT_HIGH, + dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "gpio_request_one() error\n"); + goto error; + } + } + spi_set_ctldata(spi, chip); + } + + /* force a default base state */ + chip->control &= bfin_ctl_reg; + + if (spi->mode & SPI_CPOL) + chip->control |= SPI_CTL_CPOL; + if (spi->mode & SPI_CPHA) + chip->control |= SPI_CTL_CPHA; + if (spi->mode & SPI_LSB_FIRST) + chip->control |= SPI_CTL_LSBF; + chip->control |= SPI_CTL_MSTR; + /* we choose software to controll cs */ + chip->control &= ~SPI_CTL_ASSEL; + + chip->clock = hz_to_spi_clock(drv_data->sclk, spi->max_speed_hz); + + bfin_spi_cs_enable(drv_data, chip); + bfin_spi_cs_deactive(drv_data, chip); + + return 0; +error: + if (chip) { + kfree(chip); + spi_set_ctldata(spi, NULL); + } + + return ret; +} + +static void bfin_spi_cleanup(struct spi_device *spi) +{ + struct bfin_spi_device *chip = spi_get_ctldata(spi); + struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master); + + if (!chip) + return; + + if (chip->cs < MAX_CTRL_CS) { + peripheral_free(ssel[spi->master->bus_num] + [chip->cs-1]); + bfin_spi_cs_disable(drv_data, chip); + } else { + gpio_free(chip->cs_gpio); + } + + kfree(chip); + spi_set_ctldata(spi, NULL); +} + +static irqreturn_t bfin_spi_tx_dma_isr(int irq, void *dev_id) +{ + struct bfin_spi_master *drv_data = dev_id; + u32 dma_stat = get_dma_curr_irqstat(drv_data->tx_dma); + + clear_dma_irqstat(drv_data->tx_dma); + if (dma_stat & DMA_DONE) { + drv_data->tx_num++; + } else { + dev_err(&drv_data->master->dev, + "spi tx dma error: %d\n", dma_stat); + if (drv_data->tx) + drv_data->state = ERROR_STATE; + } + bfin_write_and(&drv_data->regs->tx_control, ~SPI_TXCTL_TDR_NF); + return IRQ_HANDLED; +} + +static irqreturn_t bfin_spi_rx_dma_isr(int irq, void *dev_id) +{ + struct bfin_spi_master *drv_data = dev_id; + struct spi_message *msg = drv_data->cur_msg; + u32 dma_stat = get_dma_curr_irqstat(drv_data->rx_dma); + + clear_dma_irqstat(drv_data->rx_dma); + if (dma_stat & DMA_DONE) { + drv_data->rx_num++; + /* we may fail on tx dma */ + if (drv_data->state != ERROR_STATE) + msg->actual_length += drv_data->transfer_len; + } else { + drv_data->state = ERROR_STATE; + dev_err(&drv_data->master->dev, + "spi rx dma error: %d\n", dma_stat); + } + bfin_write(&drv_data->regs->tx_control, 0); + bfin_write(&drv_data->regs->rx_control, 0); + if (drv_data->rx_num != drv_data->tx_num) + dev_dbg(&drv_data->master->dev, + "dma interrupt missing: tx=%d,rx=%d\n", + drv_data->tx_num, drv_data->rx_num); + tasklet_schedule(&drv_data->pump_transfers); + return IRQ_HANDLED; +} + +static int bfin_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bfin_spi3_master *info = dev->platform_data; + struct spi_master *master; + struct bfin_spi_master *drv_data; + struct resource *mem, *res; + unsigned int tx_dma, rx_dma; + unsigned long sclk; + int ret; + + if (!info) { + dev_err(dev, "platform data missing!\n"); + return -ENODEV; + } + + sclk = get_sclk1(); + if (!sclk) { + dev_err(dev, "can not get sclk1\n"); + return -ENXIO; + } + + /* get register base and tx/rx dma */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(dev, "can not get register base\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(dev, "can not get tx dma resource\n"); + return -ENXIO; + } + tx_dma = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(dev, "can not get rx dma resource\n"); + return -ENXIO; + } + rx_dma = res->start; + + /* allocate master with space for drv_data */ + master = spi_alloc_master(dev, sizeof(*drv_data)); + if (!master) { + dev_err(dev, "can not alloc spi_master\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, master); + + /* the mode bits supported by this driver */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + + master->bus_num = pdev->id; + master->num_chipselect = info->num_chipselect; + master->cleanup = bfin_spi_cleanup; + master->setup = bfin_spi_setup; + master->transfer_one_message = bfin_spi_transfer_one_message; + master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1); + + drv_data = spi_master_get_devdata(master); + drv_data->master = master; + drv_data->tx_dma = tx_dma; + drv_data->rx_dma = rx_dma; + drv_data->pin_req = info->pin_req; + drv_data->sclk = sclk; + + drv_data->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(drv_data->regs)) { + ret = PTR_ERR(drv_data->regs); + goto err_put_master; + } + + /* request tx and rx dma */ + ret = request_dma(tx_dma, "SPI_TX_DMA"); + if (ret) { + dev_err(dev, "can not request SPI TX DMA channel\n"); + goto err_put_master; + } + set_dma_callback(tx_dma, bfin_spi_tx_dma_isr, drv_data); + + ret = request_dma(rx_dma, "SPI_RX_DMA"); + if (ret) { + dev_err(dev, "can not request SPI RX DMA channel\n"); + goto err_free_tx_dma; + } + set_dma_callback(drv_data->rx_dma, bfin_spi_rx_dma_isr, drv_data); + + /* request CLK, MOSI and MISO */ + ret = peripheral_request_list(drv_data->pin_req, "bfin-spi3"); + if (ret < 0) { + dev_err(dev, "can not request spi pins\n"); + goto err_free_rx_dma; + } + + bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA); + bfin_write(&drv_data->regs->ssel, 0x0000FE00); + bfin_write(&drv_data->regs->delay, 0x0); + + tasklet_init(&drv_data->pump_transfers, + bfin_spi_pump_transfers, (unsigned long)drv_data); + /* register with the SPI framework */ + ret = spi_register_master(master); + if (ret) { + dev_err(dev, "can not register spi master\n"); + goto err_free_peripheral; + } + + return ret; + +err_free_peripheral: + peripheral_free_list(drv_data->pin_req); +err_free_rx_dma: + free_dma(rx_dma); +err_free_tx_dma: + free_dma(tx_dma); +err_put_master: + spi_master_put(master); + + return ret; +} + +static int bfin_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct bfin_spi_master *drv_data = spi_master_get_devdata(master); + + bfin_spi_disable(drv_data); + + peripheral_free_list(drv_data->pin_req); + free_dma(drv_data->rx_dma); + free_dma(drv_data->tx_dma); + + spi_unregister_master(drv_data->master); + return 0; +} + +#ifdef CONFIG_PM +static int bfin_spi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct bfin_spi_master *drv_data = spi_master_get_devdata(master); + + spi_master_suspend(master); + + drv_data->control = bfin_read(&drv_data->regs->control); + drv_data->ssel = bfin_read(&drv_data->regs->ssel); + + bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA); + bfin_write(&drv_data->regs->ssel, 0x0000FE00); + dma_disable_irq(drv_data->rx_dma); + dma_disable_irq(drv_data->tx_dma); + + return 0; +} + +static int bfin_spi_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct bfin_spi_master *drv_data = spi_master_get_devdata(master); + int ret = 0; + + /* bootrom may modify spi and dma status when resume in spi boot mode */ + disable_dma(drv_data->rx_dma); + + dma_enable_irq(drv_data->rx_dma); + dma_enable_irq(drv_data->tx_dma); + bfin_write(&drv_data->regs->control, drv_data->control); + bfin_write(&drv_data->regs->ssel, drv_data->ssel); + + ret = spi_master_resume(master); + if (ret) { + free_dma(drv_data->rx_dma); + free_dma(drv_data->tx_dma); + } + + return ret; +} +#endif +static const struct dev_pm_ops bfin_spi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(bfin_spi_suspend, bfin_spi_resume) +}; + +MODULE_ALIAS("platform:bfin-spi3"); +static struct platform_driver bfin_spi_driver = { + .driver = { + .name = "bfin-spi3", + .owner = THIS_MODULE, + .pm = &bfin_spi_pm_ops, + }, + .remove = bfin_spi_remove, +}; + +module_platform_driver_probe(bfin_spi_driver, bfin_spi_probe); + +MODULE_DESCRIPTION("Analog Devices SPI3 controller driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index a63d7da3bfe..e3946e44e07 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -255,150 +255,140 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) * Drivers can provide word-at-a-time i/o primitives, or provide * transfer-at-a-time ones to leverage dma or fifo hardware. */ -static void bitbang_work(struct work_struct *work) + +static int spi_bitbang_prepare_hardware(struct spi_master *spi) { - struct spi_bitbang *bitbang = - container_of(work, struct spi_bitbang, work); + struct spi_bitbang *bitbang; unsigned long flags; - struct spi_message *m, *_m; + + bitbang = spi_master_get_devdata(spi); spin_lock_irqsave(&bitbang->lock, flags); bitbang->busy = 1; - list_for_each_entry_safe(m, _m, &bitbang->queue, queue) { - struct spi_device *spi; - unsigned nsecs; - struct spi_transfer *t = NULL; - unsigned tmp; - unsigned cs_change; - int status; - int do_setup = -1; - - list_del(&m->queue); - spin_unlock_irqrestore(&bitbang->lock, flags); - - /* FIXME this is made-up ... the correct value is known to - * word-at-a-time bitbang code, and presumably chipselect() - * should enforce these requirements too? - */ - nsecs = 100; + spin_unlock_irqrestore(&bitbang->lock, flags); - spi = m->spi; - tmp = 0; - cs_change = 1; - status = 0; + return 0; +} - list_for_each_entry (t, &m->transfers, transfer_list) { - - /* override speed or wordsize? */ - if (t->speed_hz || t->bits_per_word) - do_setup = 1; - - /* init (-1) or override (1) transfer params */ - if (do_setup != 0) { - status = bitbang->setup_transfer(spi, t); - if (status < 0) - break; - if (do_setup == -1) - do_setup = 0; - } - - /* set up default clock polarity, and activate chip; - * this implicitly updates clock and spi modes as - * previously recorded for this device via setup(). - * (and also deselects any other chip that might be - * selected ...) - */ - if (cs_change) { - bitbang->chipselect(spi, BITBANG_CS_ACTIVE); - ndelay(nsecs); - } - cs_change = t->cs_change; - if (!t->tx_buf && !t->rx_buf && t->len) { - status = -EINVAL; - break; - } +static int spi_bitbang_transfer_one(struct spi_master *master, + struct spi_message *m) +{ + struct spi_bitbang *bitbang; + unsigned nsecs; + struct spi_transfer *t = NULL; + unsigned cs_change; + int status; + int do_setup = -1; + struct spi_device *spi = m->spi; + + bitbang = spi_master_get_devdata(master); + + /* FIXME this is made-up ... the correct value is known to + * word-at-a-time bitbang code, and presumably chipselect() + * should enforce these requirements too? + */ + nsecs = 100; - /* transfer data. the lower level code handles any - * new dma mappings it needs. our caller always gave - * us dma-safe buffers. - */ - if (t->len) { - /* REVISIT dma API still needs a designated - * DMA_ADDR_INVALID; ~0 might be better. - */ - if (!m->is_dma_mapped) - t->rx_dma = t->tx_dma = 0; - status = bitbang->txrx_bufs(spi, t); - } - if (status > 0) - m->actual_length += status; - if (status != t->len) { - /* always report some kind of error */ - if (status >= 0) - status = -EREMOTEIO; + cs_change = 1; + status = 0; + + list_for_each_entry (t, &m->transfers, transfer_list) { + + /* override speed or wordsize? */ + if (t->speed_hz || t->bits_per_word) + do_setup = 1; + + /* init (-1) or override (1) transfer params */ + if (do_setup != 0) { + status = bitbang->setup_transfer(spi, t); + if (status < 0) break; - } - status = 0; - - /* protocol tweaks before next transfer */ - if (t->delay_usecs) - udelay(t->delay_usecs); - - if (cs_change && !list_is_last(&t->transfer_list, &m->transfers)) { - /* sometimes a short mid-message deselect of the chip - * may be needed to terminate a mode or command - */ - ndelay(nsecs); - bitbang->chipselect(spi, BITBANG_CS_INACTIVE); - ndelay(nsecs); - } + if (do_setup == -1) + do_setup = 0; } - m->status = status; - m->complete(m->context); + /* set up default clock polarity, and activate chip; + * this implicitly updates clock and spi modes as + * previously recorded for this device via setup(). + * (and also deselects any other chip that might be + * selected ...) + */ + if (cs_change) { + bitbang->chipselect(spi, BITBANG_CS_ACTIVE); + ndelay(nsecs); + } + cs_change = t->cs_change; + if (!t->tx_buf && !t->rx_buf && t->len) { + status = -EINVAL; + break; + } - /* normally deactivate chipselect ... unless no error and - * cs_change has hinted that the next message will probably - * be for this chip too. + /* transfer data. the lower level code handles any + * new dma mappings it needs. our caller always gave + * us dma-safe buffers. */ - if (!(status == 0 && cs_change)) { + if (t->len) { + /* REVISIT dma API still needs a designated + * DMA_ADDR_INVALID; ~0 might be better. + */ + if (!m->is_dma_mapped) + t->rx_dma = t->tx_dma = 0; + status = bitbang->txrx_bufs(spi, t); + } + if (status > 0) + m->actual_length += status; + if (status != t->len) { + /* always report some kind of error */ + if (status >= 0) + status = -EREMOTEIO; + break; + } + status = 0; + + /* protocol tweaks before next transfer */ + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (cs_change && !list_is_last(&t->transfer_list, &m->transfers)) { + /* sometimes a short mid-message deselect of the chip + * may be needed to terminate a mode or command + */ ndelay(nsecs); bitbang->chipselect(spi, BITBANG_CS_INACTIVE); ndelay(nsecs); } + } + + m->status = status; - spin_lock_irqsave(&bitbang->lock, flags); + /* normally deactivate chipselect ... unless no error and + * cs_change has hinted that the next message will probably + * be for this chip too. + */ + if (!(status == 0 && cs_change)) { + ndelay(nsecs); + bitbang->chipselect(spi, BITBANG_CS_INACTIVE); + ndelay(nsecs); } - bitbang->busy = 0; - spin_unlock_irqrestore(&bitbang->lock, flags); + + spi_finalize_current_message(master); + + return status; } -/** - * spi_bitbang_transfer - default submit to transfer queue - */ -int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m) +static int spi_bitbang_unprepare_hardware(struct spi_master *spi) { - struct spi_bitbang *bitbang; + struct spi_bitbang *bitbang; unsigned long flags; - int status = 0; - m->actual_length = 0; - m->status = -EINPROGRESS; - - bitbang = spi_master_get_devdata(spi->master); + bitbang = spi_master_get_devdata(spi); spin_lock_irqsave(&bitbang->lock, flags); - if (!spi->max_speed_hz) - status = -ENETDOWN; - else { - list_add_tail(&m->queue, &bitbang->queue); - queue_work(bitbang->workqueue, &bitbang->work); - } + bitbang->busy = 0; spin_unlock_irqrestore(&bitbang->lock, flags); - return status; + return 0; } -EXPORT_SYMBOL_GPL(spi_bitbang_transfer); /*----------------------------------------------------------------------*/ @@ -428,20 +418,22 @@ EXPORT_SYMBOL_GPL(spi_bitbang_transfer); int spi_bitbang_start(struct spi_bitbang *bitbang) { struct spi_master *master = bitbang->master; - int status; if (!master || !bitbang->chipselect) return -EINVAL; - INIT_WORK(&bitbang->work, bitbang_work); spin_lock_init(&bitbang->lock); - INIT_LIST_HEAD(&bitbang->queue); if (!master->mode_bits) master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags; - if (!master->transfer) - master->transfer = spi_bitbang_transfer; + if (master->transfer || master->transfer_one_message) + return -EINVAL; + + master->prepare_transfer_hardware = spi_bitbang_prepare_hardware; + master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware; + master->transfer_one_message = spi_bitbang_transfer_one; + if (!bitbang->txrx_bufs) { bitbang->use_dma = 0; bitbang->txrx_bufs = spi_bitbang_bufs; @@ -452,34 +444,12 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) master->setup = spi_bitbang_setup; master->cleanup = spi_bitbang_cleanup; } - } else if (!master->setup) - return -EINVAL; - if (master->transfer == spi_bitbang_transfer && - !bitbang->setup_transfer) - return -EINVAL; - - /* this task is the only thing to touch the SPI bits */ - bitbang->busy = 0; - bitbang->workqueue = create_singlethread_workqueue( - dev_name(master->dev.parent)); - if (bitbang->workqueue == NULL) { - status = -EBUSY; - goto err1; } /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ - status = spi_register_master(master); - if (status < 0) - goto err2; - - return status; - -err2: - destroy_workqueue(bitbang->workqueue); -err1: - return status; + return spi_register_master(master); } EXPORT_SYMBOL_GPL(spi_bitbang_start); @@ -490,10 +460,6 @@ int spi_bitbang_stop(struct spi_bitbang *bitbang) { spi_unregister_master(bitbang->master); - WARN_ON(!list_empty(&bitbang->queue)); - - destroy_workqueue(bitbang->workqueue); - return 0; } EXPORT_SYMBOL_GPL(spi_bitbang_stop); diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index 17965fe225c..5655acf55bf 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -239,11 +239,8 @@ static int spi_clps711x_probe(struct platform_device *pdev) } dev_err(&pdev->dev, "Failed to register master\n"); - devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw); clk_out: - devm_clk_put(&pdev->dev, hw->spi_clk); - err_out: while (--i >= 0) if (gpio_is_valid(hw->chipselect[i])) @@ -261,13 +258,10 @@ static int spi_clps711x_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct spi_clps711x_data *hw = spi_master_get_devdata(master); - devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw); - for (i = 0; i < master->num_chipselect; i++) if (gpio_is_valid(hw->chipselect[i])) gpio_free(hw->chipselect[i]); - devm_clk_put(&pdev->dev, hw->spi_clk); spi_unregister_master(master); kfree(master); diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 222d3e37fc2..707966bd561 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -609,7 +609,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) else buf = (void *)t->tx_buf; t->tx_dma = dma_map_single(&spi->dev, buf, - t->len, DMA_FROM_DEVICE); + t->len, DMA_TO_DEVICE); if (!t->tx_dma) { ret = -EFAULT; goto err_tx_map; diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c new file mode 100644 index 00000000000..7d84418a01d --- /dev/null +++ b/drivers/spi/spi-efm32.c @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2012-2013 Uwe Kleine-Koenig for Pengutronix + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/platform_data/efm32-spi.h> + +#define DRIVER_NAME "efm32-spi" + +#define MASK_VAL(mask, val) ((val << __ffs(mask)) & mask) + +#define REG_CTRL 0x00 +#define REG_CTRL_SYNC 0x0001 +#define REG_CTRL_CLKPOL 0x0100 +#define REG_CTRL_CLKPHA 0x0200 +#define REG_CTRL_MSBF 0x0400 +#define REG_CTRL_TXBIL 0x1000 + +#define REG_FRAME 0x04 +#define REG_FRAME_DATABITS__MASK 0x000f +#define REG_FRAME_DATABITS(n) ((n) - 3) + +#define REG_CMD 0x0c +#define REG_CMD_RXEN 0x0001 +#define REG_CMD_RXDIS 0x0002 +#define REG_CMD_TXEN 0x0004 +#define REG_CMD_TXDIS 0x0008 +#define REG_CMD_MASTEREN 0x0010 + +#define REG_STATUS 0x10 +#define REG_STATUS_TXENS 0x0002 +#define REG_STATUS_TXC 0x0020 +#define REG_STATUS_TXBL 0x0040 +#define REG_STATUS_RXDATAV 0x0080 + +#define REG_CLKDIV 0x14 + +#define REG_RXDATAX 0x18 +#define REG_RXDATAX_RXDATA__MASK 0x01ff +#define REG_RXDATAX_PERR 0x4000 +#define REG_RXDATAX_FERR 0x8000 + +#define REG_TXDATA 0x34 + +#define REG_IF 0x40 +#define REG_IF_TXBL 0x0002 +#define REG_IF_RXDATAV 0x0004 + +#define REG_IFS 0x44 +#define REG_IFC 0x48 +#define REG_IEN 0x4c + +#define REG_ROUTE 0x54 +#define REG_ROUTE_RXPEN 0x0001 +#define REG_ROUTE_TXPEN 0x0002 +#define REG_ROUTE_CLKPEN 0x0008 +#define REG_ROUTE_LOCATION__MASK 0x0700 +#define REG_ROUTE_LOCATION(n) MASK_VAL(REG_ROUTE_LOCATION__MASK, (n)) + +struct efm32_spi_ddata { + struct spi_bitbang bitbang; + + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + unsigned int rxirq, txirq; + struct efm32_spi_pdata pdata; + + /* irq data */ + struct completion done; + const u8 *tx_buf; + u8 *rx_buf; + unsigned tx_len, rx_len; + + /* chip selects */ + unsigned csgpio[]; +}; + +#define ddata_to_dev(ddata) (&(ddata->bitbang.master->dev)) +#define efm32_spi_vdbg(ddata, format, arg...) \ + dev_vdbg(ddata_to_dev(ddata), format, ##arg) + +static void efm32_spi_write32(struct efm32_spi_ddata *ddata, + u32 value, unsigned offset) +{ + writel_relaxed(value, ddata->base + offset); +} + +static u32 efm32_spi_read32(struct efm32_spi_ddata *ddata, unsigned offset) +{ + return readl_relaxed(ddata->base + offset); +} + +static void efm32_spi_chipselect(struct spi_device *spi, int is_on) +{ + struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master); + int value = !(spi->mode & SPI_CS_HIGH) == !(is_on == BITBANG_CS_ACTIVE); + + gpio_set_value(ddata->csgpio[spi->chip_select], value); +} + +static int efm32_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master); + + unsigned bpw = t->bits_per_word ?: spi->bits_per_word; + unsigned speed = t->speed_hz ?: spi->max_speed_hz; + unsigned long clkfreq = clk_get_rate(ddata->clk); + u32 clkdiv; + + efm32_spi_write32(ddata, REG_CTRL_SYNC | REG_CTRL_MSBF | + (spi->mode & SPI_CPHA ? REG_CTRL_CLKPHA : 0) | + (spi->mode & SPI_CPOL ? REG_CTRL_CLKPOL : 0), REG_CTRL); + + efm32_spi_write32(ddata, + REG_FRAME_DATABITS(bpw), REG_FRAME); + + if (2 * speed >= clkfreq) + clkdiv = 0; + else + clkdiv = 64 * (DIV_ROUND_UP(2 * clkfreq, speed) - 4); + + if (clkdiv > (1U << 21)) + return -EINVAL; + + efm32_spi_write32(ddata, clkdiv, REG_CLKDIV); + efm32_spi_write32(ddata, REG_CMD_MASTEREN, REG_CMD); + efm32_spi_write32(ddata, REG_CMD_RXEN | REG_CMD_TXEN, REG_CMD); + + return 0; +} + +static void efm32_spi_tx_u8(struct efm32_spi_ddata *ddata) +{ + u8 val = 0; + + if (ddata->tx_buf) { + val = *ddata->tx_buf; + ddata->tx_buf++; + } + + ddata->tx_len--; + efm32_spi_write32(ddata, val, REG_TXDATA); + efm32_spi_vdbg(ddata, "%s: tx 0x%x\n", __func__, val); +} + +static void efm32_spi_rx_u8(struct efm32_spi_ddata *ddata) +{ + u32 rxdata = efm32_spi_read32(ddata, REG_RXDATAX); + efm32_spi_vdbg(ddata, "%s: rx 0x%x\n", __func__, rxdata); + + if (ddata->rx_buf) { + *ddata->rx_buf = rxdata; + ddata->rx_buf++; + } + + ddata->rx_len--; +} + +static void efm32_spi_filltx(struct efm32_spi_ddata *ddata) +{ + while (ddata->tx_len && + ddata->tx_len + 2 > ddata->rx_len && + efm32_spi_read32(ddata, REG_STATUS) & REG_STATUS_TXBL) { + efm32_spi_tx_u8(ddata); + } +} + +static int efm32_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) +{ + struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master); + int ret = -EBUSY; + + spin_lock_irq(&ddata->lock); + + if (ddata->tx_buf || ddata->rx_buf) + goto out_unlock; + + ddata->tx_buf = t->tx_buf; + ddata->rx_buf = t->rx_buf; + ddata->tx_len = ddata->rx_len = + t->len * DIV_ROUND_UP(t->bits_per_word, 8); + + efm32_spi_filltx(ddata); + + init_completion(&ddata->done); + + efm32_spi_write32(ddata, REG_IF_TXBL | REG_IF_RXDATAV, REG_IEN); + + spin_unlock_irq(&ddata->lock); + + wait_for_completion(&ddata->done); + + spin_lock_irq(&ddata->lock); + + ret = t->len - max(ddata->tx_len, ddata->rx_len); + + efm32_spi_write32(ddata, 0, REG_IEN); + ddata->tx_buf = ddata->rx_buf = NULL; + +out_unlock: + spin_unlock_irq(&ddata->lock); + + return ret; +} + +static irqreturn_t efm32_spi_rxirq(int irq, void *data) +{ + struct efm32_spi_ddata *ddata = data; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&ddata->lock); + + while (ddata->rx_len > 0 && + efm32_spi_read32(ddata, REG_STATUS) & + REG_STATUS_RXDATAV) { + efm32_spi_rx_u8(ddata); + + ret = IRQ_HANDLED; + } + + if (!ddata->rx_len) { + u32 ien = efm32_spi_read32(ddata, REG_IEN); + + ien &= ~REG_IF_RXDATAV; + + efm32_spi_write32(ddata, ien, REG_IEN); + + complete(&ddata->done); + } + + spin_unlock(&ddata->lock); + + return ret; +} + +static irqreturn_t efm32_spi_txirq(int irq, void *data) +{ + struct efm32_spi_ddata *ddata = data; + + efm32_spi_vdbg(ddata, + "%s: txlen = %u, rxlen = %u, if=0x%08x, stat=0x%08x\n", + __func__, ddata->tx_len, ddata->rx_len, + efm32_spi_read32(ddata, REG_IF), + efm32_spi_read32(ddata, REG_STATUS)); + + spin_lock(&ddata->lock); + + efm32_spi_filltx(ddata); + + efm32_spi_vdbg(ddata, "%s: txlen = %u, rxlen = %u\n", + __func__, ddata->tx_len, ddata->rx_len); + + if (!ddata->tx_len) { + u32 ien = efm32_spi_read32(ddata, REG_IEN); + + ien &= ~REG_IF_TXBL; + + efm32_spi_write32(ddata, ien, REG_IEN); + efm32_spi_vdbg(ddata, "disable TXBL\n"); + } + + spin_unlock(&ddata->lock); + + return IRQ_HANDLED; +} + +static const struct efm32_spi_pdata efm32_spi_pdata_default = { + .location = 1, +}; + +static u32 efm32_spi_get_configured_location(struct efm32_spi_ddata *ddata) +{ + u32 reg = efm32_spi_read32(ddata, REG_ROUTE); + + return (reg & REG_ROUTE_LOCATION__MASK) >> __ffs(REG_ROUTE_LOCATION__MASK); +} + +static int efm32_spi_probe_dt(struct platform_device *pdev, + struct spi_master *master, struct efm32_spi_ddata *ddata) +{ + struct device_node *np = pdev->dev.of_node; + u32 location; + int ret; + + if (!np) + return 1; + + ret = of_property_read_u32(np, "location", &location); + if (!ret) { + dev_dbg(&pdev->dev, "using location %u\n", location); + } else { + /* default to location configured in hardware */ + location = efm32_spi_get_configured_location(ddata); + + dev_info(&pdev->dev, "fall back to location %u\n", location); + } + + ddata->pdata.location = location; + + /* spi core takes care about the bus number using an alias */ + master->bus_num = -1; + + return 0; +} + +static int efm32_spi_probe(struct platform_device *pdev) +{ + struct efm32_spi_ddata *ddata; + struct resource *res; + int ret; + struct spi_master *master; + struct device_node *np = pdev->dev.of_node; + unsigned int num_cs, i; + + num_cs = of_gpio_named_count(np, "cs-gpios"); + + master = spi_alloc_master(&pdev->dev, + sizeof(*ddata) + num_cs * sizeof(unsigned)); + if (!master) { + dev_dbg(&pdev->dev, + "failed to allocate spi master controller\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, master); + + master->dev.of_node = pdev->dev.of_node; + + master->num_chipselect = num_cs; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + + ddata = spi_master_get_devdata(master); + + ddata->bitbang.master = spi_master_get(master); + ddata->bitbang.chipselect = efm32_spi_chipselect; + ddata->bitbang.setup_transfer = efm32_spi_setup_transfer; + ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs; + + spin_lock_init(&ddata->lock); + + ddata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(ddata->clk)) { + ret = PTR_ERR(ddata->clk); + dev_err(&pdev->dev, "failed to get clock: %d\n", ret); + goto err; + } + + for (i = 0; i < num_cs; ++i) { + ret = of_get_named_gpio(np, "cs-gpios", i); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get csgpio#%u (%d)\n", + i, ret); + goto err; + } + ddata->csgpio[i] = ret; + dev_dbg(&pdev->dev, "csgpio#%u = %u\n", i, ddata->csgpio[i]); + ret = devm_gpio_request_one(&pdev->dev, ddata->csgpio[i], + GPIOF_OUT_INIT_LOW, DRIVER_NAME); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to configure csgpio#%u (%d)\n", + i, ret); + goto err; + } + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + dev_err(&pdev->dev, "failed to determine base address\n"); + goto err; + } + + if (resource_size(res) < 60) { + ret = -EINVAL; + dev_err(&pdev->dev, "memory resource too small\n"); + goto err; + } + + ddata->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ddata->base)) { + ret = PTR_ERR(ddata->base); + goto err; + } + + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "failed to get rx irq (%d)\n", ret); + goto err; + } + + ddata->rxirq = ret; + + ret = platform_get_irq(pdev, 1); + if (ret <= 0) + ret = ddata->rxirq + 1; + + ddata->txirq = ret; + + ret = clk_prepare_enable(ddata->clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret); + goto err; + } + + ret = efm32_spi_probe_dt(pdev, master, ddata); + if (ret > 0) { + /* not created by device tree */ + const struct efm32_spi_pdata *pdata = + dev_get_platdata(&pdev->dev); + + if (pdata) + ddata->pdata = *pdata; + else + ddata->pdata.location = + efm32_spi_get_configured_location(ddata); + + master->bus_num = pdev->id; + + } else if (ret < 0) { + goto err_disable_clk; + } + + efm32_spi_write32(ddata, 0, REG_IEN); + efm32_spi_write32(ddata, REG_ROUTE_TXPEN | REG_ROUTE_RXPEN | + REG_ROUTE_CLKPEN | + REG_ROUTE_LOCATION(ddata->pdata.location), REG_ROUTE); + + ret = request_irq(ddata->rxirq, efm32_spi_rxirq, + 0, DRIVER_NAME " rx", ddata); + if (ret) { + dev_err(&pdev->dev, "failed to register rxirq (%d)\n", ret); + goto err_disable_clk; + } + + ret = request_irq(ddata->txirq, efm32_spi_txirq, + 0, DRIVER_NAME " tx", ddata); + if (ret) { + dev_err(&pdev->dev, "failed to register txirq (%d)\n", ret); + goto err_free_rx_irq; + } + + ret = spi_bitbang_start(&ddata->bitbang); + if (ret) { + dev_err(&pdev->dev, "spi_bitbang_start failed (%d)\n", ret); + + free_irq(ddata->txirq, ddata); +err_free_rx_irq: + free_irq(ddata->rxirq, ddata); +err_disable_clk: + clk_disable_unprepare(ddata->clk); +err: + spi_master_put(master); + kfree(master); + } + + return ret; +} + +static int efm32_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct efm32_spi_ddata *ddata = spi_master_get_devdata(master); + + efm32_spi_write32(ddata, 0, REG_IEN); + + free_irq(ddata->txirq, ddata); + free_irq(ddata->rxirq, ddata); + clk_disable_unprepare(ddata->clk); + spi_master_put(master); + kfree(master); + + return 0; +} + +static const struct of_device_id efm32_spi_dt_ids[] = { + { + .compatible = "efm32,spi", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, efm32_spi_dt_ids); + +static struct platform_driver efm32_spi_driver = { + .probe = efm32_spi_probe, + .remove = efm32_spi_remove, + + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = efm32_spi_dt_ids, + }, +}; +module_platform_driver(efm32_spi_driver); + +MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); +MODULE_DESCRIPTION("EFM32 SPI driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c new file mode 100644 index 00000000000..6cd07d13eca --- /dev/null +++ b/drivers/spi/spi-fsl-dspi.c @@ -0,0 +1,557 @@ +/* + * drivers/spi/spi-fsl-dspi.c + * + * Copyright 2013 Freescale Semiconductor, Inc. + * + * Freescale DSPI driver + * This file contains a driver for the Freescale DSPI + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_device.h> + +#define DRIVER_NAME "fsl-dspi" + +#define TRAN_STATE_RX_VOID 0x01 +#define TRAN_STATE_TX_VOID 0x02 +#define TRAN_STATE_WORD_ODD_NUM 0x04 + +#define DSPI_FIFO_SIZE 4 + +#define SPI_MCR 0x00 +#define SPI_MCR_MASTER (1 << 31) +#define SPI_MCR_PCSIS (0x3F << 16) +#define SPI_MCR_CLR_TXF (1 << 11) +#define SPI_MCR_CLR_RXF (1 << 10) + +#define SPI_TCR 0x08 + +#define SPI_CTAR(x) (0x0c + (x * 4)) +#define SPI_CTAR_FMSZ(x) (((x) & 0x0000000f) << 27) +#define SPI_CTAR_CPOL(x) ((x) << 26) +#define SPI_CTAR_CPHA(x) ((x) << 25) +#define SPI_CTAR_LSBFE(x) ((x) << 24) +#define SPI_CTAR_PCSSCR(x) (((x) & 0x00000003) << 22) +#define SPI_CTAR_PASC(x) (((x) & 0x00000003) << 20) +#define SPI_CTAR_PDT(x) (((x) & 0x00000003) << 18) +#define SPI_CTAR_PBR(x) (((x) & 0x00000003) << 16) +#define SPI_CTAR_CSSCK(x) (((x) & 0x0000000f) << 12) +#define SPI_CTAR_ASC(x) (((x) & 0x0000000f) << 8) +#define SPI_CTAR_DT(x) (((x) & 0x0000000f) << 4) +#define SPI_CTAR_BR(x) ((x) & 0x0000000f) + +#define SPI_CTAR0_SLAVE 0x0c + +#define SPI_SR 0x2c +#define SPI_SR_EOQF 0x10000000 + +#define SPI_RSER 0x30 +#define SPI_RSER_EOQFE 0x10000000 + +#define SPI_PUSHR 0x34 +#define SPI_PUSHR_CONT (1 << 31) +#define SPI_PUSHR_CTAS(x) (((x) & 0x00000007) << 28) +#define SPI_PUSHR_EOQ (1 << 27) +#define SPI_PUSHR_CTCNT (1 << 26) +#define SPI_PUSHR_PCS(x) (((1 << x) & 0x0000003f) << 16) +#define SPI_PUSHR_TXDATA(x) ((x) & 0x0000ffff) + +#define SPI_PUSHR_SLAVE 0x34 + +#define SPI_POPR 0x38 +#define SPI_POPR_RXDATA(x) ((x) & 0x0000ffff) + +#define SPI_TXFR0 0x3c +#define SPI_TXFR1 0x40 +#define SPI_TXFR2 0x44 +#define SPI_TXFR3 0x48 +#define SPI_RXFR0 0x7c +#define SPI_RXFR1 0x80 +#define SPI_RXFR2 0x84 +#define SPI_RXFR3 0x88 + +#define SPI_FRAME_BITS(bits) SPI_CTAR_FMSZ((bits) - 1) +#define SPI_FRAME_BITS_MASK SPI_CTAR_FMSZ(0xf) +#define SPI_FRAME_BITS_16 SPI_CTAR_FMSZ(0xf) +#define SPI_FRAME_BITS_8 SPI_CTAR_FMSZ(0x7) + +#define SPI_CS_INIT 0x01 +#define SPI_CS_ASSERT 0x02 +#define SPI_CS_DROP 0x04 + +struct chip_data { + u32 mcr_val; + u32 ctar_val; + u16 void_write_data; +}; + +struct fsl_dspi { + struct spi_bitbang bitbang; + struct platform_device *pdev; + + void *base; + int irq; + struct clk *clk; + + struct spi_transfer *cur_transfer; + struct chip_data *cur_chip; + size_t len; + void *tx; + void *tx_end; + void *rx; + void *rx_end; + char dataflags; + u8 cs; + u16 void_write_data; + + wait_queue_head_t waitq; + u32 waitflags; +}; + +static inline int is_double_byte_mode(struct fsl_dspi *dspi) +{ + return ((readl(dspi->base + SPI_CTAR(dspi->cs)) & SPI_FRAME_BITS_MASK) + == SPI_FRAME_BITS(8)) ? 0 : 1; +} + +static void set_bit_mode(struct fsl_dspi *dspi, unsigned char bits) +{ + u32 temp; + + temp = readl(dspi->base + SPI_CTAR(dspi->cs)); + temp &= ~SPI_FRAME_BITS_MASK; + temp |= SPI_FRAME_BITS(bits); + writel(temp, dspi->base + SPI_CTAR(dspi->cs)); +} + +static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, + unsigned long clkrate) +{ + /* Valid baud rate pre-scaler values */ + int pbr_tbl[4] = {2, 3, 5, 7}; + int brs[16] = { 2, 4, 6, 8, + 16, 32, 64, 128, + 256, 512, 1024, 2048, + 4096, 8192, 16384, 32768 }; + int temp, i = 0, j = 0; + + temp = clkrate / 2 / speed_hz; + + for (i = 0; i < ARRAY_SIZE(pbr_tbl); i++) + for (j = 0; j < ARRAY_SIZE(brs); j++) { + if (pbr_tbl[i] * brs[j] >= temp) { + *pbr = i; + *br = j; + return; + } + } + + pr_warn("Can not find valid buad rate,speed_hz is %d,clkrate is %ld\ + ,we use the max prescaler value.\n", speed_hz, clkrate); + *pbr = ARRAY_SIZE(pbr_tbl) - 1; + *br = ARRAY_SIZE(brs) - 1; +} + +static int dspi_transfer_write(struct fsl_dspi *dspi) +{ + int tx_count = 0; + int tx_word; + u16 d16; + u8 d8; + u32 dspi_pushr = 0; + int first = 1; + + tx_word = is_double_byte_mode(dspi); + + /* If we are in word mode, but only have a single byte to transfer + * then switch to byte mode temporarily. Will switch back at the + * end of the transfer. + */ + if (tx_word && (dspi->len == 1)) { + dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; + set_bit_mode(dspi, 8); + tx_word = 0; + } + + while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) { + if (tx_word) { + if (dspi->len == 1) + break; + + if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { + d16 = *(u16 *)dspi->tx; + dspi->tx += 2; + } else { + d16 = dspi->void_write_data; + } + + dspi_pushr = SPI_PUSHR_TXDATA(d16) | + SPI_PUSHR_PCS(dspi->cs) | + SPI_PUSHR_CTAS(dspi->cs) | + SPI_PUSHR_CONT; + + dspi->len -= 2; + } else { + if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { + + d8 = *(u8 *)dspi->tx; + dspi->tx++; + } else { + d8 = (u8)dspi->void_write_data; + } + + dspi_pushr = SPI_PUSHR_TXDATA(d8) | + SPI_PUSHR_PCS(dspi->cs) | + SPI_PUSHR_CTAS(dspi->cs) | + SPI_PUSHR_CONT; + + dspi->len--; + } + + if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) { + /* last transfer in the transfer */ + dspi_pushr |= SPI_PUSHR_EOQ; + } else if (tx_word && (dspi->len == 1)) + dspi_pushr |= SPI_PUSHR_EOQ; + + if (first) { + first = 0; + dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */ + } + + writel(dspi_pushr, dspi->base + SPI_PUSHR); + tx_count++; + } + + return tx_count * (tx_word + 1); +} + +static int dspi_transfer_read(struct fsl_dspi *dspi) +{ + int rx_count = 0; + int rx_word = is_double_byte_mode(dspi); + u16 d; + while ((dspi->rx < dspi->rx_end) + && (rx_count < DSPI_FIFO_SIZE)) { + if (rx_word) { + if ((dspi->rx_end - dspi->rx) == 1) + break; + + d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR)); + + if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) + *(u16 *)dspi->rx = d; + dspi->rx += 2; + + } else { + d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR)); + if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) + *(u8 *)dspi->rx = d; + dspi->rx++; + } + rx_count++; + } + + return rx_count; +} + +static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); + dspi->cur_transfer = t; + dspi->cur_chip = spi_get_ctldata(spi); + dspi->cs = spi->chip_select; + dspi->void_write_data = dspi->cur_chip->void_write_data; + + dspi->dataflags = 0; + dspi->tx = (void *)t->tx_buf; + dspi->tx_end = dspi->tx + t->len; + dspi->rx = t->rx_buf; + dspi->rx_end = dspi->rx + t->len; + dspi->len = t->len; + + if (!dspi->rx) + dspi->dataflags |= TRAN_STATE_RX_VOID; + + if (!dspi->tx) + dspi->dataflags |= TRAN_STATE_TX_VOID; + + writel(dspi->cur_chip->mcr_val, dspi->base + SPI_MCR); + writel(dspi->cur_chip->ctar_val, dspi->base + SPI_CTAR(dspi->cs)); + writel(SPI_RSER_EOQFE, dspi->base + SPI_RSER); + + if (t->speed_hz) + writel(dspi->cur_chip->ctar_val, + dspi->base + SPI_CTAR(dspi->cs)); + + dspi_transfer_write(dspi); + + if (wait_event_interruptible(dspi->waitq, dspi->waitflags)) + dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n"); + dspi->waitflags = 0; + + return t->len - dspi->len; +} + +static void dspi_chipselect(struct spi_device *spi, int value) +{ + struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); + u32 pushr = readl(dspi->base + SPI_PUSHR); + + switch (value) { + case BITBANG_CS_ACTIVE: + pushr |= SPI_PUSHR_CONT; + case BITBANG_CS_INACTIVE: + pushr &= ~SPI_PUSHR_CONT; + } + + writel(pushr, dspi->base + SPI_PUSHR); +} + +static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct chip_data *chip; + struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); + unsigned char br = 0, pbr = 0, fmsz = 0; + + /* Only alloc on first setup */ + chip = spi_get_ctldata(spi); + if (chip == NULL) { + chip = kcalloc(1, sizeof(struct chip_data), GFP_KERNEL); + if (!chip) + return -ENOMEM; + } + + chip->mcr_val = SPI_MCR_MASTER | SPI_MCR_PCSIS | + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF; + if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) { + fmsz = spi->bits_per_word - 1; + } else { + pr_err("Invalid wordsize\n"); + kfree(chip); + return -ENODEV; + } + + chip->void_write_data = 0; + + hz_to_spi_baud(&pbr, &br, + spi->max_speed_hz, clk_get_rate(dspi->clk)); + + chip->ctar_val = SPI_CTAR_FMSZ(fmsz) + | SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0) + | SPI_CTAR_CPHA(spi->mode & SPI_CPHA ? 1 : 0) + | SPI_CTAR_LSBFE(spi->mode & SPI_LSB_FIRST ? 1 : 0) + | SPI_CTAR_PBR(pbr) + | SPI_CTAR_BR(br); + + spi_set_ctldata(spi, chip); + + return 0; +} + +static int dspi_setup(struct spi_device *spi) +{ + if (!spi->max_speed_hz) + return -EINVAL; + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + return dspi_setup_transfer(spi, NULL); +} + +static irqreturn_t dspi_interrupt(int irq, void *dev_id) +{ + struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; + + writel(SPI_SR_EOQF, dspi->base + SPI_SR); + + dspi_transfer_read(dspi); + + if (!dspi->len) { + if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) + set_bit_mode(dspi, 16); + dspi->waitflags = 1; + wake_up_interruptible(&dspi->waitq); + } else { + dspi_transfer_write(dspi); + + return IRQ_HANDLED; + } + + return IRQ_HANDLED; +} + +static struct of_device_id fsl_dspi_dt_ids[] = { + { .compatible = "fsl,vf610-dspi", .data = NULL, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids); + +#ifdef CONFIG_PM_SLEEP +static int dspi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct fsl_dspi *dspi = spi_master_get_devdata(master); + + spi_master_suspend(master); + clk_disable_unprepare(dspi->clk); + + return 0; +} + +static int dspi_resume(struct device *dev) +{ + + struct spi_master *master = dev_get_drvdata(dev); + struct fsl_dspi *dspi = spi_master_get_devdata(master); + + clk_prepare_enable(dspi->clk); + spi_master_resume(master); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops dspi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(dspi_suspend, dspi_resume) +}; + +static int dspi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spi_master *master; + struct fsl_dspi *dspi; + struct resource *res; + int ret = 0, cs_num, bus_num; + + master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi)); + if (!master) + return -ENOMEM; + + dspi = spi_master_get_devdata(master); + dspi->pdev = pdev; + dspi->bitbang.master = spi_master_get(master); + dspi->bitbang.chipselect = dspi_chipselect; + dspi->bitbang.setup_transfer = dspi_setup_transfer; + dspi->bitbang.txrx_bufs = dspi_txrx_transfer; + dspi->bitbang.master->setup = dspi_setup; + dspi->bitbang.master->dev.of_node = pdev->dev.of_node; + + master->mode_bits = SPI_CPOL | SPI_CPHA; + master->bits_per_word_mask = SPI_BPW_MASK(4) | SPI_BPW_MASK(8) | + SPI_BPW_MASK(16); + + ret = of_property_read_u32(np, "spi-num-chipselects", &cs_num); + if (ret < 0) { + dev_err(&pdev->dev, "can't get spi-num-chipselects\n"); + goto out_master_put; + } + master->num_chipselect = cs_num; + + ret = of_property_read_u32(np, "bus-num", &bus_num); + if (ret < 0) { + dev_err(&pdev->dev, "can't get bus-num\n"); + goto out_master_put; + } + master->bus_num = bus_num; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get platform resource\n"); + ret = -EINVAL; + goto out_master_put; + } + + dspi->base = devm_ioremap_resource(&pdev->dev, res); + if (!dspi->base) { + ret = -EINVAL; + goto out_master_put; + } + + dspi->irq = platform_get_irq(pdev, 0); + if (dspi->irq < 0) { + dev_err(&pdev->dev, "can't get platform irq\n"); + ret = dspi->irq; + goto out_master_put; + } + + ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, 0, + pdev->name, dspi); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n"); + goto out_master_put; + } + + dspi->clk = devm_clk_get(&pdev->dev, "dspi"); + if (IS_ERR(dspi->clk)) { + ret = PTR_ERR(dspi->clk); + dev_err(&pdev->dev, "unable to get clock\n"); + goto out_master_put; + } + clk_prepare_enable(dspi->clk); + + init_waitqueue_head(&dspi->waitq); + platform_set_drvdata(pdev, dspi); + + ret = spi_bitbang_start(&dspi->bitbang); + if (ret != 0) { + dev_err(&pdev->dev, "Problem registering DSPI master\n"); + goto out_clk_put; + } + + pr_info(KERN_INFO "Freescale DSPI master initialized\n"); + return ret; + +out_clk_put: + clk_disable_unprepare(dspi->clk); +out_master_put: + spi_master_put(master); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int dspi_remove(struct platform_device *pdev) +{ + struct fsl_dspi *dspi = platform_get_drvdata(pdev); + + /* Disconnect from the SPI framework */ + spi_bitbang_stop(&dspi->bitbang); + spi_master_put(dspi->bitbang.master); + + return 0; +} + +static struct platform_driver fsl_dspi_driver = { + .driver.name = DRIVER_NAME, + .driver.of_match_table = fsl_dspi_dt_ids, + .driver.owner = THIS_MODULE, + .driver.pm = &dspi_pm, + .probe = dspi_probe, + .remove = dspi_remove, +}; +module_platform_driver(fsl_dspi_driver); + +MODULE_DESCRIPTION("Freescale DSPI Controller Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index 58deb79d046..d36ba907743 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -315,15 +315,11 @@ static int tiny_spi_probe(struct platform_device *pdev) /* find and map our resources */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - goto exit_busy; - if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), - pdev->name)) - goto exit_busy; - hw->base = devm_ioremap_nocache(&pdev->dev, res->start, - resource_size(res)); - if (!hw->base) - goto exit_busy; + hw->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hw->base)) { + err = PTR_ERR(hw->base); + goto exit; + } /* irq is optional */ hw->irq = platform_get_irq(pdev, 0); if (hw->irq >= 0) { @@ -337,8 +333,10 @@ static int tiny_spi_probe(struct platform_device *pdev) if (platp) { hw->gpio_cs_count = platp->gpio_cs_count; hw->gpio_cs = platp->gpio_cs; - if (platp->gpio_cs_count && !platp->gpio_cs) - goto exit_busy; + if (platp->gpio_cs_count && !platp->gpio_cs) { + err = -EBUSY; + goto exit; + } hw->freq = platp->freq; hw->baudwidth = platp->baudwidth; } else { @@ -365,8 +363,6 @@ static int tiny_spi_probe(struct platform_device *pdev) exit_gpio: while (i-- > 0) gpio_free(hw->gpio_cs[i]); -exit_busy: - err = -EBUSY; exit: spi_master_put(master); return err; diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 5d90bebaa0f..d1bae77f157 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/clk.h> +#include <linux/sizes.h> #include <asm/unaligned.h> #define DRIVER_NAME "orion_spi" diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index eb53df27e7e..63e2070c6c1 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -434,6 +434,9 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) dma_cap_mask_t mask; int ret; + if (is_polling(sdd)) + return 0; + dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 978dda2c523..8bc7de019e5 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -774,7 +774,7 @@ static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg) msg->status = -EINPROGRESS; list_add_tail(&msg->queue, &master->queue); - if (master->running && !master->busy) + if (!master->busy) queue_kthread_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); @@ -1169,7 +1169,7 @@ int spi_register_master(struct spi_master *master) else { status = spi_master_initialize_queue(master); if (status) { - device_unregister(&master->dev); + device_del(&master->dev); goto done; } } @@ -1351,6 +1351,11 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) struct spi_master *master = spi->master; struct spi_transfer *xfer; + if (list_empty(&message->transfers)) + return -EINVAL; + if (!message->complete) + return -EINVAL; + /* Half-duplex links include original MicroWire, and ones with * only one data pin like SPI_3WIRE (switches direction) or where * either MOSI or MISO is missing. They can also be caused by @@ -1377,8 +1382,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) list_for_each_entry(xfer, &message->transfers, transfer_list) { if (!xfer->bits_per_word) xfer->bits_per_word = spi->bits_per_word; - if (!xfer->speed_hz) + if (!xfer->speed_hz) { xfer->speed_hz = spi->max_speed_hz; + if (master->max_speed_hz && + xfer->speed_hz > master->max_speed_hz) + xfer->speed_hz = master->max_speed_hz; + } + if (master->bits_per_word_mask) { /* Only 32 bits fit in the mask */ if (xfer->bits_per_word > 32) @@ -1387,6 +1397,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) BIT(xfer->bits_per_word - 1))) return -EINVAL; } + + if (xfer->speed_hz && master->min_speed_hz && + xfer->speed_hz < master->min_speed_hz) + return -EINVAL; + if (xfer->speed_hz && master->max_speed_hz && + xfer->speed_hz > master->max_speed_hz) + return -EINVAL; } message->spi = spi; |