From d77b5382e67d1e1394e40c5c95fb5947efe0ff9e Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 22 Feb 2013 10:52:35 +0800 Subject: spi: fix return value check in ce4100_spi_probe() In case of error, the function platform_device_register_full() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Acked-by: Mika Westerberg Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 364964d..0a11dcfc 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -47,8 +47,8 @@ static int ce4100_spi_probe(struct pci_dev *dev, pi.size_data = sizeof(spi_pdata); pdev = platform_device_register_full(&pi); - if (!pdev) - return -ENOMEM; + if (IS_ERR(pdev)) + return PTR_ERR(pdev); pci_set_drvdata(dev, pdev); -- cgit v0.10.2 From f8043872e79614ae9c5aaf7804e0b0ccb1932ed0 Mon Sep 17 00:00:00 2001 From: Chris Boot Date: Mon, 11 Mar 2013 21:38:24 -0600 Subject: spi: add driver for BCM2835 The BCM2835 contains two forms of SPI master controller (one known simply as SPI0, and the other known as the "Universal SPI Master", in the auxilliary block) and one form of SPI slave controller. This patch adds support for the SPI0 controller. This driver is taken from Chris Boot's repository at git://github.com/bootc/linux.git rpi-linear as of commit 6de2905 "spi-bcm2708: fix printf with spurious %s". In the first SPI-related commit there, Chris wrote: Thanks to csoutreach / A Robinson for his driver which I used as an inspiration. You can find his version here: http://piface.openlx.org.uk/raspberry-pi-spi-kernel-driver-available-for Changes made during upstreaming: * Renamed bcm2708 to bcm2835 as per upstream naming for this SoC. * Removed support for brcm,realtime property. * Increased transfer timeout to 30 seconds. * Return IRQ_NONE from the IRQ handler if no interrupt was handled. * Disable TA (Transfer Active) and clear FIFOs on a transfer timeout. * Wrote device tree binding documentation. * Request unnamed clock rather than "sys_pclk"; the DT will provide the correct clock. * Assume that tfr->speed_hz and tfr->bits_per_word are always set in bcm2835_spi_start_transfer(), bcm2835_spi_transfer_one(), so no need to check spi->speed_hz or tft->bits_per_word. * Re-ordered probe() to remove the need for temporary variables. * Call clk_disable_unprepare() rather than just clk_unprepare() on probe() failure. * Don't use devm_request_irq(), to ensure that the IRQ doesn't fire after we've torn down the device, but not unhooked the IRQ. * Moved probe()'s call to clk_prepare_enable() so we can be sure the clock is enabled if the IRQ handler fires immediately. * Remove redundant checks from bcm2835_spi_check_transfer() and bcm2835_spi_setup(). * Re-ordered IRQ handler to check for RXR before DONE. Added comments to ISR. * Removed empty prepare/unprepare implementations. * Removed use of devinit/devexit. * Added BCM2835_ prefix to defines. Signed-off-by: Chris Boot Signed-off-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt b/Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt new file mode 100644 index 0000000..8bf89c6 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt @@ -0,0 +1,22 @@ +Broadcom BCM2835 SPI0 controller + +The BCM2835 contains two forms of SPI master controller, one known simply as +SPI0, and the other known as the "Universal SPI Master"; part of the +auxilliary block. This binding applies to the SPI0 controller. + +Required properties: +- compatible: Should be "brcm,bcm2835-spi". +- reg: Should contain register location and length. +- interrupts: Should contain interrupt. +- clocks: The clock feeding the SPI controller. + +Example: + +spi@20204000 { + compatible = "brcm,bcm2835-spi"; + reg = <0x7e204000 0x1000>; + interrupts = <2 22>; + clocks = <&clk_spi>; + #address-cells = <1>; + #size-cells = <0>; +}; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f80eee7..32b85d4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -74,6 +74,17 @@ config SPI_ATMEL 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 + help + This selects a driver for the Broadcom BCM2835 SPI master. + + The BCM2835 contains two types of SPI master controller; the + "universal SPI master", and the regular SPI controller. This driver + is for the regular SPI controller. Slave mode operation is not also + not supported. + config SPI_BFIN5XX tristate "SPI controller driver for ADI Blackfin5xx" depends on BLACKFIN diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e53c309..3ce1d08 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera.o obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o obj-$(CONFIG_SPI_ATH79) += spi-ath79.o 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_SPORT) += spi-bfin-sport.o diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c new file mode 100644 index 0000000..346601e --- /dev/null +++ b/drivers/spi/spi-bcm2835.c @@ -0,0 +1,456 @@ +/* + * Driver for Broadcom BCM2835 SPI Controllers + * + * Copyright (C) 2012 Chris Boot + * Copyright (C) 2013 Stephen Warren + * + * This driver is inspired by: + * spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos + * spi-atmel.c, Copyright (C) 2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SPI register offsets */ +#define BCM2835_SPI_CS 0x00 +#define BCM2835_SPI_FIFO 0x04 +#define BCM2835_SPI_CLK 0x08 +#define BCM2835_SPI_DLEN 0x0c +#define BCM2835_SPI_LTOH 0x10 +#define BCM2835_SPI_DC 0x14 + +/* Bitfields in CS */ +#define BCM2835_SPI_CS_LEN_LONG 0x02000000 +#define BCM2835_SPI_CS_DMA_LEN 0x01000000 +#define BCM2835_SPI_CS_CSPOL2 0x00800000 +#define BCM2835_SPI_CS_CSPOL1 0x00400000 +#define BCM2835_SPI_CS_CSPOL0 0x00200000 +#define BCM2835_SPI_CS_RXF 0x00100000 +#define BCM2835_SPI_CS_RXR 0x00080000 +#define BCM2835_SPI_CS_TXD 0x00040000 +#define BCM2835_SPI_CS_RXD 0x00020000 +#define BCM2835_SPI_CS_DONE 0x00010000 +#define BCM2835_SPI_CS_LEN 0x00002000 +#define BCM2835_SPI_CS_REN 0x00001000 +#define BCM2835_SPI_CS_ADCS 0x00000800 +#define BCM2835_SPI_CS_INTR 0x00000400 +#define BCM2835_SPI_CS_INTD 0x00000200 +#define BCM2835_SPI_CS_DMAEN 0x00000100 +#define BCM2835_SPI_CS_TA 0x00000080 +#define BCM2835_SPI_CS_CSPOL 0x00000040 +#define BCM2835_SPI_CS_CLEAR_RX 0x00000020 +#define BCM2835_SPI_CS_CLEAR_TX 0x00000010 +#define BCM2835_SPI_CS_CPOL 0x00000008 +#define BCM2835_SPI_CS_CPHA 0x00000004 +#define BCM2835_SPI_CS_CS_10 0x00000002 +#define BCM2835_SPI_CS_CS_01 0x00000001 + +#define BCM2835_SPI_TIMEOUT_MS 30000 +#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS) + +#define DRV_NAME "spi-bcm2835" + +struct bcm2835_spi { + void __iomem *regs; + struct clk *clk; + int irq; + struct completion done; + const u8 *tx_buf; + u8 *rx_buf; + int len; +}; + +static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg) +{ + return readl(bs->regs + reg); +} + +static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned reg, u32 val) +{ + writel(val, bs->regs + reg); +} + +static inline void bcm2835_rd_fifo(struct bcm2835_spi *bs, int len) +{ + u8 byte; + + while (len--) { + byte = bcm2835_rd(bs, BCM2835_SPI_FIFO); + if (bs->rx_buf) + *bs->rx_buf++ = byte; + } +} + +static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs, int len) +{ + u8 byte; + + if (len > bs->len) + len = bs->len; + + while (len--) { + byte = bs->tx_buf ? *bs->tx_buf++ : 0; + bcm2835_wr(bs, BCM2835_SPI_FIFO, byte); + bs->len--; + } +} + +static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct bcm2835_spi *bs = spi_master_get_devdata(master); + u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); + + /* + * RXR - RX needs Reading. This means 12 (or more) bytes have been + * transmitted and hence 12 (or more) bytes have been received. + * + * The FIFO is 16-bytes deep. We check for this interrupt to keep the + * FIFO full; we have a 4-byte-time buffer for IRQ latency. We check + * this before DONE (TX empty) just in case we delayed processing this + * interrupt for some reason. + * + * We only check for this case if we have more bytes to TX; at the end + * of the transfer, we ignore this pipelining optimization, and let + * bcm2835_spi_finish_transfer() drain the RX FIFO. + */ + if (bs->len && (cs & BCM2835_SPI_CS_RXR)) { + /* Read 12 bytes of data */ + bcm2835_rd_fifo(bs, 12); + + /* Write up to 12 bytes */ + bcm2835_wr_fifo(bs, 12); + + /* + * We must have written something to the TX FIFO due to the + * bs->len check above, so cannot be DONE. Hence, return + * early. Note that DONE could also be set if we serviced an + * RXR interrupt really late. + */ + return IRQ_HANDLED; + } + + /* + * DONE - TX empty. This occurs when we first enable the transfer + * since we do not pre-fill the TX FIFO. At any other time, given that + * we refill the TX FIFO above based on RXR, and hence ignore DONE if + * RXR is set, DONE really does mean end-of-transfer. + */ + if (cs & BCM2835_SPI_CS_DONE) { + if (bs->len) { /* First interrupt in a transfer */ + bcm2835_wr_fifo(bs, 16); + } else { /* Transfer complete */ + /* Disable SPI interrupts */ + cs &= ~(BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD); + bcm2835_wr(bs, BCM2835_SPI_CS, cs); + + /* + * Wake up bcm2835_spi_transfer_one(), which will call + * bcm2835_spi_finish_transfer(), to drain the RX FIFO. + */ + complete(&bs->done); + } + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int bcm2835_spi_check_transfer(struct spi_device *spi, + struct spi_transfer *tfr) +{ + /* tfr==NULL when called from bcm2835_spi_setup() */ + u32 bpw = tfr ? tfr->bits_per_word : spi->bits_per_word; + + switch (bpw) { + case 8: + break; + default: + dev_err(&spi->dev, "unsupported bits_per_word=%d\n", bpw); + return -EINVAL; + } + + return 0; +} + +static int bcm2835_spi_start_transfer(struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct bcm2835_spi *bs = spi_master_get_devdata(spi->master); + unsigned long spi_hz, clk_hz, cdiv; + u32 cs = BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_TA; + + spi_hz = tfr->speed_hz; + clk_hz = clk_get_rate(bs->clk); + + if (spi_hz >= clk_hz / 2) { + cdiv = 2; /* clk_hz/2 is the fastest we can go */ + } else if (spi_hz) { + /* CDIV must be a power of two */ + cdiv = roundup_pow_of_two(DIV_ROUND_UP(clk_hz, spi_hz)); + + if (cdiv >= 65536) + cdiv = 0; /* 0 is the slowest we can go */ + } else + cdiv = 0; /* 0 is the slowest we can go */ + + if (spi->mode & SPI_CPOL) + cs |= BCM2835_SPI_CS_CPOL; + if (spi->mode & SPI_CPHA) + cs |= BCM2835_SPI_CS_CPHA; + + if (!(spi->mode & SPI_NO_CS)) { + if (spi->mode & SPI_CS_HIGH) { + cs |= BCM2835_SPI_CS_CSPOL; + cs |= BCM2835_SPI_CS_CSPOL0 << spi->chip_select; + } + + cs |= spi->chip_select; + } + + INIT_COMPLETION(bs->done); + bs->tx_buf = tfr->tx_buf; + bs->rx_buf = tfr->rx_buf; + bs->len = tfr->len; + + bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv); + /* + * Enable the HW block. This will immediately trigger a DONE (TX + * empty) interrupt, upon which we will fill the TX FIFO with the + * first TX bytes. Pre-filling the TX FIFO here to avoid the + * interrupt doesn't work:-( + */ + bcm2835_wr(bs, BCM2835_SPI_CS, cs); + + return 0; +} + +static int bcm2835_spi_finish_transfer(struct spi_device *spi, + struct spi_transfer *tfr, bool cs_change) +{ + struct bcm2835_spi *bs = spi_master_get_devdata(spi->master); + u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); + + /* Drain RX FIFO */ + while (cs & BCM2835_SPI_CS_RXD) { + bcm2835_rd_fifo(bs, 1); + cs = bcm2835_rd(bs, BCM2835_SPI_CS); + } + + if (tfr->delay_usecs) + udelay(tfr->delay_usecs); + + if (cs_change) + /* Clear TA flag */ + bcm2835_wr(bs, BCM2835_SPI_CS, cs & ~BCM2835_SPI_CS_TA); + + return 0; +} + +static int bcm2835_spi_setup(struct spi_device *spi) +{ + int ret; + + ret = bcm2835_spi_check_transfer(spi, NULL); + if (ret) { + dev_err(&spi->dev, "setup: invalid message\n"); + return ret; + } + + return 0; +} + +static int bcm2835_spi_transfer_one(struct spi_master *master, + struct spi_message *mesg) +{ + struct bcm2835_spi *bs = spi_master_get_devdata(master); + struct spi_transfer *tfr; + struct spi_device *spi = mesg->spi; + int err = 0; + unsigned int timeout; + bool cs_change; + + list_for_each_entry(tfr, &mesg->transfers, transfer_list) { + err = bcm2835_spi_check_transfer(spi, tfr); + if (err) + goto out; + + err = bcm2835_spi_start_transfer(spi, tfr); + if (err) + goto out; + + timeout = wait_for_completion_timeout(&bs->done, + msecs_to_jiffies(BCM2835_SPI_TIMEOUT_MS)); + if (!timeout) { + err = -ETIMEDOUT; + goto out; + } + + cs_change = tfr->cs_change || + list_is_last(&tfr->transfer_list, &mesg->transfers); + + err = bcm2835_spi_finish_transfer(spi, tfr, cs_change); + if (err) + goto out; + + mesg->actual_length += (tfr->len - bs->len); + } + +out: + /* Clear FIFOs, and disable the HW block */ + bcm2835_wr(bs, BCM2835_SPI_CS, + BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); + mesg->status = err; + spi_finalize_current_message(master); + + return 0; +} + +static int bcm2835_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct bcm2835_spi *bs; + struct resource *res; + int err; + + master = spi_alloc_master(&pdev->dev, sizeof(*bs)); + if (!master) { + dev_err(&pdev->dev, "spi_alloc_master() failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, master); + + master->mode_bits = BCM2835_SPI_MODE_BITS; + master->bus_num = -1; + master->num_chipselect = 3; + master->setup = bcm2835_spi_setup; + master->transfer_one_message = bcm2835_spi_transfer_one; + master->dev.of_node = pdev->dev.of_node; + + bs = spi_master_get_devdata(master); + + 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_request_and_ioremap(&pdev->dev, res); + if (!bs->regs) { + dev_err(&pdev->dev, "could not request/map memory region\n"); + err = -ENODEV; + goto out_master_put; + } + + bs->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(bs->clk)) { + err = PTR_ERR(bs->clk); + dev_err(&pdev->dev, "could not get clk: %d\n", err); + goto out_master_put; + } + + bs->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (bs->irq <= 0) { + dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq); + err = bs->irq ? bs->irq : -ENODEV; + goto out_master_put; + } + + clk_prepare_enable(bs->clk); + + err = request_irq(bs->irq, bcm2835_spi_interrupt, 0, + dev_name(&pdev->dev), master); + if (err) { + dev_err(&pdev->dev, "could not request IRQ: %d\n", err); + goto out_clk_disable; + } + + /* initialise the hardware */ + bcm2835_wr(bs, BCM2835_SPI_CS, + BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); + + err = spi_register_master(master); + if (err) { + dev_err(&pdev->dev, "could not register SPI master: %d\n", err); + goto out_free_irq; + } + + return 0; + +out_free_irq: + free_irq(bs->irq, master); +out_clk_disable: + clk_disable_unprepare(bs->clk); +out_master_put: + spi_master_put(master); + return err; +} + +static int bcm2835_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct bcm2835_spi *bs = spi_master_get_devdata(master); + + free_irq(bs->irq, master); + spi_unregister_master(master); + + /* Clear FIFOs, and disable the HW block */ + bcm2835_wr(bs, BCM2835_SPI_CS, + BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); + + clk_disable_unprepare(bs->clk); + spi_master_put(master); + + return 0; +} + +static const struct of_device_id bcm2835_spi_match[] = { + { .compatible = "brcm,bcm2835-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, bcm2835_spi_match); + +static struct platform_driver bcm2835_spi_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = bcm2835_spi_match, + }, + .probe = bcm2835_spi_probe, + .remove = bcm2835_spi_remove, +}; +module_platform_driver(bcm2835_spi_driver); + +MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835"); +MODULE_AUTHOR("Chris Boot "); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From c134634077942404a285f6b64bc1ce5932ac22fe Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 5 Mar 2013 12:05:16 +0200 Subject: spi/pxa2xx-pci: correct the return value check of pcim_iomap_regions() The function returns 0 on success and negative errno in case of failure. Fix this. Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 0a11dcfc..74bc187 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -22,7 +22,7 @@ static int ce4100_spi_probe(struct pci_dev *dev, return ret; ret = pcim_iomap_regions(dev, 1 << 0, "PXA2xx SPI"); - if (!ret) + if (ret) return ret; memset(&spi_pdata, 0, sizeof(spi_pdata)); -- cgit v0.10.2 From 0054e28dc9d2d7c43b569ed5d491bc8bc2f903a9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 5 Mar 2013 12:05:17 +0200 Subject: spi/pxa2xx: enable multiblock DMA transfers for LPSS devices Intel LPSS SPI controllers need to have bit 0 (disable_ssp_dma_finish) set in SSP_REG in order to properly perform DMA transfers spanning over multiple blocks. Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 90b27a3..c6d5b97 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -68,6 +68,7 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define LPSS_TX_HITHRESH_DFLT 224 /* Offset from drv_data->lpss_base */ +#define SSP_REG 0x0c #define SPI_CS_CONTROL 0x18 #define SPI_CS_CONTROL_SW_MODE BIT(0) #define SPI_CS_CONTROL_CS_HIGH BIT(1) @@ -138,6 +139,10 @@ detection_done: /* Enable software chip select control */ value = SPI_CS_CONTROL_SW_MODE | SPI_CS_CONTROL_CS_HIGH; __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); + + /* Enable multiblock DMA transfers */ + if (drv_data->master_info->enable_dma) + __lpss_ssp_write_priv(drv_data, SSP_REG, 1); } static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) -- cgit v0.10.2 From 4fbb82a76db3ef0ec8f5d2e01e288b7821eff687 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Tue, 12 Mar 2013 00:13:38 +0100 Subject: spi/bcm63xx: properly prepare clocks before enabling them Use proper clk_prepare/unprepare calls in preparation for switching to the generic clock framework. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index d7df435..ef9b89f 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -493,7 +493,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) } /* Initialize hardware */ - clk_enable(bs->clk); + clk_prepare_enable(bs->clk); bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); /* register and we are done */ @@ -509,7 +509,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) return 0; out_clk_disable: - clk_disable(clk); + clk_disable_unprepare(clk); out_err: platform_set_drvdata(pdev, NULL); spi_master_put(master); @@ -530,7 +530,7 @@ static int bcm63xx_spi_remove(struct platform_device *pdev) bcm_spi_writeb(bs, 0, SPI_INT_MASK); /* HW shutdown */ - clk_disable(bs->clk); + clk_disable_unprepare(bs->clk); clk_put(bs->clk); platform_set_drvdata(pdev, 0); @@ -549,7 +549,7 @@ static int bcm63xx_spi_suspend(struct device *dev) spi_master_suspend(master); - clk_disable(bs->clk); + clk_disable_unprepare(bs->clk); return 0; } @@ -560,7 +560,7 @@ static int bcm63xx_spi_resume(struct device *dev) platform_get_drvdata(to_platform_device(dev)); struct bcm63xx_spi *bs = spi_master_get_devdata(master); - clk_enable(bs->clk); + clk_prepare_enable(bs->clk); spi_master_resume(master); -- cgit v0.10.2 From ef9ed4b9c9de59eb3f2215b4773990159af92dc1 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Tue, 12 Mar 2013 00:13:39 +0100 Subject: spi/bcm63xx: remove duplicated mode bits check The spi subsystem already checks the mode bits before calling setup. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index ef9b89f..79ad8bc 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -158,12 +158,6 @@ static int bcm63xx_spi_setup(struct spi_device *spi) if (!spi->bits_per_word) spi->bits_per_word = 8; - if (spi->mode & ~MODEBITS) { - dev_err(&spi->dev, "%s, unsupported mode bits %x\n", - __func__, spi->mode & ~MODEBITS); - return -EINVAL; - } - dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n", __func__, spi->mode & MODEBITS, spi->bits_per_word, 0); -- cgit v0.10.2 From c3db2b0b14b487430083209c040acc672a4945c4 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Tue, 12 Mar 2013 00:13:40 +0100 Subject: spi/bcm63xx: remove unneeded debug message The spi subsystem already provides this info in a more extensive debug print except for the nsecs/bit - which wasn't calculated anyway and fixed to 0. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 79ad8bc..13806e3 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -158,9 +158,6 @@ static int bcm63xx_spi_setup(struct spi_device *spi) if (!spi->bits_per_word) spi->bits_per_word = 8; - dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n", - __func__, spi->mode & MODEBITS, spi->bits_per_word, 0); - return 0; } -- cgit v0.10.2 From 52f83bbd65c1178ac989e511943ecd6e0c5f8ad8 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Tue, 12 Mar 2013 00:13:41 +0100 Subject: spi/bcm63xx: remove unused variable bs from bcm63xx_spi_setup It is only written, but never read. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 13806e3..04c460e 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -151,10 +151,6 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi, static int bcm63xx_spi_setup(struct spi_device *spi) { - struct bcm63xx_spi *bs; - - bs = spi_master_get_devdata(spi->master); - if (!spi->bits_per_word) spi->bits_per_word = 8; -- cgit v0.10.2 From e2bdae06329ef3fb8918032735cd963efc701b7e Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Tue, 12 Mar 2013 00:13:42 +0100 Subject: spi/bcm63xx: check spi bits_per_word in spi_setup Instead of fixing up the bits_per_word (which the spi subsystem already does for us), check it for supported values. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 04c460e..b64229c 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -151,8 +151,11 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi, static int bcm63xx_spi_setup(struct spi_device *spi) { - if (!spi->bits_per_word) - spi->bits_per_word = 8; + if (spi->bits_per_word != 8) { + dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", + __func__, spi->bits_per_word); + return -EINVAL; + } return 0; } -- cgit v0.10.2 From 58d8bebea57b519cb606a59dc1263556e8746119 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Tue, 12 Mar 2013 00:13:43 +0100 Subject: spi/bcm63xx: simplify bcm63xx_spi_check_transfer bcm63xx_spi_check_transfer is only called from one place that has t always set, so directly check the transfer's bits_per_word. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index b64229c..b9c9431 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -96,12 +96,9 @@ static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { static int bcm63xx_spi_check_transfer(struct spi_device *spi, struct spi_transfer *t) { - u8 bits_per_word; - - bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; - if (bits_per_word != 8) { + if (t->bits_per_word != 8) { dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", - __func__, bits_per_word); + __func__, t->bits_per_word); return -EINVAL; } -- cgit v0.10.2 From 31e4eaaa54effd8544d1e8679e27d439bb6cb10c Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Tue, 12 Mar 2013 00:13:44 +0100 Subject: spi/bcm63xx: remove spi chip select validity check The check would belong in bcm63xx_spi_setup if the spi subsystem weren't already doing the check for us, so just drop it. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index b9c9431..9574e47 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -102,12 +102,6 @@ static int bcm63xx_spi_check_transfer(struct spi_device *spi, return -EINVAL; } - if (spi->chip_select > spi->master->num_chipselect) { - dev_err(&spi->dev, "%s, unsupported slave %d\n", - __func__, spi->chip_select); - return -EINVAL; - } - return 0; } -- cgit v0.10.2 From c94df49542a9cf2c095468e62be6a16ba86dd811 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Tue, 12 Mar 2013 00:13:45 +0100 Subject: spi/bcm63xx: inline bcm63xx_spi_check_transfer It only does one check, so just do the check directly in the caller. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 9574e47..d777f63 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -93,18 +93,6 @@ static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { { 391000, SPI_CLK_0_391MHZ } }; -static int bcm63xx_spi_check_transfer(struct spi_device *spi, - struct spi_transfer *t) -{ - if (t->bits_per_word != 8) { - dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", - __func__, t->bits_per_word); - return -EINVAL; - } - - return 0; -} - static void bcm63xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { @@ -293,9 +281,12 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master, * full-duplex transfers. */ list_for_each_entry(t, &m->transfers, transfer_list) { - status = bcm63xx_spi_check_transfer(spi, t); - if (status < 0) + if (t->bits_per_word != 8) { + dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", + __func__, t->bits_per_word); + status = -EINVAL; goto exit; + } if (!first) first = t; -- cgit v0.10.2 From 68792e2a1989bf34a9498356c3e3cc70b9231df2 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Tue, 12 Mar 2013 00:13:46 +0100 Subject: spi/bcm63xx: inline hz usage in bcm63xx_spi_setup_transfer bcm63xx_spi_setup_transfer is called from only one place, and that has t always set, to hz will always be t->speed_hz - just use it directly in the two places instead of moving it in a local variable. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index d777f63..2d64db4 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -97,15 +97,12 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); - u32 hz; u8 clk_cfg, reg; int i; - hz = (t) ? t->speed_hz : spi->max_speed_hz; - /* Find the closest clock configuration */ for (i = 0; i < SPI_CLK_MASK; i++) { - if (hz >= bcm63xx_spi_freq_table[i][0]) { + if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) { clk_cfg = bcm63xx_spi_freq_table[i][1]; break; } @@ -122,7 +119,7 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi, bcm_spi_writeb(bs, reg, SPI_CLK_CFG); dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", - clk_cfg, hz); + clk_cfg, t->speed_hz); } /* the spi->mode bits understood by this driver: */ -- cgit v0.10.2 From b66c7730027509620ced3c7ebc84e28f623ebe9a Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Tue, 12 Mar 2013 00:13:47 +0100 Subject: spi/bcm63xx: use devm_ioremap_resource() Use devm_ioremap_resource() which provides its own error messages. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 2d64db4..973099b 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -412,18 +412,9 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); bs->pdev = pdev; - if (!devm_request_mem_region(&pdev->dev, r->start, - resource_size(r), PFX)) { - dev_err(dev, "iomem request failed\n"); - ret = -ENXIO; - goto out_err; - } - - bs->regs = devm_ioremap_nocache(&pdev->dev, r->start, - resource_size(r)); - if (!bs->regs) { - dev_err(dev, "unable to ioremap regs\n"); - ret = -ENOMEM; + bs->regs = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(bs->regs)) { + ret = PTR_ERR(bs->regs); goto out_err; } -- cgit v0.10.2 From 5c725b34d438fdd3c528673a5f53f4b07f879c68 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Tue, 26 Mar 2013 10:27:35 +0100 Subject: spi: spi-s3c64xx.c Remove unused argument. The pointer to the driver data is never used to get the slave controller data. We can delete the unused argument from the function. Signed-off-by: Matthias Brugger Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index e862ab8..7f5f8ee 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -817,7 +817,6 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) } static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( - struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi) { struct s3c64xx_spi_csinfo *cs; @@ -874,7 +873,7 @@ static int s3c64xx_spi_setup(struct spi_device *spi) sdd = spi_master_get_devdata(spi->master); if (!cs && spi->dev.of_node) { - cs = s3c64xx_get_slave_ctrldata(sdd, spi); + cs = s3c64xx_get_slave_ctrldata(spi); spi->controller_data = cs; } -- cgit v0.10.2 From 543bb255a1987836e64f5b7a63664ead8b32b042 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 26 Mar 2013 20:37:57 -0600 Subject: spi: add ability to validate xfer->bits_per_word in SPI core Allow SPI masters to define the set of bits_per_word values they support. If they do this, then the SPI core will reject transfers that attempt to use an unsupported bits_per_word value. This eliminates the need for each SPI driver to implement this checking in most cases. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index f996c60..0cabf15 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1377,6 +1377,14 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) xfer->bits_per_word = spi->bits_per_word; if (!xfer->speed_hz) xfer->speed_hz = spi->max_speed_hz; + if (master->bits_per_word_mask) { + /* Only 32 bits fit in the mask */ + if (xfer->bits_per_word > 32) + return -EINVAL; + if (!(master->bits_per_word_mask & + BIT(xfer->bits_per_word - 1))) + return -EINVAL; + } } message->spi = spi; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 38c2b92..733eb5e 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -228,6 +228,11 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * every chipselect is connected to a slave. * @dma_alignment: SPI controller constraint on DMA buffers alignment. * @mode_bits: flags understood by this controller driver + * @bits_per_word_mask: A mask indicating which values of bits_per_word are + * supported by the driver. Bit n indicates that a bits_per_word n+1 is + * suported. If set, the SPI core will reject any transfer with an + * unsupported bits_per_word. If not set, this value is simply ignored, + * and it's up to the individual driver to perform any validation. * @flags: other constraints relevant to this driver * @bus_lock_spinlock: spinlock for SPI bus locking * @bus_lock_mutex: mutex for SPI bus locking @@ -301,6 +306,9 @@ struct spi_master { /* spi_device.mode flags understood by this controller driver */ u16 mode_bits; + /* bitmask of supported bits_per_word for transfers */ + u32 bits_per_word_mask; + /* other constraints relevant to this driver */ u16 flags; #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */ -- cgit v0.10.2 From f4b97eb5061256045e0f031b6a4bcf8c722b35ff Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 26 Mar 2013 20:37:58 -0600 Subject: spi: bcm2835: make use of new bits_per_word_mask core feature This driver only supports bits_per_word==8, so inform the SPI core of this. Remove all the open-coded validation that's no longer needed. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 346601e..89c0b50 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -182,23 +182,6 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) return IRQ_NONE; } -static int bcm2835_spi_check_transfer(struct spi_device *spi, - struct spi_transfer *tfr) -{ - /* tfr==NULL when called from bcm2835_spi_setup() */ - u32 bpw = tfr ? tfr->bits_per_word : spi->bits_per_word; - - switch (bpw) { - case 8: - break; - default: - dev_err(&spi->dev, "unsupported bits_per_word=%d\n", bpw); - return -EINVAL; - } - - return 0; -} - static int bcm2835_spi_start_transfer(struct spi_device *spi, struct spi_transfer *tfr) { @@ -273,19 +256,6 @@ static int bcm2835_spi_finish_transfer(struct spi_device *spi, return 0; } -static int bcm2835_spi_setup(struct spi_device *spi) -{ - int ret; - - ret = bcm2835_spi_check_transfer(spi, NULL); - if (ret) { - dev_err(&spi->dev, "setup: invalid message\n"); - return ret; - } - - return 0; -} - static int bcm2835_spi_transfer_one(struct spi_master *master, struct spi_message *mesg) { @@ -297,10 +267,6 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, bool cs_change; list_for_each_entry(tfr, &mesg->transfers, transfer_list) { - err = bcm2835_spi_check_transfer(spi, tfr); - if (err) - goto out; - err = bcm2835_spi_start_transfer(spi, tfr); if (err) goto out; @@ -348,9 +314,9 @@ 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->bus_num = -1; master->num_chipselect = 3; - master->setup = bcm2835_spi_setup; master->transfer_one_message = bcm2835_spi_transfer_one; master->dev.of_node = pdev->dev.of_node; -- cgit v0.10.2 From e761f4236e94f2dd36316f9892583b29ce986031 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 1 Apr 2013 14:17:37 +0100 Subject: spi/s3c64xx: Convert to bits_per_word_mask The core can do the validation for us. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 7f5f8ee..27ff669 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -911,15 +911,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi) spin_unlock_irqrestore(&sdd->lock, flags); - if (spi->bits_per_word != 8 - && spi->bits_per_word != 16 - && spi->bits_per_word != 32) { - dev_err(&spi->dev, "setup: %dbits/wrd not supported!\n", - spi->bits_per_word); - err = -EINVAL; - goto setup_exit; - } - pm_runtime_get_sync(&sdd->pdev->dev); /* Check if we can provide the requested rate */ @@ -1237,6 +1228,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer; master->num_chipselect = sci->num_cs; master->dma_alignment = 8; + master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1); /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; -- cgit v0.10.2 From ff23fa3bb05b296eeab2ccde92c0cdbc05942a1c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 21 Mar 2013 13:22:48 +0100 Subject: spi/omap-mcspi: check condition also after timeout It is possible that the handler gets interrupted after checking the status. After it resumes the time out is due but the condition it was waiting for might be true as well. Therefore it is necessary to check the condition in case of an time out to be sure that the condition is not true after the time passed by. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 893c3d7..61eef47 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -285,8 +285,12 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) timeout = jiffies + msecs_to_jiffies(1000); while (!(__raw_readl(reg) & bit)) { - if (time_after(jiffies, timeout)) - return -1; + if (time_after(jiffies, timeout)) { + if (!(__raw_readl(reg) & bit)) + return -ETIMEDOUT; + else + return 0; + } cpu_relax(); } return 0; -- cgit v0.10.2 From 9547acce669ec7b5613eb9be0838bff47258ccbf Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 14 Mar 2013 15:31:50 +0530 Subject: spi: spi-oc-tiny: Use of_match_ptr() macro This eliminates having an #ifdef returning NULL for the case when OF is disabled. Signed-off-by: Sachin Kamat Acked-by: Thomas Chou Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index cb2e284..e60a776 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -393,8 +393,6 @@ static const struct of_device_id tiny_spi_match[] = { {}, }; MODULE_DEVICE_TABLE(of, tiny_spi_match); -#else /* CONFIG_OF */ -#define tiny_spi_match NULL #endif /* CONFIG_OF */ static struct platform_driver tiny_spi_driver = { @@ -404,7 +402,7 @@ static struct platform_driver tiny_spi_driver = { .name = DRV_NAME, .owner = THIS_MODULE, .pm = NULL, - .of_match_table = tiny_spi_match, + .of_match_table = of_match_ptr(tiny_spi_match), }, }; module_platform_driver(tiny_spi_driver); -- cgit v0.10.2 From 691ee4edd410dd6f19227642e01a93fb88620eff Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 14 Mar 2013 15:31:51 +0530 Subject: spi: spi-sh-msiof: Use of_match_ptr() macro This eliminates having an #ifdef returning NULL for the case when OF is disabled. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 8b40d08..2bc5a6b 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -764,8 +764,6 @@ static const struct of_device_id sh_msiof_match[] = { {}, }; MODULE_DEVICE_TABLE(of, sh_msiof_match); -#else -#define sh_msiof_match NULL #endif static struct dev_pm_ops sh_msiof_spi_dev_pm_ops = { @@ -780,7 +778,7 @@ static struct platform_driver sh_msiof_spi_drv = { .name = "spi_sh_msiof", .owner = THIS_MODULE, .pm = &sh_msiof_spi_dev_pm_ops, - .of_match_table = sh_msiof_match, + .of_match_table = of_match_ptr(sh_msiof_match), }, }; module_platform_driver(sh_msiof_spi_drv); -- cgit v0.10.2 From 6ada5115af1ff1843eeba9bce8f2979967d8109a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 13 Mar 2013 21:29:12 +0800 Subject: spi: remove unused variable in tegra_slink_read_rx_fifo_to_client_rxbuf() The variable bits_per_word is initialized but never used otherwise, so remove the unused variable. Signed-off-by: Wei Yongjun Acked-By: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index a829563..17224f0 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -375,9 +375,6 @@ static unsigned int tegra_slink_read_rx_fifo_to_client_rxbuf( tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; read_words += tspi->curr_dma_words; } else { - unsigned int bits_per_word; - - bits_per_word = t->bits_per_word; for (count = 0; count < rx_full_count; count++) { x = tegra_slink_readl(tspi, SLINK_RX_FIFO); for (i = 0; (i < tspi->bytes_per_word); i++) -- cgit v0.10.2 From e4d43781df4b7da0cb86987b25dfa425407a643c Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghiu Date: Thu, 14 Mar 2013 11:07:31 +0200 Subject: spi/fsl: Use PTR_RET function Replaced calls to IS_ERR and PTR_ERR with PTR_RET function. Patch found using coccinelle. Signed-off-by: Alexandru Gheorghiu Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 086a9ee..1985ba3 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -1134,9 +1134,7 @@ static int plat_mpc8xxx_spi_probe(struct platform_device *pdev) return -EINVAL; master = fsl_spi_probe(&pdev->dev, mem, irq); - if (IS_ERR(master)) - return PTR_ERR(master); - return 0; + return PTR_RET(master); } static int plat_mpc8xxx_spi_remove(struct platform_device *pdev) -- cgit v0.10.2 From 8179bb2655b607d4c3e8cea10a60eb3cf8557b63 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghiu Date: Thu, 14 Mar 2013 11:18:18 +0200 Subject: spi/spidev: Use PTR_RET function Replaced calls to IS_ERR and PTR_ERR with PTR_RET function. Patch found using coccinelle. Signed-off-by: Alexandru Gheorghiu Signed-off-by: Mark Brown diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 2e0655d..911e9e0 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -603,7 +603,7 @@ static int spidev_probe(struct spi_device *spi) dev = device_create(spidev_class, &spi->dev, spidev->devt, spidev, "spidev%d.%d", spi->master->bus_num, spi->chip_select); - status = IS_ERR(dev) ? PTR_ERR(dev) : 0; + status = PTR_RET(dev); } else { dev_dbg(&spi->dev, "no minor number available!\n"); status = -ENODEV; -- cgit v0.10.2 From faa98f7ea6c720beec8a800c9ac6975f760467e2 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 23 Nov 2012 14:52:39 +0530 Subject: spi: tegra: slink: do not prepare dma transfer with DMA_CTRL_ACK flag Spi starts transfer using dma with DMA_CTRL_ACK which is not require becasue spi driver does not use completed dma_desc after transfer done and so it does not ack the dma descriptor. Removing the DMA_CTRL_ACK flag to avoid memory leak in dma driver. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 17224f0..4e58b53 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -467,7 +467,7 @@ static int tegra_slink_start_tx_dma(struct tegra_slink_data *tspi, int len) INIT_COMPLETION(tspi->tx_dma_complete); tspi->tx_dma_desc = dmaengine_prep_slave_single(tspi->tx_dma_chan, tspi->tx_dma_phys, len, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + DMA_PREP_INTERRUPT); if (!tspi->tx_dma_desc) { dev_err(tspi->dev, "Not able to get desc for Tx\n"); return -EIO; @@ -486,7 +486,7 @@ static int tegra_slink_start_rx_dma(struct tegra_slink_data *tspi, int len) INIT_COMPLETION(tspi->rx_dma_complete); tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma_chan, tspi->rx_dma_phys, len, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + DMA_PREP_INTERRUPT); if (!tspi->rx_dma_desc) { dev_err(tspi->dev, "Not able to get desc for Rx\n"); return -EIO; -- cgit v0.10.2 From d4820b7496219edd9a7055022681364d304525f7 Mon Sep 17 00:00:00 2001 From: Wenyou Yang Date: Tue, 19 Mar 2013 15:42:15 +0800 Subject: spi/spi-atmel: detect the capabilities of SPI core by reading the VERSION register. The "has_dma_support" needed for future use with dmaengine driver. [Fixed some unneded ternery operators -- broonie] Signed-off-by: Wenyou Yang Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 656d137..af3dbab 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -22,9 +22,8 @@ #include #include -#include -#include -#include +#include +#include /* SPI register offsets */ #define SPI_CR 0x0000 @@ -39,6 +38,7 @@ #define SPI_CSR1 0x0034 #define SPI_CSR2 0x0038 #define SPI_CSR3 0x003c +#define SPI_VERSION 0x00fc #define SPI_RPR 0x0100 #define SPI_RCR 0x0104 #define SPI_TPR 0x0108 @@ -71,6 +71,8 @@ #define SPI_FDIV_SIZE 1 #define SPI_MODFDIS_OFFSET 4 #define SPI_MODFDIS_SIZE 1 +#define SPI_WDRBT_OFFSET 5 +#define SPI_WDRBT_SIZE 1 #define SPI_LLB_OFFSET 7 #define SPI_LLB_SIZE 1 #define SPI_PCS_OFFSET 16 @@ -180,6 +182,11 @@ #define spi_writel(port,reg,value) \ __raw_writel((value), (port)->regs + SPI_##reg) +struct atmel_spi_caps { + bool is_spi2; + bool has_wdrbt; + bool has_dma_support; +}; /* * The core SPI transfer engine just talks to a register bank to set up @@ -204,6 +211,8 @@ struct atmel_spi { void *buffer; dma_addr_t buffer_dma; + + struct atmel_spi_caps caps; }; /* Controller-specific per-slave state */ @@ -222,14 +231,10 @@ struct atmel_spi_device { * - SPI_SR.TXEMPTY, SPI_SR.NSSR (and corresponding irqs) * - SPI_CSRx.CSAAT * - SPI_CSRx.SBCR allows faster clocking - * - * We can determine the controller version by reading the VERSION - * register, but I haven't checked that it exists on all chips, and - * this is cheaper anyway. */ -static bool atmel_spi_is_v2(void) +static bool atmel_spi_is_v2(struct atmel_spi *as) { - return !cpu_is_at91rm9200(); + return as->caps.is_spi2; } /* @@ -263,15 +268,20 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi) unsigned active = spi->mode & SPI_CS_HIGH; u32 mr; - if (atmel_spi_is_v2()) { + if (atmel_spi_is_v2(as)) { /* * Always use CSR0. This ensures that the clock * switches to the correct idle polarity before we * toggle the CS. */ spi_writel(as, CSR0, asd->csr); - spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(MODFDIS) + if (as->caps.has_wdrbt) { + spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(WDRBT) + | SPI_BIT(MODFDIS) | SPI_BIT(MSTR)); + } else { + spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(MODFDIS) | SPI_BIT(MSTR)); + } mr = spi_readl(as, MR); gpio_set_value(asd->npcs_pin, active); } else { @@ -318,7 +328,7 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi) asd->npcs_pin, active ? " (low)" : "", mr); - if (atmel_spi_is_v2() || spi->chip_select != 0) + if (atmel_spi_is_v2(as) || spi->chip_select != 0) gpio_set_value(asd->npcs_pin, !active); } @@ -719,7 +729,7 @@ static int atmel_spi_setup(struct spi_device *spi) } /* see notes above re chipselect */ - if (!atmel_spi_is_v2() + if (!atmel_spi_is_v2(as) && spi->chip_select == 0 && (spi->mode & SPI_CS_HIGH)) { dev_dbg(&spi->dev, "setup: can't be active-high\n"); @@ -728,7 +738,7 @@ static int atmel_spi_setup(struct spi_device *spi) /* v1 chips start out at half the peripheral bus speed. */ bus_hz = clk_get_rate(as->clk); - if (!atmel_spi_is_v2()) + if (!atmel_spi_is_v2(as)) bus_hz /= 2; if (spi->max_speed_hz) { @@ -804,7 +814,7 @@ static int atmel_spi_setup(struct spi_device *spi) "setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n", bus_hz / scbr, bits, spi->mode, spi->chip_select, csr); - if (!atmel_spi_is_v2()) + if (!atmel_spi_is_v2(as)) spi_writel(as, CSR0 + 4 * spi->chip_select, csr); return 0; @@ -910,6 +920,23 @@ static void atmel_spi_cleanup(struct spi_device *spi) kfree(asd); } +static inline unsigned int atmel_get_version(struct atmel_spi *as) +{ + return spi_readl(as, VERSION) & 0x00000fff; +} + +static void atmel_get_caps(struct atmel_spi *as) +{ + unsigned int version; + + version = atmel_get_version(as); + dev_info(&as->pdev->dev, "version: 0x%x\n", version); + + as->caps.is_spi2 = version > 0x121; + as->caps.has_wdrbt = version >= 0x210; + as->caps.has_dma_support = version >= 0x212; +} + /*-------------------------------------------------------------------------*/ static int atmel_spi_probe(struct platform_device *pdev) @@ -970,6 +997,8 @@ static int atmel_spi_probe(struct platform_device *pdev) as->irq = irq; as->clk = clk; + atmel_get_caps(as); + ret = request_irq(irq, atmel_spi_interrupt, 0, dev_name(&pdev->dev), master); if (ret) @@ -979,7 +1008,12 @@ static int atmel_spi_probe(struct platform_device *pdev) clk_enable(clk); spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ - spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS)); + if (as->caps.has_wdrbt) { + spi_writel(as, MR, SPI_BIT(WDRBT) | SPI_BIT(MODFDIS) + | SPI_BIT(MSTR)); + } else { + spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS)); + } spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); spi_writel(as, CR, SPI_BIT(SPIEN)); -- cgit v0.10.2 From 97ed465b4d3b6ec6ab12d1ee0cea48a66891c985 Mon Sep 17 00:00:00 2001 From: Wenyou Yang Date: Tue, 19 Mar 2013 15:43:01 +0800 Subject: spi/spi-atmel: add support transfer on CS1,2,3, not only on CS0 Signed-off-by: Wenyou Yang Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index af3dbab..26c126b 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -255,11 +255,6 @@ static bool atmel_spi_is_v2(struct atmel_spi *as) * Master on Chip Select 0.") No workaround exists for that ... so for * nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH, * and (c) will trigger that first erratum in some cases. - * - * TODO: Test if the atmel_spi_is_v2() branch below works on - * AT91RM9200 if we use some other register than CSR0. However, don't - * do this unconditionally since AP7000 has an errata where the BITS - * field in CSR0 overrides all other CSRs. */ static void cs_activate(struct atmel_spi *as, struct spi_device *spi) @@ -269,18 +264,22 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi) u32 mr; if (atmel_spi_is_v2(as)) { - /* - * Always use CSR0. This ensures that the clock - * switches to the correct idle polarity before we - * toggle the CS. + spi_writel(as, CSR0 + 4 * spi->chip_select, asd->csr); + /* For the low SPI version, there is a issue that PDC transfer + * on CS1,2,3 needs SPI_CSR0.BITS config as SPI_CSR1,2,3.BITS */ spi_writel(as, CSR0, asd->csr); if (as->caps.has_wdrbt) { - spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(WDRBT) - | SPI_BIT(MODFDIS) | SPI_BIT(MSTR)); + spi_writel(as, MR, + SPI_BF(PCS, ~(0x01 << spi->chip_select)) + | SPI_BIT(WDRBT) + | SPI_BIT(MODFDIS) + | SPI_BIT(MSTR)); } else { - spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(MODFDIS) - | SPI_BIT(MSTR)); + spi_writel(as, MR, + SPI_BF(PCS, ~(0x01 << spi->chip_select)) + | SPI_BIT(MODFDIS) + | SPI_BIT(MSTR)); } mr = spi_readl(as, MR); gpio_set_value(asd->npcs_pin, active); -- cgit v0.10.2 From 1888e8f2f55c40656d8eff68572abb3748068b96 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 19 Mar 2013 15:44:22 +0800 Subject: spi/spi-atmel: call unmapping on transfers buffers Signed-off-by: Nicolas Ferre Signed-off-by: Wenyou Yang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 26c126b..3c5ec60 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1047,6 +1047,7 @@ static int atmel_spi_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct atmel_spi *as = spi_master_get_devdata(master); struct spi_message *msg; + struct spi_transfer *xfer; /* reset the hardware and block queue progress */ spin_lock_irq(&as->lock); @@ -1058,9 +1059,10 @@ static int atmel_spi_remove(struct platform_device *pdev) /* Terminate remaining queued transfers */ list_for_each_entry(msg, &as->queue, queue) { - /* REVISIT unmapping the dma is a NOP on ARM and AVR32 - * but we shouldn't depend on that... - */ + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (!msg->is_dma_mapped) + atmel_spi_dma_unmap_xfer(master, xfer); + } msg->status = -ESHUTDOWN; msg->complete(msg->context); } -- cgit v0.10.2 From 823cd0454325509d84dbf8e301c182c8a2711c65 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 19 Mar 2013 15:45:01 +0800 Subject: spi/spi-atmel: status information passed through controller data The status of transfer is stored in controller data structure so that it can be used not only by atmel_spi_msg_done() function. This will be useful for upcoming dmaengine enabled driver. Signed-off-by: Nicolas Ferre Signed-off-by: Wenyou Yang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 3c5ec60..ab2ed75 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -208,6 +208,7 @@ struct atmel_spi { unsigned long current_remaining_bytes; struct spi_transfer *next_transfer; unsigned long next_remaining_bytes; + int done_status; void *buffer; dma_addr_t buffer_dma; @@ -553,15 +554,15 @@ static void atmel_spi_dma_unmap_xfer(struct spi_master *master, static void atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, - struct spi_message *msg, int status, int stay) + struct spi_message *msg, int stay) { - if (!stay || status < 0) + if (!stay || as->done_status < 0) cs_deactivate(as, msg->spi); else as->stay = msg->spi; list_del(&msg->queue); - msg->status = status; + msg->status = as->done_status; dev_dbg(master->dev.parent, "xfer complete: %u bytes transferred\n", @@ -573,6 +574,7 @@ atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, as->current_transfer = NULL; as->next_transfer = NULL; + as->done_status = 0; /* continue if needed */ if (list_empty(&as->queue) || as->stopping) @@ -650,7 +652,8 @@ atmel_spi_interrupt(int irq, void *dev_id) /* Clear any overrun happening while cleaning up */ spi_readl(as, SR); - atmel_spi_msg_done(master, as, msg, -EIO, 0); + as->done_status = -EIO; + atmel_spi_msg_done(master, as, msg, 0); } else if (pending & (SPI_BIT(RXBUFF) | SPI_BIT(ENDRX))) { ret = IRQ_HANDLED; @@ -668,7 +671,7 @@ atmel_spi_interrupt(int irq, void *dev_id) if (atmel_spi_xfer_is_last(msg, xfer)) { /* report completed message */ - atmel_spi_msg_done(master, as, msg, 0, + atmel_spi_msg_done(master, as, msg, xfer->cs_change); } else { if (xfer->cs_change) { -- cgit v0.10.2 From 997230d02cbd7d35f26b70bb67d8b09a58414aae Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 22 Mar 2013 02:09:08 +0000 Subject: spi/s3c64xx: add CONFIG_PM_SLEEP to suspend/resume functions Add CONFIG_PM_SLEEP to suspend/resume functions to fix the following build warning when CONFIG_PM_SLEEP is not selected. This is because sleep PM callbacks defined by SET_SYSTEM_SLEEP_PM_OPS are only used when the CONFIG_PM_SLEEP is enabled. drivers/spi/spi-s3c64xx.c:1362:12: warning: 's3c64xx_spi_suspend' defined but not used [-Wunused-function] drivers/spi/spi-s3c64xx.c:1381:12: warning: 's3c64xx_spi_resume' defined but not used [-Wunused-function] Signed-off-by: Jingoo Han Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index a17ca06..682b1e7 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1358,7 +1358,7 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int s3c64xx_spi_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); @@ -1399,7 +1399,7 @@ static int s3c64xx_spi_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ #ifdef CONFIG_PM_RUNTIME static int s3c64xx_spi_runtime_suspend(struct device *dev) -- cgit v0.10.2 From 72919f340245bbc9b81da35844134db2d675912a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 3 Apr 2013 18:30:31 +0100 Subject: spi: tegra: slink: do prepare dma transfer with DMA_CTRL_ACK flag This reverts commit faa98f7ea6c720beec8a800c9ac6975f760467e2 which was applied in error due to discussion ending up in the wrong thread. diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 4e58b53..17224f0 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -467,7 +467,7 @@ static int tegra_slink_start_tx_dma(struct tegra_slink_data *tspi, int len) INIT_COMPLETION(tspi->tx_dma_complete); tspi->tx_dma_desc = dmaengine_prep_slave_single(tspi->tx_dma_chan, tspi->tx_dma_phys, len, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT); + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!tspi->tx_dma_desc) { dev_err(tspi->dev, "Not able to get desc for Tx\n"); return -EIO; @@ -486,7 +486,7 @@ static int tegra_slink_start_rx_dma(struct tegra_slink_data *tspi, int len) INIT_COMPLETION(tspi->rx_dma_complete); tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma_chan, tspi->rx_dma_phys, len, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT); + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!tspi->rx_dma_desc) { dev_err(tspi->dev, "Not able to get desc for Rx\n"); return -EIO; -- cgit v0.10.2 From 58ad60bbb2abada33fbeae88943ab038e2fcc0ef Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 3 Apr 2013 21:06:40 +0800 Subject: mxs/spi: fix error return code in mxs_spi_probe() Fix to return a negative error code from the error handling case instead of 0, as returned elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 22a0af0..a1d5778 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -612,6 +612,7 @@ static int mxs_spi_probe(struct platform_device *pdev) ssp->dmach = dma_request_channel(mask, mxs_ssp_dma_filter, ssp); if (!ssp->dmach) { dev_err(ssp->dev, "Failed to request DMA\n"); + ret = -ENODEV; goto out_master_free; } -- cgit v0.10.2 From e8beacbb85a5c1de1117400c5ddb450514a8372c Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Fri, 15 Feb 2013 16:52:21 +0100 Subject: spi/spi-fsl-spi: Make driver usable in CPU mode outside of an FSL_SOC environment This makes the spi-fsl-spi driver usable in CPU mode outside of an FSL_SOC and even an powerpc environment by moving CPM mode functionality to a separate file that is only compiled and linked in an FSL_SOC environment and adding some ifdefs to hide types and functions or provide alternatives. For devicetree probing a "clock-frequency" property is used for clock frequency instead of calls to FSL_SOC-specific functions. Acked-by: Anton Vorontsov Signed-off-by: Andreas Larsson Signed-off-by: Grant Likely diff --git a/Documentation/devicetree/bindings/spi/fsl-spi.txt b/Documentation/devicetree/bindings/spi/fsl-spi.txt index 777abd7..4f2ea94 100644 --- a/Documentation/devicetree/bindings/spi/fsl-spi.txt +++ b/Documentation/devicetree/bindings/spi/fsl-spi.txt @@ -14,6 +14,7 @@ Required properties: controller you have. - interrupt-parent : the phandle for the interrupt controller that services interrupts for this device. +- clock-frequency : input clock frequency to non FSL_SOC cores Optional properties: - gpios : specifies the gpio pins to be used for chipselects. diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 32b85d4..3524bec 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -229,12 +229,17 @@ config SPI_MPC512x_PSC config SPI_FSL_LIB tristate + depends on OF + +config SPI_FSL_CPM + tristate depends on FSL_SOC config SPI_FSL_SPI bool "Freescale SPI controller" - depends on FSL_SOC + depends on OF select SPI_FSL_LIB + select SPI_FSL_CPM if FSL_SOC help This enables using the Freescale SPI controllers in master mode. MPC83xx platform uses the controller in cpu mode or CPM/QE mode. diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3ce1d08..604460d 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.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_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-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c new file mode 100644 index 0000000..07971e3 --- /dev/null +++ b/drivers/spi/spi-fsl-cpm.c @@ -0,0 +1,387 @@ +/* + * Freescale SPI controller driver cpm functions. + * + * Maintainer: Kumar Gala + * + * Copyright (C) 2006 Polycom, Inc. + * Copyright 2010 Freescale Semiconductor, Inc. + * + * CPM SPI and QE buffer descriptors mode support: + * Copyright (c) 2009 MontaVista Software, Inc. + * Author: Anton Vorontsov + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "spi-fsl-lib.h" +#include "spi-fsl-cpm.h" +#include "spi-fsl-spi.h" + +/* CPM1 and CPM2 are mutually exclusive. */ +#ifdef CONFIG_CPM1 +#include +#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0) +#else +#include +#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0) +#endif + +#define SPIE_TXB 0x00000200 /* Last char is written to tx fifo */ +#define SPIE_RXB 0x00000100 /* Last char is written to rx buf */ + +/* SPCOM register values */ +#define SPCOM_STR (1 << 23) /* Start transmit */ + +#define SPI_PRAM_SIZE 0x100 +#define SPI_MRBLR ((unsigned int)PAGE_SIZE) + +static void *fsl_dummy_rx; +static DEFINE_MUTEX(fsl_dummy_rx_lock); +static int fsl_dummy_rx_refcnt; + +void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi) +{ + if (mspi->flags & SPI_QE) { + qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock, + QE_CR_PROTOCOL_UNSPECIFIED, 0); + } else { + cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX); + if (mspi->flags & SPI_CPM1) { + out_be16(&mspi->pram->rbptr, + in_be16(&mspi->pram->rbase)); + out_be16(&mspi->pram->tbptr, + in_be16(&mspi->pram->tbase)); + } + } +} + +static void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi) +{ + struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd; + struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd; + unsigned int xfer_len = min(mspi->count, SPI_MRBLR); + unsigned int xfer_ofs; + struct fsl_spi_reg *reg_base = mspi->reg_base; + + xfer_ofs = mspi->xfer_in_progress->len - mspi->count; + + if (mspi->rx_dma == mspi->dma_dummy_rx) + out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma); + else + out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs); + out_be16(&rx_bd->cbd_datlen, 0); + out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP); + + if (mspi->tx_dma == mspi->dma_dummy_tx) + out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma); + else + out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs); + out_be16(&tx_bd->cbd_datlen, xfer_len); + out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP | + BD_SC_LAST); + + /* start transfer */ + mpc8xxx_spi_write_reg(®_base->command, SPCOM_STR); +} + +int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi, + struct spi_transfer *t, bool is_dma_mapped) +{ + struct device *dev = mspi->dev; + struct fsl_spi_reg *reg_base = mspi->reg_base; + + if (is_dma_mapped) { + mspi->map_tx_dma = 0; + mspi->map_rx_dma = 0; + } else { + mspi->map_tx_dma = 1; + mspi->map_rx_dma = 1; + } + + if (!t->tx_buf) { + mspi->tx_dma = mspi->dma_dummy_tx; + mspi->map_tx_dma = 0; + } + + if (!t->rx_buf) { + mspi->rx_dma = mspi->dma_dummy_rx; + mspi->map_rx_dma = 0; + } + + if (mspi->map_tx_dma) { + void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */ + + mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, mspi->tx_dma)) { + dev_err(dev, "unable to map tx dma\n"); + return -ENOMEM; + } + } else if (t->tx_buf) { + mspi->tx_dma = t->tx_dma; + } + + if (mspi->map_rx_dma) { + mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(dev, mspi->rx_dma)) { + dev_err(dev, "unable to map rx dma\n"); + goto err_rx_dma; + } + } else if (t->rx_buf) { + mspi->rx_dma = t->rx_dma; + } + + /* enable rx ints */ + mpc8xxx_spi_write_reg(®_base->mask, SPIE_RXB); + + mspi->xfer_in_progress = t; + mspi->count = t->len; + + /* start CPM transfers */ + fsl_spi_cpm_bufs_start(mspi); + + return 0; + +err_rx_dma: + if (mspi->map_tx_dma) + dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE); + return -ENOMEM; +} + +void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) +{ + struct device *dev = mspi->dev; + struct spi_transfer *t = mspi->xfer_in_progress; + + if (mspi->map_tx_dma) + dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE); + if (mspi->map_rx_dma) + dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE); + mspi->xfer_in_progress = NULL; +} + +void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) +{ + u16 len; + struct fsl_spi_reg *reg_base = mspi->reg_base; + + dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__, + in_be16(&mspi->rx_bd->cbd_datlen), mspi->count); + + len = in_be16(&mspi->rx_bd->cbd_datlen); + if (len > mspi->count) { + WARN_ON(1); + len = mspi->count; + } + + /* Clear the events */ + mpc8xxx_spi_write_reg(®_base->event, events); + + mspi->count -= len; + if (mspi->count) + fsl_spi_cpm_bufs_start(mspi); + else + complete(&mspi->done); +} + +static void *fsl_spi_alloc_dummy_rx(void) +{ + mutex_lock(&fsl_dummy_rx_lock); + + if (!fsl_dummy_rx) + fsl_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL); + if (fsl_dummy_rx) + fsl_dummy_rx_refcnt++; + + mutex_unlock(&fsl_dummy_rx_lock); + + return fsl_dummy_rx; +} + +static void fsl_spi_free_dummy_rx(void) +{ + mutex_lock(&fsl_dummy_rx_lock); + + switch (fsl_dummy_rx_refcnt) { + case 0: + WARN_ON(1); + break; + case 1: + kfree(fsl_dummy_rx); + fsl_dummy_rx = NULL; + /* fall through */ + default: + fsl_dummy_rx_refcnt--; + break; + } + + mutex_unlock(&fsl_dummy_rx_lock); +} + +static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi) +{ + struct device *dev = mspi->dev; + struct device_node *np = dev->of_node; + const u32 *iprop; + int size; + void __iomem *spi_base; + unsigned long pram_ofs = -ENOMEM; + + /* Can't use of_address_to_resource(), QE muram isn't at 0. */ + iprop = of_get_property(np, "reg", &size); + + /* QE with a fixed pram location? */ + if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4) + return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE); + + /* QE but with a dynamic pram location? */ + if (mspi->flags & SPI_QE) { + pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64); + qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock, + QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs); + return pram_ofs; + } + + spi_base = of_iomap(np, 1); + if (spi_base == NULL) + return -EINVAL; + + if (mspi->flags & SPI_CPM2) { + pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64); + out_be16(spi_base, pram_ofs); + } else { + struct spi_pram __iomem *pram = spi_base; + u16 rpbase = in_be16(&pram->rpbase); + + /* Microcode relocation patch applied? */ + if (rpbase) { + pram_ofs = rpbase; + } else { + pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64); + out_be16(spi_base, pram_ofs); + } + } + + iounmap(spi_base); + return pram_ofs; +} + +int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi) +{ + struct device *dev = mspi->dev; + struct device_node *np = dev->of_node; + const u32 *iprop; + int size; + unsigned long pram_ofs; + unsigned long bds_ofs; + + if (!(mspi->flags & SPI_CPM_MODE)) + return 0; + + if (!fsl_spi_alloc_dummy_rx()) + return -ENOMEM; + + if (mspi->flags & SPI_QE) { + iprop = of_get_property(np, "cell-index", &size); + if (iprop && size == sizeof(*iprop)) + mspi->subblock = *iprop; + + switch (mspi->subblock) { + default: + dev_warn(dev, "cell-index unspecified, assuming SPI1"); + /* fall through */ + case 0: + mspi->subblock = QE_CR_SUBBLOCK_SPI1; + break; + case 1: + mspi->subblock = QE_CR_SUBBLOCK_SPI2; + break; + } + } + + pram_ofs = fsl_spi_cpm_get_pram(mspi); + if (IS_ERR_VALUE(pram_ofs)) { + dev_err(dev, "can't allocate spi parameter ram\n"); + goto err_pram; + } + + bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) + + sizeof(*mspi->rx_bd), 8); + if (IS_ERR_VALUE(bds_ofs)) { + dev_err(dev, "can't allocate bds\n"); + goto err_bds; + } + + mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, mspi->dma_dummy_tx)) { + dev_err(dev, "unable to map dummy tx buffer\n"); + goto err_dummy_tx; + } + + mspi->dma_dummy_rx = dma_map_single(dev, fsl_dummy_rx, SPI_MRBLR, + DMA_FROM_DEVICE); + if (dma_mapping_error(dev, mspi->dma_dummy_rx)) { + dev_err(dev, "unable to map dummy rx buffer\n"); + goto err_dummy_rx; + } + + mspi->pram = cpm_muram_addr(pram_ofs); + + mspi->tx_bd = cpm_muram_addr(bds_ofs); + mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd)); + + /* Initialize parameter ram. */ + out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd)); + out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd)); + out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL); + out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL); + out_be16(&mspi->pram->mrblr, SPI_MRBLR); + out_be32(&mspi->pram->rstate, 0); + out_be32(&mspi->pram->rdp, 0); + out_be16(&mspi->pram->rbptr, 0); + out_be16(&mspi->pram->rbc, 0); + out_be32(&mspi->pram->rxtmp, 0); + out_be32(&mspi->pram->tstate, 0); + out_be32(&mspi->pram->tdp, 0); + out_be16(&mspi->pram->tbptr, 0); + out_be16(&mspi->pram->tbc, 0); + out_be32(&mspi->pram->txtmp, 0); + + return 0; + +err_dummy_rx: + dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE); +err_dummy_tx: + cpm_muram_free(bds_ofs); +err_bds: + cpm_muram_free(pram_ofs); +err_pram: + fsl_spi_free_dummy_rx(); + return -ENOMEM; +} + +void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi) +{ + struct device *dev = mspi->dev; + + if (!(mspi->flags & SPI_CPM_MODE)) + return; + + dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE); + dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE); + cpm_muram_free(cpm_muram_offset(mspi->tx_bd)); + cpm_muram_free(cpm_muram_offset(mspi->pram)); + fsl_spi_free_dummy_rx(); +} diff --git a/drivers/spi/spi-fsl-cpm.h b/drivers/spi/spi-fsl-cpm.h new file mode 100644 index 0000000..c711158 --- /dev/null +++ b/drivers/spi/spi-fsl-cpm.h @@ -0,0 +1,43 @@ +/* + * Freescale SPI controller driver cpm functions. + * + * Maintainer: Kumar Gala + * + * Copyright (C) 2006 Polycom, Inc. + * Copyright 2010 Freescale Semiconductor, Inc. + * + * CPM SPI and QE buffer descriptors mode support: + * Copyright (c) 2009 MontaVista Software, Inc. + * Author: Anton Vorontsov + * + * 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. + */ + +#ifndef __SPI_FSL_CPM_H__ +#define __SPI_FSL_CPM_H__ + +#include "spi-fsl-lib.h" + +#ifdef CONFIG_FSL_SOC +extern void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi); +extern int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi, + struct spi_transfer *t, bool is_dma_mapped); +extern void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi); +extern void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events); +extern int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi); +extern void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi); +#else +static inline void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi) { } +static inline int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi, + struct spi_transfer *t, + bool is_dma_mapped) { return 0; } +static inline void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) { } +static inline void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) { } +static inline int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi) { return 0; } +static inline void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi) { } +#endif + +#endif /* __SPI_FSL_CPM_H__ */ diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index 8ade675..a91db0e 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -23,7 +23,9 @@ #include #include #include +#ifdef CONFIG_FSL_SOC #include +#endif #include "spi-fsl-lib.h" @@ -208,6 +210,7 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev) /* Allocate bus num dynamically. */ pdata->bus_num = -1; +#ifdef CONFIG_FSL_SOC /* SPI controller is either clocked from QE or SoC clock. */ pdata->sysclk = get_brgfreq(); if (pdata->sysclk == -1) { @@ -217,6 +220,11 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev) goto err; } } +#else + ret = of_property_read_u32(np, "clock-frequency", &pdata->sysclk); + if (ret) + goto err; +#endif prop = of_get_property(np, "mode", NULL); if (prop && !strcmp(prop, "cpu-qe")) diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index cbe881b..d785595 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -34,8 +34,10 @@ struct mpc8xxx_spi { int subblock; struct spi_pram __iomem *pram; +#ifdef CONFIG_FSL_SOC struct cpm_buf_desc __iomem *tx_bd; struct cpm_buf_desc __iomem *rx_bd; +#endif struct spi_transfer *xfer_in_progress; @@ -87,12 +89,12 @@ struct spi_mpc8xxx_cs { static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val) { - out_be32(reg, val); + iowrite32be(val, reg); } static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg) { - return in_be32(reg); + return ioread32be(reg); } struct mpc8xxx_spi_probe_info { diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 1985ba3..9878911 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -30,75 +30,14 @@ #include #include #include +#include +#include #include #include -#include -#include -#include - #include "spi-fsl-lib.h" - -/* CPM1 and CPM2 are mutually exclusive. */ -#ifdef CONFIG_CPM1 -#include -#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0) -#else -#include -#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0) -#endif - -/* SPI Controller registers */ -struct fsl_spi_reg { - u8 res1[0x20]; - __be32 mode; - __be32 event; - __be32 mask; - __be32 command; - __be32 transmit; - __be32 receive; -}; - -/* SPI Controller mode register definitions */ -#define SPMODE_LOOP (1 << 30) -#define SPMODE_CI_INACTIVEHIGH (1 << 29) -#define SPMODE_CP_BEGIN_EDGECLK (1 << 28) -#define SPMODE_DIV16 (1 << 27) -#define SPMODE_REV (1 << 26) -#define SPMODE_MS (1 << 25) -#define SPMODE_ENABLE (1 << 24) -#define SPMODE_LEN(x) ((x) << 20) -#define SPMODE_PM(x) ((x) << 16) -#define SPMODE_OP (1 << 14) -#define SPMODE_CG(x) ((x) << 7) - -/* - * Default for SPI Mode: - * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk - */ -#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \ - SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf)) - -/* SPIE register values */ -#define SPIE_NE 0x00000200 /* Not empty */ -#define SPIE_NF 0x00000100 /* Not full */ - -/* SPIM register values */ -#define SPIM_NE 0x00000200 /* Not empty */ -#define SPIM_NF 0x00000100 /* Not full */ - -#define SPIE_TXB 0x00000200 /* Last char is written to tx fifo */ -#define SPIE_RXB 0x00000100 /* Last char is written to rx buf */ - -/* SPCOM register values */ -#define SPCOM_STR (1 << 23) /* Start transmit */ - -#define SPI_PRAM_SIZE 0x100 -#define SPI_MRBLR ((unsigned int)PAGE_SIZE) - -static void *fsl_dummy_rx; -static DEFINE_MUTEX(fsl_dummy_rx_lock); -static int fsl_dummy_rx_refcnt; +#include "spi-fsl-cpm.h" +#include "spi-fsl-spi.h" static void fsl_spi_change_mode(struct spi_device *spi) { @@ -119,18 +58,7 @@ static void fsl_spi_change_mode(struct spi_device *spi) /* When in CPM mode, we need to reinit tx and rx. */ if (mspi->flags & SPI_CPM_MODE) { - if (mspi->flags & SPI_QE) { - qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock, - QE_CR_PROTOCOL_UNSPECIFIED, 0); - } else { - cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX); - if (mspi->flags & SPI_CPM1) { - out_be16(&mspi->pram->rbptr, - in_be16(&mspi->pram->rbase)); - out_be16(&mspi->pram->tbptr, - in_be16(&mspi->pram->tbase)); - } - } + fsl_spi_cpm_reinit_txrx(mspi); } mpc8xxx_spi_write_reg(mode, cs->hw_mode); local_irq_restore(flags); @@ -295,112 +223,6 @@ static int fsl_spi_setup_transfer(struct spi_device *spi, return 0; } -static void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi) -{ - struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd; - struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd; - unsigned int xfer_len = min(mspi->count, SPI_MRBLR); - unsigned int xfer_ofs; - struct fsl_spi_reg *reg_base = mspi->reg_base; - - xfer_ofs = mspi->xfer_in_progress->len - mspi->count; - - if (mspi->rx_dma == mspi->dma_dummy_rx) - out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma); - else - out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs); - out_be16(&rx_bd->cbd_datlen, 0); - out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP); - - if (mspi->tx_dma == mspi->dma_dummy_tx) - out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma); - else - out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs); - out_be16(&tx_bd->cbd_datlen, xfer_len); - out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP | - BD_SC_LAST); - - /* start transfer */ - mpc8xxx_spi_write_reg(®_base->command, SPCOM_STR); -} - -static int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi, - struct spi_transfer *t, bool is_dma_mapped) -{ - struct device *dev = mspi->dev; - struct fsl_spi_reg *reg_base = mspi->reg_base; - - if (is_dma_mapped) { - mspi->map_tx_dma = 0; - mspi->map_rx_dma = 0; - } else { - mspi->map_tx_dma = 1; - mspi->map_rx_dma = 1; - } - - if (!t->tx_buf) { - mspi->tx_dma = mspi->dma_dummy_tx; - mspi->map_tx_dma = 0; - } - - if (!t->rx_buf) { - mspi->rx_dma = mspi->dma_dummy_rx; - mspi->map_rx_dma = 0; - } - - if (mspi->map_tx_dma) { - void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */ - - mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, mspi->tx_dma)) { - dev_err(dev, "unable to map tx dma\n"); - return -ENOMEM; - } - } else if (t->tx_buf) { - mspi->tx_dma = t->tx_dma; - } - - if (mspi->map_rx_dma) { - mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, mspi->rx_dma)) { - dev_err(dev, "unable to map rx dma\n"); - goto err_rx_dma; - } - } else if (t->rx_buf) { - mspi->rx_dma = t->rx_dma; - } - - /* enable rx ints */ - mpc8xxx_spi_write_reg(®_base->mask, SPIE_RXB); - - mspi->xfer_in_progress = t; - mspi->count = t->len; - - /* start CPM transfers */ - fsl_spi_cpm_bufs_start(mspi); - - return 0; - -err_rx_dma: - if (mspi->map_tx_dma) - dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE); - return -ENOMEM; -} - -static void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) -{ - struct device *dev = mspi->dev; - struct spi_transfer *t = mspi->xfer_in_progress; - - if (mspi->map_tx_dma) - dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE); - if (mspi->map_rx_dma) - dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE); - mspi->xfer_in_progress = NULL; -} - static int fsl_spi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, unsigned int len) { @@ -568,30 +390,6 @@ static int fsl_spi_setup(struct spi_device *spi) return 0; } -static void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) -{ - u16 len; - struct fsl_spi_reg *reg_base = mspi->reg_base; - - dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__, - in_be16(&mspi->rx_bd->cbd_datlen), mspi->count); - - len = in_be16(&mspi->rx_bd->cbd_datlen); - if (len > mspi->count) { - WARN_ON(1); - len = mspi->count; - } - - /* Clear the events */ - mpc8xxx_spi_write_reg(®_base->event, events); - - mspi->count -= len; - if (mspi->count) - fsl_spi_cpm_bufs_start(mspi); - else - complete(&mspi->done); -} - static void fsl_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) { struct fsl_spi_reg *reg_base = mspi->reg_base; @@ -646,197 +444,6 @@ static irqreturn_t fsl_spi_irq(s32 irq, void *context_data) return ret; } -static void *fsl_spi_alloc_dummy_rx(void) -{ - mutex_lock(&fsl_dummy_rx_lock); - - if (!fsl_dummy_rx) - fsl_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL); - if (fsl_dummy_rx) - fsl_dummy_rx_refcnt++; - - mutex_unlock(&fsl_dummy_rx_lock); - - return fsl_dummy_rx; -} - -static void fsl_spi_free_dummy_rx(void) -{ - mutex_lock(&fsl_dummy_rx_lock); - - switch (fsl_dummy_rx_refcnt) { - case 0: - WARN_ON(1); - break; - case 1: - kfree(fsl_dummy_rx); - fsl_dummy_rx = NULL; - /* fall through */ - default: - fsl_dummy_rx_refcnt--; - break; - } - - mutex_unlock(&fsl_dummy_rx_lock); -} - -static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi) -{ - struct device *dev = mspi->dev; - struct device_node *np = dev->of_node; - const u32 *iprop; - int size; - void __iomem *spi_base; - unsigned long pram_ofs = -ENOMEM; - - /* Can't use of_address_to_resource(), QE muram isn't at 0. */ - iprop = of_get_property(np, "reg", &size); - - /* QE with a fixed pram location? */ - if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4) - return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE); - - /* QE but with a dynamic pram location? */ - if (mspi->flags & SPI_QE) { - pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64); - qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock, - QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs); - return pram_ofs; - } - - spi_base = of_iomap(np, 1); - if (spi_base == NULL) - return -EINVAL; - - if (mspi->flags & SPI_CPM2) { - pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64); - out_be16(spi_base, pram_ofs); - } else { - struct spi_pram __iomem *pram = spi_base; - u16 rpbase = in_be16(&pram->rpbase); - - /* Microcode relocation patch applied? */ - if (rpbase) - pram_ofs = rpbase; - else { - pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64); - out_be16(spi_base, pram_ofs); - } - } - - iounmap(spi_base); - return pram_ofs; -} - -static int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi) -{ - struct device *dev = mspi->dev; - struct device_node *np = dev->of_node; - const u32 *iprop; - int size; - unsigned long pram_ofs; - unsigned long bds_ofs; - - if (!(mspi->flags & SPI_CPM_MODE)) - return 0; - - if (!fsl_spi_alloc_dummy_rx()) - return -ENOMEM; - - if (mspi->flags & SPI_QE) { - iprop = of_get_property(np, "cell-index", &size); - if (iprop && size == sizeof(*iprop)) - mspi->subblock = *iprop; - - switch (mspi->subblock) { - default: - dev_warn(dev, "cell-index unspecified, assuming SPI1"); - /* fall through */ - case 0: - mspi->subblock = QE_CR_SUBBLOCK_SPI1; - break; - case 1: - mspi->subblock = QE_CR_SUBBLOCK_SPI2; - break; - } - } - - pram_ofs = fsl_spi_cpm_get_pram(mspi); - if (IS_ERR_VALUE(pram_ofs)) { - dev_err(dev, "can't allocate spi parameter ram\n"); - goto err_pram; - } - - bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) + - sizeof(*mspi->rx_bd), 8); - if (IS_ERR_VALUE(bds_ofs)) { - dev_err(dev, "can't allocate bds\n"); - goto err_bds; - } - - mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, mspi->dma_dummy_tx)) { - dev_err(dev, "unable to map dummy tx buffer\n"); - goto err_dummy_tx; - } - - mspi->dma_dummy_rx = dma_map_single(dev, fsl_dummy_rx, SPI_MRBLR, - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, mspi->dma_dummy_rx)) { - dev_err(dev, "unable to map dummy rx buffer\n"); - goto err_dummy_rx; - } - - mspi->pram = cpm_muram_addr(pram_ofs); - - mspi->tx_bd = cpm_muram_addr(bds_ofs); - mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd)); - - /* Initialize parameter ram. */ - out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd)); - out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd)); - out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL); - out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL); - out_be16(&mspi->pram->mrblr, SPI_MRBLR); - out_be32(&mspi->pram->rstate, 0); - out_be32(&mspi->pram->rdp, 0); - out_be16(&mspi->pram->rbptr, 0); - out_be16(&mspi->pram->rbc, 0); - out_be32(&mspi->pram->rxtmp, 0); - out_be32(&mspi->pram->tstate, 0); - out_be32(&mspi->pram->tdp, 0); - out_be16(&mspi->pram->tbptr, 0); - out_be16(&mspi->pram->tbc, 0); - out_be32(&mspi->pram->txtmp, 0); - - return 0; - -err_dummy_rx: - dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE); -err_dummy_tx: - cpm_muram_free(bds_ofs); -err_bds: - cpm_muram_free(pram_ofs); -err_pram: - fsl_spi_free_dummy_rx(); - return -ENOMEM; -} - -static void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi) -{ - struct device *dev = mspi->dev; - - if (!(mspi->flags & SPI_CPM_MODE)) - return; - - dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE); - dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE); - cpm_muram_free(cpm_muram_offset(mspi->tx_bd)); - cpm_muram_free(cpm_muram_offset(mspi->pram)); - fsl_spi_free_dummy_rx(); -} - static void fsl_spi_remove(struct mpc8xxx_spi *mspi) { iounmap(mspi->reg_base); @@ -1047,7 +654,7 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) struct device_node *np = ofdev->dev.of_node; struct spi_master *master; struct resource mem; - struct resource irq; + int irq; int ret = -ENOMEM; ret = of_mpc8xxx_spi_probe(ofdev); @@ -1062,13 +669,13 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) if (ret) goto err; - ret = of_irq_to_resource(np, 0, &irq); - if (!ret) { + irq = irq_of_parse_and_map(np, 0); + if (!irq) { ret = -EINVAL; goto err; } - master = fsl_spi_probe(dev, &mem, irq.start); + master = fsl_spi_probe(dev, &mem, irq); if (IS_ERR(master)) { ret = PTR_ERR(master); goto err; diff --git a/drivers/spi/spi-fsl-spi.h b/drivers/spi/spi-fsl-spi.h new file mode 100644 index 0000000..8bd73a4 --- /dev/null +++ b/drivers/spi/spi-fsl-spi.h @@ -0,0 +1,61 @@ +/* + * Freescale SPI controller driver. + * + * Maintainer: Kumar Gala + * + * Copyright (C) 2006 Polycom, Inc. + * Copyright 2010 Freescale Semiconductor, Inc. + * + * CPM SPI and QE buffer descriptors mode support: + * Copyright (c) 2009 MontaVista Software, Inc. + * Author: Anton Vorontsov + * + * 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. + */ + +#ifndef __SPI_FSL_SPI_H__ +#define __SPI_FSL_SPI_H__ + +/* SPI Controller registers */ +struct fsl_spi_reg { + u8 res1[0x20]; + __be32 mode; + __be32 event; + __be32 mask; + __be32 command; + __be32 transmit; + __be32 receive; +}; + +/* SPI Controller mode register definitions */ +#define SPMODE_LOOP (1 << 30) +#define SPMODE_CI_INACTIVEHIGH (1 << 29) +#define SPMODE_CP_BEGIN_EDGECLK (1 << 28) +#define SPMODE_DIV16 (1 << 27) +#define SPMODE_REV (1 << 26) +#define SPMODE_MS (1 << 25) +#define SPMODE_ENABLE (1 << 24) +#define SPMODE_LEN(x) ((x) << 20) +#define SPMODE_PM(x) ((x) << 16) +#define SPMODE_OP (1 << 14) +#define SPMODE_CG(x) ((x) << 7) + +/* + * Default for SPI Mode: + * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk + */ +#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \ + SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf)) + +/* SPIE register values */ +#define SPIE_NE 0x00000200 /* Not empty */ +#define SPIE_NF 0x00000100 /* Not full */ + +/* SPIM register values */ +#define SPIM_NE 0x00000200 /* Not empty */ +#define SPIM_NF 0x00000100 /* Not full */ + +#endif /* __SPI_FSL_SPI_H__ */ -- cgit v0.10.2 From f482cd0ff506cd74c75edceec4b737c46c60cb12 Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Fri, 15 Feb 2013 16:52:22 +0100 Subject: spi/spi-fsl-spi: Make sure in spi_fsl_setup that chipselect becomes inactive This is needed for a device in SPI_CS_HIGH mode that otherwise could start out active for the first transaction. Acked-by: Anton Vorontsov Signed-off-by: Andreas Larsson Signed-off-by: Grant Likely diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 9878911..1569f7d 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -387,6 +387,10 @@ static int fsl_spi_setup(struct spi_device *spi) cs->hw_mode = hw_mode; /* Restore settings */ return retval; } + + /* Initialize chipselect - might be active for SPI_CS_HIGH mode */ + fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE); + return 0; } -- cgit v0.10.2 From b48c4e3c944e8c52dcb0f477e9d80da045c7cab4 Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Fri, 15 Feb 2013 16:52:23 +0100 Subject: spi/spi-fsl-spi: Move setting non-zero tx and rx shifts to a function accessed by a function pointer Acked-by: Anton Vorontsov Signed-off-by: Andreas Larsson Signed-off-by: Grant Likely diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index d785595..eae54b6 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -69,6 +69,11 @@ struct mpc8xxx_spi { unsigned int flags; +#ifdef CONFIG_SPI_FSL_SPI + void (*set_shifts)(u32 *rx_shift, u32 *tx_shift, + int bits_per_word, int msb_first); +#endif + struct workqueue_struct *workqueue; struct work_struct work; diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 1569f7d..aba00fd 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -91,6 +91,25 @@ static void fsl_spi_chipselect(struct spi_device *spi, int value) } } +static void fsl_spi_qe_cpu_set_shifts(u32 *rx_shift, u32 *tx_shift, + int bits_per_word, int msb_first) +{ + *rx_shift = 0; + *tx_shift = 0; + if (msb_first) { + if (bits_per_word <= 8) { + *rx_shift = 16; + *tx_shift = 24; + } else if (bits_per_word <= 16) { + *rx_shift = 16; + *tx_shift = 16; + } + } else { + if (bits_per_word <= 8) + *rx_shift = 8; + } +} + static int mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs, struct spi_device *spi, struct mpc8xxx_spi *mpc8xxx_spi, @@ -101,31 +120,20 @@ static int mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs, if (bits_per_word <= 8) { cs->get_rx = mpc8xxx_spi_rx_buf_u8; cs->get_tx = mpc8xxx_spi_tx_buf_u8; - if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { - cs->rx_shift = 16; - cs->tx_shift = 24; - } } else if (bits_per_word <= 16) { cs->get_rx = mpc8xxx_spi_rx_buf_u16; cs->get_tx = mpc8xxx_spi_tx_buf_u16; - if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { - cs->rx_shift = 16; - cs->tx_shift = 16; - } } else if (bits_per_word <= 32) { cs->get_rx = mpc8xxx_spi_rx_buf_u32; cs->get_tx = mpc8xxx_spi_tx_buf_u32; } else return -EINVAL; - if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE && - spi->mode & SPI_LSB_FIRST) { - cs->tx_shift = 0; - if (bits_per_word <= 8) - cs->rx_shift = 8; - else - cs->rx_shift = 0; - } + if (mpc8xxx_spi->set_shifts) + mpc8xxx_spi->set_shifts(&cs->rx_shift, &cs->tx_shift, + bits_per_word, + !(spi->mode & SPI_LSB_FIRST)); + mpc8xxx_spi->rx_shift = cs->rx_shift; mpc8xxx_spi->tx_shift = cs->tx_shift; mpc8xxx_spi->get_rx = cs->get_rx; @@ -487,10 +495,13 @@ static struct spi_master * fsl_spi_probe(struct device *dev, if (ret) goto err_cpm_init; - if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { - mpc8xxx_spi->rx_shift = 16; - mpc8xxx_spi->tx_shift = 24; - } + if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) + mpc8xxx_spi->set_shifts = fsl_spi_qe_cpu_set_shifts; + + if (mpc8xxx_spi->set_shifts) + /* 8 bits per word and MSB first */ + mpc8xxx_spi->set_shifts(&mpc8xxx_spi->rx_shift, + &mpc8xxx_spi->tx_shift, 8, 1); mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem)); if (mpc8xxx_spi->reg_base == NULL) { -- cgit v0.10.2 From c3f3e7717f1cf01d9117a98ed89decc41d7cb5db Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Fri, 15 Feb 2013 16:52:24 +0100 Subject: spi/spi-fsl-spi: Introduce a type for the driver For being able to distinguishing between the regular type of cores and others with different entries in of_fsl_spi_match. Acked-by: Anton Vorontsov Signed-off-by: Andreas Larsson Signed-off-by: Grant Likely diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index eae54b6..5a9c36c 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -70,6 +70,8 @@ struct mpc8xxx_spi { unsigned int flags; #ifdef CONFIG_SPI_FSL_SPI + int type; + void (*set_shifts)(u32 *rx_shift, u32 *tx_shift, int bits_per_word, int msb_first); #endif diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index aba00fd..b906443 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -39,6 +39,37 @@ #include "spi-fsl-cpm.h" #include "spi-fsl-spi.h" +#define TYPE_FSL 0 + +struct fsl_spi_match_data { + int type; +}; + +static struct fsl_spi_match_data of_fsl_spi_fsl_config = { + .type = TYPE_FSL, +}; + +static struct of_device_id of_fsl_spi_match[] = { + { + .compatible = "fsl,spi", + .data = &of_fsl_spi_fsl_config, + }, + {} +}; +MODULE_DEVICE_TABLE(of, of_fsl_spi_match); + +static int fsl_spi_get_type(struct device *dev) +{ + const struct of_device_id *match; + + if (dev->of_node) { + match = of_match_node(of_fsl_spi_match, dev->of_node); + if (match && match->data) + return ((struct fsl_spi_match_data *)match->data)->type; + } + return TYPE_FSL; +} + static void fsl_spi_change_mode(struct spi_device *spi) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); @@ -489,7 +520,7 @@ static struct spi_master * fsl_spi_probe(struct device *dev, mpc8xxx_spi = spi_master_get_devdata(master); mpc8xxx_spi->spi_do_one_msg = fsl_spi_do_one_msg; mpc8xxx_spi->spi_remove = fsl_spi_remove; - + mpc8xxx_spi->type = fsl_spi_get_type(dev); ret = fsl_spi_cpm_init(mpc8xxx_spi); if (ret) @@ -714,12 +745,6 @@ static int of_fsl_spi_remove(struct platform_device *ofdev) return 0; } -static const struct of_device_id of_fsl_spi_match[] = { - { .compatible = "fsl,spi" }, - {} -}; -MODULE_DEVICE_TABLE(of, of_fsl_spi_match); - static struct platform_driver of_fsl_spi_driver = { .driver = { .name = "fsl_spi", -- cgit v0.10.2 From 8922a366ddd20964e3542e12f0315a8a88b3a638 Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Fri, 15 Feb 2013 16:52:25 +0100 Subject: spi/spi-fsl-spi: Add support for setting a maximum number of bits per word Acked-by: Anton Vorontsov Signed-off-by: Andreas Larsson Signed-off-by: Grant Likely diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 5a9c36c..d5c788b 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -71,6 +71,7 @@ struct mpc8xxx_spi { #ifdef CONFIG_SPI_FSL_SPI int type; + u8 max_bits_per_word; void (*set_shifts)(u32 *rx_shift, u32 *tx_shift, int bits_per_word, int msb_first); diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index b906443..7eff63a 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -213,7 +213,8 @@ static int fsl_spi_setup_transfer(struct spi_device *spi, /* Make sure its a bit width we support [4..16, 32] */ if ((bits_per_word < 4) - || ((bits_per_word > 16) && (bits_per_word != 32))) + || ((bits_per_word > 16) && (bits_per_word != 32)) + || (bits_per_word > mpc8xxx_spi->max_bits_per_word)) return -EINVAL; if (!hz) @@ -520,6 +521,7 @@ static struct spi_master * fsl_spi_probe(struct device *dev, mpc8xxx_spi = spi_master_get_devdata(master); mpc8xxx_spi->spi_do_one_msg = fsl_spi_do_one_msg; mpc8xxx_spi->spi_remove = fsl_spi_remove; + mpc8xxx_spi->max_bits_per_word = 32; mpc8xxx_spi->type = fsl_spi_get_type(dev); ret = fsl_spi_cpm_init(mpc8xxx_spi); @@ -557,6 +559,10 @@ static struct spi_master * fsl_spi_probe(struct device *dev, /* Enable SPI interface */ regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; + if (mpc8xxx_spi->max_bits_per_word < 8) { + regval &= ~SPMODE_LEN(0xF); + regval |= SPMODE_LEN(mpc8xxx_spi->max_bits_per_word - 1); + } if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) regval |= SPMODE_OP; -- cgit v0.10.2 From 447b0c7b939f1d9e4024edf07a471ce7b1bcf002 Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Fri, 15 Feb 2013 16:52:26 +0100 Subject: spi/spi-fsl-spi: Add support for Aeroflex Gaisler GRLIB cores normally running on SPARC This adds support for the mostly register-compatible SPICTRL cores from the GRLIB VHDL IP core library from Aeroflex Gaisler. They are normally running on SPARC. A different entry in of_fsl_spi_match matches this core and indicates a different hardware type that is used to set up different function pointers and special cases. The GRLIB core operates in cpu mode. The number of bits per word might be limited. There might be native chipselects selected via a slave select register. These differences to the FSL type cores, if present, are indicated by a capabilities register. Other register and function differences exists but are not relevant to the driver. Acked-by: Anton Vorontsov Signed-off-by: Andreas Larsson Signed-off-by: Grant Likely diff --git a/Documentation/devicetree/bindings/spi/fsl-spi.txt b/Documentation/devicetree/bindings/spi/fsl-spi.txt index 4f2ea94..b032dd7 100644 --- a/Documentation/devicetree/bindings/spi/fsl-spi.txt +++ b/Documentation/devicetree/bindings/spi/fsl-spi.txt @@ -4,7 +4,7 @@ Required properties: - cell-index : QE SPI subblock index. 0: QE subblock SPI1 1: QE subblock SPI2 -- compatible : should be "fsl,spi". +- compatible : should be "fsl,spi" or "aeroflexgaisler,spictrl". - mode : the SPI operation mode, it can be "cpu" or "cpu-qe". - reg : Offset and length of the register set for the device - interrupts : where a is the interrupt number and b is a diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 19e1ef7..3b3b019 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -5,6 +5,7 @@ using them to avoid name-space collisions. ad Avionic Design GmbH adi Analog Devices, Inc. +aeroflexgaisler Aeroflex Gaisler AB ak Asahi Kasei Corp. amcc Applied Micro Circuits Corporation (APM, formally AMCC) apm Applied Micro Circuits Corporation (APM) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 3524bec..2946ab4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -236,7 +236,7 @@ config SPI_FSL_CPM depends on FSL_SOC config SPI_FSL_SPI - bool "Freescale SPI controller" + bool "Freescale SPI controller and Aeroflex Gaisler GRLIB SPI controller" depends on OF select SPI_FSL_LIB select SPI_FSL_CPM if FSL_SOC @@ -244,6 +244,8 @@ config SPI_FSL_SPI This enables using the Freescale SPI controllers in master mode. MPC83xx platform uses the controller in cpu mode or CPM/QE mode. MPC8569 uses the controller in QE mode, MPC8610 in cpu mode. + This also enables using the Aeroflex Gaisler GRLIB SPI controller in + master mode. config SPI_FSL_ESPI bool "Freescale eSPI controller" diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 7eff63a..6846459 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -10,6 +10,10 @@ * Copyright (c) 2009 MontaVista Software, Inc. * Author: Anton Vorontsov * + * GRLIB support: + * Copyright (c) 2012 Aeroflex Gaisler AB. + * Author: Andreas Larsson + * * 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 @@ -40,6 +44,7 @@ #include "spi-fsl-spi.h" #define TYPE_FSL 0 +#define TYPE_GRLIB 1 struct fsl_spi_match_data { int type; @@ -49,11 +54,19 @@ static struct fsl_spi_match_data of_fsl_spi_fsl_config = { .type = TYPE_FSL, }; +static struct fsl_spi_match_data of_fsl_spi_grlib_config = { + .type = TYPE_GRLIB, +}; + static struct of_device_id of_fsl_spi_match[] = { { .compatible = "fsl,spi", .data = &of_fsl_spi_fsl_config, }, + { + .compatible = "aeroflexgaisler,spictrl", + .data = &of_fsl_spi_grlib_config, + }, {} }; MODULE_DEVICE_TABLE(of, of_fsl_spi_match); @@ -141,6 +154,21 @@ static void fsl_spi_qe_cpu_set_shifts(u32 *rx_shift, u32 *tx_shift, } } +static void fsl_spi_grlib_set_shifts(u32 *rx_shift, u32 *tx_shift, + int bits_per_word, int msb_first) +{ + *rx_shift = 0; + *tx_shift = 0; + if (bits_per_word <= 16) { + if (msb_first) { + *rx_shift = 16; /* LSB in bit 16 */ + *tx_shift = 32 - bits_per_word; /* MSB in bit 31 */ + } else { + *rx_shift = 16 - bits_per_word; /* MSB in bit 15 */ + } + } +} + static int mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs, struct spi_device *spi, struct mpc8xxx_spi *mpc8xxx_spi, @@ -494,6 +522,42 @@ static void fsl_spi_remove(struct mpc8xxx_spi *mspi) fsl_spi_cpm_free(mspi); } +static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on) +{ + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + struct fsl_spi_reg *reg_base = mpc8xxx_spi->reg_base; + u32 slvsel; + u16 cs = spi->chip_select; + + slvsel = mpc8xxx_spi_read_reg(®_base->slvsel); + slvsel = on ? (slvsel | (1 << cs)) : (slvsel & ~(1 << cs)); + mpc8xxx_spi_write_reg(®_base->slvsel, slvsel); +} + +static void fsl_spi_grlib_probe(struct device *dev) +{ + struct fsl_spi_platform_data *pdata = dev->platform_data; + struct spi_master *master = dev_get_drvdata(dev); + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); + struct fsl_spi_reg *reg_base = mpc8xxx_spi->reg_base; + int mbits; + u32 capabilities; + + capabilities = mpc8xxx_spi_read_reg(®_base->cap); + + mpc8xxx_spi->set_shifts = fsl_spi_grlib_set_shifts; + mbits = SPCAP_MAXWLEN(capabilities); + if (mbits) + mpc8xxx_spi->max_bits_per_word = mbits + 1; + + master->num_chipselect = 1; /* Allow for an always selected chip */ + if (SPCAP_SSEN(capabilities)) { + master->num_chipselect = SPCAP_SSSZ(capabilities); + mpc8xxx_spi_write_reg(®_base->slvsel, 0xffffffff); + } + pdata->cs_control = fsl_spi_grlib_cs_control; +} + static struct spi_master * fsl_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) { @@ -528,6 +592,15 @@ static struct spi_master * fsl_spi_probe(struct device *dev, if (ret) goto err_cpm_init; + mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem)); + if (mpc8xxx_spi->reg_base == NULL) { + ret = -ENOMEM; + goto err_ioremap; + } + + if (mpc8xxx_spi->type == TYPE_GRLIB) + fsl_spi_grlib_probe(dev); + if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) mpc8xxx_spi->set_shifts = fsl_spi_qe_cpu_set_shifts; @@ -536,12 +609,6 @@ static struct spi_master * fsl_spi_probe(struct device *dev, mpc8xxx_spi->set_shifts(&mpc8xxx_spi->rx_shift, &mpc8xxx_spi->tx_shift, 8, 1); - mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem)); - if (mpc8xxx_spi->reg_base == NULL) { - ret = -ENOMEM; - goto err_ioremap; - } - /* Register for SPI Interrupt */ ret = request_irq(mpc8xxx_spi->irq, fsl_spi_irq, 0, "fsl_spi", mpc8xxx_spi); @@ -706,16 +773,19 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) struct device_node *np = ofdev->dev.of_node; struct spi_master *master; struct resource mem; - int irq; + int irq, type; int ret = -ENOMEM; ret = of_mpc8xxx_spi_probe(ofdev); if (ret) return ret; - ret = of_fsl_spi_get_chipselects(dev); - if (ret) - goto err; + type = fsl_spi_get_type(&ofdev->dev); + if (type == TYPE_FSL) { + ret = of_fsl_spi_get_chipselects(dev); + if (ret) + goto err; + } ret = of_address_to_resource(np, 0, &mem); if (ret) @@ -736,18 +806,22 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) return 0; err: - of_fsl_spi_free_chipselects(dev); + if (type == TYPE_FSL) + of_fsl_spi_free_chipselects(dev); return ret; } static int of_fsl_spi_remove(struct platform_device *ofdev) { + struct spi_master *master = dev_get_drvdata(&ofdev->dev); + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); int ret; ret = mpc8xxx_spi_remove(&ofdev->dev); if (ret) return ret; - of_fsl_spi_free_chipselects(&ofdev->dev); + if (mpc8xxx_spi->type == TYPE_FSL) + of_fsl_spi_free_chipselects(&ofdev->dev); return 0; } diff --git a/drivers/spi/spi-fsl-spi.h b/drivers/spi/spi-fsl-spi.h index 8bd73a4..9a6dae0 100644 --- a/drivers/spi/spi-fsl-spi.h +++ b/drivers/spi/spi-fsl-spi.h @@ -10,6 +10,10 @@ * Copyright (c) 2009 MontaVista Software, Inc. * Author: Anton Vorontsov * + * GRLIB support: + * Copyright (c) 2012 Aeroflex Gaisler AB. + * Author: Andreas Larsson + * * 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 @@ -21,13 +25,15 @@ /* SPI Controller registers */ struct fsl_spi_reg { - u8 res1[0x20]; + __be32 cap; /* TYPE_GRLIB specific */ + u8 res1[0x1C]; __be32 mode; __be32 event; __be32 mask; __be32 command; __be32 transmit; __be32 receive; + __be32 slvsel; /* TYPE_GRLIB specific */ }; /* SPI Controller mode register definitions */ @@ -43,6 +49,11 @@ struct fsl_spi_reg { #define SPMODE_OP (1 << 14) #define SPMODE_CG(x) ((x) << 7) +/* TYPE_GRLIB SPI Controller capability register definitions */ +#define SPCAP_SSEN(x) (((x) >> 16) & 0x1) +#define SPCAP_SSSZ(x) (((x) >> 24) & 0xff) +#define SPCAP_MAXWLEN(x) (((x) >> 20) & 0xf) + /* * Default for SPI Mode: * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk -- cgit v0.10.2 From 76a7498f691f4c315dba0747141eeee3ceb740dc Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Fri, 15 Feb 2013 16:52:27 +0100 Subject: spi/spi-fsl-spi: Add support for gpio chipselects for GRLIB type cores This relies upon of_spi_register_master to find out which gpios to use. Acked-by: Anton Vorontsov Signed-off-by: Andreas Larsson Signed-off-by: Grant Likely diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index d5c788b..52db693 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -71,6 +71,7 @@ struct mpc8xxx_spi { #ifdef CONFIG_SPI_FSL_SPI int type; + int native_chipselects; u8 max_bits_per_word; void (*set_shifts)(u32 *rx_shift, u32 *tx_shift, diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 6846459..14e202e 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -456,12 +456,46 @@ static int fsl_spi_setup(struct spi_device *spi) return retval; } + if (mpc8xxx_spi->type == TYPE_GRLIB) { + if (gpio_is_valid(spi->cs_gpio)) { + int desel; + + retval = gpio_request(spi->cs_gpio, + dev_name(&spi->dev)); + if (retval) + return retval; + + desel = !(spi->mode & SPI_CS_HIGH); + retval = gpio_direction_output(spi->cs_gpio, desel); + if (retval) { + gpio_free(spi->cs_gpio); + return retval; + } + } else if (spi->cs_gpio != -ENOENT) { + if (spi->cs_gpio < 0) + return spi->cs_gpio; + return -EINVAL; + } + /* When spi->cs_gpio == -ENOENT, a hole in the phandle list + * indicates to use native chipselect if present, or allow for + * an always selected chip + */ + } + /* Initialize chipselect - might be active for SPI_CS_HIGH mode */ fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE); return 0; } +static void fsl_spi_cleanup(struct spi_device *spi) +{ + struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + + if (mpc8xxx_spi->type == TYPE_GRLIB && gpio_is_valid(spi->cs_gpio)) + gpio_free(spi->cs_gpio); +} + static void fsl_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) { struct fsl_spi_reg *reg_base = mspi->reg_base; @@ -529,9 +563,13 @@ static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on) u32 slvsel; u16 cs = spi->chip_select; - slvsel = mpc8xxx_spi_read_reg(®_base->slvsel); - slvsel = on ? (slvsel | (1 << cs)) : (slvsel & ~(1 << cs)); - mpc8xxx_spi_write_reg(®_base->slvsel, slvsel); + if (gpio_is_valid(spi->cs_gpio)) { + gpio_set_value(spi->cs_gpio, on); + } else if (cs < mpc8xxx_spi->native_chipselects) { + slvsel = mpc8xxx_spi_read_reg(®_base->slvsel); + slvsel = on ? (slvsel | (1 << cs)) : (slvsel & ~(1 << cs)); + mpc8xxx_spi_write_reg(®_base->slvsel, slvsel); + } } static void fsl_spi_grlib_probe(struct device *dev) @@ -550,11 +588,12 @@ static void fsl_spi_grlib_probe(struct device *dev) if (mbits) mpc8xxx_spi->max_bits_per_word = mbits + 1; - master->num_chipselect = 1; /* Allow for an always selected chip */ + mpc8xxx_spi->native_chipselects = 0; if (SPCAP_SSEN(capabilities)) { - master->num_chipselect = SPCAP_SSSZ(capabilities); + mpc8xxx_spi->native_chipselects = SPCAP_SSSZ(capabilities); mpc8xxx_spi_write_reg(®_base->slvsel, 0xffffffff); } + master->num_chipselect = mpc8xxx_spi->native_chipselects; pdata->cs_control = fsl_spi_grlib_cs_control; } @@ -581,6 +620,7 @@ static struct spi_master * fsl_spi_probe(struct device *dev, goto err_probe; master->setup = fsl_spi_setup; + master->cleanup = fsl_spi_cleanup; mpc8xxx_spi = spi_master_get_devdata(master); mpc8xxx_spi->spi_do_one_msg = fsl_spi_do_one_msg; -- cgit v0.10.2 From ede2738a73f44885cc92655bda158dce6712a3c7 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 15 Feb 2013 15:03:47 -0700 Subject: spi/tegra-slink: remove redundant code There is no code to set spi->controller_data, and hence the HW CS logic can never trigger. Remove the unused code. Signed-off-by: Stephen Warren Signed-off-by: Grant Likely diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 17224f0..d353154 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -189,7 +189,6 @@ struct tegra_slink_data { unsigned dma_buf_size; unsigned max_buf_size; bool is_curr_dma_xfer; - bool is_hw_based_cs; struct completion rx_dma_complete; struct completion tx_dma_complete; @@ -717,7 +716,6 @@ static int tegra_slink_start_transfer_one(struct spi_device *spi, u8 bits_per_word; unsigned total_fifo_words; int ret; - struct tegra_spi_device_controller_data *cdata = spi->controller_data; unsigned long command; unsigned long command2; @@ -740,39 +738,11 @@ static int tegra_slink_start_transfer_one(struct spi_device *spi, command = tspi->def_command_reg; command |= SLINK_BIT_LENGTH(bits_per_word - 1); + command |= SLINK_CS_SW | SLINK_CS_VALUE; command2 = tspi->def_command2_reg; command2 |= SLINK_SS_EN_CS(spi->chip_select); - /* possibly use the hw based chip select */ - tspi->is_hw_based_cs = false; - if (cdata && cdata->is_hw_based_cs && is_single_xfer && - ((tspi->curr_dma_words * tspi->bytes_per_word) == - (t->len - tspi->cur_pos))) { - int setup_count; - int sts2; - - setup_count = cdata->cs_setup_clk_count >> 1; - setup_count = max(setup_count, 3); - command2 |= SLINK_SS_SETUP(setup_count); - if (tspi->chip_data->cs_hold_time) { - int hold_count; - - hold_count = cdata->cs_hold_clk_count; - hold_count = max(hold_count, 0xF); - sts2 = tegra_slink_readl(tspi, SLINK_STATUS2); - sts2 &= ~SLINK_SS_HOLD_TIME(0xF); - sts2 |= SLINK_SS_HOLD_TIME(hold_count); - tegra_slink_writel(tspi, sts2, SLINK_STATUS2); - } - tspi->is_hw_based_cs = true; - } - - if (tspi->is_hw_based_cs) - command &= ~SLINK_CS_SW; - else - command |= SLINK_CS_SW | SLINK_CS_VALUE; - command &= ~SLINK_MODES; if (spi->mode & SPI_CPHA) command |= SLINK_CK_SDA; -- cgit v0.10.2 From e2546959192756f474ef299cbfe070cbfb675ef3 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 15 Feb 2013 15:03:48 -0700 Subject: spi/tegra-sflash: assume CONFIG_OF, remove platform data Tegra only supports, and always enables, device tree. Remove all ifdefs and runtime checks for DT support from the driver. Platform data is therefore no longer required. Rework the driver to parse the device tree directly into struct tegra_sflash_data. Signed-off-by: Stephen Warren Signed-off-by: Grant Likely diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 3d6a12b..d65c000 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #define SPI_COMMAND 0x000 @@ -439,23 +438,13 @@ static irqreturn_t tegra_sflash_isr(int irq, void *context_data) return handle_cpu_based_xfer(tsd); } -static struct tegra_spi_platform_data *tegra_sflash_parse_dt( - struct platform_device *pdev) +static void tegra_sflash_parse_dt(struct tegra_sflash_data *tsd) { - struct tegra_spi_platform_data *pdata; - struct device_node *np = pdev->dev.of_node; - u32 max_freq; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(&pdev->dev, "Memory alloc for pdata failed\n"); - return NULL; - } - - if (!of_property_read_u32(np, "spi-max-frequency", &max_freq)) - pdata->spi_max_frequency = max_freq; + struct device_node *np = tsd->dev->of_node; - return pdata; + if (of_property_read_u32(np, "spi-max-frequency", + &tsd->spi_max_frequency)) + tsd->spi_max_frequency = 25000000; /* 25MHz */ } static struct of_device_id tegra_sflash_of_match[] = { @@ -469,28 +458,15 @@ static int tegra_sflash_probe(struct platform_device *pdev) struct spi_master *master; struct tegra_sflash_data *tsd; struct resource *r; - struct tegra_spi_platform_data *pdata = pdev->dev.platform_data; int ret; const struct of_device_id *match; - match = of_match_device(of_match_ptr(tegra_sflash_of_match), - &pdev->dev); + match = of_match_device(tegra_sflash_of_match, &pdev->dev); if (!match) { dev_err(&pdev->dev, "Error: No device match found\n"); return -ENODEV; } - if (!pdata && pdev->dev.of_node) - pdata = tegra_sflash_parse_dt(pdev); - - if (!pdata) { - dev_err(&pdev->dev, "No platform data, exiting\n"); - return -ENODEV; - } - - if (!pdata->spi_max_frequency) - pdata->spi_max_frequency = 25000000; /* 25MHz */ - master = spi_alloc_master(&pdev->dev, sizeof(*tsd)); if (!master) { dev_err(&pdev->dev, "master allocation failed\n"); @@ -510,6 +486,8 @@ static int tegra_sflash_probe(struct platform_device *pdev) tsd->dev = &pdev->dev; spin_lock_init(&tsd->lock); + tegra_sflash_parse_dt(tsd); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { dev_err(&pdev->dev, "No IO memory resource\n"); @@ -538,7 +516,6 @@ static int tegra_sflash_probe(struct platform_device *pdev) goto exit_free_irq; } - tsd->spi_max_frequency = pdata->spi_max_frequency; init_completion(&tsd->xfer_completion); pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { @@ -658,7 +635,7 @@ static struct platform_driver tegra_sflash_driver = { .name = "spi-tegra-sflash", .owner = THIS_MODULE, .pm = &slink_pm_ops, - .of_match_table = of_match_ptr(tegra_sflash_of_match), + .of_match_table = tegra_sflash_of_match, }, .probe = tegra_sflash_probe, .remove = tegra_sflash_remove, -- cgit v0.10.2 From c60fea02141167cc277ff92fd6ee21448f56449f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 15 Feb 2013 15:03:49 -0700 Subject: spi/tegra-slink: assume CONFIG_OF, remove platform data Tegra only supports, and always enables, device tree. Remove all ifdefs and runtime checks for DT support from the driver. Platform data is therefore no longer required. Rework the driver to parse the device tree directly into struct tegra_slink_data. Signed-off-by: Stephen Warren Signed-off-by: Grant Likely diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index d353154..9c02470 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #define SLINK_COMMAND 0x000 @@ -1032,29 +1031,18 @@ static irqreturn_t tegra_slink_isr(int irq, void *context_data) return IRQ_WAKE_THREAD; } -static struct tegra_spi_platform_data *tegra_slink_parse_dt( - struct platform_device *pdev) +static void tegra_slink_parse_dt(struct tegra_slink_data *tspi) { - struct tegra_spi_platform_data *pdata; - const unsigned int *prop; - struct device_node *np = pdev->dev.of_node; + struct device_node *np = tspi->dev->of_node; u32 of_dma[2]; - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(&pdev->dev, "Memory alloc for pdata failed\n"); - return NULL; - } - if (of_property_read_u32_array(np, "nvidia,dma-request-selector", of_dma, 2) >= 0) - pdata->dma_req_sel = of_dma[1]; + tspi->dma_req_sel = of_dma[1]; - prop = of_get_property(np, "spi-max-frequency", NULL); - if (prop) - pdata->spi_max_frequency = be32_to_cpup(prop); - - return pdata; + if (of_property_read_u32(np, "spi-max-frequency", + &tspi->spi_max_frequency)) + tspi->spi_max_frequency = 25000000; /* 25MHz */ } const struct tegra_slink_chip_data tegra30_spi_cdata = { @@ -1077,27 +1065,16 @@ static int tegra_slink_probe(struct platform_device *pdev) struct spi_master *master; struct tegra_slink_data *tspi; struct resource *r; - struct tegra_spi_platform_data *pdata = pdev->dev.platform_data; int ret, spi_irq; const struct tegra_slink_chip_data *cdata = NULL; const struct of_device_id *match; - match = of_match_device(of_match_ptr(tegra_slink_of_match), &pdev->dev); + match = of_match_device(tegra_slink_of_match, &pdev->dev); if (!match) { dev_err(&pdev->dev, "Error: No device match found\n"); return -ENODEV; } cdata = match->data; - if (!pdata && pdev->dev.of_node) - pdata = tegra_slink_parse_dt(pdev); - - if (!pdata) { - dev_err(&pdev->dev, "No platform data, exiting\n"); - return -ENODEV; - } - - if (!pdata->spi_max_frequency) - pdata->spi_max_frequency = 25000000; /* 25MHz */ master = spi_alloc_master(&pdev->dev, sizeof(*tspi)); if (!master) { @@ -1115,11 +1092,12 @@ static int tegra_slink_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, master); tspi = spi_master_get_devdata(master); tspi->master = master; - tspi->dma_req_sel = pdata->dma_req_sel; tspi->dev = &pdev->dev; tspi->chip_data = cdata; spin_lock_init(&tspi->lock); + tegra_slink_parse_dt(tspi); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { dev_err(&pdev->dev, "No IO memory resource\n"); @@ -1153,9 +1131,8 @@ static int tegra_slink_probe(struct platform_device *pdev) tspi->max_buf_size = SLINK_FIFO_DEPTH << 2; tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN; - tspi->spi_max_frequency = pdata->spi_max_frequency; - if (pdata->dma_req_sel) { + if (tspi->dma_req_sel) { ret = tegra_slink_init_dma_param(tspi, true); if (ret < 0) { dev_err(&pdev->dev, "RxDma Init failed, err %d\n", ret); @@ -1298,7 +1275,7 @@ static struct platform_driver tegra_slink_driver = { .name = "spi-tegra-slink", .owner = THIS_MODULE, .pm = &slink_pm_ops, - .of_match_table = of_match_ptr(tegra_slink_of_match), + .of_match_table = tegra_slink_of_match, }, .probe = tegra_slink_probe, .remove = tegra_slink_remove, -- cgit v0.10.2 From 5e49035e26abf557737c1281439548f33b41578e Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 15 Feb 2013 15:03:50 -0700 Subject: spi/tegra: remove unused Tegra platform data header The platform data header is no longer used. Delete it. Signed-off-by: Stephen Warren Signed-off-by: Grant Likely diff --git a/include/linux/spi/spi-tegra.h b/include/linux/spi/spi-tegra.h deleted file mode 100644 index 786932c..0000000 --- a/include/linux/spi/spi-tegra.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * spi-tegra.h: SPI interface for Nvidia Tegra20 SLINK controller. - * - * Copyright (C) 2011 NVIDIA Corporation - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef _LINUX_SPI_TEGRA_H -#define _LINUX_SPI_TEGRA_H - -struct tegra_spi_platform_data { - int dma_req_sel; - unsigned int spi_max_frequency; -}; - -/* - * Controller data from device to pass some info like - * hw based chip select can be used or not and if yes - * then CS hold and setup time. - */ -struct tegra_spi_device_controller_data { - bool is_hw_based_cs; - int cs_setup_clk_count; - int cs_hold_clk_count; -}; - -#endif /* _LINUX_SPI_TEGRA_H */ -- cgit v0.10.2 From f333a331adfacf8c7a9dbf7e5f72b10a0356156b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 22 Feb 2013 18:07:39 +0530 Subject: spi/tegra114: add spi driver Add SPI driver for NVIDIA's Tegra114 SPI controller. This controller is different than the older SoCs SPI controller in internal design as well as register interface. This driver supports the: - non DMA based transfer for smaller transfer i.e. less than FIFO depth. - APB DMA based transfer for larger transfer i.e. more than FIFO depth. - Clock gating through runtime PM callbacks. - registration through DT only. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Grant Likely diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt b/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt new file mode 100644 index 0000000..91ff771 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra114-spi.txt @@ -0,0 +1,26 @@ +NVIDIA Tegra114 SPI controller. + +Required properties: +- compatible : should be "nvidia,tegra114-spi". +- reg: Should contain SPI registers location and length. +- interrupts: Should contain SPI interrupts. +- nvidia,dma-request-selector : The Tegra DMA controller's phandle and + request selector for this SPI controller. +- This is also require clock named "spi" as per binding document + Documentation/devicetree/bindings/clock/clock-bindings.txt + +Recommended properties: +- spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt +Example: + +spi@7000d600 { + compatible = "nvidia,tegra114-spi"; + reg = <0x7000d600 0x200>; + interrupts = <0 82 0x04>; + nvidia,dma-request-selector = <&apbdma 16>; + spi-max-frequency = <25000000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; +}; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 2946ab4..864d87f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -415,6 +415,14 @@ config SPI_MXS help SPI driver for Freescale MXS devices. +config SPI_TEGRA114 + tristate "NVIDIA Tegra114 SPI Controller" + depends on ARCH_TEGRA && TEGRA20_APB_DMA + help + SPI driver for NVIDIA Tegra114 SPI Controller interface. This controller + is different than the older SoCs SPI controller and also register interface + get changed with this controller. + config SPI_TEGRA20_SFLASH tristate "Nvidia Tegra20 Serial flash Controller" depends on ARCH_TEGRA diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 604460d..33f9c09 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIRF) += spi-sirf.o +obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c new file mode 100644 index 0000000..598eb45 --- /dev/null +++ b/drivers/spi/spi-tegra114.c @@ -0,0 +1,1246 @@ +/* + * SPI driver for NVIDIA's Tegra114 SPI Controller. + * + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPI_COMMAND1 0x000 +#define SPI_BIT_LENGTH(x) (((x) & 0x1f) << 0) +#define SPI_PACKED (1 << 5) +#define SPI_TX_EN (1 << 11) +#define SPI_RX_EN (1 << 12) +#define SPI_BOTH_EN_BYTE (1 << 13) +#define SPI_BOTH_EN_BIT (1 << 14) +#define SPI_LSBYTE_FE (1 << 15) +#define SPI_LSBIT_FE (1 << 16) +#define SPI_BIDIROE (1 << 17) +#define SPI_IDLE_SDA_DRIVE_LOW (0 << 18) +#define SPI_IDLE_SDA_DRIVE_HIGH (1 << 18) +#define SPI_IDLE_SDA_PULL_LOW (2 << 18) +#define SPI_IDLE_SDA_PULL_HIGH (3 << 18) +#define SPI_IDLE_SDA_MASK (3 << 18) +#define SPI_CS_SS_VAL (1 << 20) +#define SPI_CS_SW_HW (1 << 21) +/* SPI_CS_POL_INACTIVE bits are default high */ +#define SPI_CS_POL_INACTIVE 22 +#define SPI_CS_POL_INACTIVE_0 (1 << 22) +#define SPI_CS_POL_INACTIVE_1 (1 << 23) +#define SPI_CS_POL_INACTIVE_2 (1 << 24) +#define SPI_CS_POL_INACTIVE_3 (1 << 25) +#define SPI_CS_POL_INACTIVE_MASK (0xF << 22) + +#define SPI_CS_SEL_0 (0 << 26) +#define SPI_CS_SEL_1 (1 << 26) +#define SPI_CS_SEL_2 (2 << 26) +#define SPI_CS_SEL_3 (3 << 26) +#define SPI_CS_SEL_MASK (3 << 26) +#define SPI_CS_SEL(x) (((x) & 0x3) << 26) +#define SPI_CONTROL_MODE_0 (0 << 28) +#define SPI_CONTROL_MODE_1 (1 << 28) +#define SPI_CONTROL_MODE_2 (2 << 28) +#define SPI_CONTROL_MODE_3 (3 << 28) +#define SPI_CONTROL_MODE_MASK (3 << 28) +#define SPI_MODE_SEL(x) (((x) & 0x3) << 28) +#define SPI_M_S (1 << 30) +#define SPI_PIO (1 << 31) + +#define SPI_COMMAND2 0x004 +#define SPI_TX_TAP_DELAY(x) (((x) & 0x3F) << 6) +#define SPI_RX_TAP_DELAY(x) (((x) & 0x3F) << 0) + +#define SPI_CS_TIMING1 0x008 +#define SPI_SETUP_HOLD(setup, hold) (((setup) << 4) | (hold)) +#define SPI_CS_SETUP_HOLD(reg, cs, val) \ + ((((val) & 0xFFu) << ((cs) * 8)) | \ + ((reg) & ~(0xFFu << ((cs) * 8)))) + +#define SPI_CS_TIMING2 0x00C +#define CYCLES_BETWEEN_PACKETS_0(x) (((x) & 0x1F) << 0) +#define CS_ACTIVE_BETWEEN_PACKETS_0 (1 << 5) +#define CYCLES_BETWEEN_PACKETS_1(x) (((x) & 0x1F) << 8) +#define CS_ACTIVE_BETWEEN_PACKETS_1 (1 << 13) +#define CYCLES_BETWEEN_PACKETS_2(x) (((x) & 0x1F) << 16) +#define CS_ACTIVE_BETWEEN_PACKETS_2 (1 << 21) +#define CYCLES_BETWEEN_PACKETS_3(x) (((x) & 0x1F) << 24) +#define CS_ACTIVE_BETWEEN_PACKETS_3 (1 << 29) +#define SPI_SET_CS_ACTIVE_BETWEEN_PACKETS(reg, cs, val) \ + (reg = (((val) & 0x1) << ((cs) * 8 + 5)) | \ + ((reg) & ~(1 << ((cs) * 8 + 5)))) +#define SPI_SET_CYCLES_BETWEEN_PACKETS(reg, cs, val) \ + (reg = (((val) & 0xF) << ((cs) * 8)) | \ + ((reg) & ~(0xF << ((cs) * 8)))) + +#define SPI_TRANS_STATUS 0x010 +#define SPI_BLK_CNT(val) (((val) >> 0) & 0xFFFF) +#define SPI_SLV_IDLE_COUNT(val) (((val) >> 16) & 0xFF) +#define SPI_RDY (1 << 30) + +#define SPI_FIFO_STATUS 0x014 +#define SPI_RX_FIFO_EMPTY (1 << 0) +#define SPI_RX_FIFO_FULL (1 << 1) +#define SPI_TX_FIFO_EMPTY (1 << 2) +#define SPI_TX_FIFO_FULL (1 << 3) +#define SPI_RX_FIFO_UNF (1 << 4) +#define SPI_RX_FIFO_OVF (1 << 5) +#define SPI_TX_FIFO_UNF (1 << 6) +#define SPI_TX_FIFO_OVF (1 << 7) +#define SPI_ERR (1 << 8) +#define SPI_TX_FIFO_FLUSH (1 << 14) +#define SPI_RX_FIFO_FLUSH (1 << 15) +#define SPI_TX_FIFO_EMPTY_COUNT(val) (((val) >> 16) & 0x7F) +#define SPI_RX_FIFO_FULL_COUNT(val) (((val) >> 23) & 0x7F) +#define SPI_FRAME_END (1 << 30) +#define SPI_CS_INACTIVE (1 << 31) + +#define SPI_FIFO_ERROR (SPI_RX_FIFO_UNF | \ + SPI_RX_FIFO_OVF | SPI_TX_FIFO_UNF | SPI_TX_FIFO_OVF) +#define SPI_FIFO_EMPTY (SPI_RX_FIFO_EMPTY | SPI_TX_FIFO_EMPTY) + +#define SPI_TX_DATA 0x018 +#define SPI_RX_DATA 0x01C + +#define SPI_DMA_CTL 0x020 +#define SPI_TX_TRIG_1 (0 << 15) +#define SPI_TX_TRIG_4 (1 << 15) +#define SPI_TX_TRIG_8 (2 << 15) +#define SPI_TX_TRIG_16 (3 << 15) +#define SPI_TX_TRIG_MASK (3 << 15) +#define SPI_RX_TRIG_1 (0 << 19) +#define SPI_RX_TRIG_4 (1 << 19) +#define SPI_RX_TRIG_8 (2 << 19) +#define SPI_RX_TRIG_16 (3 << 19) +#define SPI_RX_TRIG_MASK (3 << 19) +#define SPI_IE_TX (1 << 28) +#define SPI_IE_RX (1 << 29) +#define SPI_CONT (1 << 30) +#define SPI_DMA (1 << 31) +#define SPI_DMA_EN SPI_DMA + +#define SPI_DMA_BLK 0x024 +#define SPI_DMA_BLK_SET(x) (((x) & 0xFFFF) << 0) + +#define SPI_TX_FIFO 0x108 +#define SPI_RX_FIFO 0x188 +#define MAX_CHIP_SELECT 4 +#define SPI_FIFO_DEPTH 64 +#define DATA_DIR_TX (1 << 0) +#define DATA_DIR_RX (1 << 1) + +#define SPI_DMA_TIMEOUT (msecs_to_jiffies(1000)) +#define DEFAULT_SPI_DMA_BUF_LEN (16*1024) +#define TX_FIFO_EMPTY_COUNT_MAX SPI_TX_FIFO_EMPTY_COUNT(0x40) +#define RX_FIFO_FULL_COUNT_ZERO SPI_RX_FIFO_FULL_COUNT(0) +#define MAX_HOLD_CYCLES 16 +#define SPI_DEFAULT_SPEED 25000000 + +#define MAX_CHIP_SELECT 4 +#define SPI_FIFO_DEPTH 64 + +struct tegra_spi_data { + struct device *dev; + struct spi_master *master; + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + phys_addr_t phys; + unsigned irq; + int dma_req_sel; + u32 spi_max_frequency; + u32 cur_speed; + + struct spi_device *cur_spi; + unsigned cur_pos; + unsigned cur_len; + unsigned words_per_32bit; + unsigned bytes_per_word; + unsigned curr_dma_words; + unsigned cur_direction; + + unsigned cur_rx_pos; + unsigned cur_tx_pos; + + unsigned dma_buf_size; + unsigned max_buf_size; + bool is_curr_dma_xfer; + + struct completion rx_dma_complete; + struct completion tx_dma_complete; + + u32 tx_status; + u32 rx_status; + u32 status_reg; + bool is_packed; + unsigned long packed_size; + + u32 command1_reg; + u32 dma_control_reg; + u32 def_command1_reg; + u32 spi_cs_timing; + + struct completion xfer_completion; + struct spi_transfer *curr_xfer; + struct dma_chan *rx_dma_chan; + u32 *rx_dma_buf; + dma_addr_t rx_dma_phys; + struct dma_async_tx_descriptor *rx_dma_desc; + + struct dma_chan *tx_dma_chan; + u32 *tx_dma_buf; + dma_addr_t tx_dma_phys; + struct dma_async_tx_descriptor *tx_dma_desc; +}; + +static int tegra_spi_runtime_suspend(struct device *dev); +static int tegra_spi_runtime_resume(struct device *dev); + +static inline unsigned long tegra_spi_readl(struct tegra_spi_data *tspi, + unsigned long reg) +{ + return readl(tspi->base + reg); +} + +static inline void tegra_spi_writel(struct tegra_spi_data *tspi, + unsigned long val, unsigned long reg) +{ + writel(val, tspi->base + reg); + + /* Read back register to make sure that register writes completed */ + if (reg != SPI_TX_FIFO) + readl(tspi->base + SPI_COMMAND1); +} + +static void tegra_spi_clear_status(struct tegra_spi_data *tspi) +{ + unsigned long val; + + /* Write 1 to clear status register */ + val = tegra_spi_readl(tspi, SPI_TRANS_STATUS); + tegra_spi_writel(tspi, val, SPI_TRANS_STATUS); + + /* Clear fifo status error if any */ + val = tegra_spi_readl(tspi, SPI_FIFO_STATUS); + if (val & SPI_ERR) + tegra_spi_writel(tspi, SPI_ERR | SPI_FIFO_ERROR, + SPI_FIFO_STATUS); +} + +static unsigned tegra_spi_calculate_curr_xfer_param( + struct spi_device *spi, struct tegra_spi_data *tspi, + struct spi_transfer *t) +{ + unsigned remain_len = t->len - tspi->cur_pos; + unsigned max_word; + unsigned bits_per_word = t->bits_per_word; + unsigned max_len; + unsigned total_fifo_words; + + tspi->bytes_per_word = (bits_per_word - 1) / 8 + 1; + + if (bits_per_word == 8 || bits_per_word == 16) { + tspi->is_packed = 1; + tspi->words_per_32bit = 32/bits_per_word; + } else { + tspi->is_packed = 0; + tspi->words_per_32bit = 1; + } + + if (tspi->is_packed) { + max_len = min(remain_len, tspi->max_buf_size); + tspi->curr_dma_words = max_len/tspi->bytes_per_word; + total_fifo_words = (max_len + 3) / 4; + } else { + max_word = (remain_len - 1) / tspi->bytes_per_word + 1; + max_word = min(max_word, tspi->max_buf_size/4); + tspi->curr_dma_words = max_word; + total_fifo_words = max_word; + } + return total_fifo_words; +} + +static unsigned tegra_spi_fill_tx_fifo_from_client_txbuf( + struct tegra_spi_data *tspi, struct spi_transfer *t) +{ + unsigned nbytes; + unsigned tx_empty_count; + unsigned long fifo_status; + unsigned max_n_32bit; + unsigned i, count; + unsigned long x; + unsigned int written_words; + unsigned fifo_words_left; + u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos; + + fifo_status = tegra_spi_readl(tspi, SPI_FIFO_STATUS); + tx_empty_count = SPI_TX_FIFO_EMPTY_COUNT(fifo_status); + + if (tspi->is_packed) { + fifo_words_left = tx_empty_count * tspi->words_per_32bit; + written_words = min(fifo_words_left, tspi->curr_dma_words); + nbytes = written_words * tspi->bytes_per_word; + max_n_32bit = DIV_ROUND_UP(nbytes, 4); + for (count = 0; count < max_n_32bit; count++) { + x = 0; + for (i = 0; (i < 4) && nbytes; i++, nbytes--) + x |= (*tx_buf++) << (i*8); + tegra_spi_writel(tspi, x, SPI_TX_FIFO); + } + } else { + max_n_32bit = min(tspi->curr_dma_words, tx_empty_count); + written_words = max_n_32bit; + nbytes = written_words * tspi->bytes_per_word; + for (count = 0; count < max_n_32bit; count++) { + x = 0; + for (i = 0; nbytes && (i < tspi->bytes_per_word); + i++, nbytes--) + x |= ((*tx_buf++) << i*8); + tegra_spi_writel(tspi, x, SPI_TX_FIFO); + } + } + tspi->cur_tx_pos += written_words * tspi->bytes_per_word; + return written_words; +} + +static unsigned int tegra_spi_read_rx_fifo_to_client_rxbuf( + struct tegra_spi_data *tspi, struct spi_transfer *t) +{ + unsigned rx_full_count; + unsigned long fifo_status; + unsigned i, count; + unsigned long x; + unsigned int read_words = 0; + unsigned len; + u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_rx_pos; + + fifo_status = tegra_spi_readl(tspi, SPI_FIFO_STATUS); + rx_full_count = SPI_RX_FIFO_FULL_COUNT(fifo_status); + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + for (count = 0; count < rx_full_count; count++) { + x = tegra_spi_readl(tspi, SPI_RX_FIFO); + for (i = 0; len && (i < 4); i++, len--) + *rx_buf++ = (x >> i*8) & 0xFF; + } + tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + read_words += tspi->curr_dma_words; + } else { + unsigned int rx_mask; + unsigned int bits_per_word = t->bits_per_word; + + rx_mask = (1 << bits_per_word) - 1; + for (count = 0; count < rx_full_count; count++) { + x = tegra_spi_readl(tspi, SPI_RX_FIFO); + x &= rx_mask; + for (i = 0; (i < tspi->bytes_per_word); i++) + *rx_buf++ = (x >> (i*8)) & 0xFF; + } + tspi->cur_rx_pos += rx_full_count * tspi->bytes_per_word; + read_words += rx_full_count; + } + return read_words; +} + +static void tegra_spi_copy_client_txbuf_to_spi_txbuf( + struct tegra_spi_data *tspi, struct spi_transfer *t) +{ + unsigned len; + + /* Make the dma buffer to read by cpu */ + dma_sync_single_for_cpu(tspi->dev, tspi->tx_dma_phys, + tspi->dma_buf_size, DMA_TO_DEVICE); + + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + memcpy(tspi->tx_dma_buf, t->tx_buf + tspi->cur_pos, len); + } else { + unsigned int i; + unsigned int count; + u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos; + unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word; + unsigned int x; + + for (count = 0; count < tspi->curr_dma_words; count++) { + x = 0; + for (i = 0; consume && (i < tspi->bytes_per_word); + i++, consume--) + x |= ((*tx_buf++) << i * 8); + tspi->tx_dma_buf[count] = x; + } + } + tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->tx_dma_phys, + tspi->dma_buf_size, DMA_TO_DEVICE); +} + +static void tegra_spi_copy_spi_rxbuf_to_client_rxbuf( + struct tegra_spi_data *tspi, struct spi_transfer *t) +{ + unsigned len; + + /* Make the dma buffer to read by cpu */ + dma_sync_single_for_cpu(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); + + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + memcpy(t->rx_buf + tspi->cur_rx_pos, tspi->rx_dma_buf, len); + } else { + unsigned int i; + unsigned int count; + unsigned char *rx_buf = t->rx_buf + tspi->cur_rx_pos; + unsigned int x; + unsigned int rx_mask; + unsigned int bits_per_word = t->bits_per_word; + + rx_mask = (1 << bits_per_word) - 1; + for (count = 0; count < tspi->curr_dma_words; count++) { + x = tspi->rx_dma_buf[count]; + x &= rx_mask; + for (i = 0; (i < tspi->bytes_per_word); i++) + *rx_buf++ = (x >> (i*8)) & 0xFF; + } + } + tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); +} + +static void tegra_spi_dma_complete(void *args) +{ + struct completion *dma_complete = args; + + complete(dma_complete); +} + +static int tegra_spi_start_tx_dma(struct tegra_spi_data *tspi, int len) +{ + INIT_COMPLETION(tspi->tx_dma_complete); + tspi->tx_dma_desc = dmaengine_prep_slave_single(tspi->tx_dma_chan, + tspi->tx_dma_phys, len, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tspi->tx_dma_desc) { + dev_err(tspi->dev, "Not able to get desc for Tx\n"); + return -EIO; + } + + tspi->tx_dma_desc->callback = tegra_spi_dma_complete; + tspi->tx_dma_desc->callback_param = &tspi->tx_dma_complete; + + dmaengine_submit(tspi->tx_dma_desc); + dma_async_issue_pending(tspi->tx_dma_chan); + return 0; +} + +static int tegra_spi_start_rx_dma(struct tegra_spi_data *tspi, int len) +{ + INIT_COMPLETION(tspi->rx_dma_complete); + tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma_chan, + tspi->rx_dma_phys, len, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tspi->rx_dma_desc) { + dev_err(tspi->dev, "Not able to get desc for Rx\n"); + return -EIO; + } + + tspi->rx_dma_desc->callback = tegra_spi_dma_complete; + tspi->rx_dma_desc->callback_param = &tspi->rx_dma_complete; + + dmaengine_submit(tspi->rx_dma_desc); + dma_async_issue_pending(tspi->rx_dma_chan); + return 0; +} + +static int tegra_spi_start_dma_based_transfer( + struct tegra_spi_data *tspi, struct spi_transfer *t) +{ + unsigned long val; + unsigned int len; + int ret = 0; + unsigned long status; + + /* Make sure that Rx and Tx fifo are empty */ + status = tegra_spi_readl(tspi, SPI_FIFO_STATUS); + if ((status & SPI_FIFO_EMPTY) != SPI_FIFO_EMPTY) { + dev_err(tspi->dev, + "Rx/Tx fifo are not empty status 0x%08lx\n", status); + return -EIO; + } + + val = SPI_DMA_BLK_SET(tspi->curr_dma_words - 1); + tegra_spi_writel(tspi, val, SPI_DMA_BLK); + + if (tspi->is_packed) + len = DIV_ROUND_UP(tspi->curr_dma_words * tspi->bytes_per_word, + 4) * 4; + else + len = tspi->curr_dma_words * 4; + + /* Set attention level based on length of transfer */ + if (len & 0xF) + val |= SPI_TX_TRIG_1 | SPI_RX_TRIG_1; + else if (((len) >> 4) & 0x1) + val |= SPI_TX_TRIG_4 | SPI_RX_TRIG_4; + else + val |= SPI_TX_TRIG_8 | SPI_RX_TRIG_8; + + if (tspi->cur_direction & DATA_DIR_TX) + val |= SPI_IE_TX; + + if (tspi->cur_direction & DATA_DIR_RX) + val |= SPI_IE_RX; + + tegra_spi_writel(tspi, val, SPI_DMA_CTL); + tspi->dma_control_reg = val; + + if (tspi->cur_direction & DATA_DIR_TX) { + tegra_spi_copy_client_txbuf_to_spi_txbuf(tspi, t); + ret = tegra_spi_start_tx_dma(tspi, len); + if (ret < 0) { + dev_err(tspi->dev, + "Starting tx dma failed, err %d\n", ret); + return ret; + } + } + + if (tspi->cur_direction & DATA_DIR_RX) { + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); + + ret = tegra_spi_start_rx_dma(tspi, len); + if (ret < 0) { + dev_err(tspi->dev, + "Starting rx dma failed, err %d\n", ret); + if (tspi->cur_direction & DATA_DIR_TX) + dmaengine_terminate_all(tspi->tx_dma_chan); + return ret; + } + } + tspi->is_curr_dma_xfer = true; + tspi->dma_control_reg = val; + + val |= SPI_DMA_EN; + tegra_spi_writel(tspi, val, SPI_DMA_CTL); + return ret; +} + +static int tegra_spi_start_cpu_based_transfer( + struct tegra_spi_data *tspi, struct spi_transfer *t) +{ + unsigned long val; + unsigned cur_words; + + if (tspi->cur_direction & DATA_DIR_TX) + cur_words = tegra_spi_fill_tx_fifo_from_client_txbuf(tspi, t); + else + cur_words = tspi->curr_dma_words; + + val = SPI_DMA_BLK_SET(cur_words - 1); + tegra_spi_writel(tspi, val, SPI_DMA_BLK); + + val = 0; + if (tspi->cur_direction & DATA_DIR_TX) + val |= SPI_IE_TX; + + if (tspi->cur_direction & DATA_DIR_RX) + val |= SPI_IE_RX; + + tegra_spi_writel(tspi, val, SPI_DMA_CTL); + tspi->dma_control_reg = val; + + tspi->is_curr_dma_xfer = false; + + val |= SPI_DMA_EN; + tegra_spi_writel(tspi, val, SPI_DMA_CTL); + return 0; +} + +static int tegra_spi_init_dma_param(struct tegra_spi_data *tspi, + bool dma_to_memory) +{ + struct dma_chan *dma_chan; + u32 *dma_buf; + dma_addr_t dma_phys; + int ret; + struct dma_slave_config dma_sconfig; + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_chan = dma_request_channel(mask, NULL, NULL); + if (!dma_chan) { + dev_err(tspi->dev, + "Dma channel is not available, will try later\n"); + return -EPROBE_DEFER; + } + + dma_buf = dma_alloc_coherent(tspi->dev, tspi->dma_buf_size, + &dma_phys, GFP_KERNEL); + if (!dma_buf) { + dev_err(tspi->dev, " Not able to allocate the dma buffer\n"); + dma_release_channel(dma_chan); + return -ENOMEM; + } + + dma_sconfig.slave_id = tspi->dma_req_sel; + if (dma_to_memory) { + dma_sconfig.src_addr = tspi->phys + SPI_RX_FIFO; + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.src_maxburst = 0; + } else { + dma_sconfig.dst_addr = tspi->phys + SPI_TX_FIFO; + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.dst_maxburst = 0; + } + + ret = dmaengine_slave_config(dma_chan, &dma_sconfig); + if (ret) + goto scrub; + if (dma_to_memory) { + tspi->rx_dma_chan = dma_chan; + tspi->rx_dma_buf = dma_buf; + tspi->rx_dma_phys = dma_phys; + } else { + tspi->tx_dma_chan = dma_chan; + tspi->tx_dma_buf = dma_buf; + tspi->tx_dma_phys = dma_phys; + } + return 0; + +scrub: + dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys); + dma_release_channel(dma_chan); + return ret; +} + +static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi, + bool dma_to_memory) +{ + u32 *dma_buf; + dma_addr_t dma_phys; + struct dma_chan *dma_chan; + + if (dma_to_memory) { + dma_buf = tspi->rx_dma_buf; + dma_chan = tspi->rx_dma_chan; + dma_phys = tspi->rx_dma_phys; + tspi->rx_dma_chan = NULL; + tspi->rx_dma_buf = NULL; + } else { + dma_buf = tspi->tx_dma_buf; + dma_chan = tspi->tx_dma_chan; + dma_phys = tspi->tx_dma_phys; + tspi->tx_dma_buf = NULL; + tspi->tx_dma_chan = NULL; + } + if (!dma_chan) + return; + + dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys); + dma_release_channel(dma_chan); +} + +static int tegra_spi_start_transfer_one(struct spi_device *spi, + struct spi_transfer *t, bool is_first_of_msg, + bool is_single_xfer) +{ + struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); + u32 speed = t->speed_hz; + u8 bits_per_word = t->bits_per_word; + unsigned total_fifo_words; + int ret; + unsigned long command1; + int req_mode; + + if (speed != tspi->cur_speed) { + clk_set_rate(tspi->clk, speed); + tspi->cur_speed = speed; + } + + tspi->cur_spi = spi; + tspi->cur_pos = 0; + tspi->cur_rx_pos = 0; + tspi->cur_tx_pos = 0; + tspi->curr_xfer = t; + total_fifo_words = tegra_spi_calculate_curr_xfer_param(spi, tspi, t); + + if (is_first_of_msg) { + tegra_spi_clear_status(tspi); + + command1 = tspi->def_command1_reg; + command1 |= SPI_BIT_LENGTH(bits_per_word - 1); + + command1 &= ~SPI_CONTROL_MODE_MASK; + req_mode = spi->mode & 0x3; + if (req_mode == SPI_MODE_0) + command1 |= SPI_CONTROL_MODE_0; + else if (req_mode == SPI_MODE_1) + command1 |= SPI_CONTROL_MODE_1; + else if (req_mode == SPI_MODE_2) + command1 |= SPI_CONTROL_MODE_2; + else if (req_mode == SPI_MODE_3) + command1 |= SPI_CONTROL_MODE_3; + + tegra_spi_writel(tspi, command1, SPI_COMMAND1); + + command1 |= SPI_CS_SW_HW; + if (spi->mode & SPI_CS_HIGH) + command1 |= SPI_CS_SS_VAL; + else + command1 &= ~SPI_CS_SS_VAL; + + tegra_spi_writel(tspi, 0, SPI_COMMAND2); + } else { + command1 = tspi->command1_reg; + command1 &= ~SPI_BIT_LENGTH(~0); + command1 |= SPI_BIT_LENGTH(bits_per_word - 1); + } + + if (tspi->is_packed) + command1 |= SPI_PACKED; + + command1 &= ~(SPI_CS_SEL_MASK | SPI_TX_EN | SPI_RX_EN); + tspi->cur_direction = 0; + if (t->rx_buf) { + command1 |= SPI_RX_EN; + tspi->cur_direction |= DATA_DIR_RX; + } + if (t->tx_buf) { + command1 |= SPI_TX_EN; + tspi->cur_direction |= DATA_DIR_TX; + } + command1 |= SPI_CS_SEL(spi->chip_select); + tegra_spi_writel(tspi, command1, SPI_COMMAND1); + tspi->command1_reg = command1; + + dev_dbg(tspi->dev, "The def 0x%x and written 0x%lx\n", + tspi->def_command1_reg, command1); + + if (total_fifo_words > SPI_FIFO_DEPTH) + ret = tegra_spi_start_dma_based_transfer(tspi, t); + else + ret = tegra_spi_start_cpu_based_transfer(tspi, t); + return ret; +} + +static int tegra_spi_setup(struct spi_device *spi) +{ + struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); + unsigned long val; + unsigned long flags; + int ret; + unsigned int cs_pol_bit[MAX_CHIP_SELECT] = { + SPI_CS_POL_INACTIVE_0, + SPI_CS_POL_INACTIVE_1, + SPI_CS_POL_INACTIVE_2, + SPI_CS_POL_INACTIVE_3, + }; + + dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n", + spi->bits_per_word, + spi->mode & SPI_CPOL ? "" : "~", + spi->mode & SPI_CPHA ? "" : "~", + spi->max_speed_hz); + + BUG_ON(spi->chip_select >= MAX_CHIP_SELECT); + + /* Set speed to the spi max fequency if spi device has not set */ + spi->max_speed_hz = spi->max_speed_hz ? : tspi->spi_max_frequency; + + ret = pm_runtime_get_sync(tspi->dev); + if (ret < 0) { + dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); + return ret; + } + + spin_lock_irqsave(&tspi->lock, flags); + val = tspi->def_command1_reg; + if (spi->mode & SPI_CS_HIGH) + val &= ~cs_pol_bit[spi->chip_select]; + else + val |= cs_pol_bit[spi->chip_select]; + tspi->def_command1_reg = val; + tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1); + spin_unlock_irqrestore(&tspi->lock, flags); + + pm_runtime_put(tspi->dev); + return 0; +} + +static int tegra_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + bool is_first_msg = true; + int single_xfer; + struct tegra_spi_data *tspi = spi_master_get_devdata(master); + struct spi_transfer *xfer; + struct spi_device *spi = msg->spi; + int ret; + + msg->status = 0; + msg->actual_length = 0; + + ret = pm_runtime_get_sync(tspi->dev); + if (ret < 0) { + dev_err(tspi->dev, "runtime PM get failed: %d\n", ret); + msg->status = ret; + spi_finalize_current_message(master); + return ret; + } + + single_xfer = list_is_singular(&msg->transfers); + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + INIT_COMPLETION(tspi->xfer_completion); + ret = tegra_spi_start_transfer_one(spi, xfer, + is_first_msg, single_xfer); + if (ret < 0) { + dev_err(tspi->dev, + "spi can not start transfer, err %d\n", ret); + goto exit; + } + is_first_msg = false; + ret = wait_for_completion_timeout(&tspi->xfer_completion, + SPI_DMA_TIMEOUT); + if (WARN_ON(ret == 0)) { + dev_err(tspi->dev, + "spi trasfer timeout, err %d\n", ret); + ret = -EIO; + goto exit; + } + + if (tspi->tx_status || tspi->rx_status) { + dev_err(tspi->dev, "Error in Transfer\n"); + ret = -EIO; + goto exit; + } + msg->actual_length += xfer->len; + if (xfer->cs_change && xfer->delay_usecs) { + tegra_spi_writel(tspi, tspi->def_command1_reg, + SPI_COMMAND1); + udelay(xfer->delay_usecs); + } + } + ret = 0; +exit: + tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1); + pm_runtime_put(tspi->dev); + msg->status = ret; + spi_finalize_current_message(master); + return ret; +} + +static irqreturn_t handle_cpu_based_xfer(struct tegra_spi_data *tspi) +{ + struct spi_transfer *t = tspi->curr_xfer; + unsigned long flags; + + spin_lock_irqsave(&tspi->lock, flags); + if (tspi->tx_status || tspi->rx_status) { + dev_err(tspi->dev, "CpuXfer ERROR bit set 0x%x\n", + tspi->status_reg); + dev_err(tspi->dev, "CpuXfer 0x%08x:0x%08x\n", + tspi->command1_reg, tspi->dma_control_reg); + tegra_periph_reset_assert(tspi->clk); + udelay(2); + tegra_periph_reset_deassert(tspi->clk); + complete(&tspi->xfer_completion); + goto exit; + } + + if (tspi->cur_direction & DATA_DIR_RX) + tegra_spi_read_rx_fifo_to_client_rxbuf(tspi, t); + + if (tspi->cur_direction & DATA_DIR_TX) + tspi->cur_pos = tspi->cur_tx_pos; + else + tspi->cur_pos = tspi->cur_rx_pos; + + if (tspi->cur_pos == t->len) { + complete(&tspi->xfer_completion); + goto exit; + } + + tegra_spi_calculate_curr_xfer_param(tspi->cur_spi, tspi, t); + tegra_spi_start_cpu_based_transfer(tspi, t); +exit: + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t handle_dma_based_xfer(struct tegra_spi_data *tspi) +{ + struct spi_transfer *t = tspi->curr_xfer; + long wait_status; + int err = 0; + unsigned total_fifo_words; + unsigned long flags; + + /* Abort dmas if any error */ + if (tspi->cur_direction & DATA_DIR_TX) { + if (tspi->tx_status) { + dmaengine_terminate_all(tspi->tx_dma_chan); + err += 1; + } else { + wait_status = wait_for_completion_interruptible_timeout( + &tspi->tx_dma_complete, SPI_DMA_TIMEOUT); + if (wait_status <= 0) { + dmaengine_terminate_all(tspi->tx_dma_chan); + dev_err(tspi->dev, "TxDma Xfer failed\n"); + err += 1; + } + } + } + + if (tspi->cur_direction & DATA_DIR_RX) { + if (tspi->rx_status) { + dmaengine_terminate_all(tspi->rx_dma_chan); + err += 2; + } else { + wait_status = wait_for_completion_interruptible_timeout( + &tspi->rx_dma_complete, SPI_DMA_TIMEOUT); + if (wait_status <= 0) { + dmaengine_terminate_all(tspi->rx_dma_chan); + dev_err(tspi->dev, "RxDma Xfer failed\n"); + err += 2; + } + } + } + + spin_lock_irqsave(&tspi->lock, flags); + if (err) { + dev_err(tspi->dev, "DmaXfer: ERROR bit set 0x%x\n", + tspi->status_reg); + dev_err(tspi->dev, "DmaXfer 0x%08x:0x%08x\n", + tspi->command1_reg, tspi->dma_control_reg); + tegra_periph_reset_assert(tspi->clk); + udelay(2); + tegra_periph_reset_deassert(tspi->clk); + complete(&tspi->xfer_completion); + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; + } + + if (tspi->cur_direction & DATA_DIR_RX) + tegra_spi_copy_spi_rxbuf_to_client_rxbuf(tspi, t); + + if (tspi->cur_direction & DATA_DIR_TX) + tspi->cur_pos = tspi->cur_tx_pos; + else + tspi->cur_pos = tspi->cur_rx_pos; + + if (tspi->cur_pos == t->len) { + complete(&tspi->xfer_completion); + goto exit; + } + + /* Continue transfer in current message */ + total_fifo_words = tegra_spi_calculate_curr_xfer_param(tspi->cur_spi, + tspi, t); + if (total_fifo_words > SPI_FIFO_DEPTH) + err = tegra_spi_start_dma_based_transfer(tspi, t); + else + err = tegra_spi_start_cpu_based_transfer(tspi, t); + +exit: + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t tegra_spi_isr_thread(int irq, void *context_data) +{ + struct tegra_spi_data *tspi = context_data; + + if (!tspi->is_curr_dma_xfer) + return handle_cpu_based_xfer(tspi); + return handle_dma_based_xfer(tspi); +} + +static irqreturn_t tegra_spi_isr(int irq, void *context_data) +{ + struct tegra_spi_data *tspi = context_data; + + tspi->status_reg = tegra_spi_readl(tspi, SPI_FIFO_STATUS); + if (tspi->cur_direction & DATA_DIR_TX) + tspi->tx_status = tspi->status_reg & + (SPI_TX_FIFO_UNF | SPI_TX_FIFO_OVF); + + if (tspi->cur_direction & DATA_DIR_RX) + tspi->rx_status = tspi->status_reg & + (SPI_RX_FIFO_OVF | SPI_RX_FIFO_UNF); + tegra_spi_clear_status(tspi); + + return IRQ_WAKE_THREAD; +} + +static void tegra_spi_parse_dt(struct platform_device *pdev, + struct tegra_spi_data *tspi) +{ + struct device_node *np = pdev->dev.of_node; + u32 of_dma[2]; + + if (of_property_read_u32_array(np, "nvidia,dma-request-selector", + of_dma, 2) >= 0) + tspi->dma_req_sel = of_dma[1]; + + if (of_property_read_u32(np, "spi-max-frequency", + &tspi->spi_max_frequency)) + tspi->spi_max_frequency = 25000000; /* 25MHz */ +} + +static struct of_device_id tegra_spi_of_match[] = { + { .compatible = "nvidia,tegra114-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, tegra_spi_of_match); + +static int tegra_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct tegra_spi_data *tspi; + struct resource *r; + int ret, spi_irq; + + master = spi_alloc_master(&pdev->dev, sizeof(*tspi)); + if (!master) { + dev_err(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + dev_set_drvdata(&pdev->dev, master); + tspi = spi_master_get_devdata(master); + + /* Parse DT */ + tegra_spi_parse_dt(pdev, tspi); + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->setup = tegra_spi_setup; + master->transfer_one_message = tegra_spi_transfer_one_message; + master->num_chipselect = MAX_CHIP_SELECT; + master->bus_num = -1; + + tspi->master = master; + tspi->dev = &pdev->dev; + spin_lock_init(&tspi->lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "No IO memory resource\n"); + ret = -ENODEV; + goto exit_free_master; + } + tspi->phys = r->start; + tspi->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(tspi->base)) { + ret = PTR_ERR(tspi->base); + dev_err(&pdev->dev, "ioremap failed: err = %d\n", ret); + goto exit_free_master; + } + + spi_irq = platform_get_irq(pdev, 0); + tspi->irq = spi_irq; + ret = request_threaded_irq(tspi->irq, tegra_spi_isr, + tegra_spi_isr_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), tspi); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", + tspi->irq); + goto exit_free_master; + } + + tspi->clk = devm_clk_get(&pdev->dev, "spi"); + if (IS_ERR(tspi->clk)) { + dev_err(&pdev->dev, "can not get clock\n"); + ret = PTR_ERR(tspi->clk); + goto exit_free_irq; + } + + tspi->max_buf_size = SPI_FIFO_DEPTH << 2; + tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN; + + if (tspi->dma_req_sel) { + ret = tegra_spi_init_dma_param(tspi, true); + if (ret < 0) { + dev_err(&pdev->dev, "RxDma Init failed, err %d\n", ret); + goto exit_free_irq; + } + + ret = tegra_spi_init_dma_param(tspi, false); + if (ret < 0) { + dev_err(&pdev->dev, "TxDma Init failed, err %d\n", ret); + goto exit_rx_dma_free; + } + tspi->max_buf_size = tspi->dma_buf_size; + init_completion(&tspi->tx_dma_complete); + init_completion(&tspi->rx_dma_complete); + } + + init_completion(&tspi->xfer_completion); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra_spi_runtime_resume(&pdev->dev); + if (ret) + goto exit_pm_disable; + } + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + goto exit_pm_disable; + } + tspi->def_command1_reg = SPI_M_S; + tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1); + pm_runtime_put(&pdev->dev); + + master->dev.of_node = pdev->dev.of_node; + ret = spi_register_master(master); + if (ret < 0) { + dev_err(&pdev->dev, "can not register to master err %d\n", ret); + goto exit_pm_disable; + } + return ret; + +exit_pm_disable: + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_spi_runtime_suspend(&pdev->dev); + tegra_spi_deinit_dma_param(tspi, false); +exit_rx_dma_free: + tegra_spi_deinit_dma_param(tspi, true); +exit_free_irq: + free_irq(spi_irq, tspi); +exit_free_master: + spi_master_put(master); + return ret; +} + +static int tegra_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct tegra_spi_data *tspi = spi_master_get_devdata(master); + + free_irq(tspi->irq, tspi); + spi_unregister_master(master); + + if (tspi->tx_dma_chan) + tegra_spi_deinit_dma_param(tspi, false); + + if (tspi->rx_dma_chan) + tegra_spi_deinit_dma_param(tspi, true); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_spi_runtime_suspend(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_spi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + + return spi_master_suspend(master); +} + +static int tegra_spi_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_spi_data *tspi = spi_master_get_devdata(master); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm runtime failed, e = %d\n", ret); + return ret; + } + tegra_spi_writel(tspi, tspi->command1_reg, SPI_COMMAND1); + pm_runtime_put(dev); + + return spi_master_resume(master); +} +#endif + +static int tegra_spi_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_spi_data *tspi = spi_master_get_devdata(master); + + /* Flush all write which are in PPSB queue by reading back */ + tegra_spi_readl(tspi, SPI_COMMAND1); + + clk_disable_unprepare(tspi->clk); + return 0; +} + +static int tegra_spi_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_spi_data *tspi = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(tspi->clk); + if (ret < 0) { + dev_err(tspi->dev, "clk_prepare failed: %d\n", ret); + return ret; + } + return 0; +} + +static const struct dev_pm_ops tegra_spi_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_spi_runtime_suspend, + tegra_spi_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(tegra_spi_suspend, tegra_spi_resume) +}; +static struct platform_driver tegra_spi_driver = { + .driver = { + .name = "spi-tegra114", + .owner = THIS_MODULE, + .pm = &tegra_spi_pm_ops, + .of_match_table = tegra_spi_of_match, + }, + .probe = tegra_spi_probe, + .remove = tegra_spi_remove, +}; +module_platform_driver(tegra_spi_driver); + +MODULE_ALIAS("platform:spi-tegra114"); +MODULE_DESCRIPTION("NVIDIA Tegra114 SPI Controller Driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From cbfd6a21b6fae968d10374bc5913823573b517ef Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 8 Apr 2013 15:49:33 +0530 Subject: spi/pxa2xx: Convert to devm_ioremap_resource() Use the newly introduced devm_ioremap_resource() instead of devm_request_and_ioremap() which provides more consistent error handling. devm_ioremap_resource() provides its own error messages; so all explicit error messages can be removed from the failure code paths. Signed-off-by: Sachin Kamat Acked-by: Eric Miao Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index c6d5b97..75a8446 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1088,11 +1089,9 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) ssp = &pdata->ssp; ssp->phys_base = res->start; - ssp->mmio_base = devm_request_and_ioremap(&pdev->dev, res); - if (!ssp->mmio_base) { - dev_err(&pdev->dev, "failed to ioremap mmio_base\n"); - return NULL; - } + ssp->mmio_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ssp->mmio_base)) + return PTR_ERR(ssp->mmio_base); ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->irq = platform_get_irq(pdev, 0); -- cgit v0.10.2 From 8b0bebe2c821f0bb872da94cc815096dad89850c Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 5 Apr 2013 21:45:36 +0800 Subject: spi: tegra: slink: make local symbols static Neither tegra20_spi_cdata nor tegra30_spi_cdata are used outside this file so they can, and should, be static. Signed-off-by: Wei Yongjun Reviewed-by: Thierry Reding Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 9c02470..3faf88d 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1045,11 +1045,11 @@ static void tegra_slink_parse_dt(struct tegra_slink_data *tspi) tspi->spi_max_frequency = 25000000; /* 25MHz */ } -const struct tegra_slink_chip_data tegra30_spi_cdata = { +static const struct tegra_slink_chip_data tegra30_spi_cdata = { .cs_hold_time = true, }; -const struct tegra_slink_chip_data tegra20_spi_cdata = { +static const struct tegra_slink_chip_data tegra20_spi_cdata = { .cs_hold_time = false, }; -- cgit v0.10.2 From ab300d18175aaae8e4c6f90c0b0577384721dea7 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Sat, 6 Apr 2013 13:18:57 +0200 Subject: spi/bcm63xx: remove unused speed_hz variable speed_hz is a write only member, so we can safely remove it and its generation. Also fixes the missing clk_put after getting the periph clock. Signed-off-by: Jonas Gorski Signed-off-by: Mark Brown diff --git a/arch/mips/bcm63xx/dev-spi.c b/arch/mips/bcm63xx/dev-spi.c index f1c9c3e..e97fd60 100644 --- a/arch/mips/bcm63xx/dev-spi.c +++ b/arch/mips/bcm63xx/dev-spi.c @@ -85,20 +85,9 @@ static struct platform_device bcm63xx_spi_device = { int __init bcm63xx_spi_register(void) { - struct clk *periph_clk; - if (BCMCPU_IS_6328() || BCMCPU_IS_6345()) return -ENODEV; - periph_clk = clk_get(NULL, "periph"); - if (IS_ERR(periph_clk)) { - pr_err("unable to get periph clock\n"); - return -ENODEV; - } - - /* Set bus frequency */ - spi_pdata.speed_hz = clk_get_rate(periph_clk); - spi_resources[0].start = bcm63xx_regset_address(RSET_SPI); spi_resources[0].end = spi_resources[0].start; spi_resources[1].start = bcm63xx_get_irq_number(IRQ_SPI); diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h index c9bae13..b0184cf 100644 --- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h +++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h @@ -13,7 +13,6 @@ struct bcm63xx_spi_pdata { unsigned int msg_ctl_width; int bus_num; int num_chipselect; - u32 speed_hz; }; enum bcm63xx_regs_spi { diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 973099b..a4ec5f4 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -46,7 +46,6 @@ struct bcm63xx_spi { int irq; /* Platform data */ - u32 speed_hz; unsigned fifo_size; unsigned int msg_type_shift; unsigned int msg_ctl_width; @@ -436,7 +435,6 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer; master->transfer_one_message = bcm63xx_spi_transfer_one; master->mode_bits = MODEBITS; - bs->speed_hz = pdata->speed_hz; bs->msg_type_shift = pdata->msg_type_shift; bs->msg_ctl_width = pdata->msg_ctl_width; bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); -- cgit v0.10.2 From 86e98743ec168b98eea3dfacc1e8b5ff442eb70d Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 1 Apr 2013 17:29:21 +0200 Subject: spi: spi-mpc512x-psc: add support for gpio chip selects Currently the driver only uses one internal chip select. Add support for gpio chip selects configured by cs-gpios DT binding. Signed-off-by: Anatolij Gustschin Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 3e490ee..da60f26 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include struct mpc512x_psc_spi { @@ -113,7 +114,7 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi) out_be32(&psc->ccr, ccr); mps->bits_per_word = cs->bits_per_word; - if (mps->cs_control) + if (mps->cs_control && gpio_is_valid(spi->cs_gpio)) mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 1 : 0); } @@ -121,7 +122,7 @@ static void mpc512x_psc_spi_deactivate_cs(struct spi_device *spi) { struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master); - if (mps->cs_control) + if (mps->cs_control && gpio_is_valid(spi->cs_gpio)) mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 0 : 1); } @@ -278,6 +279,7 @@ static int mpc512x_psc_spi_setup(struct spi_device *spi) struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master); struct mpc512x_psc_spi_cs *cs = spi->controller_state; unsigned long flags; + int ret; if (spi->bits_per_word % 8) return -EINVAL; @@ -286,6 +288,19 @@ static int mpc512x_psc_spi_setup(struct spi_device *spi) cs = kzalloc(sizeof *cs, GFP_KERNEL); if (!cs) return -ENOMEM; + + if (gpio_is_valid(spi->cs_gpio)) { + ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "can't get CS gpio: %d\n", + ret); + kfree(cs); + return ret; + } + gpio_direction_output(spi->cs_gpio, + spi->mode & SPI_CS_HIGH ? 0 : 1); + } + spi->controller_state = cs; } @@ -319,6 +334,8 @@ static int mpc512x_psc_spi_transfer(struct spi_device *spi, static void mpc512x_psc_spi_cleanup(struct spi_device *spi) { + if (gpio_is_valid(spi->cs_gpio)) + gpio_free(spi->cs_gpio); kfree(spi->controller_state); } @@ -405,6 +422,11 @@ static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id) return IRQ_NONE; } +static void mpc512x_spi_cs_control(struct spi_device *spi, bool onoff) +{ + gpio_set_value(spi->cs_gpio, onoff); +} + /* bus_num is used only for the case dev->platform_data == NULL */ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, u32 size, unsigned int irq, @@ -425,12 +447,9 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, mps->irq = irq; if (pdata == NULL) { - dev_err(dev, "probe called without platform data, no " - "cs_control function will be called\n"); - mps->cs_control = NULL; + mps->cs_control = mpc512x_spi_cs_control; mps->sysclk = 0; master->bus_num = bus_num; - master->num_chipselect = 255; } else { mps->cs_control = pdata->cs_control; mps->sysclk = pdata->sysclk; -- cgit v0.10.2 From c3e2aa861005ec390e8a7556d1a1e980be1a6e1f Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 1 Apr 2013 17:31:19 +0200 Subject: spi: spi-mpc512x-psc: let transmiter/receiver enabled when in xfer loop There is no need to disable transmitter/receiver after each loop iteration and re-enable it for next loop iteration. Enable the transmitter/receiver before xfer loop starts and disable it when the whole transfer is done. Signed-off-by: Anatolij Gustschin Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index da60f26..dfddf33 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -149,6 +149,9 @@ static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi, in_8(&psc->mode); out_8(&psc->mode, 0x0); + /* enable transmiter/receiver */ + out_8(&psc->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); + while (len) { int count; int i; @@ -177,10 +180,6 @@ static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi, out_be32(&fifo->txisr, MPC512x_PSC_FIFO_EMPTY); out_be32(&fifo->tximr, MPC512x_PSC_FIFO_EMPTY); - /* enable transmiter/receiver */ - out_8(&psc->command, - MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); - wait_for_completion(&mps->done); mdelay(1); @@ -205,9 +204,6 @@ static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi, while (in_be32(&fifo->rxcnt)) { in_8(&fifo->rxdata_8); } - - out_8(&psc->command, - MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); } /* disable transmiter/receiver and fifo interrupt */ out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); -- cgit v0.10.2 From 6b8cc3306e78490bda26815b04c786d8e1fc1489 Mon Sep 17 00:00:00 2001 From: Josef Ahmad Date: Tue, 9 Apr 2013 18:25:34 +0100 Subject: spi-gpio: init CS before spi_bitbang_setup() spi_bitbang_setup() deasserts the chip select line to initialise the device. The chip select GPIO line is obtained from spi_gpio->cs_gpios[] private data. Currently, devices that are not registered under devicetree environment will call into spi_bitbang_setup() with stale cs_gpios[]. This patch ensures spi_gpio->cs_gpios[] is always initialised prior to calling spi_bitbang_setup(). Reviewed-by: Daniel Mack Signed-off-by: Josef Ahmad Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 9ddef55..0021fc4 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -265,9 +265,9 @@ static int spi_gpio_setup(struct spi_device *spi) } } if (!status) { - status = spi_bitbang_setup(spi); /* in case it was initialized from static board data */ spi_gpio->cs_gpios[spi->chip_select] = cs; + status = spi_bitbang_setup(spi); } if (status) { -- cgit v0.10.2 From 788437273fa8b824810ea9a23f7ed4d7fdb2949a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 11 Apr 2013 22:42:03 +0200 Subject: spi: s3c64xx: move to generic dmaengine API The spi-s3c64xx uses a Samsung proprietary interface for talking to the DMA engine, which does not work with multiplatform kernels. This version of the patch leaves the old code in place, behind an #ifdef. This can be removed in the future, after the s3c64xx platform start supporting the regular dmaengine interface. An earlier version of this patch was tested successfully on exynos5250 by Padma Venkat. The conversion was rather mechanical, since the samsung interface is just a shallow wrapper around the dmaengine interface. Signed-off-by: Arnd Bergmann diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c index 51afedd..03db14d 100644 --- a/arch/arm/plat-samsung/devs.c +++ b/arch/arm/plat-samsung/devs.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -1552,6 +1553,9 @@ void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr, pd.num_cs = num_cs; pd.src_clk_nr = src_clk_nr; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi0_cfg_gpio; +#ifdef CONFIG_PL330_DMA + pd.filter = pl330_filter; +#endif s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi0); } @@ -1590,6 +1594,9 @@ void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr, pd.num_cs = num_cs; pd.src_clk_nr = src_clk_nr; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi1_cfg_gpio; +#ifdef CONFIG_PL330_DMA + pd.filter = pl330_filter; +#endif s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi1); } @@ -1628,6 +1635,9 @@ void __init s3c64xx_spi2_set_platdata(int (*cfg_gpio)(void), int src_clk_nr, pd.num_cs = num_cs; pd.src_clk_nr = src_clk_nr; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi2_cfg_gpio; +#ifdef CONFIG_PL330_DMA + pd.filter = pl330_filter; +#endif s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi2); } diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 682b1e7..4989aeb 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -31,9 +32,12 @@ #include #include -#include #include +#ifdef CONFIG_SAMSUNG_DMADEV +#include +#endif + #define MAX_SPI_PORTS 3 /* Registers and bit-fields */ @@ -131,9 +135,9 @@ #define TXBUSY (1<<3) struct s3c64xx_spi_dma_data { - unsigned ch; + struct dma_chan *ch; enum dma_transfer_direction direction; - enum dma_ch dmach; + unsigned int dmach; }; /** @@ -195,16 +199,14 @@ struct s3c64xx_spi_driver_data { unsigned cur_speed; struct s3c64xx_spi_dma_data rx_dma; struct s3c64xx_spi_dma_data tx_dma; +#ifdef CONFIG_SAMSUNG_DMADEV struct samsung_dma_ops *ops; +#endif struct s3c64xx_spi_port_config *port_conf; unsigned int port_id; unsigned long gpios[4]; }; -static struct s3c2410_dma_client s3c64xx_spi_dma_client = { - .name = "samsung-spi-dma", -}; - static void flush_fifo(struct s3c64xx_spi_driver_data *sdd) { void __iomem *regs = sdd->regs; @@ -281,6 +283,13 @@ static void s3c64xx_spi_dmacb(void *data) spin_unlock_irqrestore(&sdd->lock, flags); } +#ifdef CONFIG_SAMSUNG_DMADEV +/* FIXME: remove this section once arch/arm/mach-s3c64xx uses dmaengine */ + +static struct s3c2410_dma_client s3c64xx_spi_dma_client = { + .name = "samsung-spi-dma", +}; + static void prepare_dma(struct s3c64xx_spi_dma_data *dma, unsigned len, dma_addr_t buf) { @@ -294,14 +303,14 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, config.direction = sdd->rx_dma.direction; config.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA; config.width = sdd->cur_bpw / 8; - sdd->ops->config(sdd->rx_dma.ch, &config); + sdd->ops->config((enum dma_ch)sdd->rx_dma.ch, &config); } else { sdd = container_of((void *)dma, struct s3c64xx_spi_driver_data, tx_dma); config.direction = sdd->tx_dma.direction; config.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA; config.width = sdd->cur_bpw / 8; - sdd->ops->config(sdd->tx_dma.ch, &config); + sdd->ops->config((enum dma_ch)sdd->tx_dma.ch, &config); } info.cap = DMA_SLAVE; @@ -311,8 +320,8 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, info.direction = dma->direction; info.buf = buf; - sdd->ops->prepare(dma->ch, &info); - sdd->ops->trigger(dma->ch); + sdd->ops->prepare((enum dma_ch)dma->ch, &info); + sdd->ops->trigger((enum dma_ch)dma->ch); } static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) @@ -325,12 +334,126 @@ static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) req.cap = DMA_SLAVE; req.client = &s3c64xx_spi_dma_client; - sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req, dev, "rx"); - sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req, dev, "tx"); + sdd->rx_dma.ch = (void *)sdd->ops->request(sdd->rx_dma.dmach, &req, dev, "rx"); + sdd->tx_dma.ch = (void *)sdd->ops->request(sdd->tx_dma.dmach, &req, dev, "tx"); return 1; } +static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) +{ + struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); + + /* Acquire DMA channels */ + while (!acquire_dma(sdd)) + usleep_range(10000, 11000); + + pm_runtime_get_sync(&sdd->pdev->dev); + + return 0; +} + +static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) +{ + struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); + + /* Free DMA channels */ + sdd->ops->release((enum dma_ch)sdd->rx_dma.ch, &s3c64xx_spi_dma_client); + sdd->ops->release((enum dma_ch)sdd->tx_dma.ch, &s3c64xx_spi_dma_client); + + pm_runtime_put(&sdd->pdev->dev); + + return 0; +} + +static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd, + struct s3c64xx_spi_dma_data *dma) +{ + sdd->ops->stop((enum dma_ch)dma->ch); +} +#else + +static void prepare_dma(struct s3c64xx_spi_dma_data *dma, + unsigned len, dma_addr_t buf) +{ + struct s3c64xx_spi_driver_data *sdd; + struct dma_slave_config config; + struct scatterlist sg; + struct dma_async_tx_descriptor *desc; + + if (dma->direction == DMA_DEV_TO_MEM) { + sdd = container_of((void *)dma, + struct s3c64xx_spi_driver_data, rx_dma); + config.direction = dma->direction; + config.src_addr = sdd->sfr_start + S3C64XX_SPI_RX_DATA; + config.src_addr_width = sdd->cur_bpw / 8; + config.src_maxburst = 1; + dmaengine_slave_config(dma->ch, &config); + } else { + sdd = container_of((void *)dma, + struct s3c64xx_spi_driver_data, tx_dma); + config.direction = dma->direction; + config.dst_addr = sdd->sfr_start + S3C64XX_SPI_TX_DATA; + config.dst_addr_width = sdd->cur_bpw / 8; + config.dst_maxburst = 1; + dmaengine_slave_config(dma->ch, &config); + } + + sg_init_table(&sg, 1); + sg_dma_len(&sg) = len; + sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf)), + len, offset_in_page(buf)); + sg_dma_address(&sg) = buf; + + desc = dmaengine_prep_slave_sg(dma->ch, + &sg, 1, dma->direction, DMA_PREP_INTERRUPT); + + desc->callback = s3c64xx_spi_dmacb; + desc->callback_param = dma; + + dmaengine_submit(desc); + dma_async_issue_pending(dma->ch); +} + +static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) +{ + struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); + dma_filter_fn filter = sdd->cntrlr_info->filter; + struct device *dev = &sdd->pdev->dev; + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* Acquire DMA channels */ + sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter, + (void*)sdd->rx_dma.dmach, dev, "rx"); + sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, + (void*)sdd->tx_dma.dmach, dev, "tx"); + pm_runtime_get_sync(&sdd->pdev->dev); + + return 0; +} + +static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) +{ + struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); + + /* Free DMA channels */ + dma_release_channel(sdd->rx_dma.ch); + dma_release_channel(sdd->tx_dma.ch); + + pm_runtime_put(&sdd->pdev->dev); + return 0; +} + +static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd, + struct s3c64xx_spi_dma_data *dma) +{ + dmaengine_terminate_all(dma->ch); +} +#endif + static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi, struct spi_transfer *xfer, int dma_mode) @@ -713,9 +836,9 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, } /* Polling method for xfers not bigger than FIFO capacity */ - if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1)) - use_dma = 0; - else + use_dma = 0; + if (sdd->rx_dma.ch && sdd->tx_dma.ch && + (xfer->len > ((FIFO_LVL_MASK(sdd) >> 1) + 1))) use_dma = 1; spin_lock_irqsave(&sdd->lock, flags); @@ -750,10 +873,10 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, if (use_dma) { if (xfer->tx_buf != NULL && (sdd->state & TXBUSY)) - sdd->ops->stop(sdd->tx_dma.ch); + s3c64xx_spi_dma_stop(sdd, &sdd->tx_dma); if (xfer->rx_buf != NULL && (sdd->state & RXBUSY)) - sdd->ops->stop(sdd->rx_dma.ch); + s3c64xx_spi_dma_stop(sdd, &sdd->rx_dma); } goto out; @@ -790,32 +913,6 @@ out: return 0; } -static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) -{ - struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); - - /* Acquire DMA channels */ - while (!acquire_dma(sdd)) - usleep_range(10000, 11000); - - pm_runtime_get_sync(&sdd->pdev->dev); - - return 0; -} - -static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) -{ - struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); - - /* Free DMA channels */ - sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client); - sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client); - - pm_runtime_put(&sdd->pdev->dev); - - return 0; -} - static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( struct spi_device *spi) { diff --git a/include/linux/platform_data/spi-s3c64xx.h b/include/linux/platform_data/spi-s3c64xx.h index ceba18d..8447f63 100644 --- a/include/linux/platform_data/spi-s3c64xx.h +++ b/include/linux/platform_data/spi-s3c64xx.h @@ -11,6 +11,8 @@ #ifndef __S3C64XX_PLAT_SPI_H #define __S3C64XX_PLAT_SPI_H +#include + struct platform_device; /** @@ -38,6 +40,7 @@ struct s3c64xx_spi_info { int src_clk_nr; int num_cs; int (*cfg_gpio)(void); + dma_filter_fn filter; }; /** -- cgit v0.10.2 From ddc5cdf161bce3d5191e7e8b84b7af5ad621a531 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 12 Apr 2013 17:25:07 -0700 Subject: spi: omap2-mcspi: Fix transfers if DMADEVICES is not set Selecting CONFIG_DMADEVICES is optional, and we must be able to continue even without DMA. Otherwise things like omap4430sdp nfsroot will fail if DMA is not selected. Note that the driver already supports PIO mode, but we fail to fall back to PIO if requesting DMA channels fails. Signed-off-by: Tony Lindgren Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 61eef47..b3db461 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -809,6 +809,10 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, return 0; } +/* + * Note that we currently allow DMA only if we get a channel + * for both rx and tx. Otherwise we'll do PIO for both rx and tx. + */ static int omap2_mcspi_request_dma(struct spi_device *spi) { struct spi_master *master = spi->master; @@ -827,21 +831,22 @@ static int omap2_mcspi_request_dma(struct spi_device *spi) dma_cap_set(DMA_SLAVE, mask); sig = mcspi_dma->dma_rx_sync_dev; mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig); - if (!mcspi_dma->dma_rx) { - dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n"); - return -EAGAIN; - } + if (!mcspi_dma->dma_rx) + goto no_dma; sig = mcspi_dma->dma_tx_sync_dev; mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig); if (!mcspi_dma->dma_tx) { - dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n"); dma_release_channel(mcspi_dma->dma_rx); mcspi_dma->dma_rx = NULL; - return -EAGAIN; + goto no_dma; } return 0; + +no_dma: + dev_warn(&spi->dev, "not using DMA for McSPI\n"); + return -EAGAIN; } static int omap2_mcspi_setup(struct spi_device *spi) @@ -874,7 +879,7 @@ static int omap2_mcspi_setup(struct spi_device *spi) if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) { ret = omap2_mcspi_request_dma(spi); - if (ret < 0) + if (ret < 0 && ret != -EAGAIN) return ret; } @@ -932,6 +937,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) struct spi_device *spi; struct spi_transfer *t = NULL; struct spi_master *master; + struct omap2_mcspi_dma *mcspi_dma; int cs_active = 0; struct omap2_mcspi_cs *cs; struct omap2_mcspi_device_config *cd; @@ -941,6 +947,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) spi = m->spi; master = spi->master; + mcspi_dma = mcspi->dma_channels + spi->chip_select; cs = spi->controller_state; cd = spi->controller_data; @@ -997,7 +1004,8 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) __raw_writel(0, cs->base + OMAP2_MCSPI_TX0); - if (m->is_dma_mapped || t->len >= DMA_MIN_BYTES) + if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && + (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)) count = omap2_mcspi_txrx_dma(spi, t); else count = omap2_mcspi_txrx_pio(spi, t); @@ -1044,10 +1052,14 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) static int omap2_mcspi_transfer_one_message(struct spi_master *master, struct spi_message *m) { + struct spi_device *spi; struct omap2_mcspi *mcspi; + struct omap2_mcspi_dma *mcspi_dma; struct spi_transfer *t; + spi = m->spi; mcspi = spi_master_get_devdata(master); + mcspi_dma = mcspi->dma_channels + spi->chip_select; m->actual_length = 0; m->status = 0; @@ -1082,7 +1094,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, if (m->is_dma_mapped || len < DMA_MIN_BYTES) continue; - if (tx_buf != NULL) { + if (mcspi_dma->dma_tx && tx_buf != NULL) { t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf, len, DMA_TO_DEVICE); if (dma_mapping_error(mcspi->dev, t->tx_dma)) { @@ -1091,7 +1103,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, return -EINVAL; } } - if (rx_buf != NULL) { + if (mcspi_dma->dma_rx && rx_buf != NULL) { t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len, DMA_FROM_DEVICE); if (dma_mapping_error(mcspi->dev, t->rx_dma)) { -- cgit v0.10.2 From 19624236cce1b33a5d43895a92e3a9d438dc36e2 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 8 Apr 2013 11:51:42 +0100 Subject: MAINTAINERS: Update Grant's email address and maintainership I've taken a full time position with Linaro and so I'll be using my Linaro email address from this point on. It has also been many years since I've touched any of the Xilinx related code so mark those items as unmaintained. In addition, Mark Brown is taking the lead on SPI maintainership now, so I've reversed the order of our names for that entry. Signed-off-by: Grant Likely Cc: Mark Brown Cc: Linus Walleij Cc: Rob Herring diff --git a/MAINTAINERS b/MAINTAINERS index 9561658..a3be666 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3502,7 +3502,7 @@ F: drivers/isdn/gigaset/ F: include/uapi/linux/gigaset_dev.h GPIO SUBSYSTEM -M: Grant Likely +M: Grant Likely M: Linus Walleij S: Maintained T: git git://git.secretlab.ca/git/linux-2.6.git @@ -4319,7 +4319,7 @@ F: drivers/irqchip/ IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY) M: Benjamin Herrenschmidt -M: Grant Likely +M: Grant Likely T: git git://git.secretlab.ca/git/linux-2.6.git irqdomain/next S: Maintained F: Documentation/IRQ-domain.txt @@ -4806,11 +4806,8 @@ F: arch/powerpc/platforms/40x/ F: arch/powerpc/platforms/44x/ LINUX FOR POWERPC EMBEDDED XILINX VIRTEX -M: Grant Likely -W: http://wiki.secretlab.ca/index.php/Linux_on_Xilinx_Virtex L: linuxppc-dev@lists.ozlabs.org -T: git git://git.secretlab.ca/git/linux-2.6.git -S: Maintained +S: Unmaintained F: arch/powerpc/*/*virtex* F: arch/powerpc/*/*/*virtex* @@ -5798,7 +5795,7 @@ F: Documentation/i2c/busses/i2c-ocores F: drivers/i2c/busses/i2c-ocores.c OPEN FIRMWARE AND FLATTENED DEVICE TREE -M: Grant Likely +M: Grant Likely M: Rob Herring L: devicetree-discuss@lists.ozlabs.org (moderated for non-subscribers) W: http://fdt.secretlab.ca @@ -7434,11 +7431,10 @@ S: Maintained F: drivers/clk/spear/ SPI SUBSYSTEM -M: Grant Likely M: Mark Brown +M: Grant Likely L: spi-devel-general@lists.sourceforge.net Q: http://patchwork.kernel.org/project/spi-devel-general/list/ -T: git git://git.secretlab.ca/git/linux-2.6.git S: Maintained F: Documentation/spi/ F: drivers/spi/ @@ -8942,9 +8938,7 @@ S: Maintained F: drivers/net/ethernet/xilinx/xilinx_axienet* XILINX SYSTEMACE DRIVER -M: Grant Likely -W: http://www.secretlab.ca/ -S: Maintained +S: Unmaintained F: drivers/block/xsysace.c XILINX UARTLITE SERIAL DRIVER -- cgit v0.10.2 From 00ab5392cbc3f87ddfeada311e22f55dfb0bc5c6 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Mon, 15 Apr 2013 20:42:57 -0700 Subject: spi/s3c64xx: let device core setup the default pin configuration With device core now able to setup the default pin configuration, the pin configuration code based on the deprecated Samsung specific gpio bindings is removed. Signed-off-by: Thomas Abraham Signed-off-by: Doug Anderson Acked-by: Linus Walleij Reviewed-by: Doug Anderson Tested-by: Doug Anderson Signed-off-by: Grant Likely diff --git a/Documentation/devicetree/bindings/spi/spi-samsung.txt b/Documentation/devicetree/bindings/spi/spi-samsung.txt index a15ffed..86aa061 100644 --- a/Documentation/devicetree/bindings/spi/spi-samsung.txt +++ b/Documentation/devicetree/bindings/spi/spi-samsung.txt @@ -31,9 +31,6 @@ Required Board Specific Properties: - #address-cells: should be 1. - #size-cells: should be 0. -- gpios: The gpio specifier for clock, mosi and miso interface lines (in the - order specified). The format of the gpio specifier depends on the gpio - controller. Optional Board Specific Properties: @@ -86,9 +83,8 @@ Example: spi_0: spi@12d20000 { #address-cells = <1>; #size-cells = <0>; - gpios = <&gpa2 4 2 3 0>, - <&gpa2 6 2 3 0>, - <&gpa2 7 2 3 0>; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_bus>; w25q80bw@0 { #address-cells = <1>; diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 4989aeb..4ab992b 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1148,41 +1148,6 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) } #ifdef CONFIG_OF -static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd) -{ - struct device *dev = &sdd->pdev->dev; - int idx, gpio, ret; - - /* find gpios for mosi, miso and clock lines */ - for (idx = 0; idx < 3; idx++) { - gpio = of_get_gpio(dev->of_node, idx); - if (!gpio_is_valid(gpio)) { - dev_err(dev, "invalid gpio[%d]: %d\n", idx, gpio); - goto free_gpio; - } - sdd->gpios[idx] = gpio; - ret = gpio_request(gpio, "spi-bus"); - if (ret) { - dev_err(dev, "gpio [%d] request failed: %d\n", - gpio, ret); - goto free_gpio; - } - } - return 0; - -free_gpio: - while (--idx >= 0) - gpio_free(sdd->gpios[idx]); - return -EINVAL; -} - -static void s3c64xx_spi_dt_gpio_free(struct s3c64xx_spi_driver_data *sdd) -{ - unsigned int idx; - for (idx = 0; idx < 3; idx++) - gpio_free(sdd->gpios[idx]); -} - static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) { struct s3c64xx_spi_info *sci; @@ -1215,15 +1180,6 @@ static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) { return dev->platform_data; } - -static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd) -{ - return -EINVAL; -} - -static void s3c64xx_spi_dt_gpio_free(struct s3c64xx_spi_driver_data *sdd) -{ -} #endif static const struct of_device_id s3c64xx_spi_dt_match[]; @@ -1344,10 +1300,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) goto err0; } - if (!sci->cfg_gpio && pdev->dev.of_node) { - if (s3c64xx_spi_parse_dt_gpio(sdd)) - return -EBUSY; - } else if (sci->cfg_gpio == NULL || sci->cfg_gpio()) { + if (sci->cfg_gpio && sci->cfg_gpio()) { dev_err(&pdev->dev, "Unable to config gpio\n"); ret = -EBUSY; goto err0; @@ -1358,13 +1311,13 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) if (IS_ERR(sdd->clk)) { dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n"); ret = PTR_ERR(sdd->clk); - goto err1; + goto err0; } if (clk_prepare_enable(sdd->clk)) { dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n"); ret = -EBUSY; - goto err1; + goto err0; } sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr); @@ -1421,9 +1374,6 @@ err3: clk_disable_unprepare(sdd->src_clk); err2: clk_disable_unprepare(sdd->clk); -err1: - if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node) - s3c64xx_spi_dt_gpio_free(sdd); err0: platform_set_drvdata(pdev, NULL); spi_master_put(master); @@ -1446,9 +1396,6 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) clk_disable_unprepare(sdd->clk); - if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node) - s3c64xx_spi_dt_gpio_free(sdd); - platform_set_drvdata(pdev, NULL); spi_master_put(master); @@ -1467,9 +1414,6 @@ static int s3c64xx_spi_suspend(struct device *dev) clk_disable_unprepare(sdd->src_clk); clk_disable_unprepare(sdd->clk); - if (!sdd->cntrlr_info->cfg_gpio && dev->of_node) - s3c64xx_spi_dt_gpio_free(sdd); - sdd->cur_speed = 0; /* Output Clock is stopped */ return 0; @@ -1481,9 +1425,7 @@ static int s3c64xx_spi_resume(struct device *dev) struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); struct s3c64xx_spi_info *sci = sdd->cntrlr_info; - if (!sci->cfg_gpio && dev->of_node) - s3c64xx_spi_parse_dt_gpio(sdd); - else + if (sci->cfg_gpio) sci->cfg_gpio(); /* Enable the clock */ -- cgit v0.10.2 From 142e07beabfa8850e9e433b986e217fb596b9774 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 18 Apr 2013 11:14:59 +0800 Subject: spi: omap2-mcspi: fix error return code in omap2_mcspi_probe() Fix to return a negative error code from the error handling case instead of 0, as returned elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index b3db461..86d2158 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1293,7 +1293,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); pm_runtime_enable(&pdev->dev); - if (status || omap2_mcspi_master_setup(mcspi) < 0) + status = omap2_mcspi_master_setup(mcspi); + if (status < 0) goto disable_pm; status = spi_register_master(master); -- cgit v0.10.2 From 563b444e33810f3120838620c990480304e24e63 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Apr 2013 18:06:05 +0100 Subject: spi/s3c64xx: Fix non-dmaengine usage The multiplatform conversion in commit 788437 (spi: s3c64xx: move to generic dmaengine API) tested for the use of the Samsung-specific DMA API with SAMSUNG_DMADEV when in fact S3C_DMA should be used. This renderd DMA based transfers non-functional on platforms not using dmaengine. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 4ab992b..6d6537d 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -34,7 +34,7 @@ #include -#ifdef CONFIG_SAMSUNG_DMADEV +#ifdef CONFIG_S3C_DMA #include #endif @@ -199,7 +199,7 @@ struct s3c64xx_spi_driver_data { unsigned cur_speed; struct s3c64xx_spi_dma_data rx_dma; struct s3c64xx_spi_dma_data tx_dma; -#ifdef CONFIG_SAMSUNG_DMADEV +#ifdef CONFIG_S3C_DMA struct samsung_dma_ops *ops; #endif struct s3c64xx_spi_port_config *port_conf; @@ -283,7 +283,7 @@ static void s3c64xx_spi_dmacb(void *data) spin_unlock_irqrestore(&sdd->lock, flags); } -#ifdef CONFIG_SAMSUNG_DMADEV +#ifdef CONFIG_S3C_DMA /* FIXME: remove this section once arch/arm/mach-s3c64xx uses dmaengine */ static struct s3c2410_dma_client s3c64xx_spi_dma_client = { -- cgit v0.10.2 From fb9d044efbb54918f35a718691126248b236a06e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Apr 2013 18:12:00 +0100 Subject: spi/s3c64xx: Check for errors in dmaengine prepare_transfer() Don't silently ignore errors, report them. Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 6d6537d..5000586 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -421,6 +421,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) dma_filter_fn filter = sdd->cntrlr_info->filter; struct device *dev = &sdd->pdev->dev; dma_cap_mask_t mask; + int ret; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -428,11 +429,34 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) /* Acquire DMA channels */ sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter, (void*)sdd->rx_dma.dmach, dev, "rx"); + if (!sdd->rx_dma.ch) { + dev_err(dev, "Failed to get RX DMA channel\n"); + ret = -EBUSY; + goto out; + } + sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, (void*)sdd->tx_dma.dmach, dev, "tx"); - pm_runtime_get_sync(&sdd->pdev->dev); + if (!sdd->tx_dma.ch) { + dev_err(dev, "Failed to get TX DMA channel\n"); + ret = -EBUSY; + goto out_rx; + } + + ret = pm_runtime_get_sync(&sdd->pdev->dev); + if (ret != 0) { + dev_err(dev, "Failed to enable device: %d\n", ret); + goto out_tx; + } return 0; + +out_tx: + dma_release_channel(sdd->tx_dma.ch); +out_rx: + dma_release_channel(sdd->rx_dma.ch); +out: + return ret; } static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) -- cgit v0.10.2 From e7e4e13c540657b6db189528a90921df939c5930 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Apr 2013 18:18:47 +0100 Subject: MAINTAINERS: Add git repository and update my address Signed-off-by: Mark Brown diff --git a/MAINTAINERS b/MAINTAINERS index a3be666..0c9c104 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7431,9 +7431,10 @@ S: Maintained F: drivers/clk/spear/ SPI SUBSYSTEM -M: Mark Brown +M: Mark Brown M: Grant Likely L: spi-devel-general@lists.sourceforge.net +T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git Q: http://patchwork.kernel.org/project/spi-devel-general/list/ S: Maintained F: Documentation/spi/ -- cgit v0.10.2 From 3af4ed70252b172dd5b83ac9222ff38bbac6fdbe Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 23 Apr 2013 18:30:41 +0200 Subject: spi/sirf: fix MODULE_DEVICE_TABLE This fixes building the spi-sirf driver as a loadable module, which uses an incorrect MODULE_DEVICE_TABLE, by changing the reference to the correct symbol. Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index f59d417..0808cd5 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -660,7 +660,7 @@ static const struct of_device_id spi_sirfsoc_of_match[] = { { .compatible = "sirf,marco-spi", }, {} }; -MODULE_DEVICE_TABLE(of, sirfsoc_spi_of_match); +MODULE_DEVICE_TABLE(of, spi_sirfsoc_of_match); static struct platform_driver spi_sirfsoc_driver = { .driver = { -- cgit v0.10.2 From dfab30ee6184210ac3b91e3f70efaa47f14be4c4 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 3 Apr 2013 13:57:42 +0800 Subject: spi/spi-atmel: add physical base address Needed for future use with dmaengine enabled driver. Signed-off-by: Nicolas Ferre [wenyou.yang@atmel.com: submit the patch] Signed-off-by: Wenyou Yang Tested-by: Richard Genoud Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index ab2ed75..7e10bdb 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -196,6 +196,7 @@ struct atmel_spi_caps { struct atmel_spi { spinlock_t lock; + phys_addr_t phybase; void __iomem *regs; int irq; struct clk *clk; @@ -996,6 +997,7 @@ static int atmel_spi_probe(struct platform_device *pdev) as->regs = ioremap(regs->start, resource_size(regs)); if (!as->regs) goto out_free_buffer; + as->phybase = regs->start; as->irq = irq; as->clk = clk; -- cgit v0.10.2 From 8aad7924b5f55e330aebc1351525df9fa0056461 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 3 Apr 2013 13:58:36 +0800 Subject: spi/spi-atmel: add flag to controller data for lock operations Will allow to drop the lock during DMA operations. Replacing non-irqsave versions with irqsave versions of the lock to make it correct in both pdc and dmaengine transfer mode Signed-off-by: Nicolas Ferre [wenyou.yang@atmel.com: submit the patch] Signed-off-by: Wenyou Yang Tested-by: Richard Genoud Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 7e10bdb..3625951 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -195,6 +195,7 @@ struct atmel_spi_caps { */ struct atmel_spi { spinlock_t lock; + unsigned long flags; phys_addr_t phybase; void __iomem *regs; @@ -333,6 +334,16 @@ 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) +{ + spin_lock_irqsave(&as->lock, as->flags); +} + +static void atmel_spi_unlock(struct atmel_spi *as) +{ + spin_unlock_irqrestore(&as->lock, as->flags); +} + static inline int atmel_spi_xfer_is_last(struct spi_message *msg, struct spi_transfer *xfer) { @@ -569,9 +580,9 @@ atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, "xfer complete: %u bytes transferred\n", msg->actual_length); - spin_unlock(&as->lock); + atmel_spi_unlock(as); msg->complete(msg->context); - spin_lock(&as->lock); + atmel_spi_lock(as); as->current_transfer = NULL; as->next_transfer = NULL; @@ -594,7 +605,7 @@ atmel_spi_interrupt(int irq, void *dev_id) u32 status, pending, imr; int ret = IRQ_NONE; - spin_lock(&as->lock); + atmel_spi_lock(as); xfer = as->current_transfer; msg = list_entry(as->queue.next, struct spi_message, queue); @@ -697,7 +708,7 @@ atmel_spi_interrupt(int irq, void *dev_id) } } - spin_unlock(&as->lock); + atmel_spi_unlock(as); return ret; } @@ -802,13 +813,11 @@ static int atmel_spi_setup(struct spi_device *spi) spi->controller_state = asd; gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH)); } else { - unsigned long flags; - - spin_lock_irqsave(&as->lock, flags); + atmel_spi_lock(as); if (as->stay == spi) as->stay = NULL; cs_deactivate(as, spi); - spin_unlock_irqrestore(&as->lock, flags); + atmel_spi_unlock(as); } asd->csr = csr; @@ -827,7 +836,6 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) { struct atmel_spi *as; struct spi_transfer *xfer; - unsigned long flags; struct device *controller = spi->master->dev.parent; u8 bits; struct atmel_spi_device *asd; @@ -892,11 +900,11 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) msg->status = -EINPROGRESS; msg->actual_length = 0; - spin_lock_irqsave(&as->lock, flags); + atmel_spi_lock(as); list_add_tail(&msg->queue, &as->queue); if (!as->current_transfer) atmel_spi_next_message(spi->master); - spin_unlock_irqrestore(&as->lock, flags); + atmel_spi_unlock(as); return 0; } @@ -906,17 +914,16 @@ static void atmel_spi_cleanup(struct spi_device *spi) struct atmel_spi *as = spi_master_get_devdata(spi->master); struct atmel_spi_device *asd = spi->controller_state; unsigned gpio = (unsigned) spi->controller_data; - unsigned long flags; if (!asd) return; - spin_lock_irqsave(&as->lock, flags); + atmel_spi_lock(as); if (as->stay == spi) { as->stay = NULL; cs_deactivate(as, spi); } - spin_unlock_irqrestore(&as->lock, flags); + atmel_spi_unlock(as); spi->controller_state = NULL; gpio_free(gpio); -- cgit v0.10.2 From 1ccc404a7fc48dc39aa9605da9a3e579fca7dbf7 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 3 Apr 2013 13:59:19 +0800 Subject: spi/spi-atmel: add dmaengine support Add dmaengine support. Using "has_dma_support" member of struct is used to select the transfer mode: dmaengine or pdc. For the dmaengine transfer mode, it supports both 8 bits and 16 bits transfer. For the dmaengine transfer mode, if it fails to config dmaengine, or if the message length is less than 16 bytes, it will use the PIO transfer mode. Signed-off-by: Nicolas Ferre [wenyou.yang@atmel.com: using "has_dma_support" to select dmaengine as the spi xfer mode] [wenyou.yang@atmel.com: fix DMA: OOPS if buffer > 4096 bytes] [wenyou.yang@atmel.com: submit the patch] Signed-off-by: Wenyou Yang Signed-off-by: Richard Genoud [richard.genoud@gmail.com: update with dmaengine interface] [richard.genoud@gmail.com: fix __init/__devinit sections mismatch] [richard.genoud@gmail.com: adapt to slave_config changes] [richard.genoud@gmail.com: add support t0 16 bits transfer] Tested-by: Richard Genoud Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 3625951..787bd2c 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -15,11 +15,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -182,6 +184,22 @@ #define spi_writel(port,reg,value) \ __raw_writel((value), (port)->regs + SPI_##reg) +/* use PIO for small transfers, avoiding DMA setup/teardown overhead and + * cache operations; better heuristics consider wordsize and bitrate. + */ +#define DMA_MIN_BYTES 16 + +struct atmel_spi_dma { + struct dma_chan *chan_rx; + struct dma_chan *chan_tx; + struct scatterlist sgrx; + struct scatterlist sgtx; + struct dma_async_tx_descriptor *data_desc_rx; + struct dma_async_tx_descriptor *data_desc_tx; + + struct at_dma_slave dma_slave; +}; + struct atmel_spi_caps { bool is_spi2; bool has_wdrbt; @@ -206,16 +224,23 @@ struct atmel_spi { u8 stopping; struct list_head queue; + struct tasklet_struct tasklet; struct spi_transfer *current_transfer; unsigned long current_remaining_bytes; struct spi_transfer *next_transfer; unsigned long next_remaining_bytes; int done_status; + /* scratch buffer */ void *buffer; dma_addr_t buffer_dma; struct atmel_spi_caps caps; + + bool use_dma; + bool use_pdc; + /* dmaengine data */ + struct atmel_spi_dma dma; }; /* Controller-specific per-slave state */ @@ -284,6 +309,7 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi) | SPI_BIT(MODFDIS) | SPI_BIT(MSTR)); } + mr = spi_readl(as, MR); gpio_set_value(asd->npcs_pin, active); } else { @@ -344,6 +370,12 @@ static void atmel_spi_unlock(struct atmel_spi *as) spin_unlock_irqrestore(&as->lock, as->flags); } +static inline bool atmel_spi_use_dma(struct atmel_spi *as, + struct spi_transfer *xfer) +{ + return as->use_dma && xfer->len >= DMA_MIN_BYTES; +} + static inline int atmel_spi_xfer_is_last(struct spi_message *msg, struct spi_transfer *xfer) { @@ -355,6 +387,265 @@ static inline int atmel_spi_xfer_can_be_chained(struct spi_transfer *xfer) return xfer->delay_usecs == 0 && !xfer->cs_change; } +static int atmel_spi_dma_slave_config(struct atmel_spi *as, + struct dma_slave_config *slave_config, + u8 bits_per_word) +{ + int err = 0; + + if (bits_per_word > 8) { + slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + } else { + slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } + + slave_config->dst_addr = (dma_addr_t)as->phybase + SPI_TDR; + slave_config->src_addr = (dma_addr_t)as->phybase + SPI_RDR; + slave_config->src_maxburst = 1; + slave_config->dst_maxburst = 1; + slave_config->device_fc = false; + + slave_config->direction = DMA_MEM_TO_DEV; + if (dmaengine_slave_config(as->dma.chan_tx, slave_config)) { + dev_err(&as->pdev->dev, + "failed to configure tx dma channel\n"); + err = -EINVAL; + } + + slave_config->direction = DMA_DEV_TO_MEM; + if (dmaengine_slave_config(as->dma.chan_rx, slave_config)) { + dev_err(&as->pdev->dev, + "failed to configure rx dma channel\n"); + err = -EINVAL; + } + + return err; +} + +static bool filter(struct dma_chan *chan, void *slave) +{ + struct at_dma_slave *sl = slave; + + if (sl->dma_dev == chan->device->dev) { + chan->private = sl; + return true; + } else { + return false; + } +} + +static int atmel_spi_configure_dma(struct atmel_spi *as) +{ + struct at_dma_slave *sdata = &as->dma.dma_slave; + struct dma_slave_config slave_config; + int err; + + if (sdata && sdata->dma_dev) { + dma_cap_mask_t mask; + + /* Try to grab two DMA channels */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + as->dma.chan_tx = dma_request_channel(mask, filter, sdata); + if (as->dma.chan_tx) + as->dma.chan_rx = + dma_request_channel(mask, filter, sdata); + } + if (!as->dma.chan_rx || !as->dma.chan_tx) { + dev_err(&as->pdev->dev, + "DMA channel not available, SPI unable to use DMA\n"); + err = -EBUSY; + goto error; + } + + err = atmel_spi_dma_slave_config(as, &slave_config, 8); + if (err) + goto error; + + dev_info(&as->pdev->dev, + "Using %s (tx) and %s (rx) for DMA transfers\n", + dma_chan_name(as->dma.chan_tx), + dma_chan_name(as->dma.chan_rx)); + return 0; +error: + if (as->dma.chan_rx) + dma_release_channel(as->dma.chan_rx); + if (as->dma.chan_tx) + dma_release_channel(as->dma.chan_tx); + return err; +} + +static void atmel_spi_stop_dma(struct atmel_spi *as) +{ + if (as->dma.chan_rx) + as->dma.chan_rx->device->device_control(as->dma.chan_rx, + DMA_TERMINATE_ALL, 0); + if (as->dma.chan_tx) + as->dma.chan_tx->device->device_control(as->dma.chan_tx, + DMA_TERMINATE_ALL, 0); +} + +static void atmel_spi_release_dma(struct atmel_spi *as) +{ + if (as->dma.chan_rx) + dma_release_channel(as->dma.chan_rx); + if (as->dma.chan_tx) + dma_release_channel(as->dma.chan_tx); +} + +/* This function is called by the DMA driver from tasklet context */ +static void dma_callback(void *data) +{ + struct spi_master *master = data; + struct atmel_spi *as = spi_master_get_devdata(master); + + /* trigger SPI tasklet */ + tasklet_schedule(&as->tasklet); +} + +/* + * Next transfer using PIO. + * lock is held, spi tasklet is blocked + */ +static void atmel_spi_next_xfer_pio(struct spi_master *master, + struct spi_transfer *xfer) +{ + struct atmel_spi *as = spi_master_get_devdata(master); + + dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_pio\n"); + + as->current_remaining_bytes = xfer->len; + + /* Make sure data is not remaining in RDR */ + spi_readl(as, RDR); + while (spi_readl(as, SR) & SPI_BIT(RDRF)) { + spi_readl(as, RDR); + cpu_relax(); + } + + if (xfer->tx_buf) + spi_writel(as, TDR, *(u8 *)(xfer->tx_buf)); + else + spi_writel(as, TDR, 0); + + dev_dbg(master->dev.parent, + " start pio xfer %p: len %u tx %p rx %p\n", + xfer, xfer->len, xfer->tx_buf, xfer->rx_buf); + + /* Enable relevant interrupts */ + spi_writel(as, IER, SPI_BIT(RDRF) | SPI_BIT(OVRES)); +} + +/* + * Submit next transfer for DMA. + * lock is held, spi tasklet is blocked + */ +static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, + struct spi_transfer *xfer, + u32 *plen) +{ + struct atmel_spi *as = spi_master_get_devdata(master); + struct dma_chan *rxchan = as->dma.chan_rx; + struct dma_chan *txchan = as->dma.chan_tx; + struct dma_async_tx_descriptor *rxdesc; + struct dma_async_tx_descriptor *txdesc; + struct dma_slave_config slave_config; + dma_cookie_t cookie; + u32 len = *plen; + + dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_dma_submit\n"); + + /* Check that the channels are available */ + if (!rxchan || !txchan) + return -ENODEV; + + /* release lock for DMA operations */ + atmel_spi_unlock(as); + + /* prepare the RX dma transfer */ + sg_init_table(&as->dma.sgrx, 1); + if (xfer->rx_buf) { + as->dma.sgrx.dma_address = xfer->rx_dma + xfer->len - *plen; + } else { + as->dma.sgrx.dma_address = as->buffer_dma; + if (len > BUFFER_SIZE) + len = BUFFER_SIZE; + } + + /* prepare the TX dma transfer */ + sg_init_table(&as->dma.sgtx, 1); + if (xfer->tx_buf) { + as->dma.sgtx.dma_address = xfer->tx_dma + xfer->len - *plen; + } else { + as->dma.sgtx.dma_address = as->buffer_dma; + if (len > BUFFER_SIZE) + len = BUFFER_SIZE; + memset(as->buffer, 0, len); + } + + sg_dma_len(&as->dma.sgtx) = len; + sg_dma_len(&as->dma.sgrx) = len; + + *plen = len; + + if (atmel_spi_dma_slave_config(as, &slave_config, 8)) + goto err_exit; + + /* Send both scatterlists */ + rxdesc = rxchan->device->device_prep_slave_sg(rxchan, + &as->dma.sgrx, + 1, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK, + NULL); + if (!rxdesc) + goto err_dma; + + txdesc = txchan->device->device_prep_slave_sg(txchan, + &as->dma.sgtx, + 1, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK, + NULL); + if (!txdesc) + 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); + + /* Enable relevant interrupts */ + spi_writel(as, IER, SPI_BIT(OVRES)); + + /* Put the callback on the RX transfer only, that should finish last */ + rxdesc->callback = dma_callback; + rxdesc->callback_param = master; + + /* Submit and fire RX and TX with TX last so we're ready to read! */ + cookie = rxdesc->tx_submit(rxdesc); + if (dma_submit_error(cookie)) + goto err_dma; + cookie = txdesc->tx_submit(txdesc); + if (dma_submit_error(cookie)) + goto err_dma; + rxchan->device->device_issue_pending(rxchan); + txchan->device->device_issue_pending(txchan); + + /* take back lock */ + atmel_spi_lock(as); + return 0; + +err_dma: + spi_writel(as, IDR, SPI_BIT(OVRES)); + atmel_spi_stop_dma(as); +err_exit: + atmel_spi_lock(as); + return -ENOMEM; +} + static void atmel_spi_next_xfer_data(struct spi_master *master, struct spi_transfer *xfer, dma_addr_t *tx_dma, @@ -372,6 +663,7 @@ static void atmel_spi_next_xfer_data(struct spi_master *master, if (len > BUFFER_SIZE) len = BUFFER_SIZE; } + if (xfer->tx_buf) *tx_dma = xfer->tx_dma + xfer->len - *plen; else { @@ -387,10 +679,10 @@ static void atmel_spi_next_xfer_data(struct spi_master *master, } /* - * Submit next transfer for DMA. + * Submit next transfer for PDC. * lock is held, spi irq is blocked */ -static void atmel_spi_next_xfer(struct spi_master *master, +static void atmel_spi_pdc_next_xfer(struct spi_master *master, struct spi_message *msg) { struct atmel_spi *as = spi_master_get_devdata(master); @@ -487,6 +779,48 @@ static void atmel_spi_next_xfer(struct spi_master *master, spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN)); } +/* + * Choose way to submit next transfer and start it. + * lock is held, spi tasklet is blocked + */ +static void atmel_spi_dma_next_xfer(struct spi_master *master, + struct spi_message *msg) +{ + struct atmel_spi *as = spi_master_get_devdata(master); + struct spi_transfer *xfer; + u32 remaining, len; + + remaining = as->current_remaining_bytes; + if (remaining) { + xfer = as->current_transfer; + len = remaining; + } else { + if (!as->current_transfer) + xfer = list_entry(msg->transfers.next, + struct spi_transfer, transfer_list); + else + xfer = list_entry( + as->current_transfer->transfer_list.next, + struct spi_transfer, transfer_list); + + as->current_transfer = xfer; + len = xfer->len; + } + + if (atmel_spi_use_dma(as, xfer)) { + u32 total = len; + if (!atmel_spi_next_xfer_dma_submit(master, xfer, &len)) { + as->current_remaining_bytes = total - len; + return; + } else { + dev_err(&msg->spi->dev, "unable to use DMA, fallback to PIO\n"); + } + } + + /* use PIO if error appened using DMA */ + atmel_spi_next_xfer_pio(master, xfer); +} + static void atmel_spi_next_message(struct spi_master *master) { struct atmel_spi *as = spi_master_get_devdata(master); @@ -511,7 +845,10 @@ static void atmel_spi_next_message(struct spi_master *master) } else cs_activate(as, spi); - atmel_spi_next_xfer(master, msg); + if (as->use_pdc) + atmel_spi_pdc_next_xfer(master, msg); + else + atmel_spi_dma_next_xfer(master, msg); } /* @@ -564,6 +901,11 @@ static void atmel_spi_dma_unmap_xfer(struct spi_master *master, xfer->len, DMA_FROM_DEVICE); } +static void atmel_spi_disable_pdc_transfer(struct atmel_spi *as) +{ + spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); +} + static void atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, struct spi_message *msg, int stay) @@ -589,14 +931,183 @@ atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, as->done_status = 0; /* continue if needed */ - if (list_empty(&as->queue) || as->stopping) - spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); - else + if (list_empty(&as->queue) || as->stopping) { + if (as->use_pdc) + atmel_spi_disable_pdc_transfer(as); + } else { atmel_spi_next_message(master); + } +} + +/* Called from IRQ + * lock is held + * + * Must update "current_remaining_bytes" to keep track of data + * to transfer. + */ +static void +atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer) +{ + u8 *txp; + u8 *rxp; + unsigned long xfer_pos = xfer->len - as->current_remaining_bytes; + + if (xfer->rx_buf) { + rxp = ((u8 *)xfer->rx_buf) + xfer_pos; + *rxp = spi_readl(as, RDR); + } else { + spi_readl(as, RDR); + } + + as->current_remaining_bytes--; + + if (as->current_remaining_bytes) { + if (xfer->tx_buf) { + txp = ((u8 *)xfer->tx_buf) + xfer_pos + 1; + spi_writel(as, TDR, *txp); + } else { + spi_writel(as, TDR, 0); + } + } +} + +/* Tasklet + * Called from DMA callback + pio transfer and overrun IRQ. + */ +static void atmel_spi_tasklet_func(unsigned long data) +{ + struct spi_master *master = (struct spi_master *)data; + struct atmel_spi *as = spi_master_get_devdata(master); + struct spi_message *msg; + struct spi_transfer *xfer; + + dev_vdbg(master->dev.parent, "atmel_spi_tasklet_func\n"); + + atmel_spi_lock(as); + + xfer = as->current_transfer; + + if (xfer == NULL) + /* already been there */ + goto tasklet_out; + + msg = list_entry(as->queue.next, struct spi_message, queue); + + if (as->current_remaining_bytes == 0) { + if (as->done_status < 0) { + /* error happened (overrun) */ + if (atmel_spi_use_dma(as, xfer)) + atmel_spi_stop_dma(as); + } else { + /* only update length if no error */ + msg->actual_length += xfer->len; + } + + if (atmel_spi_use_dma(as, xfer)) + if (!msg->is_dma_mapped) + atmel_spi_dma_unmap_xfer(master, xfer); + + if (xfer->delay_usecs) + udelay(xfer->delay_usecs); + + if (atmel_spi_xfer_is_last(msg, xfer) || as->done_status < 0) { + /* report completed (or erroneous) message */ + atmel_spi_msg_done(master, as, msg, xfer->cs_change); + } else { + if (xfer->cs_change) { + cs_deactivate(as, msg->spi); + udelay(1); + cs_activate(as, msg->spi); + } + + /* + * Not done yet. Submit the next transfer. + * + * FIXME handle protocol options for xfer + */ + atmel_spi_dma_next_xfer(master, msg); + } + } else { + /* + * Keep going, we still have data to send in + * the current transfer. + */ + atmel_spi_dma_next_xfer(master, msg); + } + +tasklet_out: + atmel_spi_unlock(as); +} + +/* Interrupt + * + * No need for locking in this Interrupt handler: done_status is the + * only information modified. What we need is the update of this field + * before tasklet runs. This is ensured by using barrier. + */ +static irqreturn_t +atmel_spi_pio_interrupt(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct atmel_spi *as = spi_master_get_devdata(master); + u32 status, pending, imr; + struct spi_transfer *xfer; + int ret = IRQ_NONE; + + imr = spi_readl(as, IMR); + status = spi_readl(as, SR); + pending = status & imr; + + if (pending & SPI_BIT(OVRES)) { + ret = IRQ_HANDLED; + spi_writel(as, IDR, SPI_BIT(OVRES)); + dev_warn(master->dev.parent, "overrun\n"); + + /* + * When we get an overrun, we disregard the current + * transfer. Data will not be copied back from any + * bounce buffer and msg->actual_len will not be + * updated with the last xfer. + * + * We will also not process any remaning transfers in + * the message. + * + * All actions are done in tasklet with done_status indication + */ + as->done_status = -EIO; + smp_wmb(); + + /* Clear any overrun happening while cleaning up */ + spi_readl(as, SR); + + tasklet_schedule(&as->tasklet); + + } else if (pending & SPI_BIT(RDRF)) { + atmel_spi_lock(as); + + if (as->current_remaining_bytes) { + ret = IRQ_HANDLED; + xfer = as->current_transfer; + atmel_spi_pump_pio_data(as, xfer); + if (!as->current_remaining_bytes) { + /* no more data to xfer, kick tasklet */ + spi_writel(as, IDR, pending); + tasklet_schedule(&as->tasklet); + } + } + + atmel_spi_unlock(as); + } else { + WARN_ONCE(pending, "IRQ not handled, pending = %x\n", pending); + ret = IRQ_HANDLED; + spi_writel(as, IDR, pending); + } + + return ret; } static irqreturn_t -atmel_spi_interrupt(int irq, void *dev_id) +atmel_spi_pdc_interrupt(int irq, void *dev_id) { struct spi_master *master = dev_id; struct atmel_spi *as = spi_master_get_devdata(master); @@ -697,14 +1208,14 @@ atmel_spi_interrupt(int irq, void *dev_id) * * FIXME handle protocol options for xfer */ - atmel_spi_next_xfer(master, msg); + atmel_spi_pdc_next_xfer(master, msg); } } else { /* * Keep going, we still have data to send in * the current transfer. */ - atmel_spi_next_xfer(master, msg); + atmel_spi_pdc_next_xfer(master, msg); } } @@ -875,13 +1386,10 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) /* * DMA map early, for performance (empties dcache ASAP) and - * better fault reporting. This is a DMA-only driver. - * - * NOTE that if dma_unmap_single() ever starts to do work on - * platforms supported by this driver, we would need to clean - * up mappings for previously-mapped transfers. + * better fault reporting. */ - if (!msg->is_dma_mapped) { + if ((!msg->is_dma_mapped) && (atmel_spi_use_dma(as, xfer) + || as->use_pdc)) { if (atmel_spi_dma_map_xfer(as, xfer) < 0) return -ENOMEM; } @@ -1000,6 +1508,7 @@ static int atmel_spi_probe(struct platform_device *pdev) spin_lock_init(&as->lock); INIT_LIST_HEAD(&as->queue); + as->pdev = pdev; as->regs = ioremap(regs->start, resource_size(regs)); if (!as->regs) @@ -1010,8 +1519,28 @@ static int atmel_spi_probe(struct platform_device *pdev) atmel_get_caps(as); - ret = request_irq(irq, atmel_spi_interrupt, 0, - dev_name(&pdev->dev), master); + as->use_dma = false; + as->use_pdc = false; + if (as->caps.has_dma_support) { + if (atmel_spi_configure_dma(as) == 0) + as->use_dma = true; + } else { + as->use_pdc = true; + } + + if (as->caps.has_dma_support && !as->use_dma) + dev_info(&pdev->dev, "Atmel SPI Controller using PIO only\n"); + + if (as->use_pdc) { + ret = request_irq(irq, atmel_spi_pdc_interrupt, 0, + dev_name(&pdev->dev), master); + } else { + tasklet_init(&as->tasklet, atmel_spi_tasklet_func, + (unsigned long)master); + + ret = request_irq(irq, atmel_spi_pio_interrupt, 0, + dev_name(&pdev->dev), master); + } if (ret) goto out_unmap_regs; @@ -1025,7 +1554,9 @@ static int atmel_spi_probe(struct platform_device *pdev) } else { spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS)); } - spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); + + if (as->use_pdc) + spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); spi_writel(as, CR, SPI_BIT(SPIEN)); /* go! */ @@ -1034,11 +1565,14 @@ static int atmel_spi_probe(struct platform_device *pdev) ret = spi_register_master(master); if (ret) - goto out_reset_hw; + goto out_free_dma; return 0; -out_reset_hw: +out_free_dma: + if (as->use_dma) + atmel_spi_release_dma(as); + spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ clk_disable(clk); @@ -1046,6 +1580,8 @@ out_reset_hw: out_unmap_regs: iounmap(as->regs); out_free_buffer: + if (!as->use_pdc) + tasklet_kill(&as->tasklet); dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, as->buffer_dma); out_free: @@ -1064,6 +1600,11 @@ static int atmel_spi_remove(struct platform_device *pdev) /* reset the hardware and block queue progress */ spin_lock_irq(&as->lock); as->stopping = 1; + if (as->use_dma) { + atmel_spi_stop_dma(as); + atmel_spi_release_dma(as); + } + spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ spi_readl(as, SR); @@ -1072,13 +1613,17 @@ static int atmel_spi_remove(struct platform_device *pdev) /* Terminate remaining queued transfers */ list_for_each_entry(msg, &as->queue, queue) { list_for_each_entry(xfer, &msg->transfers, transfer_list) { - if (!msg->is_dma_mapped) + if (!msg->is_dma_mapped + && (atmel_spi_use_dma(as, xfer) + || as->use_pdc)) atmel_spi_dma_unmap_xfer(master, xfer); } msg->status = -ESHUTDOWN; msg->complete(msg->context); } + if (!as->use_pdc) + tasklet_kill(&as->tasklet); dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, as->buffer_dma); -- cgit v0.10.2 From f0db66a5cd8f494f9034a5883094102bc46f73bc Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Wed, 3 Apr 2013 14:01:22 +0800 Subject: ARM: at91: add clocks for spi dt entries Signed-off-by: Richard Genoud [ Signed-off-by: Mark Brown diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index b67cd53..44199bc 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -232,6 +232,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("t2_clk", "fffdc000.timer", &tc5_clk), CLKDEV_CON_DEV_ID("hclk", "500000.ohci", &ohci_clk), CLKDEV_CON_DEV_ID("mci_clk", "fffa8000.mmc", &mmc_clk), + CLKDEV_CON_DEV_ID("spi_clk", "fffc8000.spi", &spi0_clk), + CLKDEV_CON_DEV_ID("spi_clk", "fffcc000.spi", &spi1_clk), /* fake hclk clock */ CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), CLKDEV_CON_ID("pioA", &pioA_clk), diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index d3addee..2ec5efe 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -262,6 +262,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("mci_clk", "fffd0000.mmc", &mmc1_clk), CLKDEV_CON_DEV_ID(NULL, "fff84000.i2c", &twi0_clk), CLKDEV_CON_DEV_ID(NULL, "fff88000.i2c", &twi1_clk), + CLKDEV_CON_DEV_ID("spi_clk", "fffa4000.spi", &spi0_clk), + CLKDEV_CON_DEV_ID("spi_clk", "fffa8000.spi", &spi1_clk), /* fake hclk clock */ CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &uhphs_clk), CLKDEV_CON_DEV_ID(NULL, "fffff200.gpio", &pioA_clk), diff --git a/arch/arm/mach-at91/at91sam9n12.c b/arch/arm/mach-at91/at91sam9n12.c index 5dfc8fd..ccd0783 100644 --- a/arch/arm/mach-at91/at91sam9n12.c +++ b/arch/arm/mach-at91/at91sam9n12.c @@ -172,6 +172,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma_clk), CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk), CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk), + CLKDEV_CON_DEV_ID("spi_clk", "f0000000.spi", &spi0_clk), + CLKDEV_CON_DEV_ID("spi_clk", "f0004000.spi", &spi1_clk), CLKDEV_CON_DEV_ID(NULL, "fffff400.gpio", &pioAB_clk), CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioAB_clk), CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioCD_clk), diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index 44a9a62..a200d8a 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c @@ -237,6 +237,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk), CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk), CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk), + CLKDEV_CON_DEV_ID("spi_clk", "f0000000.spi", &spi0_clk), + CLKDEV_CON_DEV_ID("spi_clk", "f0004000.spi", &spi1_clk), CLKDEV_CON_DEV_ID(NULL, "fffff400.gpio", &pioAB_clk), CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioAB_clk), CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioCD_clk), -- cgit v0.10.2 From d50f88a0c304835fd38ea91327a882679ac413db Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Wed, 3 Apr 2013 14:02:18 +0800 Subject: ARM: dts: add spi nodes for atmel SoC Signed-off-by: Richard Genoud [wenyou.yang@atmel.com: add spi nodes for sam9260, sam9263, sam9g45 and sam9n12] [wenyou.yang@atmel.com: remove spi property "cs-gpios" to the board dts files] [wenyou.yang@atmel.com: submit the patch] Signed-off-by: Wenyou Yang Signed-off-by: Mark Brown diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi index cb7bcc5..6e31dc8 100644 --- a/arch/arm/boot/dts/at91sam9260.dtsi +++ b/arch/arm/boot/dts/at91sam9260.dtsi @@ -471,6 +471,24 @@ status = "disabled"; }; + spi0: spi@fffc8000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "atmel,at91rm9200-spi"; + reg = <0xfffc8000 0x200>; + interrupts = <12 4 3>; + status = "disabled"; + }; + + spi1: spi@fffcc000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "atmel,at91rm9200-spi"; + reg = <0xfffcc000 0x200>; + interrupts = <13 4 3>; + status = "disabled"; + }; + adc0: adc@fffe0000 { compatible = "atmel,at91sam9260-adc"; reg = <0xfffe0000 0x100>; diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi index 271d4de..6c6d9ae 100644 --- a/arch/arm/boot/dts/at91sam9263.dtsi +++ b/arch/arm/boot/dts/at91sam9263.dtsi @@ -462,6 +462,24 @@ reg = <0xfffffd40 0x10>; status = "disabled"; }; + + spi0: spi@fffa4000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "atmel,at91rm9200-spi"; + reg = <0xfffa4000 0x200>; + interrupts = <14 4 3>; + status = "disabled"; + }; + + spi1: spi@fffa8000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "atmel,at91rm9200-spi"; + reg = <0xfffa8000 0x200>; + interrupts = <15 4 3>; + status = "disabled"; + }; }; nand0: nand@40000000 { diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index 6b1d4ca..e085b8a 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -531,6 +531,24 @@ reg = <0xfffffd40 0x10>; status = "disabled"; }; + + spi0: spi@fffa4000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "atmel,at91rm9200-spi"; + reg = <0xfffa4000 0x200>; + interrupts = <14 4 3>; + status = "disabled"; + }; + + spi1: spi@fffa8000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "atmel,at91rm9200-spi"; + reg = <0xfffa8000 0x200>; + interrupts = <15 4 3>; + status = "disabled"; + }; }; nand0: nand@40000000 { diff --git a/arch/arm/boot/dts/at91sam9n12.dtsi b/arch/arm/boot/dts/at91sam9n12.dtsi index 7750f98..f3f87ef3 100644 --- a/arch/arm/boot/dts/at91sam9n12.dtsi +++ b/arch/arm/boot/dts/at91sam9n12.dtsi @@ -373,6 +373,24 @@ #size-cells = <0>; status = "disabled"; }; + + spi0: spi@f0000000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "atmel,at91rm9200-spi"; + reg = <0xf0000000 0x100>; + interrupts = <13 4 3>; + status = "disabled"; + }; + + spi1: spi@f0004000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "atmel,at91rm9200-spi"; + reg = <0xf0004000 0x100>; + interrupts = <14 4 3>; + status = "disabled"; + }; }; nand0: nand@40000000 { diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index aa98e64..83a4889 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -505,6 +505,24 @@ trigger-value = <0x6>; }; }; + + spi0: spi@f0000000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "atmel,at91rm9200-spi"; + reg = <0xf0000000 0x100>; + interrupts = <13 4 3>; + status = "disabled"; + }; + + spi1: spi@f0004000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "atmel,at91rm9200-spi"; + reg = <0xf0004000 0x100>; + interrupts = <14 4 3>; + status = "disabled"; + }; }; nand0: nand@40000000 { -- cgit v0.10.2 From b6811e9ab714ca821f6663fa0b809eb790f99dd7 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Wed, 3 Apr 2013 14:03:05 +0800 Subject: ARM: dts: add spi nodes for the atmel boards Signed-off-by: Richard Genoud [wenyou.yang@atmel.com: added spi nodes for the sam9263ek, sam9g20ek, sam9m10g45ek and sam9n12ek boards] [wenyou.yang@atmel.com: submit the patch] Signed-off-by: Wenyou Yang Signed-off-by: Mark Brown diff --git a/arch/arm/boot/dts/at91sam9263ek.dts b/arch/arm/boot/dts/at91sam9263ek.dts index 1eb0872..a14e424 100644 --- a/arch/arm/boot/dts/at91sam9263ek.dts +++ b/arch/arm/boot/dts/at91sam9263ek.dts @@ -79,6 +79,16 @@ }; }; }; + + spi0: spi@fffa4000 { + status = "okay"; + cs-gpios = <&pioA 5 0>, <0>, <0>, <0>; + mtd_dataflash@0 { + compatible = "atmel,at45", "atmel,dataflash"; + spi-max-frequency = <50000000>; + reg = <0>; + }; + }; }; nand0: nand@40000000 { diff --git a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi index da15e83..23d1f46 100644 --- a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi +++ b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi @@ -96,6 +96,16 @@ status = "okay"; pinctrl-0 = <&pinctrl_ssc0_tx>; }; + + spi0: spi@fffc8000 { + status = "okay"; + cs-gpios = <0>, <&pioC 11 0>, <0>, <0>; + mtd_dataflash@0 { + compatible = "atmel,at45", "atmel,dataflash"; + spi-max-frequency = <50000000>; + reg = <1>; + }; + }; }; nand0: nand@40000000 { diff --git a/arch/arm/boot/dts/at91sam9m10g45ek.dts b/arch/arm/boot/dts/at91sam9m10g45ek.dts index 20c3191..92c52a7 100644 --- a/arch/arm/boot/dts/at91sam9m10g45ek.dts +++ b/arch/arm/boot/dts/at91sam9m10g45ek.dts @@ -102,6 +102,16 @@ }; }; }; + + spi0: spi@fffa4000{ + status = "okay"; + cs-gpios = <&pioB 3 0>, <0>, <0>, <0>; + mtd_dataflash@0 { + compatible = "atmel,at45", "atmel,dataflash"; + spi-max-frequency = <13000000>; + reg = <0>; + }; + }; }; nand0: nand@40000000 { diff --git a/arch/arm/boot/dts/at91sam9n12ek.dts b/arch/arm/boot/dts/at91sam9n12ek.dts index d400f8d..34c842b 100644 --- a/arch/arm/boot/dts/at91sam9n12ek.dts +++ b/arch/arm/boot/dts/at91sam9n12ek.dts @@ -67,6 +67,16 @@ }; }; }; + + spi0: spi@f0000000 { + status = "okay"; + cs-gpios = <&pioA 14 0>, <0>, <0>, <0>; + m25p80@0 { + compatible = "atmel,at25df321a"; + spi-max-frequency = <50000000>; + reg = <0>; + }; + }; }; nand0: nand@40000000 { diff --git a/arch/arm/boot/dts/at91sam9x5ek.dtsi b/arch/arm/boot/dts/at91sam9x5ek.dtsi index 8a7cf1d..09f5e66 100644 --- a/arch/arm/boot/dts/at91sam9x5ek.dtsi +++ b/arch/arm/boot/dts/at91sam9x5ek.dtsi @@ -84,6 +84,16 @@ }; }; }; + + spi0: spi@f0000000 { + status = "okay"; + cs-gpios = <&pioA 14 0>, <0>, <0>, <0>; + m25p80@0 { + compatible = "atmel,at25df321a"; + spi-max-frequency = <50000000>; + reg = <0>; + }; + }; }; usb0: ohci@00600000 { -- cgit v0.10.2 From a68b728f7a217ce30b0eb77f6cb8e955d6ce6841 Mon Sep 17 00:00:00 2001 From: Wenyou Yang Date: Wed, 3 Apr 2013 14:03:52 +0800 Subject: ARM: dts: add pinctrl property for spi node for atmel SoC Signed-off-by: Wenyou Yang Tested-by: Richard Genoud Signed-off-by: Mark Brown diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi index 6e31dc8..39253b9 100644 --- a/arch/arm/boot/dts/at91sam9260.dtsi +++ b/arch/arm/boot/dts/at91sam9260.dtsi @@ -322,6 +322,24 @@ }; }; + spi0 { + pinctrl_spi0: spi0-0 { + atmel,pins = + <0 0 0x1 0x0 /* PA0 periph A SPI0_MISO pin */ + 0 1 0x1 0x0 /* PA1 periph A SPI0_MOSI pin */ + 0 2 0x1 0x0>; /* PA2 periph A SPI0_SPCK pin */ + }; + }; + + spi1 { + pinctrl_spi1: spi1-0 { + atmel,pins = + <1 0 0x1 0x0 /* PB0 periph A SPI1_MISO pin */ + 1 1 0x1 0x0 /* PB1 periph A SPI1_MOSI pin */ + 1 2 0x1 0x0>; /* PB2 periph A SPI1_SPCK pin */ + }; + }; + pioA: gpio@fffff400 { compatible = "atmel,at91rm9200-gpio"; reg = <0xfffff400 0x200>; @@ -477,6 +495,8 @@ compatible = "atmel,at91rm9200-spi"; reg = <0xfffc8000 0x200>; interrupts = <12 4 3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi0>; status = "disabled"; }; @@ -486,6 +506,8 @@ compatible = "atmel,at91rm9200-spi"; reg = <0xfffcc000 0x200>; interrupts = <13 4 3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi1>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi index 6c6d9ae..94b58ab 100644 --- a/arch/arm/boot/dts/at91sam9263.dtsi +++ b/arch/arm/boot/dts/at91sam9263.dtsi @@ -303,6 +303,24 @@ }; }; + spi0 { + pinctrl_spi0: spi0-0 { + atmel,pins = + <0 0 0x2 0x0 /* PA0 periph B SPI0_MISO pin */ + 0 1 0x2 0x0 /* PA1 periph B SPI0_MOSI pin */ + 0 2 0x2 0x0>; /* PA2 periph B SPI0_SPCK pin */ + }; + }; + + spi1 { + pinctrl_spi1: spi1-0 { + atmel,pins = + <1 12 0x1 0x0 /* PB12 periph A SPI1_MISO pin */ + 1 13 0x1 0x0 /* PB13 periph A SPI1_MOSI pin */ + 1 14 0x1 0x0>; /* PB14 periph A SPI1_SPCK pin */ + }; + }; + pioA: gpio@fffff200 { compatible = "atmel,at91rm9200-gpio"; reg = <0xfffff200 0x200>; @@ -469,6 +487,8 @@ compatible = "atmel,at91rm9200-spi"; reg = <0xfffa4000 0x200>; interrupts = <14 4 3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi0>; status = "disabled"; }; @@ -478,6 +498,8 @@ compatible = "atmel,at91rm9200-spi"; reg = <0xfffa8000 0x200>; interrupts = <15 4 3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi1>; status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index e085b8a..cfdf429 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -322,6 +322,24 @@ }; }; + spi0 { + pinctrl_spi0: spi0-0 { + atmel,pins = + <1 0 0x1 0x0 /* PB0 periph A SPI0_MISO pin */ + 1 1 0x1 0x0 /* PB1 periph A SPI0_MOSI pin */ + 1 2 0x1 0x0>; /* PB2 periph A SPI0_SPCK pin */ + }; + }; + + spi1 { + pinctrl_spi1: spi1-0 { + atmel,pins = + <1 14 0x1 0x0 /* PB14 periph A SPI1_MISO pin */ + 1 15 0x1 0x0 /* PB15 periph A SPI1_MOSI pin */ + 1 16 0x1 0x0>; /* PB16 periph A SPI1_SPCK pin */ + }; + }; + pioA: gpio@fffff200 { compatible = "atmel,at91rm9200-gpio"; reg = <0xfffff200 0x200>; @@ -538,6 +556,8 @@ compatible = "atmel,at91rm9200-spi"; reg = <0xfffa4000 0x200>; interrupts = <14 4 3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi0>; status = "disabled"; }; @@ -547,6 +567,8 @@ compatible = "atmel,at91rm9200-spi"; reg = <0xfffa8000 0x200>; interrupts = <15 4 3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi1>; status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/at91sam9n12.dtsi b/arch/arm/boot/dts/at91sam9n12.dtsi index f3f87ef3..b2961f1 100644 --- a/arch/arm/boot/dts/at91sam9n12.dtsi +++ b/arch/arm/boot/dts/at91sam9n12.dtsi @@ -261,6 +261,24 @@ }; }; + spi0 { + pinctrl_spi0: spi0-0 { + atmel,pins = + <0 11 0x1 0x0 /* PA11 periph A SPI0_MISO pin */ + 0 12 0x1 0x0 /* PA12 periph A SPI0_MOSI pin */ + 0 13 0x1 0x0>; /* PA13 periph A SPI0_SPCK pin */ + }; + }; + + spi1 { + pinctrl_spi1: spi1-0 { + atmel,pins = + <0 21 0x2 0x0 /* PA21 periph B SPI1_MISO pin */ + 0 22 0x2 0x0 /* PA22 periph B SPI1_MOSI pin */ + 0 23 0x2 0x0>; /* PA23 periph B SPI1_SPCK pin */ + }; + }; + pioA: gpio@fffff400 { compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; reg = <0xfffff400 0x200>; @@ -380,6 +398,8 @@ compatible = "atmel,at91rm9200-spi"; reg = <0xf0000000 0x100>; interrupts = <13 4 3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi0>; status = "disabled"; }; @@ -389,6 +409,8 @@ compatible = "atmel,at91rm9200-spi"; reg = <0xf0004000 0x100>; interrupts = <14 4 3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi1>; status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index 83a4889..04fbf9d 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -319,6 +319,24 @@ }; }; + spi0 { + pinctrl_spi0: spi0-0 { + atmel,pins = + <0 11 0x1 0x0 /* PA11 periph A SPI0_MISO pin */ + 0 12 0x1 0x0 /* PA12 periph A SPI0_MOSI pin */ + 0 13 0x1 0x0>; /* PA13 periph A SPI0_SPCK pin */ + }; + }; + + spi1 { + pinctrl_spi1: spi1-0 { + atmel,pins = + <0 21 0x2 0x0 /* PA21 periph B SPI1_MISO pin */ + 0 22 0x2 0x0 /* PA22 periph B SPI1_MOSI pin */ + 0 23 0x2 0x0>; /* PA23 periph B SPI1_SPCK pin */ + }; + }; + pioA: gpio@fffff400 { compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; reg = <0xfffff400 0x200>; @@ -512,6 +530,8 @@ compatible = "atmel,at91rm9200-spi"; reg = <0xf0000000 0x100>; interrupts = <13 4 3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi0>; status = "disabled"; }; @@ -521,6 +541,8 @@ compatible = "atmel,at91rm9200-spi"; reg = <0xf0004000 0x100>; interrupts = <14 4 3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi1>; status = "disabled"; }; }; -- cgit v0.10.2 From 0113f22ee49ff1972de55632dda44eb9299e625d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 25 Apr 2013 15:18:02 +0800 Subject: spi-topcliff-pch: missing platform_driver_unregister() on error in pch_spi_init() Add the missing platform_driver_unregister() before return from pch_spi_init() in the error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index f756481..c8b672e 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1789,8 +1789,10 @@ static int __init pch_spi_init(void) return ret; ret = pci_register_driver(&pch_spi_pcidev_driver); - if (ret) + if (ret) { + platform_driver_unregister(&pch_spi_pd_driver); return ret; + } return 0; } -- cgit v0.10.2 From cd8d984f0def2a8c5733a9468634ec3e0feec03d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 27 Apr 2013 14:06:00 +0800 Subject: spi-topcliff-pch: fix to use list_for_each_entry_safe() when delete list items Since we will remove items off the list using list_del_init() we need to use a safe version of the list_for_each_entry() macro aptly named list_for_each_entry_safe(). Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index c8b672e..35f60bd 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -615,7 +615,7 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw) int size; u32 n_writes; int j; - struct spi_message *pmsg; + struct spi_message *pmsg, *tmp; const u8 *tx_buf; const u16 *tx_sbuf; @@ -656,7 +656,7 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw) if (!data->pkt_rx_buff) { /* flush queue and set status of all transfers to -ENOMEM */ dev_err(&data->master->dev, "%s :kzalloc failed\n", __func__); - list_for_each_entry(pmsg, data->queue.next, queue) { + list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) { pmsg->status = -ENOMEM; if (pmsg->complete != 0) @@ -703,7 +703,7 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw) static void pch_spi_nomore_transfer(struct pch_spi_data *data) { - struct spi_message *pmsg; + struct spi_message *pmsg, *tmp; dev_dbg(&data->master->dev, "%s called\n", __func__); /* Invoke complete callback * [To the spi core..indicating end of transfer] */ @@ -740,7 +740,7 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data) dev_dbg(&data->master->dev, "%s suspend/remove initiated, flushing queue\n", __func__); - list_for_each_entry(pmsg, data->queue.next, queue) { + list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) { pmsg->status = -EIO; if (pmsg->complete) @@ -1187,7 +1187,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) static void pch_spi_process_messages(struct work_struct *pwork) { - struct spi_message *pmsg; + struct spi_message *pmsg, *tmp; struct pch_spi_data *data; int bpw; @@ -1199,7 +1199,7 @@ static void pch_spi_process_messages(struct work_struct *pwork) if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) { dev_dbg(&data->master->dev, "%s suspend/remove initiated," "flushing queue\n", __func__); - list_for_each_entry(pmsg, data->queue.next, queue) { + list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) { pmsg->status = -EIO; if (pmsg->complete != 0) { -- cgit v0.10.2