Merge branches 'spi-drivers' and 'spi-mxs' into spi-next
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Fri, 28 Sep 2012 13:05:29 +0000 (14:05 +0100)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Fri, 28 Sep 2012 13:05:29 +0000 (14:05 +0100)
33 files changed:
Documentation/devicetree/bindings/spi/mxs-spi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/spi/spi-bus.txt
Documentation/devicetree/bindings/spi/spi-gpio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/spi/spi-sc18is602.txt [new file with mode: 0644]
Documentation/devicetree/bindings/spi/spi_pl022.txt
Documentation/spi/spi-sc18is602 [new file with mode: 0644]
arch/arm/mach-u300/core.c
drivers/clk/mxs/Makefile
drivers/clk/mxs/clk-ssp.c [new file with mode: 0644]
drivers/mmc/host/mxs-mmc.c
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/spi-altera.c
drivers/spi/spi-gpio.c
drivers/spi/spi-imx.c
drivers/spi/spi-mpc512x-psc.c
drivers/spi/spi-mpc52xx-psc.c
drivers/spi/spi-mpc52xx.c
drivers/spi/spi-mxs.c [new file with mode: 0644]
drivers/spi/spi-omap2-mcspi.c
drivers/spi/spi-orion.c
drivers/spi/spi-pl022.c
drivers/spi/spi-s3c24xx.c
drivers/spi/spi-s3c64xx.c
drivers/spi/spi-sc18is602.c [new file with mode: 0644]
drivers/spi/spi-sh-hspi.c
drivers/spi/spi-stmp.c
drivers/spi/spi-tegra.c
drivers/spi/spi-tle62x0.c
drivers/spi/spi-topcliff-pch.c
include/linux/amba/pl022.h
include/linux/platform_data/sc18is602.h [new file with mode: 0644]
include/linux/spi/mxs-spi.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/spi/mxs-spi.txt b/Documentation/devicetree/bindings/spi/mxs-spi.txt
new file mode 100644 (file)
index 0000000..e2e1395
--- /dev/null
@@ -0,0 +1,22 @@
+* Freescale MX233/MX28 SSP/SPI
+
+Required properties:
+- compatible: Should be "fsl,<soc>-spi", where soc is "imx23" or "imx28"
+- reg: Offset and length of the register set for the device
+- interrupts: Should contain SSP interrupts (error irq first, dma irq second)
+- fsl,ssp-dma-channel: APBX DMA channel for the SSP
+
+Optional properties:
+- clock-frequency : Input clock frequency to the SPI block in Hz.
+                   Default is 160000000 Hz.
+
+Example:
+
+ssp0: ssp@80010000 {
+       #address-cells = <1>;
+       #size-cells = <0>;
+       compatible = "fsl,imx28-spi";
+       reg = <0x80010000 0x2000>;
+       interrupts = <96 82>;
+       fsl,ssp-dma-channel = <0>;
+};
index e782add2e457df2b9e22d75f799900a4d9256e7f..d2c33d0f533ec8702938b017c470faf073db9032 100644 (file)
@@ -21,6 +21,9 @@ assumption that board specific platform code will be used to manage
 chip selects.  Individual drivers can define additional properties to
 support describing the chip select layout.
 
+Optional property:
+- num-cs : total number of chipselects
+
 SPI slave nodes must be children of the SPI master node and can
 contain the following properties.
 - reg             - (required) chip select address of device.
diff --git a/Documentation/devicetree/bindings/spi/spi-gpio.txt b/Documentation/devicetree/bindings/spi/spi-gpio.txt
new file mode 100644 (file)
index 0000000..8a824be
--- /dev/null
@@ -0,0 +1,29 @@
+SPI-GPIO devicetree bindings
+
+Required properties:
+
+ - compatible: should be set to "spi-gpio"
+ - #address-cells: should be set to <0x1>
+ - ranges
+ - gpio-sck: GPIO spec for the SCK line to use
+ - gpio-miso: GPIO spec for the MISO line to use
+ - gpio-mosi: GPIO spec for the MOSI line to use
+ - cs-gpios: GPIOs to use for chipselect lines
+ - num-chipselects: number of chipselect lines
+
+Example:
+
+       spi {
+               compatible = "spi-gpio";
+               #address-cells = <0x1>;
+               ranges;
+
+               gpio-sck = <&gpio 95 0>;
+               gpio-miso = <&gpio 98 0>;
+               gpio-mosi = <&gpio 97 0>;
+               cs-gpios = <&gpio 125 0>;
+               num-chipselects = <1>;
+
+               /* clients */
+       };
+
diff --git a/Documentation/devicetree/bindings/spi/spi-sc18is602.txt b/Documentation/devicetree/bindings/spi/spi-sc18is602.txt
new file mode 100644 (file)
index 0000000..02f9033
--- /dev/null
@@ -0,0 +1,23 @@
+NXP SC18IS602/SCIS603
+
+Required properties:
+       - compatible : Should be one of
+               "nxp,sc18is602"
+               "nxp,sc18is602b"
+               "nxp,sc18is603"
+       - reg: I2C bus address
+
+Optional properties:
+       - clock-frequency : external oscillator clock frequency. If not
+         specified, the SC18IS602 default frequency (7372000) will be used.
+
+The clock-frequency property is relevant and needed only if the chip has an
+external oscillator (SC18IS603).
+
+Example:
+
+       sc18is603@28 {
+               compatible = "nxp,sc18is603";
+               reg = <0x28>;
+               clock-frequency = <14744000>;
+       }
index 306ec3ff3c0e3be27a200ae5fdf215f483fe5eab..f158fd31cfda71a3ab69984c54cbeab3fa22bc61 100644 (file)
@@ -6,7 +6,29 @@ Required properties:
 - interrupts : Should contain SPI controller interrupt
 
 Optional properties:
+- num-cs : total number of chipselects
 - cs-gpios : should specify GPIOs used for chipselects.
   The gpios will be referred to as reg = <index> in the SPI child nodes.
   If unspecified, a single SPI device without a chip select can be used.
+- pl022,autosuspend-delay : delay in ms following transfer completion before
+                           the runtime power management system suspends the
+                           device. A setting of 0 indicates no delay and the
+                            device will be suspended immediately
+- pl022,rt : indicates the controller should run the message pump with realtime
+             priority to minimise the transfer latency on the bus (boolean)
+
+
+SPI slave nodes must be children of the SPI master node and can
+contain the following properties.
+
+- pl022,interface : interface type:
+       0: SPI
+       1: Texas Instruments Synchronous Serial Frame Format
+       2: Microwire (Half Duplex)
+- pl022,com-mode : polling, interrupt or dma
+- pl022,rx-level-trig : Rx FIFO watermark level
+- pl022,tx-level-trig : Tx FIFO watermark level
+- pl022,ctrl-len : Microwire interface: Control length
+- pl022,wait-state : Microwire interface: Wait state
+- pl022,duplex : Microwire interface: Full/Half duplex
 
diff --git a/Documentation/spi/spi-sc18is602 b/Documentation/spi/spi-sc18is602
new file mode 100644 (file)
index 0000000..a457028
--- /dev/null
@@ -0,0 +1,36 @@
+Kernel driver spi-sc18is602
+===========================
+
+Supported chips:
+  * NXP SI18IS602/602B/603
+    Datasheet: http://www.nxp.com/documents/data_sheet/SC18IS602_602B_603.pdf
+
+Author:
+        Guenter Roeck <linux@roeck-us.net>
+
+
+Description
+-----------
+
+This driver provides connects a NXP SC18IS602/603 I2C-bus to SPI bridge to the
+kernel's SPI core subsystem.
+
+The driver does not probe for supported chips, since the SI18IS602/603 does not
+support Chip ID registers. You will have to instantiate the devices explicitly.
+Please see Documentation/i2c/instantiating-devices for details.
+
+
+Usage Notes
+-----------
+
+This driver requires the I2C adapter driver to support raw I2C messages. I2C
+adapter drivers which can only handle the SMBus protocol are not supported.
+
+The maximum SPI message size supported by SC18IS602/603 is 200 bytes. Attempts
+to initiate longer transfers will fail with -EINVAL. EEPROM read operations and
+similar large accesses have to be split into multiple chunks of no more than
+200 bytes per SPI message (128 bytes of data per message is recommended). This
+means that programs such as "cp" or "od", which automatically use large block
+sizes to access a device, can not be used directly to read data from EEPROM.
+Programs such as dd, where the block size can be specified, should be used
+instead.
index 03acf1883ec74be7cee10d86e485a4f1d440bffb..281292ebac287dbe8e315af8bb90597d70811783 100644 (file)
@@ -1604,9 +1604,6 @@ static struct u300_mux_hog u300_mux_hogs[] = {
        {
                .dev = &uart0_device.dev,
        },
-       {
-               .dev = &pl022_device.dev,
-       },
        {
                .dev = &mmcsd_device.dev,
        },
index 7bedeec08524bd54f0d6a085c7c97dd4ee8c3536..a6a22237e860e9de980b1aeed3d86baa0e32cd24 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for mxs specific clk
 #
 
-obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o
+obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o clk-ssp.o
 
 obj-$(CONFIG_SOC_IMX23) += clk-imx23.o
 obj-$(CONFIG_SOC_IMX28) += clk-imx28.o
diff --git a/drivers/clk/mxs/clk-ssp.c b/drivers/clk/mxs/clk-ssp.c
new file mode 100644 (file)
index 0000000..af7bdbf
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012 DENX Software Engineering, GmbH
+ *
+ * Pulled from code:
+ * Portions copyright (C) 2003 Russell King, PXA MMCI Driver
+ * Portions copyright (C) 2004-2005 Pierre Ossman, W83L51xD SD/MMC driver
+ *
+ * Copyright 2008 Embedded Alley Solutions, Inc.
+ * Copyright 2009-2011 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/spi/mxs-spi.h>
+
+void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate)
+{
+       unsigned int ssp_clk, ssp_sck;
+       u32 clock_divide, clock_rate;
+       u32 val;
+
+       ssp_clk = clk_get_rate(ssp->clk);
+
+       for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) {
+               clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide);
+               clock_rate = (clock_rate > 0) ? clock_rate - 1 : 0;
+               if (clock_rate <= 255)
+                       break;
+       }
+
+       if (clock_divide > 254) {
+               dev_err(ssp->dev,
+                       "%s: cannot set clock to %d\n", __func__, rate);
+               return;
+       }
+
+       ssp_sck = ssp_clk / clock_divide / (1 + clock_rate);
+
+       val = readl(ssp->base + HW_SSP_TIMING(ssp));
+       val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE);
+       val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE);
+       val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE);
+       writel(val, ssp->base + HW_SSP_TIMING(ssp));
+
+       ssp->clk_rate = ssp_sck;
+
+       dev_dbg(ssp->dev,
+               "%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n",
+               __func__, clock_divide, clock_rate, ssp_clk, ssp_sck, rate);
+}
+EXPORT_SYMBOL_GPL(mxs_ssp_set_clk_rate);
index ad3fcea1269ebc179105563333b01cfdf139a9ed..bb4c2bf04d094f60cbbffc6a7be34e43effd38e0 100644 (file)
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/module.h>
-#include <linux/fsl/mxs-dma.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/stmp_device.h>
 #include <linux/mmc/mxs-mmc.h>
+#include <linux/spi/mxs-spi.h>
 
 #define DRIVER_NAME    "mxs-mmc"
 
-/* card detect polling timeout */
-#define MXS_MMC_DETECT_TIMEOUT                 (HZ/2)
-
-#define ssp_is_old(host)       ((host)->devid == IMX23_MMC)
-
-/* SSP registers */
-#define HW_SSP_CTRL0                           0x000
-#define  BM_SSP_CTRL0_RUN                      (1 << 29)
-#define  BM_SSP_CTRL0_SDIO_IRQ_CHECK           (1 << 28)
-#define  BM_SSP_CTRL0_IGNORE_CRC               (1 << 26)
-#define  BM_SSP_CTRL0_READ                     (1 << 25)
-#define  BM_SSP_CTRL0_DATA_XFER                        (1 << 24)
-#define  BP_SSP_CTRL0_BUS_WIDTH                        (22)
-#define  BM_SSP_CTRL0_BUS_WIDTH                        (0x3 << 22)
-#define  BM_SSP_CTRL0_WAIT_FOR_IRQ             (1 << 21)
-#define  BM_SSP_CTRL0_LONG_RESP                        (1 << 19)
-#define  BM_SSP_CTRL0_GET_RESP                 (1 << 17)
-#define  BM_SSP_CTRL0_ENABLE                   (1 << 16)
-#define  BP_SSP_CTRL0_XFER_COUNT               (0)
-#define  BM_SSP_CTRL0_XFER_COUNT               (0xffff)
-#define HW_SSP_CMD0                            0x010
-#define  BM_SSP_CMD0_DBL_DATA_RATE_EN          (1 << 25)
-#define  BM_SSP_CMD0_SLOW_CLKING_EN            (1 << 22)
-#define  BM_SSP_CMD0_CONT_CLKING_EN            (1 << 21)
-#define  BM_SSP_CMD0_APPEND_8CYC               (1 << 20)
-#define  BP_SSP_CMD0_BLOCK_SIZE                        (16)
-#define  BM_SSP_CMD0_BLOCK_SIZE                        (0xf << 16)
-#define  BP_SSP_CMD0_BLOCK_COUNT               (8)
-#define  BM_SSP_CMD0_BLOCK_COUNT               (0xff << 8)
-#define  BP_SSP_CMD0_CMD                       (0)
-#define  BM_SSP_CMD0_CMD                       (0xff)
-#define HW_SSP_CMD1                            0x020
-#define HW_SSP_XFER_SIZE                       0x030
-#define HW_SSP_BLOCK_SIZE                      0x040
-#define  BP_SSP_BLOCK_SIZE_BLOCK_COUNT         (4)
-#define  BM_SSP_BLOCK_SIZE_BLOCK_COUNT         (0xffffff << 4)
-#define  BP_SSP_BLOCK_SIZE_BLOCK_SIZE          (0)
-#define  BM_SSP_BLOCK_SIZE_BLOCK_SIZE          (0xf)
-#define HW_SSP_TIMING(h)                       (ssp_is_old(h) ? 0x050 : 0x070)
-#define  BP_SSP_TIMING_TIMEOUT                 (16)
-#define  BM_SSP_TIMING_TIMEOUT                 (0xffff << 16)
-#define  BP_SSP_TIMING_CLOCK_DIVIDE            (8)
-#define  BM_SSP_TIMING_CLOCK_DIVIDE            (0xff << 8)
-#define  BP_SSP_TIMING_CLOCK_RATE              (0)
-#define  BM_SSP_TIMING_CLOCK_RATE              (0xff)
-#define HW_SSP_CTRL1(h)                                (ssp_is_old(h) ? 0x060 : 0x080)
-#define  BM_SSP_CTRL1_SDIO_IRQ                 (1 << 31)
-#define  BM_SSP_CTRL1_SDIO_IRQ_EN              (1 << 30)
-#define  BM_SSP_CTRL1_RESP_ERR_IRQ             (1 << 29)
-#define  BM_SSP_CTRL1_RESP_ERR_IRQ_EN          (1 << 28)
-#define  BM_SSP_CTRL1_RESP_TIMEOUT_IRQ         (1 << 27)
-#define  BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN      (1 << 26)
-#define  BM_SSP_CTRL1_DATA_TIMEOUT_IRQ         (1 << 25)
-#define  BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN      (1 << 24)
-#define  BM_SSP_CTRL1_DATA_CRC_IRQ             (1 << 23)
-#define  BM_SSP_CTRL1_DATA_CRC_IRQ_EN          (1 << 22)
-#define  BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ                (1 << 21)
-#define  BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ_EN     (1 << 20)
-#define  BM_SSP_CTRL1_RECV_TIMEOUT_IRQ         (1 << 17)
-#define  BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN      (1 << 16)
-#define  BM_SSP_CTRL1_FIFO_OVERRUN_IRQ         (1 << 15)
-#define  BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN      (1 << 14)
-#define  BM_SSP_CTRL1_DMA_ENABLE               (1 << 13)
-#define  BM_SSP_CTRL1_POLARITY                 (1 << 9)
-#define  BP_SSP_CTRL1_WORD_LENGTH              (4)
-#define  BM_SSP_CTRL1_WORD_LENGTH              (0xf << 4)
-#define  BP_SSP_CTRL1_SSP_MODE                 (0)
-#define  BM_SSP_CTRL1_SSP_MODE                 (0xf)
-#define HW_SSP_SDRESP0(h)                      (ssp_is_old(h) ? 0x080 : 0x0a0)
-#define HW_SSP_SDRESP1(h)                      (ssp_is_old(h) ? 0x090 : 0x0b0)
-#define HW_SSP_SDRESP2(h)                      (ssp_is_old(h) ? 0x0a0 : 0x0c0)
-#define HW_SSP_SDRESP3(h)                      (ssp_is_old(h) ? 0x0b0 : 0x0d0)
-#define HW_SSP_STATUS(h)                       (ssp_is_old(h) ? 0x0c0 : 0x100)
-#define  BM_SSP_STATUS_CARD_DETECT             (1 << 28)
-#define  BM_SSP_STATUS_SDIO_IRQ                        (1 << 17)
-
-#define BF_SSP(value, field)   (((value) << BP_SSP_##field) & BM_SSP_##field)
-
 #define MXS_MMC_IRQ_BITS       (BM_SSP_CTRL1_SDIO_IRQ          | \
                                 BM_SSP_CTRL1_RESP_ERR_IRQ      | \
                                 BM_SSP_CTRL1_RESP_TIMEOUT_IRQ  | \
                                 BM_SSP_CTRL1_RECV_TIMEOUT_IRQ  | \
                                 BM_SSP_CTRL1_FIFO_OVERRUN_IRQ)
 
-#define SSP_PIO_NUM    3
-
-enum mxs_mmc_id {
-       IMX23_MMC,
-       IMX28_MMC,
-};
+/* card detect polling timeout */
+#define MXS_MMC_DETECT_TIMEOUT                 (HZ/2)
 
 struct mxs_mmc_host {
+       struct mxs_ssp                  ssp;
+
        struct mmc_host                 *mmc;
        struct mmc_request              *mrq;
        struct mmc_command              *cmd;
        struct mmc_data                 *data;
 
-       void __iomem                    *base;
-       int                             dma_channel;
-       struct clk                      *clk;
-       unsigned int                    clk_rate;
-
-       struct dma_chan                 *dmach;
-       struct mxs_dma_data             dma_data;
-       unsigned int                    dma_dir;
-       enum dma_transfer_direction     slave_dirn;
-       u32                             ssp_pio_words[SSP_PIO_NUM];
-
-       enum mxs_mmc_id                 devid;
        unsigned char                   bus_width;
        spinlock_t                      lock;
        int                             sdio_irq_en;
@@ -186,16 +94,18 @@ static int mxs_mmc_get_ro(struct mmc_host *mmc)
 static int mxs_mmc_get_cd(struct mmc_host *mmc)
 {
        struct mxs_mmc_host *host = mmc_priv(mmc);
+       struct mxs_ssp *ssp = &host->ssp;
 
-       return !(readl(host->base + HW_SSP_STATUS(host)) &
+       return !(readl(ssp->base + HW_SSP_STATUS(ssp)) &
                 BM_SSP_STATUS_CARD_DETECT);
 }
 
 static void mxs_mmc_reset(struct mxs_mmc_host *host)
 {
+       struct mxs_ssp *ssp = &host->ssp;
        u32 ctrl0, ctrl1;
 
-       stmp_reset_block(host->base);
+       stmp_reset_block(ssp->base);
 
        ctrl0 = BM_SSP_CTRL0_IGNORE_CRC;
        ctrl1 = BF_SSP(0x3, CTRL1_SSP_MODE) |
@@ -211,15 +121,15 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host)
        writel(BF_SSP(0xffff, TIMING_TIMEOUT) |
               BF_SSP(2, TIMING_CLOCK_DIVIDE) |
               BF_SSP(0, TIMING_CLOCK_RATE),
-              host->base + HW_SSP_TIMING(host));
+              ssp->base + HW_SSP_TIMING(ssp));
 
        if (host->sdio_irq_en) {
                ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
                ctrl1 |= BM_SSP_CTRL1_SDIO_IRQ_EN;
        }
 
-       writel(ctrl0, host->base + HW_SSP_CTRL0);
-       writel(ctrl1, host->base + HW_SSP_CTRL1(host));
+       writel(ctrl0, ssp->base + HW_SSP_CTRL0);
+       writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp));
 }
 
 static void mxs_mmc_start_cmd(struct mxs_mmc_host *host,
@@ -230,21 +140,22 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host)
        struct mmc_command *cmd = host->cmd;
        struct mmc_data *data = host->data;
        struct mmc_request *mrq = host->mrq;
+       struct mxs_ssp *ssp = &host->ssp;
 
        if (mmc_resp_type(cmd) & MMC_RSP_PRESENT) {
                if (mmc_resp_type(cmd) & MMC_RSP_136) {
-                       cmd->resp[3] = readl(host->base + HW_SSP_SDRESP0(host));
-                       cmd->resp[2] = readl(host->base + HW_SSP_SDRESP1(host));
-                       cmd->resp[1] = readl(host->base + HW_SSP_SDRESP2(host));
-                       cmd->resp[0] = readl(host->base + HW_SSP_SDRESP3(host));
+                       cmd->resp[3] = readl(ssp->base + HW_SSP_SDRESP0(ssp));
+                       cmd->resp[2] = readl(ssp->base + HW_SSP_SDRESP1(ssp));
+                       cmd->resp[1] = readl(ssp->base + HW_SSP_SDRESP2(ssp));
+                       cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP3(ssp));
                } else {
-                       cmd->resp[0] = readl(host->base + HW_SSP_SDRESP0(host));
+                       cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP0(ssp));
                }
        }
 
        if (data) {
                dma_unmap_sg(mmc_dev(host->mmc), data->sg,
-                            data->sg_len, host->dma_dir);
+                            data->sg_len, ssp->dma_dir);
                /*
                 * If there was an error on any block, we mark all
                 * data blocks as being in error.
@@ -277,13 +188,14 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
        struct mxs_mmc_host *host = dev_id;
        struct mmc_command *cmd = host->cmd;
        struct mmc_data *data = host->data;
+       struct mxs_ssp *ssp = &host->ssp;
        u32 stat;
 
        spin_lock(&host->lock);
 
-       stat = readl(host->base + HW_SSP_CTRL1(host));
+       stat = readl(ssp->base + HW_SSP_CTRL1(ssp));
        writel(stat & MXS_MMC_IRQ_BITS,
-              host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR);
+              ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
 
        spin_unlock(&host->lock);
 
@@ -312,6 +224,7 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
 static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
        struct mxs_mmc_host *host, unsigned long flags)
 {
+       struct mxs_ssp *ssp = &host->ssp;
        struct dma_async_tx_descriptor *desc;
        struct mmc_data *data = host->data;
        struct scatterlist * sgl;
@@ -320,24 +233,24 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
        if (data) {
                /* data */
                dma_map_sg(mmc_dev(host->mmc), data->sg,
-                          data->sg_len, host->dma_dir);
+                          data->sg_len, ssp->dma_dir);
                sgl = data->sg;
                sg_len = data->sg_len;
        } else {
                /* pio */
-               sgl = (struct scatterlist *) host->ssp_pio_words;
+               sgl = (struct scatterlist *) ssp->ssp_pio_words;
                sg_len = SSP_PIO_NUM;
        }
 
-       desc = dmaengine_prep_slave_sg(host->dmach,
-                               sgl, sg_len, host->slave_dirn, flags);
+       desc = dmaengine_prep_slave_sg(ssp->dmach,
+                               sgl, sg_len, ssp->slave_dirn, flags);
        if (desc) {
                desc->callback = mxs_mmc_dma_irq_callback;
                desc->callback_param = host;
        } else {
                if (data)
                        dma_unmap_sg(mmc_dev(host->mmc), data->sg,
-                                    data->sg_len, host->dma_dir);
+                                    data->sg_len, ssp->dma_dir);
        }
 
        return desc;
@@ -345,6 +258,7 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
 
 static void mxs_mmc_bc(struct mxs_mmc_host *host)
 {
+       struct mxs_ssp *ssp = &host->ssp;
        struct mmc_command *cmd = host->cmd;
        struct dma_async_tx_descriptor *desc;
        u32 ctrl0, cmd0, cmd1;
@@ -358,17 +272,17 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host)
                cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
        }
 
-       host->ssp_pio_words[0] = ctrl0;
-       host->ssp_pio_words[1] = cmd0;
-       host->ssp_pio_words[2] = cmd1;
-       host->dma_dir = DMA_NONE;
-       host->slave_dirn = DMA_TRANS_NONE;
+       ssp->ssp_pio_words[0] = ctrl0;
+       ssp->ssp_pio_words[1] = cmd0;
+       ssp->ssp_pio_words[2] = cmd1;
+       ssp->dma_dir = DMA_NONE;
+       ssp->slave_dirn = DMA_TRANS_NONE;
        desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
        if (!desc)
                goto out;
 
        dmaengine_submit(desc);
-       dma_async_issue_pending(host->dmach);
+       dma_async_issue_pending(ssp->dmach);
        return;
 
 out:
@@ -378,6 +292,7 @@ out:
 
 static void mxs_mmc_ac(struct mxs_mmc_host *host)
 {
+       struct mxs_ssp *ssp = &host->ssp;
        struct mmc_command *cmd = host->cmd;
        struct dma_async_tx_descriptor *desc;
        u32 ignore_crc, get_resp, long_resp;
@@ -399,17 +314,17 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
                cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
        }
 
-       host->ssp_pio_words[0] = ctrl0;
-       host->ssp_pio_words[1] = cmd0;
-       host->ssp_pio_words[2] = cmd1;
-       host->dma_dir = DMA_NONE;
-       host->slave_dirn = DMA_TRANS_NONE;
+       ssp->ssp_pio_words[0] = ctrl0;
+       ssp->ssp_pio_words[1] = cmd0;
+       ssp->ssp_pio_words[2] = cmd1;
+       ssp->dma_dir = DMA_NONE;
+       ssp->slave_dirn = DMA_TRANS_NONE;
        desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK);
        if (!desc)
                goto out;
 
        dmaengine_submit(desc);
-       dma_async_issue_pending(host->dmach);
+       dma_async_issue_pending(ssp->dmach);
        return;
 
 out:
@@ -447,6 +362,8 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
        unsigned int data_size = 0, log2_blksz;
        unsigned int blocks = data->blocks;
 
+       struct mxs_ssp *ssp = &host->ssp;
+
        u32 ignore_crc, get_resp, long_resp, read;
        u32 ctrl0, cmd0, cmd1, val;
 
@@ -489,15 +406,15 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
                blocks = 1;
 
        /* xfer count, block size and count need to be set differently */
-       if (ssp_is_old(host)) {
+       if (ssp_is_old(ssp)) {
                ctrl0 |= BF_SSP(data_size, CTRL0_XFER_COUNT);
                cmd0 |= BF_SSP(log2_blksz, CMD0_BLOCK_SIZE) |
                        BF_SSP(blocks - 1, CMD0_BLOCK_COUNT);
        } else {
-               writel(data_size, host->base + HW_SSP_XFER_SIZE);
+               writel(data_size, ssp->base + HW_SSP_XFER_SIZE);
                writel(BF_SSP(log2_blksz, BLOCK_SIZE_BLOCK_SIZE) |
                       BF_SSP(blocks - 1, BLOCK_SIZE_BLOCK_COUNT),
-                      host->base + HW_SSP_BLOCK_SIZE);
+                      ssp->base + HW_SSP_BLOCK_SIZE);
        }
 
        if ((cmd->opcode == MMC_STOP_TRANSMISSION) ||
@@ -512,18 +429,18 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
        }
 
        /* set the timeout count */
-       timeout = mxs_ns_to_ssp_ticks(host->clk_rate, data->timeout_ns);
-       val = readl(host->base + HW_SSP_TIMING(host));
+       timeout = mxs_ns_to_ssp_ticks(ssp->clk_rate, data->timeout_ns);
+       val = readl(ssp->base + HW_SSP_TIMING(ssp));
        val &= ~(BM_SSP_TIMING_TIMEOUT);
        val |= BF_SSP(timeout, TIMING_TIMEOUT);
-       writel(val, host->base + HW_SSP_TIMING(host));
+       writel(val, ssp->base + HW_SSP_TIMING(ssp));
 
        /* pio */
-       host->ssp_pio_words[0] = ctrl0;
-       host->ssp_pio_words[1] = cmd0;
-       host->ssp_pio_words[2] = cmd1;
-       host->dma_dir = DMA_NONE;
-       host->slave_dirn = DMA_TRANS_NONE;
+       ssp->ssp_pio_words[0] = ctrl0;
+       ssp->ssp_pio_words[1] = cmd0;
+       ssp->ssp_pio_words[2] = cmd1;
+       ssp->dma_dir = DMA_NONE;
+       ssp->slave_dirn = DMA_TRANS_NONE;
        desc = mxs_mmc_prep_dma(host, 0);
        if (!desc)
                goto out;
@@ -531,14 +448,14 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
        /* append data sg */
        WARN_ON(host->data != NULL);
        host->data = data;
-       host->dma_dir = dma_data_dir;
-       host->slave_dirn = slave_dirn;
+       ssp->dma_dir = dma_data_dir;
+       ssp->slave_dirn = slave_dirn;
        desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
        if (!desc)
                goto out;
 
        dmaengine_submit(desc);
-       dma_async_issue_pending(host->dmach);
+       dma_async_issue_pending(ssp->dmach);
        return;
 out:
        dev_warn(mmc_dev(host->mmc),
@@ -579,42 +496,6 @@ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
        mxs_mmc_start_cmd(host, mrq->cmd);
 }
 
-static void mxs_mmc_set_clk_rate(struct mxs_mmc_host *host, unsigned int rate)
-{
-       unsigned int ssp_clk, ssp_sck;
-       u32 clock_divide, clock_rate;
-       u32 val;
-
-       ssp_clk = clk_get_rate(host->clk);
-
-       for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) {
-               clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide);
-               clock_rate = (clock_rate > 0) ? clock_rate - 1 : 0;
-               if (clock_rate <= 255)
-                       break;
-       }
-
-       if (clock_divide > 254) {
-               dev_err(mmc_dev(host->mmc),
-                       "%s: cannot set clock to %d\n", __func__, rate);
-               return;
-       }
-
-       ssp_sck = ssp_clk / clock_divide / (1 + clock_rate);
-
-       val = readl(host->base + HW_SSP_TIMING(host));
-       val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE);
-       val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE);
-       val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE);
-       writel(val, host->base + HW_SSP_TIMING(host));
-
-       host->clk_rate = ssp_sck;
-
-       dev_dbg(mmc_dev(host->mmc),
-               "%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n",
-               __func__, clock_divide, clock_rate, ssp_clk, ssp_sck, rate);
-}
-
 static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
        struct mxs_mmc_host *host = mmc_priv(mmc);
@@ -627,12 +508,13 @@ static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                host->bus_width = 0;
 
        if (ios->clock)
-               mxs_mmc_set_clk_rate(host, ios->clock);
+               mxs_ssp_set_clk_rate(&host->ssp, ios->clock);
 }
 
 static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
 {
        struct mxs_mmc_host *host = mmc_priv(mmc);
+       struct mxs_ssp *ssp = &host->ssp;
        unsigned long flags;
 
        spin_lock_irqsave(&host->lock, flags);
@@ -641,19 +523,19 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
 
        if (enable) {
                writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
-                      host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
+                      ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
                writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
                       host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET);
        } else {
                writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
-                      host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
+                      ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
                writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
-                      host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR);
+                      ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
        }
 
        spin_unlock_irqrestore(&host->lock, flags);
 
-       if (enable && readl(host->base + HW_SSP_STATUS(host)) &
+       if (enable && readl(ssp->base + HW_SSP_STATUS(ssp)) &
                        BM_SSP_STATUS_SDIO_IRQ)
                mmc_signal_sdio_irq(host->mmc);
 
@@ -670,34 +552,35 @@ static const struct mmc_host_ops mxs_mmc_ops = {
 static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param)
 {
        struct mxs_mmc_host *host = param;
+       struct mxs_ssp *ssp = &host->ssp;
 
        if (!mxs_dma_is_apbh(chan))
                return false;
 
-       if (chan->chan_id != host->dma_channel)
+       if (chan->chan_id != ssp->dma_channel)
                return false;
 
-       chan->private = &host->dma_data;
+       chan->private = &ssp->dma_data;
 
        return true;
 }
 
-static struct platform_device_id mxs_mmc_ids[] = {
+static struct platform_device_id mxs_ssp_ids[] = {
        {
                .name = "imx23-mmc",
-               .driver_data = IMX23_MMC,
+               .driver_data = IMX23_SSP,
        }, {
                .name = "imx28-mmc",
-               .driver_data = IMX28_MMC,
+               .driver_data = IMX28_SSP,
        }, {
                /* sentinel */
        }
 };
-MODULE_DEVICE_TABLE(platform, mxs_mmc_ids);
+MODULE_DEVICE_TABLE(platform, mxs_ssp_ids);
 
 static const struct of_device_id mxs_mmc_dt_ids[] = {
-       { .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_MMC, },
-       { .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_MMC, },
+       { .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_SSP, },
+       { .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_SSP, },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, mxs_mmc_dt_ids);
@@ -716,6 +599,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
        dma_cap_mask_t mask;
        struct regulator *reg_vmmc;
        enum of_gpio_flags flags;
+       struct mxs_ssp *ssp;
 
        iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
@@ -729,28 +613,30 @@ static int mxs_mmc_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        host = mmc_priv(mmc);
-       host->base = devm_request_and_ioremap(&pdev->dev, iores);
-       if (!host->base) {
+       ssp = &host->ssp;
+       ssp->dev = &pdev->dev;
+       ssp->base = devm_request_and_ioremap(&pdev->dev, iores);
+       if (!ssp->base) {
                ret = -EADDRNOTAVAIL;
                goto out_mmc_free;
        }
 
        if (np) {
-               host->devid = (enum mxs_mmc_id) of_id->data;
+               ssp->devid = (enum mxs_ssp_id) of_id->data;
                /*
                 * TODO: This is a temporary solution and should be changed
                 * to use generic DMA binding later when the helpers get in.
                 */
                ret = of_property_read_u32(np, "fsl,ssp-dma-channel",
-                                          &host->dma_channel);
+                                          &ssp->dma_channel);
                if (ret) {
                        dev_err(mmc_dev(host->mmc),
                                "failed to get dma channel\n");
                        goto out_mmc_free;
                }
        } else {
-               host->devid = pdev->id_entry->driver_data;
-               host->dma_channel = dmares->start;
+               ssp->devid = pdev->id_entry->driver_data;
+               ssp->dma_channel = dmares->start;
        }
 
        host->mmc = mmc;
@@ -772,20 +658,20 @@ static int mxs_mmc_probe(struct platform_device *pdev)
                goto out_mmc_free;
        }
 
-       host->clk = clk_get(&pdev->dev, NULL);
-       if (IS_ERR(host->clk)) {
-               ret = PTR_ERR(host->clk);
+       ssp->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(ssp->clk)) {
+               ret = PTR_ERR(ssp->clk);
                goto out_mmc_free;
        }
-       clk_prepare_enable(host->clk);
+       clk_prepare_enable(ssp->clk);
 
        mxs_mmc_reset(host);
 
        dma_cap_zero(mask);
        dma_cap_set(DMA_SLAVE, mask);
-       host->dma_data.chan_irq = irq_dma;
-       host->dmach = dma_request_channel(mask, mxs_mmc_dma_filter, host);
-       if (!host->dmach) {
+       ssp->dma_data.chan_irq = irq_dma;
+       ssp->dmach = dma_request_channel(mask, mxs_mmc_dma_filter, host);
+       if (!ssp->dmach) {
                dev_err(mmc_dev(host->mmc),
                        "%s: failed to request dma\n", __func__);
                goto out_clk_put;
@@ -822,9 +708,9 @@ static int mxs_mmc_probe(struct platform_device *pdev)
 
        mmc->max_segs = 52;
        mmc->max_blk_size = 1 << 0xf;
-       mmc->max_blk_count = (ssp_is_old(host)) ? 0xff : 0xffffff;
-       mmc->max_req_size = (ssp_is_old(host)) ? 0xffff : 0xffffffff;
-       mmc->max_seg_size = dma_get_max_seg_size(host->dmach->device->dev);
+       mmc->max_blk_count = (ssp_is_old(ssp)) ? 0xff : 0xffffff;
+       mmc->max_req_size = (ssp_is_old(ssp)) ? 0xffff : 0xffffffff;
+       mmc->max_seg_size = dma_get_max_seg_size(ssp->dmach->device->dev);
 
        platform_set_drvdata(pdev, mmc);
 
@@ -844,11 +730,11 @@ static int mxs_mmc_probe(struct platform_device *pdev)
        return 0;
 
 out_free_dma:
-       if (host->dmach)
-               dma_release_channel(host->dmach);
+       if (ssp->dmach)
+               dma_release_channel(ssp->dmach);
 out_clk_put:
-       clk_disable_unprepare(host->clk);
-       clk_put(host->clk);
+       clk_disable_unprepare(ssp->clk);
+       clk_put(ssp->clk);
 out_mmc_free:
        mmc_free_host(mmc);
        return ret;
@@ -858,16 +744,17 @@ static int mxs_mmc_remove(struct platform_device *pdev)
 {
        struct mmc_host *mmc = platform_get_drvdata(pdev);
        struct mxs_mmc_host *host = mmc_priv(mmc);
+       struct mxs_ssp *ssp = &host->ssp;
 
        mmc_remove_host(mmc);
 
        platform_set_drvdata(pdev, NULL);
 
-       if (host->dmach)
-               dma_release_channel(host->dmach);
+       if (ssp->dmach)
+               dma_release_channel(ssp->dmach);
 
-       clk_disable_unprepare(host->clk);
-       clk_put(host->clk);
+       clk_disable_unprepare(ssp->clk);
+       clk_put(ssp->clk);
 
        mmc_free_host(mmc);
 
@@ -879,11 +766,12 @@ static int mxs_mmc_suspend(struct device *dev)
 {
        struct mmc_host *mmc = dev_get_drvdata(dev);
        struct mxs_mmc_host *host = mmc_priv(mmc);
+       struct mxs_ssp *ssp = &host->ssp;
        int ret = 0;
 
        ret = mmc_suspend_host(mmc);
 
-       clk_disable_unprepare(host->clk);
+       clk_disable_unprepare(ssp->clk);
 
        return ret;
 }
@@ -892,9 +780,10 @@ static int mxs_mmc_resume(struct device *dev)
 {
        struct mmc_host *mmc = dev_get_drvdata(dev);
        struct mxs_mmc_host *host = mmc_priv(mmc);
+       struct mxs_ssp *ssp = &host->ssp;
        int ret = 0;
 
-       clk_prepare_enable(host->clk);
+       clk_prepare_enable(ssp->clk);
 
        ret = mmc_resume_host(mmc);
 
@@ -910,7 +799,7 @@ static const struct dev_pm_ops mxs_mmc_pm_ops = {
 static struct platform_driver mxs_mmc_driver = {
        .probe          = mxs_mmc_probe,
        .remove         = mxs_mmc_remove,
-       .id_table       = mxs_mmc_ids,
+       .id_table       = mxs_ssp_ids,
        .driver         = {
                .name   = DRIVER_NAME,
                .owner  = THIS_MODULE,
index 5f84b5563c2d1baff1cd7a61ea2a9b3400388484..63a79437aca1b94c0bd9f31c8f037b4413e3253b 100644 (file)
@@ -325,6 +325,12 @@ config SPI_S3C64XX
        help
          SPI driver for Samsung S3C64XX and newer SoCs.
 
+config SPI_SC18IS602
+       tristate "NXP SC18IS602/602B/603 I2C to SPI bridge"
+       depends on I2C
+       help
+         SPI driver for NXP SC18IS602/602B/603 I2C to SPI bridge.
+
 config SPI_SH_MSIOF
        tristate "SuperH MSIOF SPI controller"
        depends on SUPERH && HAVE_CLK
@@ -364,6 +370,13 @@ config SPI_STMP3XXX
        help
          SPI driver for Freescale STMP37xx/378x SoC SSP interface
 
+config SPI_MXS
+       tristate "Freescale MXS SPI controller"
+       depends on ARCH_MXS
+       select STMP_DEVICE
+       help
+         SPI driver for Freescale MXS devices.
+
 config SPI_TEGRA
        tristate "Nvidia Tegra SPI controller"
        depends on ARCH_TEGRA && (TEGRA_SYSTEM_DMA || TEGRA20_APB_DMA)
index 3920dcf4c7400e2c64bfb82803316f5f27535a63..938705644bb604809e092020d62cc8eb6eddfb7d 100644 (file)
@@ -36,6 +36,7 @@ obj-$(CONFIG_SPI_LM70_LLP)            += spi-lm70llp.o
 obj-$(CONFIG_SPI_MPC512x_PSC)          += spi-mpc512x-psc.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)          += spi-mpc52xx-psc.o
 obj-$(CONFIG_SPI_MPC52xx)              += spi-mpc52xx.o
+obj-$(CONFIG_SPI_MXS)                  += spi-mxs.o
 obj-$(CONFIG_SPI_NUC900)               += spi-nuc900.o
 obj-$(CONFIG_SPI_OC_TINY)              += spi-oc-tiny.o
 obj-$(CONFIG_SPI_OMAP_UWIRE)           += spi-omap-uwire.o
@@ -51,6 +52,7 @@ obj-$(CONFIG_SPI_S3C24XX)             += spi-s3c24xx-hw.o
 spi-s3c24xx-hw-y                       := spi-s3c24xx.o
 spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
 obj-$(CONFIG_SPI_S3C64XX)              += spi-s3c64xx.o
+obj-$(CONFIG_SPI_SC18IS602)            += spi-sc18is602.o
 obj-$(CONFIG_SPI_SH)                   += spi-sh.o
 obj-$(CONFIG_SPI_SH_HSPI)              += spi-sh-hspi.o
 obj-$(CONFIG_SPI_SH_MSIOF)             += spi-sh-msiof.o
index c00d00e96ee43784d8cd37715258f760a54bcbf8..f1fec2a19d101b110ec3da178299400afb2a1e29 100644 (file)
@@ -307,8 +307,6 @@ static const struct of_device_id altera_spi_match[] = {
        {},
 };
 MODULE_DEVICE_TABLE(of, altera_spi_match);
-#else /* CONFIG_OF */
-#define altera_spi_match NULL
 #endif /* CONFIG_OF */
 
 static struct platform_driver altera_spi_driver = {
@@ -318,7 +316,7 @@ static struct platform_driver altera_spi_driver = {
                .name = DRV_NAME,
                .owner = THIS_MODULE,
                .pm = NULL,
-               .of_match_table = altera_spi_match,
+               .of_match_table = of_match_ptr(altera_spi_match),
        },
 };
 module_platform_driver(altera_spi_driver);
index 0b56cfc71fabe28968d1f6d2a299bc5c281861e9..a2b50c516b318b38f13d131bd665c65c7d7f4d25 100644 (file)
@@ -22,6 +22,8 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 
 #include <linux/spi/spi.h>
 #include <linux/spi/spi_bitbang.h>
@@ -46,6 +48,7 @@ struct spi_gpio {
        struct spi_bitbang              bitbang;
        struct spi_gpio_platform_data   pdata;
        struct platform_device          *pdev;
+       int                             cs_gpios[0];
 };
 
 /*----------------------------------------------------------------------*/
@@ -89,15 +92,21 @@ struct spi_gpio {
 
 /*----------------------------------------------------------------------*/
 
-static inline const struct spi_gpio_platform_data * __pure
-spi_to_pdata(const struct spi_device *spi)
+static inline struct spi_gpio * __pure
+spi_to_spi_gpio(const struct spi_device *spi)
 {
        const struct spi_bitbang        *bang;
-       const struct spi_gpio           *spi_gpio;
+       struct spi_gpio                 *spi_gpio;
 
        bang = spi_master_get_devdata(spi->master);
        spi_gpio = container_of(bang, struct spi_gpio, bitbang);
-       return &spi_gpio->pdata;
+       return spi_gpio;
+}
+
+static inline struct spi_gpio_platform_data * __pure
+spi_to_pdata(const struct spi_device *spi)
+{
+       return &spi_to_spi_gpio(spi)->pdata;
 }
 
 /* this is #defined to avoid unused-variable warnings when inlining */
@@ -210,7 +219,8 @@ static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi,
 
 static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
 {
-       unsigned long cs = (unsigned long) spi->controller_data;
+       struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
+       unsigned int cs = spi_gpio->cs_gpios[spi->chip_select];
 
        /* set initial clock polarity */
        if (is_active)
@@ -224,12 +234,27 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
 
 static int spi_gpio_setup(struct spi_device *spi)
 {
-       unsigned long   cs = (unsigned long) spi->controller_data;
-       int             status = 0;
+       unsigned int            cs;
+       int                     status = 0;
+       struct spi_gpio         *spi_gpio = spi_to_spi_gpio(spi);
+       struct device_node      *np = spi->master->dev.of_node;
 
        if (spi->bits_per_word > 32)
                return -EINVAL;
 
+       if (np) {
+               /*
+                * In DT environments, the CS GPIOs have already been
+                * initialized from the "cs-gpios" property of the node.
+                */
+               cs = spi_gpio->cs_gpios[spi->chip_select];
+       } else {
+               /*
+                * ... otherwise, take it from spi->controller_data
+                */
+               cs = (unsigned int) spi->controller_data;
+       }
+
        if (!spi->controller_state) {
                if (cs != SPI_GPIO_NO_CHIPSELECT) {
                        status = gpio_request(cs, dev_name(&spi->dev));
@@ -239,8 +264,12 @@ static int spi_gpio_setup(struct spi_device *spi)
                                        !(spi->mode & SPI_CS_HIGH));
                }
        }
-       if (!status)
+       if (!status) {
                status = spi_bitbang_setup(spi);
+               /* in case it was initialized from static board data */
+               spi_gpio->cs_gpios[spi->chip_select] = cs;
+       }
+
        if (status) {
                if (!spi->controller_state && cs != SPI_GPIO_NO_CHIPSELECT)
                        gpio_free(cs);
@@ -250,7 +279,8 @@ static int spi_gpio_setup(struct spi_device *spi)
 
 static void spi_gpio_cleanup(struct spi_device *spi)
 {
-       unsigned long   cs = (unsigned long) spi->controller_data;
+       struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
+       unsigned int cs = spi_gpio->cs_gpios[spi->chip_select];
 
        if (cs != SPI_GPIO_NO_CHIPSELECT)
                gpio_free(cs);
@@ -313,6 +343,55 @@ done:
        return value;
 }
 
+#ifdef CONFIG_OF
+static struct of_device_id spi_gpio_dt_ids[] = {
+       { .compatible = "spi-gpio" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids);
+
+static int spi_gpio_probe_dt(struct platform_device *pdev)
+{
+       int ret;
+       u32 tmp;
+       struct spi_gpio_platform_data   *pdata;
+       struct device_node *np = pdev->dev.of_node;
+       const struct of_device_id *of_id =
+                       of_match_device(spi_gpio_dt_ids, &pdev->dev);
+
+       if (!of_id)
+               return 0;
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       pdata->sck = of_get_named_gpio(np, "gpio-sck", 0);
+       pdata->miso = of_get_named_gpio(np, "gpio-miso", 0);
+       pdata->mosi = of_get_named_gpio(np, "gpio-mosi", 0);
+
+       ret = of_property_read_u32(np, "num-chipselects", &tmp);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "num-chipselects property not found\n");
+               goto error_free;
+       }
+
+       pdata->num_chipselect = tmp;
+       pdev->dev.platform_data = pdata;
+
+       return 1;
+
+error_free:
+       devm_kfree(&pdev->dev, pdata);
+       return ret;
+}
+#else
+static inline int spi_gpio_probe_dt(struct platform_device *pdev)
+{
+       return 0;
+}
+#endif
+
 static int __devinit spi_gpio_probe(struct platform_device *pdev)
 {
        int                             status;
@@ -320,6 +399,13 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
        struct spi_gpio                 *spi_gpio;
        struct spi_gpio_platform_data   *pdata;
        u16 master_flags = 0;
+       bool use_of = 0;
+
+       status = spi_gpio_probe_dt(pdev);
+       if (status < 0)
+               return status;
+       if (status > 0)
+               use_of = 1;
 
        pdata = pdev->dev.platform_data;
 #ifdef GENERIC_BITBANG
@@ -331,7 +417,8 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
        if (status < 0)
                return status;
 
-       master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio);
+       master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) +
+                                       (sizeof(int) * SPI_N_CHIPSEL));
        if (!master) {
                status = -ENOMEM;
                goto gpio_free;
@@ -348,6 +435,23 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev)
        master->num_chipselect = SPI_N_CHIPSEL;
        master->setup = spi_gpio_setup;
        master->cleanup = spi_gpio_cleanup;
+#ifdef CONFIG_OF
+       master->dev.of_node = pdev->dev.of_node;
+
+       if (use_of) {
+               int i;
+               struct device_node *np = pdev->dev.of_node;
+
+               /*
+                * In DT environments, take the CS GPIO from the "cs-gpios"
+                * property of the node.
+                */
+
+               for (i = 0; i < SPI_N_CHIPSEL; i++)
+                       spi_gpio->cs_gpios[i] =
+                               of_get_named_gpio(np, "cs-gpios", i);
+       }
+#endif
 
        spi_gpio->bitbang.master = spi_master_get(master);
        spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
@@ -408,8 +512,11 @@ static int __devexit spi_gpio_remove(struct platform_device *pdev)
 MODULE_ALIAS("platform:" DRIVER_NAME);
 
 static struct platform_driver spi_gpio_driver = {
-       .driver.name    = DRIVER_NAME,
-       .driver.owner   = THIS_MODULE,
+       .driver = {
+               .name   = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(spi_gpio_dt_ids),
+       },
        .probe          = spi_gpio_probe,
        .remove         = __devexit_p(spi_gpio_remove),
 };
index e834ff8c0188281dc0fe25304c31df4294afde5f..d64655b70b59d45386799f8f550fb69d5a53fc4f 100644 (file)
@@ -197,6 +197,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
 #define MX51_ECSPI_CONFIG_SCLKPOL(cs)  (1 << ((cs) +  4))
 #define MX51_ECSPI_CONFIG_SBBCTRL(cs)  (1 << ((cs) +  8))
 #define MX51_ECSPI_CONFIG_SSBPOL(cs)   (1 << ((cs) + 12))
+#define MX51_ECSPI_CONFIG_SCLKCTL(cs)  (1 << ((cs) + 20))
 
 #define MX51_ECSPI_INT         0x10
 #define MX51_ECSPI_INT_TEEN            (1 <<  0)
@@ -287,9 +288,10 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
        if (config->mode & SPI_CPHA)
                cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs);
 
-       if (config->mode & SPI_CPOL)
+       if (config->mode & SPI_CPOL) {
                cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs);
-
+               cfg |= MX51_ECSPI_CONFIG_SCLKCTL(config->cs);
+       }
        if (config->mode & SPI_CS_HIGH)
                cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs);
 
index 4c63f772780adaf64e1412af167c791123eecb13..0a1e39e94d06a0292f4ebb60eb4f36b1a374ad6d 100644 (file)
@@ -494,7 +494,7 @@ free_master:
 
 static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
 {
-       struct spi_master *master = dev_get_drvdata(dev);
+       struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
        struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
 
        flush_workqueue(mps->workqueue);
@@ -503,6 +503,7 @@ static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
        free_irq(mps->irq, mps);
        if (mps->psc)
                iounmap(mps->psc);
+       spi_master_put(master);
 
        return 0;
 }
index 66047156d90dbc8d1bd9980492050629ad1f40d1..bd47d262d53faa252003a6e707104a6459e7c6ce 100644 (file)
@@ -481,7 +481,7 @@ static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op)
 
 static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op)
 {
-       struct spi_master *master = dev_get_drvdata(&op->dev);
+       struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev));
        struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master);
 
        flush_workqueue(mps->workqueue);
@@ -490,6 +490,7 @@ static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op)
        free_irq(mps->irq, mps);
        if (mps->psc)
                iounmap(mps->psc);
+       spi_master_put(master);
 
        return 0;
 }
index cb3a3830b0a53ef42d4f02d86bd16b6c5e3d7a16..045410650212cf95bf431e63fb5d4951447a4209 100644 (file)
@@ -454,7 +454,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
                                GFP_KERNEL);
                if (!ms->gpio_cs) {
                        rc = -ENOMEM;
-                       goto err_alloc;
+                       goto err_alloc_gpio;
                }
 
                for (i = 0; i < ms->gpio_cs_count; i++) {
@@ -514,12 +514,13 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
 
  err_register:
        dev_err(&ms->master->dev, "initialization failed\n");
-       spi_master_put(master);
  err_gpio:
        while (i-- > 0)
                gpio_free(ms->gpio_cs[i]);
 
        kfree(ms->gpio_cs);
+ err_alloc_gpio:
+       spi_master_put(master);
  err_alloc:
  err_init:
        iounmap(regs);
@@ -528,7 +529,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
 
 static int __devexit mpc52xx_spi_remove(struct platform_device *op)
 {
-       struct spi_master *master = dev_get_drvdata(&op->dev);
+       struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev));
        struct mpc52xx_spi *ms = spi_master_get_devdata(master);
        int i;
 
@@ -540,8 +541,8 @@ static int __devexit mpc52xx_spi_remove(struct platform_device *op)
 
        kfree(ms->gpio_cs);
        spi_unregister_master(master);
-       spi_master_put(master);
        iounmap(ms->regs);
+       spi_master_put(master);
 
        return 0;
 }
diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c
new file mode 100644 (file)
index 0000000..edf1360
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ * Freescale MXS SPI master driver
+ *
+ * Copyright 2012 DENX Software Engineering, GmbH.
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ *
+ * Rework and transition to new API by:
+ * Marek Vasut <marex@denx.de>
+ *
+ * Based on previous attempt by:
+ * Fabio Estevam <fabio.estevam@freescale.com>
+ *
+ * Based on code from U-Boot bootloader by:
+ * Marek Vasut <marex@denx.de>
+ *
+ * Based on spi-stmp.c, which is:
+ * Author: Dmitry Pervushin <dimka@embeddedalley.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/highmem.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/stmp_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mxs-spi.h>
+
+#define DRIVER_NAME            "mxs-spi"
+
+/* Use 10S timeout for very long transfers, it should suffice. */
+#define SSP_TIMEOUT            10000
+
+#define SG_MAXLEN              0xff00
+
+struct mxs_spi {
+       struct mxs_ssp          ssp;
+       struct completion       c;
+};
+
+static int mxs_spi_setup_transfer(struct spi_device *dev,
+                               struct spi_transfer *t)
+{
+       struct mxs_spi *spi = spi_master_get_devdata(dev->master);
+       struct mxs_ssp *ssp = &spi->ssp;
+       uint8_t bits_per_word;
+       uint32_t hz = 0;
+
+       bits_per_word = dev->bits_per_word;
+       if (t && t->bits_per_word)
+               bits_per_word = t->bits_per_word;
+
+       if (bits_per_word != 8) {
+               dev_err(&dev->dev, "%s, unsupported bits_per_word=%d\n",
+                                       __func__, bits_per_word);
+               return -EINVAL;
+       }
+
+       hz = dev->max_speed_hz;
+       if (t && t->speed_hz)
+               hz = min(hz, t->speed_hz);
+       if (hz == 0) {
+               dev_err(&dev->dev, "Cannot continue with zero clock\n");
+               return -EINVAL;
+       }
+
+       mxs_ssp_set_clk_rate(ssp, hz);
+
+       writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) |
+                    BF_SSP_CTRL1_WORD_LENGTH
+                    (BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) |
+                    ((dev->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) |
+                    ((dev->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0),
+                    ssp->base + HW_SSP_CTRL1(ssp));
+
+       writel(0x0, ssp->base + HW_SSP_CMD0);
+       writel(0x0, ssp->base + HW_SSP_CMD1);
+
+       return 0;
+}
+
+static int mxs_spi_setup(struct spi_device *dev)
+{
+       int err = 0;
+
+       if (!dev->bits_per_word)
+               dev->bits_per_word = 8;
+
+       if (dev->mode & ~(SPI_CPOL | SPI_CPHA))
+               return -EINVAL;
+
+       err = mxs_spi_setup_transfer(dev, NULL);
+       if (err) {
+               dev_err(&dev->dev,
+                       "Failed to setup transfer, error = %d\n", err);
+       }
+
+       return err;
+}
+
+static uint32_t mxs_spi_cs_to_reg(unsigned cs)
+{
+       uint32_t select = 0;
+
+       /*
+        * i.MX28 Datasheet: 17.10.1: HW_SSP_CTRL0
+        *
+        * The bits BM_SSP_CTRL0_WAIT_FOR_CMD and BM_SSP_CTRL0_WAIT_FOR_IRQ
+        * in HW_SSP_CTRL0 register do have multiple usage, please refer to
+        * the datasheet for further details. In SPI mode, they are used to
+        * toggle the chip-select lines (nCS pins).
+        */
+       if (cs & 1)
+               select |= BM_SSP_CTRL0_WAIT_FOR_CMD;
+       if (cs & 2)
+               select |= BM_SSP_CTRL0_WAIT_FOR_IRQ;
+
+       return select;
+}
+
+static void mxs_spi_set_cs(struct mxs_spi *spi, unsigned cs)
+{
+       const uint32_t mask =
+               BM_SSP_CTRL0_WAIT_FOR_CMD | BM_SSP_CTRL0_WAIT_FOR_IRQ;
+       uint32_t select;
+       struct mxs_ssp *ssp = &spi->ssp;
+
+       writel(mask, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
+       select = mxs_spi_cs_to_reg(cs);
+       writel(select, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
+}
+
+static inline void mxs_spi_enable(struct mxs_spi *spi)
+{
+       struct mxs_ssp *ssp = &spi->ssp;
+
+       writel(BM_SSP_CTRL0_LOCK_CS,
+               ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
+       writel(BM_SSP_CTRL0_IGNORE_CRC,
+               ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
+}
+
+static inline void mxs_spi_disable(struct mxs_spi *spi)
+{
+       struct mxs_ssp *ssp = &spi->ssp;
+
+       writel(BM_SSP_CTRL0_LOCK_CS,
+               ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
+       writel(BM_SSP_CTRL0_IGNORE_CRC,
+               ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
+}
+
+static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set)
+{
+       const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT);
+       struct mxs_ssp *ssp = &spi->ssp;
+       uint32_t reg;
+
+       do {
+               reg = readl_relaxed(ssp->base + offset);
+
+               if (!set)
+                       reg = ~reg;
+
+               reg &= mask;
+
+               if (reg == mask)
+                       return 0;
+       } while (time_before(jiffies, timeout));
+
+       return -ETIMEDOUT;
+}
+
+static void mxs_ssp_dma_irq_callback(void *param)
+{
+       struct mxs_spi *spi = param;
+       complete(&spi->c);
+}
+
+static irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id)
+{
+       struct mxs_ssp *ssp = dev_id;
+       dev_err(ssp->dev, "%s[%i] CTRL1=%08x STATUS=%08x\n",
+               __func__, __LINE__,
+               readl(ssp->base + HW_SSP_CTRL1(ssp)),
+               readl(ssp->base + HW_SSP_STATUS(ssp)));
+       return IRQ_HANDLED;
+}
+
+static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs,
+                           unsigned char *buf, int len,
+                           int *first, int *last, int write)
+{
+       struct mxs_ssp *ssp = &spi->ssp;
+       struct dma_async_tx_descriptor *desc = NULL;
+       const bool vmalloced_buf = is_vmalloc_addr(buf);
+       const int desc_len = vmalloced_buf ? PAGE_SIZE : SG_MAXLEN;
+       const int sgs = DIV_ROUND_UP(len, desc_len);
+       int sg_count;
+       int min, ret;
+       uint32_t ctrl0;
+       struct page *vm_page;
+       void *sg_buf;
+       struct {
+               uint32_t                pio[4];
+               struct scatterlist      sg;
+       } *dma_xfer;
+
+       if (!len)
+               return -EINVAL;
+
+       dma_xfer = kzalloc(sizeof(*dma_xfer) * sgs, GFP_KERNEL);
+       if (!dma_xfer)
+               return -ENOMEM;
+
+       INIT_COMPLETION(spi->c);
+
+       ctrl0 = readl(ssp->base + HW_SSP_CTRL0);
+       ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs);
+
+       if (*first)
+               ctrl0 |= BM_SSP_CTRL0_LOCK_CS;
+       if (!write)
+               ctrl0 |= BM_SSP_CTRL0_READ;
+
+       /* Queue the DMA data transfer. */
+       for (sg_count = 0; sg_count < sgs; sg_count++) {
+               min = min(len, desc_len);
+
+               /* Prepare the transfer descriptor. */
+               if ((sg_count + 1 == sgs) && *last)
+                       ctrl0 |= BM_SSP_CTRL0_IGNORE_CRC;
+
+               if (ssp->devid == IMX23_SSP)
+                       ctrl0 |= min;
+
+               dma_xfer[sg_count].pio[0] = ctrl0;
+               dma_xfer[sg_count].pio[3] = min;
+
+               if (vmalloced_buf) {
+                       vm_page = vmalloc_to_page(buf);
+                       if (!vm_page) {
+                               ret = -ENOMEM;
+                               goto err_vmalloc;
+                       }
+                       sg_buf = page_address(vm_page) +
+                               ((size_t)buf & ~PAGE_MASK);
+               } else {
+                       sg_buf = buf;
+               }
+
+               sg_init_one(&dma_xfer[sg_count].sg, sg_buf, min);
+               ret = dma_map_sg(ssp->dev, &dma_xfer[sg_count].sg, 1,
+                       write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+               len -= min;
+               buf += min;
+
+               /* Queue the PIO register write transfer. */
+               desc = dmaengine_prep_slave_sg(ssp->dmach,
+                               (struct scatterlist *)dma_xfer[sg_count].pio,
+                               (ssp->devid == IMX23_SSP) ? 1 : 4,
+                               DMA_TRANS_NONE,
+                               sg_count ? DMA_PREP_INTERRUPT : 0);
+               if (!desc) {
+                       dev_err(ssp->dev,
+                               "Failed to get PIO reg. write descriptor.\n");
+                       ret = -EINVAL;
+                       goto err_mapped;
+               }
+
+               desc = dmaengine_prep_slave_sg(ssp->dmach,
+                               &dma_xfer[sg_count].sg, 1,
+                               write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+                               DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+               if (!desc) {
+                       dev_err(ssp->dev,
+                               "Failed to get DMA data write descriptor.\n");
+                       ret = -EINVAL;
+                       goto err_mapped;
+               }
+       }
+
+       /*
+        * The last descriptor must have this callback,
+        * to finish the DMA transaction.
+        */
+       desc->callback = mxs_ssp_dma_irq_callback;
+       desc->callback_param = spi;
+
+       /* Start the transfer. */
+       dmaengine_submit(desc);
+       dma_async_issue_pending(ssp->dmach);
+
+       ret = wait_for_completion_timeout(&spi->c,
+                               msecs_to_jiffies(SSP_TIMEOUT));
+       if (!ret) {
+               dev_err(ssp->dev, "DMA transfer timeout\n");
+               ret = -ETIMEDOUT;
+               goto err_vmalloc;
+       }
+
+       ret = 0;
+
+err_vmalloc:
+       while (--sg_count >= 0) {
+err_mapped:
+               dma_unmap_sg(ssp->dev, &dma_xfer[sg_count].sg, 1,
+                       write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+       }
+
+       kfree(dma_xfer);
+
+       return ret;
+}
+
+static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs,
+                           unsigned char *buf, int len,
+                           int *first, int *last, int write)
+{
+       struct mxs_ssp *ssp = &spi->ssp;
+
+       if (*first)
+               mxs_spi_enable(spi);
+
+       mxs_spi_set_cs(spi, cs);
+
+       while (len--) {
+               if (*last && len == 0)
+                       mxs_spi_disable(spi);
+
+               if (ssp->devid == IMX23_SSP) {
+                       writel(BM_SSP_CTRL0_XFER_COUNT,
+                               ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
+                       writel(1,
+                               ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
+               } else {
+                       writel(1, ssp->base + HW_SSP_XFER_SIZE);
+               }
+
+               if (write)
+                       writel(BM_SSP_CTRL0_READ,
+                               ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
+               else
+                       writel(BM_SSP_CTRL0_READ,
+                               ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
+
+               writel(BM_SSP_CTRL0_RUN,
+                               ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
+
+               if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 1))
+                       return -ETIMEDOUT;
+
+               if (write)
+                       writel(*buf, ssp->base + HW_SSP_DATA(ssp));
+
+               writel(BM_SSP_CTRL0_DATA_XFER,
+                            ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
+
+               if (!write) {
+                       if (mxs_ssp_wait(spi, HW_SSP_STATUS(ssp),
+                                               BM_SSP_STATUS_FIFO_EMPTY, 0))
+                               return -ETIMEDOUT;
+
+                       *buf = (readl(ssp->base + HW_SSP_DATA(ssp)) & 0xff);
+               }
+
+               if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 0))
+                       return -ETIMEDOUT;
+
+               buf++;
+       }
+
+       if (len <= 0)
+               return 0;
+
+       return -ETIMEDOUT;
+}
+
+static int mxs_spi_transfer_one(struct spi_master *master,
+                               struct spi_message *m)
+{
+       struct mxs_spi *spi = spi_master_get_devdata(master);
+       struct mxs_ssp *ssp = &spi->ssp;
+       int first, last;
+       struct spi_transfer *t, *tmp_t;
+       int status = 0;
+       int cs;
+
+       first = last = 0;
+
+       cs = m->spi->chip_select;
+
+       list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) {
+
+               status = mxs_spi_setup_transfer(m->spi, t);
+               if (status)
+                       break;
+
+               if (&t->transfer_list == m->transfers.next)
+                       first = 1;
+               if (&t->transfer_list == m->transfers.prev)
+                       last = 1;
+               if ((t->rx_buf && t->tx_buf) || (t->rx_dma && t->tx_dma)) {
+                       dev_err(ssp->dev,
+                               "Cannot send and receive simultaneously\n");
+                       status = -EINVAL;
+                       break;
+               }
+
+               /*
+                * Small blocks can be transfered via PIO.
+                * Measured by empiric means:
+                *
+                * dd if=/dev/mtdblock0 of=/dev/null bs=1024k count=1
+                *
+                * DMA only: 2.164808 seconds, 473.0KB/s
+                * Combined: 1.676276 seconds, 610.9KB/s
+                */
+               if (t->len < 32) {
+                       writel(BM_SSP_CTRL1_DMA_ENABLE,
+                               ssp->base + HW_SSP_CTRL1(ssp) +
+                               STMP_OFFSET_REG_CLR);
+
+                       if (t->tx_buf)
+                               status = mxs_spi_txrx_pio(spi, cs,
+                                               (void *)t->tx_buf,
+                                               t->len, &first, &last, 1);
+                       if (t->rx_buf)
+                               status = mxs_spi_txrx_pio(spi, cs,
+                                               t->rx_buf, t->len,
+                                               &first, &last, 0);
+               } else {
+                       writel(BM_SSP_CTRL1_DMA_ENABLE,
+                               ssp->base + HW_SSP_CTRL1(ssp) +
+                               STMP_OFFSET_REG_SET);
+
+                       if (t->tx_buf)
+                               status = mxs_spi_txrx_dma(spi, cs,
+                                               (void *)t->tx_buf, t->len,
+                                               &first, &last, 1);
+                       if (t->rx_buf)
+                               status = mxs_spi_txrx_dma(spi, cs,
+                                               t->rx_buf, t->len,
+                                               &first, &last, 0);
+               }
+
+               if (status) {
+                       stmp_reset_block(ssp->base);
+                       break;
+               }
+
+               m->actual_length += t->len;
+               first = last = 0;
+       }
+
+       m->status = 0;
+       spi_finalize_current_message(master);
+
+       return status;
+}
+
+static bool mxs_ssp_dma_filter(struct dma_chan *chan, void *param)
+{
+       struct mxs_ssp *ssp = param;
+
+       if (!mxs_dma_is_apbh(chan))
+               return false;
+
+       if (chan->chan_id != ssp->dma_channel)
+               return false;
+
+       chan->private = &ssp->dma_data;
+
+       return true;
+}
+
+static const struct of_device_id mxs_spi_dt_ids[] = {
+       { .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, },
+       { .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_spi_dt_ids);
+
+static int __devinit mxs_spi_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *of_id =
+                       of_match_device(mxs_spi_dt_ids, &pdev->dev);
+       struct device_node *np = pdev->dev.of_node;
+       struct spi_master *master;
+       struct mxs_spi *spi;
+       struct mxs_ssp *ssp;
+       struct resource *iores, *dmares;
+       struct pinctrl *pinctrl;
+       struct clk *clk;
+       void __iomem *base;
+       int devid, dma_channel, clk_freq;
+       int ret = 0, irq_err, irq_dma;
+       dma_cap_mask_t mask;
+
+       /*
+        * Default clock speed for the SPI core. 160MHz seems to
+        * work reasonably well with most SPI flashes, so use this
+        * as a default. Override with "clock-frequency" DT prop.
+        */
+       const int clk_freq_default = 160000000;
+
+       iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       irq_err = platform_get_irq(pdev, 0);
+       irq_dma = platform_get_irq(pdev, 1);
+       if (!iores || irq_err < 0 || irq_dma < 0)
+               return -EINVAL;
+
+       base = devm_request_and_ioremap(&pdev->dev, iores);
+       if (!base)
+               return -EADDRNOTAVAIL;
+
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl))
+               return PTR_ERR(pinctrl);
+
+       clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       if (np) {
+               devid = (enum mxs_ssp_id) of_id->data;
+               /*
+                * TODO: This is a temporary solution and should be changed
+                * to use generic DMA binding later when the helpers get in.
+                */
+               ret = of_property_read_u32(np, "fsl,ssp-dma-channel",
+                                          &dma_channel);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "Failed to get DMA channel\n");
+                       return -EINVAL;
+               }
+
+               ret = of_property_read_u32(np, "clock-frequency",
+                                          &clk_freq);
+               if (ret)
+                       clk_freq = clk_freq_default;
+       } else {
+               dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+               if (!dmares)
+                       return -EINVAL;
+               devid = pdev->id_entry->driver_data;
+               dma_channel = dmares->start;
+               clk_freq = clk_freq_default;
+       }
+
+       master = spi_alloc_master(&pdev->dev, sizeof(*spi));
+       if (!master)
+               return -ENOMEM;
+
+       master->transfer_one_message = mxs_spi_transfer_one;
+       master->setup = mxs_spi_setup;
+       master->mode_bits = SPI_CPOL | SPI_CPHA;
+       master->num_chipselect = 3;
+       master->dev.of_node = np;
+       master->flags = SPI_MASTER_HALF_DUPLEX;
+
+       spi = spi_master_get_devdata(master);
+       ssp = &spi->ssp;
+       ssp->dev = &pdev->dev;
+       ssp->clk = clk;
+       ssp->base = base;
+       ssp->devid = devid;
+       ssp->dma_channel = dma_channel;
+
+       init_completion(&spi->c);
+
+       ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0,
+                              DRIVER_NAME, ssp);
+       if (ret)
+               goto out_master_free;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       ssp->dma_data.chan_irq = irq_dma;
+       ssp->dmach = dma_request_channel(mask, mxs_ssp_dma_filter, ssp);
+       if (!ssp->dmach) {
+               dev_err(ssp->dev, "Failed to request DMA\n");
+               goto out_master_free;
+       }
+
+       clk_prepare_enable(ssp->clk);
+       clk_set_rate(ssp->clk, clk_freq);
+       ssp->clk_rate = clk_get_rate(ssp->clk) / 1000;
+
+       stmp_reset_block(ssp->base);
+
+       platform_set_drvdata(pdev, master);
+
+       ret = spi_register_master(master);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret);
+               goto out_free_dma;
+       }
+
+       return 0;
+
+out_free_dma:
+       dma_release_channel(ssp->dmach);
+       clk_disable_unprepare(ssp->clk);
+out_master_free:
+       spi_master_put(master);
+       return ret;
+}
+
+static int __devexit mxs_spi_remove(struct platform_device *pdev)
+{
+       struct spi_master *master;
+       struct mxs_spi *spi;
+       struct mxs_ssp *ssp;
+
+       master = spi_master_get(platform_get_drvdata(pdev));
+       spi = spi_master_get_devdata(master);
+       ssp = &spi->ssp;
+
+       spi_unregister_master(master);
+
+       dma_release_channel(ssp->dmach);
+
+       clk_disable_unprepare(ssp->clk);
+
+       spi_master_put(master);
+
+       return 0;
+}
+
+static struct platform_driver mxs_spi_driver = {
+       .probe  = mxs_spi_probe,
+       .remove = __devexit_p(mxs_spi_remove),
+       .driver = {
+               .name   = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = mxs_spi_dt_ids,
+       },
+};
+
+module_platform_driver(mxs_spi_driver);
+
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_DESCRIPTION("MXS SPI master driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mxs-spi");
index b2fb141da37565de69139fee9743b153fb463699..06684d995d3bff0c5542503bbcdfeec1ac4dca95 100644 (file)
@@ -38,6 +38,8 @@
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/err.h>
 
 #include <linux/spi/spi.h>
 
@@ -140,13 +142,6 @@ struct omap2_mcspi_cs {
        u32                     chconf0;
 };
 
-#define MOD_REG_BIT(val, mask, set) do { \
-       if (set) \
-               val |= mask; \
-       else \
-               val &= ~mask; \
-} while (0)
-
 static inline void mcspi_write_reg(struct spi_master *master,
                int idx, u32 val)
 {
@@ -205,7 +200,11 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi,
        else
                rw = OMAP2_MCSPI_CHCONF_DMAW;
 
-       MOD_REG_BIT(l, rw, enable);
+       if (enable)
+               l |= rw;
+       else
+               l &= ~rw;
+
        mcspi_write_chconf0(spi, l);
 }
 
@@ -224,7 +223,11 @@ static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active)
        u32 l;
 
        l = mcspi_cached_chconf0(spi);
-       MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active);
+       if (cs_active)
+               l |= OMAP2_MCSPI_CHCONF_FORCE;
+       else
+               l &= ~OMAP2_MCSPI_CHCONF_FORCE;
+
        mcspi_write_chconf0(spi, l);
 }
 
@@ -239,9 +242,8 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master)
         * to single-channel master mode
         */
        l = mcspi_read_reg(master, OMAP2_MCSPI_MODULCTRL);
-       MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_STEST, 0);
-       MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0);
-       MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1);
+       l &= ~(OMAP2_MCSPI_MODULCTRL_STEST | OMAP2_MCSPI_MODULCTRL_MS);
+       l |= OMAP2_MCSPI_MODULCTRL_SINGLE;
        mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l);
 
        ctx->modulctrl = l;
@@ -260,16 +262,6 @@ static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi)
        list_for_each_entry(cs, &ctx->cs, node)
                __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
 }
-static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi)
-{
-       pm_runtime_mark_last_busy(mcspi->dev);
-       pm_runtime_put_autosuspend(mcspi->dev);
-}
-
-static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi)
-{
-       return pm_runtime_get_sync(mcspi->dev);
-}
 
 static int omap2_prepare_transfer(struct spi_master *master)
 {
@@ -325,49 +317,27 @@ static void omap2_mcspi_tx_callback(void *data)
        omap2_mcspi_set_dma_req(spi, 0, 0);
 }
 
-static unsigned
-omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
+static void omap2_mcspi_tx_dma(struct spi_device *spi,
+                               struct spi_transfer *xfer,
+                               struct dma_slave_config cfg)
 {
        struct omap2_mcspi      *mcspi;
-       struct omap2_mcspi_cs   *cs = spi->controller_state;
        struct omap2_mcspi_dma  *mcspi_dma;
        unsigned int            count;
-       int                     word_len, element_count;
-       int                     elements = 0;
-       u32                     l;
        u8                      * rx;
        const u8                * tx;
        void __iomem            *chstat_reg;
-       struct dma_slave_config cfg;
-       enum dma_slave_buswidth width;
-       unsigned es;
+       struct omap2_mcspi_cs   *cs = spi->controller_state;
 
        mcspi = spi_master_get_devdata(spi->master);
        mcspi_dma = &mcspi->dma_channels[spi->chip_select];
-       l = mcspi_cached_chconf0(spi);
+       count = xfer->len;
 
+       rx = xfer->rx_buf;
+       tx = xfer->tx_buf;
        chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
 
-       if (cs->word_len <= 8) {
-               width = DMA_SLAVE_BUSWIDTH_1_BYTE;
-               es = 1;
-       } else if (cs->word_len <= 16) {
-               width = DMA_SLAVE_BUSWIDTH_2_BYTES;
-               es = 2;
-       } else {
-               width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-               es = 4;
-       }
-
-       memset(&cfg, 0, sizeof(cfg));
-       cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
-       cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
-       cfg.src_addr_width = width;
-       cfg.dst_addr_width = width;
-       cfg.src_maxburst = 1;
-       cfg.dst_maxburst = 1;
-
-       if (xfer->tx_buf && mcspi_dma->dma_tx) {
+       if (mcspi_dma->dma_tx) {
                struct dma_async_tx_descriptor *tx;
                struct scatterlist sg;
 
@@ -378,7 +348,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
                sg_dma_len(&sg) = xfer->len;
 
                tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1,
-                       DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+               DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
                if (tx) {
                        tx->callback = omap2_mcspi_tx_callback;
                        tx->callback_param = spi;
@@ -387,8 +357,50 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
                        /* FIXME: fall back to PIO? */
                }
        }
+       dma_async_issue_pending(mcspi_dma->dma_tx);
+       omap2_mcspi_set_dma_req(spi, 0, 1);
+
+       wait_for_completion(&mcspi_dma->dma_tx_completion);
+       dma_unmap_single(mcspi->dev, xfer->tx_dma, count,
+                        DMA_TO_DEVICE);
+
+       /* for TX_ONLY mode, be sure all words have shifted out */
+       if (rx == NULL) {
+               if (mcspi_wait_for_reg_bit(chstat_reg,
+                                       OMAP2_MCSPI_CHSTAT_TXS) < 0)
+                       dev_err(&spi->dev, "TXS timed out\n");
+               else if (mcspi_wait_for_reg_bit(chstat_reg,
+                                       OMAP2_MCSPI_CHSTAT_EOT) < 0)
+                       dev_err(&spi->dev, "EOT timed out\n");
+       }
+}
+
+static unsigned
+omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
+                               struct dma_slave_config cfg,
+                               unsigned es)
+{
+       struct omap2_mcspi      *mcspi;
+       struct omap2_mcspi_dma  *mcspi_dma;
+       unsigned int            count;
+       u32                     l;
+       int                     elements = 0;
+       int                     word_len, element_count;
+       struct omap2_mcspi_cs   *cs = spi->controller_state;
+       mcspi = spi_master_get_devdata(spi->master);
+       mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+       count = xfer->len;
+       word_len = cs->word_len;
+       l = mcspi_cached_chconf0(spi);
 
-       if (xfer->rx_buf && mcspi_dma->dma_rx) {
+       if (word_len <= 8)
+               element_count = count;
+       else if (word_len <= 16)
+               element_count = count >> 1;
+       else /* word_len <= 32 */
+               element_count = count >> 2;
+
+       if (mcspi_dma->dma_rx) {
                struct dma_async_tx_descriptor *tx;
                struct scatterlist sg;
                size_t len = xfer->len - es;
@@ -403,108 +415,120 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
                sg_dma_len(&sg) = len;
 
                tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
-                       DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+                               DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT |
+                               DMA_CTRL_ACK);
                if (tx) {
                        tx->callback = omap2_mcspi_rx_callback;
                        tx->callback_param = spi;
                        dmaengine_submit(tx);
                } else {
-                       /* FIXME: fall back to PIO? */
-               }
-       }
-
-       count = xfer->len;
-       word_len = cs->word_len;
-
-       rx = xfer->rx_buf;
-       tx = xfer->tx_buf;
-
-       if (word_len <= 8) {
-               element_count = count;
-       } else if (word_len <= 16) {
-               element_count = count >> 1;
-       } else /* word_len <= 32 */ {
-               element_count = count >> 2;
-       }
-
-       if (tx != NULL) {
-               dma_async_issue_pending(mcspi_dma->dma_tx);
-               omap2_mcspi_set_dma_req(spi, 0, 1);
-       }
-
-       if (rx != NULL) {
-               dma_async_issue_pending(mcspi_dma->dma_rx);
-               omap2_mcspi_set_dma_req(spi, 1, 1);
-       }
-
-       if (tx != NULL) {
-               wait_for_completion(&mcspi_dma->dma_tx_completion);
-               dma_unmap_single(mcspi->dev, xfer->tx_dma, count,
-                                DMA_TO_DEVICE);
-
-               /* for TX_ONLY mode, be sure all words have shifted out */
-               if (rx == NULL) {
-                       if (mcspi_wait_for_reg_bit(chstat_reg,
-                                               OMAP2_MCSPI_CHSTAT_TXS) < 0)
-                               dev_err(&spi->dev, "TXS timed out\n");
-                       else if (mcspi_wait_for_reg_bit(chstat_reg,
-                                               OMAP2_MCSPI_CHSTAT_EOT) < 0)
-                               dev_err(&spi->dev, "EOT timed out\n");
+                               /* FIXME: fall back to PIO? */
                }
        }
 
-       if (rx != NULL) {
-               wait_for_completion(&mcspi_dma->dma_rx_completion);
-               dma_unmap_single(mcspi->dev, xfer->rx_dma, count,
-                                DMA_FROM_DEVICE);
-               omap2_mcspi_set_enable(spi, 0);
+       dma_async_issue_pending(mcspi_dma->dma_rx);
+       omap2_mcspi_set_dma_req(spi, 1, 1);
 
-               elements = element_count - 1;
+       wait_for_completion(&mcspi_dma->dma_rx_completion);
+       dma_unmap_single(mcspi->dev, xfer->rx_dma, count,
+                        DMA_FROM_DEVICE);
+       omap2_mcspi_set_enable(spi, 0);
 
-               if (l & OMAP2_MCSPI_CHCONF_TURBO) {
-                       elements--;
+       elements = element_count - 1;
 
-                       if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
-                                  & OMAP2_MCSPI_CHSTAT_RXS)) {
-                               u32 w;
-
-                               w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
-                               if (word_len <= 8)
-                                       ((u8 *)xfer->rx_buf)[elements++] = w;
-                               else if (word_len <= 16)
-                                       ((u16 *)xfer->rx_buf)[elements++] = w;
-                               else /* word_len <= 32 */
-                                       ((u32 *)xfer->rx_buf)[elements++] = w;
-                       } else {
-                               dev_err(&spi->dev,
-                                       "DMA RX penultimate word empty");
-                               count -= (word_len <= 8)  ? 2 :
-                                       (word_len <= 16) ? 4 :
-                                       /* word_len <= 32 */ 8;
-                               omap2_mcspi_set_enable(spi, 1);
-                               return count;
-                       }
-               }
+       if (l & OMAP2_MCSPI_CHCONF_TURBO) {
+               elements--;
 
                if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
-                               & OMAP2_MCSPI_CHSTAT_RXS)) {
+                                  & OMAP2_MCSPI_CHSTAT_RXS)) {
                        u32 w;
 
                        w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
                        if (word_len <= 8)
-                               ((u8 *)xfer->rx_buf)[elements] = w;
+                               ((u8 *)xfer->rx_buf)[elements++] = w;
                        else if (word_len <= 16)
-                               ((u16 *)xfer->rx_buf)[elements] = w;
+                               ((u16 *)xfer->rx_buf)[elements++] = w;
                        else /* word_len <= 32 */
-                               ((u32 *)xfer->rx_buf)[elements] = w;
+                               ((u32 *)xfer->rx_buf)[elements++] = w;
                } else {
-                       dev_err(&spi->dev, "DMA RX last word empty");
-                       count -= (word_len <= 8)  ? 1 :
-                                (word_len <= 16) ? 2 :
-                              /* word_len <= 32 */ 4;
+                       dev_err(&spi->dev, "DMA RX penultimate word empty");
+                       count -= (word_len <= 8)  ? 2 :
+                               (word_len <= 16) ? 4 :
+                               /* word_len <= 32 */ 8;
+                       omap2_mcspi_set_enable(spi, 1);
+                       return count;
                }
-               omap2_mcspi_set_enable(spi, 1);
        }
+       if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
+                               & OMAP2_MCSPI_CHSTAT_RXS)) {
+               u32 w;
+
+               w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
+               if (word_len <= 8)
+                       ((u8 *)xfer->rx_buf)[elements] = w;
+               else if (word_len <= 16)
+                       ((u16 *)xfer->rx_buf)[elements] = w;
+               else /* word_len <= 32 */
+                       ((u32 *)xfer->rx_buf)[elements] = w;
+       } else {
+               dev_err(&spi->dev, "DMA RX last word empty");
+               count -= (word_len <= 8)  ? 1 :
+                        (word_len <= 16) ? 2 :
+                      /* word_len <= 32 */ 4;
+       }
+       omap2_mcspi_set_enable(spi, 1);
+       return count;
+}
+
+static unsigned
+omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
+{
+       struct omap2_mcspi      *mcspi;
+       struct omap2_mcspi_cs   *cs = spi->controller_state;
+       struct omap2_mcspi_dma  *mcspi_dma;
+       unsigned int            count;
+       u32                     l;
+       u8                      *rx;
+       const u8                *tx;
+       struct dma_slave_config cfg;
+       enum dma_slave_buswidth width;
+       unsigned es;
+
+       mcspi = spi_master_get_devdata(spi->master);
+       mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+       l = mcspi_cached_chconf0(spi);
+
+
+       if (cs->word_len <= 8) {
+               width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+               es = 1;
+       } else if (cs->word_len <= 16) {
+               width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+               es = 2;
+       } else {
+               width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+               es = 4;
+       }
+
+       memset(&cfg, 0, sizeof(cfg));
+       cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
+       cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
+       cfg.src_addr_width = width;
+       cfg.dst_addr_width = width;
+       cfg.src_maxburst = 1;
+       cfg.dst_maxburst = 1;
+
+       rx = xfer->rx_buf;
+       tx = xfer->tx_buf;
+
+       count = xfer->len;
+
+       if (tx != NULL)
+               omap2_mcspi_tx_dma(spi, xfer, cfg);
+
+       if (rx != NULL)
+               return omap2_mcspi_rx_dma(spi, xfer, cfg, es);
+
        return count;
 }
 
@@ -848,12 +872,13 @@ static int omap2_mcspi_setup(struct spi_device *spi)
                        return ret;
        }
 
-       ret = omap2_mcspi_enable_clocks(mcspi);
+       ret = pm_runtime_get_sync(mcspi->dev);
        if (ret < 0)
                return ret;
 
        ret = omap2_mcspi_setup_transfer(spi, NULL);
-       omap2_mcspi_disable_clocks(mcspi);
+       pm_runtime_mark_last_busy(mcspi->dev);
+       pm_runtime_put_autosuspend(mcspi->dev);
 
        return ret;
 }
@@ -1067,7 +1092,7 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
        struct omap2_mcspi_regs *ctx = &mcspi->ctx;
        int                     ret = 0;
 
-       ret = omap2_mcspi_enable_clocks(mcspi);
+       ret = pm_runtime_get_sync(mcspi->dev);
        if (ret < 0)
                return ret;
 
@@ -1076,7 +1101,8 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
        ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN;
 
        omap2_mcspi_set_master_mode(master);
-       omap2_mcspi_disable_clocks(mcspi);
+       pm_runtime_mark_last_busy(mcspi->dev);
+       pm_runtime_put_autosuspend(mcspi->dev);
        return 0;
 }
 
@@ -1124,6 +1150,7 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
        static int              bus_num = 1;
        struct device_node      *node = pdev->dev.of_node;
        const struct of_device_id *match;
+       struct pinctrl *pinctrl;
 
        master = spi_alloc_master(&pdev->dev, sizeof *mcspi);
        if (master == NULL) {
@@ -1219,6 +1246,11 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
        if (status < 0)
                goto dma_chnl_free;
 
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl))
+               dev_warn(&pdev->dev,
+                       "pins are not configured from the driver\n");
+
        pm_runtime_use_autosuspend(&pdev->dev);
        pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
        pm_runtime_enable(&pdev->dev);
@@ -1238,7 +1270,6 @@ dma_chnl_free:
        kfree(mcspi->dma_channels);
 free_master:
        spi_master_put(master);
-       platform_set_drvdata(pdev, NULL);
        return status;
 }
 
@@ -1252,12 +1283,11 @@ static int __devexit omap2_mcspi_remove(struct platform_device *pdev)
        mcspi = spi_master_get_devdata(master);
        dma_channels = mcspi->dma_channels;
 
-       omap2_mcspi_disable_clocks(mcspi);
+       pm_runtime_put_sync(mcspi->dev);
        pm_runtime_disable(&pdev->dev);
 
        spi_unregister_master(master);
        kfree(dma_channels);
-       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
@@ -1278,20 +1308,21 @@ static int omap2_mcspi_resume(struct device *dev)
        struct omap2_mcspi_regs *ctx = &mcspi->ctx;
        struct omap2_mcspi_cs   *cs;
 
-       omap2_mcspi_enable_clocks(mcspi);
+       pm_runtime_get_sync(mcspi->dev);
        list_for_each_entry(cs, &ctx->cs, node) {
                if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) {
                        /*
                         * We need to toggle CS state for OMAP take this
                         * change in account.
                         */
-                       MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 1);
+                       cs->chconf0 |= OMAP2_MCSPI_CHCONF_FORCE;
                        __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
-                       MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 0);
+                       cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE;
                        __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
                }
        }
-       omap2_mcspi_disable_clocks(mcspi);
+       pm_runtime_mark_last_busy(mcspi->dev);
+       pm_runtime_put_autosuspend(mcspi->dev);
        return 0;
 }
 #else
index 9b0caddce5037b5b347615969ae9c6bd22eaf8f7..b17c09cf0a054c271cbd2f9e4c0841a0364d2706 100644 (file)
 #define ORION_SPI_CLK_PRESCALE_MASK    0x1F
 
 struct orion_spi {
-       struct work_struct      work;
-
-       /* Lock access to transfer list.        */
-       spinlock_t              lock;
-
-       struct list_head        msg_queue;
        struct spi_master       *master;
        void __iomem            *base;
        unsigned int            max_speed;
@@ -49,8 +43,6 @@ struct orion_spi {
        struct clk              *clk;
 };
 
-static struct workqueue_struct *orion_spi_wq;
-
 static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
 {
        return orion_spi->base + reg;
@@ -277,73 +269,78 @@ out:
 }
 
 
-static void orion_spi_work(struct work_struct *work)
+static int orion_spi_transfer_one_message(struct spi_master *master,
+                                          struct spi_message *m)
 {
-       struct orion_spi *orion_spi =
-               container_of(work, struct orion_spi, work);
-
-       spin_lock_irq(&orion_spi->lock);
-       while (!list_empty(&orion_spi->msg_queue)) {
-               struct spi_message *m;
-               struct spi_device *spi;
-               struct spi_transfer *t = NULL;
-               int par_override = 0;
-               int status = 0;
-               int cs_active = 0;
-
-               m = container_of(orion_spi->msg_queue.next, struct spi_message,
-                                queue);
+       struct orion_spi *orion_spi = spi_master_get_devdata(master);
+       struct spi_device *spi = m->spi;
+       struct spi_transfer *t = NULL;
+       int par_override = 0;
+       int status = 0;
+       int cs_active = 0;
 
-               list_del_init(&m->queue);
-               spin_unlock_irq(&orion_spi->lock);
+       /* Load defaults */
+       status = orion_spi_setup_transfer(spi, NULL);
 
-               spi = m->spi;
+       if (status < 0)
+               goto msg_done;
 
-               /* Load defaults */
-               status = orion_spi_setup_transfer(spi, NULL);
+       list_for_each_entry(t, &m->transfers, transfer_list) {
+               /* make sure buffer length is even when working in 16
+                * bit mode*/
+               if ((t->bits_per_word == 16) && (t->len & 1)) {
+                       dev_err(&spi->dev,
+                               "message rejected : "
+                               "odd data length %d while in 16 bit mode\n",
+                               t->len);
+                       status = -EIO;
+                       goto msg_done;
+               }
 
-               if (status < 0)
+               if (t->speed_hz && t->speed_hz < orion_spi->min_speed) {
+                       dev_err(&spi->dev,
+                               "message rejected : "
+                               "device min speed (%d Hz) exceeds "
+                               "required transfer speed (%d Hz)\n",
+                               orion_spi->min_speed, t->speed_hz);
+                       status = -EIO;
                        goto msg_done;
+               }
 
-               list_for_each_entry(t, &m->transfers, transfer_list) {
-                       if (par_override || t->speed_hz || t->bits_per_word) {
-                               par_override = 1;
-                               status = orion_spi_setup_transfer(spi, t);
-                               if (status < 0)
-                                       break;
-                               if (!t->speed_hz && !t->bits_per_word)
-                                       par_override = 0;
-                       }
-
-                       if (!cs_active) {
-                               orion_spi_set_cs(orion_spi, 1);
-                               cs_active = 1;
-                       }
-
-                       if (t->len)
-                               m->actual_length +=
-                                       orion_spi_write_read(spi, t);
-
-                       if (t->delay_usecs)
-                               udelay(t->delay_usecs);
-
-                       if (t->cs_change) {
-                               orion_spi_set_cs(orion_spi, 0);
-                               cs_active = 0;
-                       }
+               if (par_override || t->speed_hz || t->bits_per_word) {
+                       par_override = 1;
+                       status = orion_spi_setup_transfer(spi, t);
+                       if (status < 0)
+                               break;
+                       if (!t->speed_hz && !t->bits_per_word)
+                               par_override = 0;
                }
 
-msg_done:
-               if (cs_active)
-                       orion_spi_set_cs(orion_spi, 0);
+               if (!cs_active) {
+                       orion_spi_set_cs(orion_spi, 1);
+                       cs_active = 1;
+               }
 
-               m->status = status;
-               m->complete(m->context);
+               if (t->len)
+                       m->actual_length += orion_spi_write_read(spi, t);
 
-               spin_lock_irq(&orion_spi->lock);
+               if (t->delay_usecs)
+                       udelay(t->delay_usecs);
+
+               if (t->cs_change) {
+                       orion_spi_set_cs(orion_spi, 0);
+                       cs_active = 0;
+               }
        }
 
-       spin_unlock_irq(&orion_spi->lock);
+msg_done:
+       if (cs_active)
+               orion_spi_set_cs(orion_spi, 0);
+
+       m->status = status;
+       spi_finalize_current_message(master);
+
+       return 0;
 }
 
 static int __init orion_spi_reset(struct orion_spi *orion_spi)
@@ -376,75 +373,6 @@ static int orion_spi_setup(struct spi_device *spi)
        return 0;
 }
 
-static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m)
-{
-       struct orion_spi *orion_spi;
-       struct spi_transfer *t = NULL;
-       unsigned long flags;
-
-       m->actual_length = 0;
-       m->status = 0;
-
-       /* reject invalid messages and transfers */
-       if (list_empty(&m->transfers) || !m->complete)
-               return -EINVAL;
-
-       orion_spi = spi_master_get_devdata(spi->master);
-
-       list_for_each_entry(t, &m->transfers, transfer_list) {
-               unsigned int bits_per_word = spi->bits_per_word;
-
-               if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
-                       dev_err(&spi->dev,
-                               "message rejected : "
-                               "invalid transfer data buffers\n");
-                       goto msg_rejected;
-               }
-
-               if (t->bits_per_word)
-                       bits_per_word = t->bits_per_word;
-
-               if ((bits_per_word != 8) && (bits_per_word != 16)) {
-                       dev_err(&spi->dev,
-                               "message rejected : "
-                               "invalid transfer bits_per_word (%d bits)\n",
-                               bits_per_word);
-                       goto msg_rejected;
-               }
-               /*make sure buffer length is even when working in 16 bit mode*/
-               if ((t->bits_per_word == 16) && (t->len & 1)) {
-                       dev_err(&spi->dev,
-                               "message rejected : "
-                               "odd data length (%d) while in 16 bit mode\n",
-                               t->len);
-                       goto msg_rejected;
-               }
-
-               if (t->speed_hz && t->speed_hz < orion_spi->min_speed) {
-                       dev_err(&spi->dev,
-                               "message rejected : "
-                               "device min speed (%d Hz) exceeds "
-                               "required transfer speed (%d Hz)\n",
-                               orion_spi->min_speed, t->speed_hz);
-                       goto msg_rejected;
-               }
-       }
-
-
-       spin_lock_irqsave(&orion_spi->lock, flags);
-       list_add_tail(&m->queue, &orion_spi->msg_queue);
-       queue_work(orion_spi_wq, &orion_spi->work);
-       spin_unlock_irqrestore(&orion_spi->lock, flags);
-
-       return 0;
-msg_rejected:
-       /* Message rejected and not queued */
-       m->status = -EINVAL;
-       if (m->complete)
-               m->complete(m->context);
-       return -EINVAL;
-}
-
 static int __init orion_spi_probe(struct platform_device *pdev)
 {
        struct spi_master *master;
@@ -474,7 +402,7 @@ static int __init orion_spi_probe(struct platform_device *pdev)
        master->mode_bits = 0;
 
        master->setup = orion_spi_setup;
-       master->transfer = orion_spi_transfer;
+       master->transfer_one_message = orion_spi_transfer_one_message;
        master->num_chipselect = ORION_NUM_CHIPSELECTS;
 
        dev_set_drvdata(&pdev->dev, master);
@@ -507,11 +435,6 @@ static int __init orion_spi_probe(struct platform_device *pdev)
        }
        spi->base = ioremap(r->start, SZ_1K);
 
-       INIT_WORK(&spi->work, orion_spi_work);
-
-       spin_lock_init(&spi->lock);
-       INIT_LIST_HEAD(&spi->msg_queue);
-
        if (orion_spi_reset(spi) < 0)
                goto out_rel_mem;
 
@@ -536,14 +459,12 @@ out:
 static int __exit orion_spi_remove(struct platform_device *pdev)
 {
        struct spi_master *master;
-       struct orion_spi *spi;
        struct resource *r;
+       struct orion_spi *spi;
 
        master = dev_get_drvdata(&pdev->dev);
        spi = spi_master_get_devdata(master);
 
-       cancel_work_sync(&spi->work);
-
        clk_disable_unprepare(spi->clk);
        clk_put(spi->clk);
 
@@ -574,21 +495,13 @@ static struct platform_driver orion_spi_driver = {
 
 static int __init orion_spi_init(void)
 {
-       orion_spi_wq = create_singlethread_workqueue(
-                               orion_spi_driver.driver.name);
-       if (orion_spi_wq == NULL)
-               return -ENOMEM;
-
        return platform_driver_probe(&orion_spi_driver, orion_spi_probe);
 }
 module_init(orion_spi_init);
 
 static void __exit orion_spi_exit(void)
 {
-       flush_workqueue(orion_spi_wq);
        platform_driver_unregister(&orion_spi_driver);
-
-       destroy_workqueue(orion_spi_wq);
 }
 module_exit(orion_spi_exit);
 
index 6abbe23c39b4751a3da3fb4d940912771c0fbfbe..919464102d33afb2d493ace07c81c80403d1d51d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * A driver for the ARM PL022 PrimeCell SSP/SPI bus master.
  *
- * Copyright (C) 2008-2009 ST-Ericsson AB
+ * Copyright (C) 2008-2012 ST-Ericsson AB
  * Copyright (C) 2006 STMicroelectronics Pvt. Ltd.
  *
  * Author: Linus Walleij <linus.walleij@stericsson.com>
@@ -40,6 +40,9 @@
 #include <linux/dma-mapping.h>
 #include <linux/scatterlist.h>
 #include <linux/pm_runtime.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
 
 /*
  * This macro is used to define some register default values.
@@ -356,6 +359,8 @@ struct vendor_data {
  * @sgt_rx: scattertable for the RX transfer
  * @sgt_tx: scattertable for the TX transfer
  * @dummypage: a dummy page used for driving data on the bus with DMA
+ * @cur_cs: current chip select (gpio)
+ * @chipselects: list of chipselects (gpios)
  */
 struct pl022 {
        struct amba_device              *adev;
@@ -363,6 +368,10 @@ struct pl022 {
        resource_size_t                 phybase;
        void __iomem                    *virtbase;
        struct clk                      *clk;
+       /* Two optional pin states - default & sleep */
+       struct pinctrl                  *pinctrl;
+       struct pinctrl_state            *pins_default;
+       struct pinctrl_state            *pins_sleep;
        struct spi_master               *master;
        struct pl022_ssp_controller     *master_info;
        /* Message per-transfer pump */
@@ -389,6 +398,8 @@ struct pl022 {
        char                            *dummypage;
        bool                            dma_running;
 #endif
+       int cur_cs;
+       int *chipselects;
 };
 
 /**
@@ -433,6 +444,14 @@ static void null_cs_control(u32 command)
        pr_debug("pl022: dummy chip select control, CS=0x%x\n", command);
 }
 
+static void pl022_cs_control(struct pl022 *pl022, u32 command)
+{
+       if (gpio_is_valid(pl022->cur_cs))
+               gpio_set_value(pl022->cur_cs, command);
+       else
+               pl022->cur_chip->cs_control(command);
+}
+
 /**
  * giveback - current spi_message is over, schedule next message and call
  * callback of this message. Assumes that caller already
@@ -479,7 +498,7 @@ static void giveback(struct pl022 *pl022)
                if (next_msg && next_msg->spi != pl022->cur_msg->spi)
                        next_msg = NULL;
                if (!next_msg || pl022->cur_msg->state == STATE_ERROR)
-                       pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
+                       pl022_cs_control(pl022, SSP_CHIP_DESELECT);
                else
                        pl022->next_msg_cs_active = true;
 
@@ -818,8 +837,7 @@ static void dma_callback(void *data)
        /* Update total bytes transferred */
        msg->actual_length += pl022->cur_transfer->len;
        if (pl022->cur_transfer->cs_change)
-               pl022->cur_chip->
-                       cs_control(SSP_CHIP_DESELECT);
+               pl022_cs_control(pl022, SSP_CHIP_DESELECT);
 
        /* Move to next transfer */
        msg->state = next_transfer(pl022);
@@ -1252,8 +1270,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
                /* Update total bytes transferred */
                msg->actual_length += pl022->cur_transfer->len;
                if (pl022->cur_transfer->cs_change)
-                       pl022->cur_chip->
-                               cs_control(SSP_CHIP_DESELECT);
+                       pl022_cs_control(pl022, SSP_CHIP_DESELECT);
                /* Move to next transfer */
                msg->state = next_transfer(pl022);
                tasklet_schedule(&pl022->pump_transfers);
@@ -1338,7 +1355,7 @@ static void pump_transfers(unsigned long data)
 
                /* Reselect chip select only if cs_change was requested */
                if (previous->cs_change)
-                       pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+                       pl022_cs_control(pl022, SSP_CHIP_SELECT);
        } else {
                /* STATE_START */
                message->state = STATE_RUNNING;
@@ -1377,7 +1394,7 @@ static void do_interrupt_dma_transfer(struct pl022 *pl022)
 
        /* Enable target chip, if not already active */
        if (!pl022->next_msg_cs_active)
-               pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+               pl022_cs_control(pl022, SSP_CHIP_SELECT);
 
        if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
                /* Error path */
@@ -1429,12 +1446,12 @@ static void do_polling_transfer(struct pl022 *pl022)
                        if (previous->delay_usecs)
                                udelay(previous->delay_usecs);
                        if (previous->cs_change)
-                               pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+                               pl022_cs_control(pl022, SSP_CHIP_SELECT);
                } else {
                        /* STATE_START */
                        message->state = STATE_RUNNING;
                        if (!pl022->next_msg_cs_active)
-                               pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
+                               pl022_cs_control(pl022, SSP_CHIP_SELECT);
                }
 
                /* Configuration Changing Per Transfer */
@@ -1466,7 +1483,7 @@ static void do_polling_transfer(struct pl022 *pl022)
                /* Update total byte transferred */
                message->actual_length += pl022->cur_transfer->len;
                if (pl022->cur_transfer->cs_change)
-                       pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
+                       pl022_cs_control(pl022, SSP_CHIP_DESELECT);
                /* Move to next transfer */
                message->state = next_transfer(pl022);
        }
@@ -1495,6 +1512,7 @@ static int pl022_transfer_one_message(struct spi_master *master,
 
        /* Setup the SPI using the per chip configuration */
        pl022->cur_chip = spi_get_ctldata(msg->spi);
+       pl022->cur_cs = pl022->chipselects[msg->spi->chip_select];
 
        restore_state(pl022);
        flush(pl022);
@@ -1766,12 +1784,14 @@ static const struct pl022_config_chip pl022_default_chip_info = {
 static int pl022_setup(struct spi_device *spi)
 {
        struct pl022_config_chip const *chip_info;
+       struct pl022_config_chip chip_info_dt;
        struct chip_data *chip;
        struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0};
        int status = 0;
        struct pl022 *pl022 = spi_master_get_devdata(spi->master);
        unsigned int bits = spi->bits_per_word;
        u32 tmp;
+       struct device_node *np = spi->dev.of_node;
 
        if (!spi->max_speed_hz)
                return -EINVAL;
@@ -1794,10 +1814,32 @@ static int pl022_setup(struct spi_device *spi)
        chip_info = spi->controller_data;
 
        if (chip_info == NULL) {
-               chip_info = &pl022_default_chip_info;
-               /* spi_board_info.controller_data not is supplied */
-               dev_dbg(&spi->dev,
-                       "using default controller_data settings\n");
+               if (np) {
+                       chip_info_dt = pl022_default_chip_info;
+
+                       chip_info_dt.hierarchy = SSP_MASTER;
+                       of_property_read_u32(np, "pl022,interface",
+                               &chip_info_dt.iface);
+                       of_property_read_u32(np, "pl022,com-mode",
+                               &chip_info_dt.com_mode);
+                       of_property_read_u32(np, "pl022,rx-level-trig",
+                               &chip_info_dt.rx_lev_trig);
+                       of_property_read_u32(np, "pl022,tx-level-trig",
+                               &chip_info_dt.tx_lev_trig);
+                       of_property_read_u32(np, "pl022,ctrl-len",
+                               &chip_info_dt.ctrl_len);
+                       of_property_read_u32(np, "pl022,wait-state",
+                               &chip_info_dt.wait_state);
+                       of_property_read_u32(np, "pl022,duplex",
+                               &chip_info_dt.duplex);
+
+                       chip_info = &chip_info_dt;
+               } else {
+                       chip_info = &pl022_default_chip_info;
+                       /* spi_board_info.controller_data not is supplied */
+                       dev_dbg(&spi->dev,
+                               "using default controller_data settings\n");
+               }
        } else
                dev_dbg(&spi->dev,
                        "using user supplied controller_data settings\n");
@@ -1840,8 +1882,9 @@ static int pl022_setup(struct spi_device *spi)
        chip->xfer_type = chip_info->com_mode;
        if (!chip_info->cs_control) {
                chip->cs_control = null_cs_control;
-               dev_warn(&spi->dev,
-                        "chip select function is NULL for this chip\n");
+               if (!gpio_is_valid(pl022->chipselects[spi->chip_select]))
+                       dev_warn(&spi->dev,
+                                "invalid chip select\n");
        } else
                chip->cs_control = chip_info->cs_control;
 
@@ -1986,6 +2029,34 @@ static void pl022_cleanup(struct spi_device *spi)
        kfree(chip);
 }
 
+static struct pl022_ssp_controller *
+pl022_platform_data_dt_get(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct pl022_ssp_controller *pd;
+       u32 tmp;
+
+       if (!np) {
+               dev_err(dev, "no dt node defined\n");
+               return NULL;
+       }
+
+       pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL);
+       if (!pd) {
+               dev_err(dev, "cannot allocate platform data memory\n");
+               return NULL;
+       }
+
+       pd->bus_id = -1;
+       of_property_read_u32(np, "num-cs", &tmp);
+       pd->num_chipselect = tmp;
+       of_property_read_u32(np, "pl022,autosuspend-delay",
+                            &pd->autosuspend_delay);
+       pd->rt = of_property_read_bool(np, "pl022,rt");
+
+       return pd;
+}
+
 static int __devinit
 pl022_probe(struct amba_device *adev, const struct amba_id *id)
 {
@@ -1993,22 +2064,31 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
        struct pl022_ssp_controller *platform_info = adev->dev.platform_data;
        struct spi_master *master;
        struct pl022 *pl022 = NULL;     /*Data for this driver */
-       int status = 0;
+       struct device_node *np = adev->dev.of_node;
+       int status = 0, i, num_cs;
 
        dev_info(&adev->dev,
                 "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
-       if (platform_info == NULL) {
-               dev_err(&adev->dev, "probe - no platform data supplied\n");
-               status = -ENODEV;
-               goto err_no_pdata;
+       if (!platform_info && IS_ENABLED(CONFIG_OF))
+               platform_info = pl022_platform_data_dt_get(dev);
+
+       if (!platform_info) {
+               dev_err(dev, "probe: no platform data defined\n");
+               return -ENODEV;
+       }
+
+       if (platform_info->num_chipselect) {
+               num_cs = platform_info->num_chipselect;
+       } else {
+               dev_err(dev, "probe: no chip select defined\n");
+               return -ENODEV;
        }
 
        /* Allocate master with space for data */
        master = spi_alloc_master(dev, sizeof(struct pl022));
        if (master == NULL) {
                dev_err(&adev->dev, "probe - cannot alloc SPI master\n");
-               status = -ENOMEM;
-               goto err_no_master;
+               return -ENOMEM;
        }
 
        pl022 = spi_master_get_devdata(master);
@@ -2016,19 +2096,71 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
        pl022->master_info = platform_info;
        pl022->adev = adev;
        pl022->vendor = id->data;
+       pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int),
+                                         GFP_KERNEL);
+
+       pl022->pinctrl = devm_pinctrl_get(dev);
+       if (IS_ERR(pl022->pinctrl)) {
+               status = PTR_ERR(pl022->pinctrl);
+               goto err_no_pinctrl;
+       }
+
+       pl022->pins_default = pinctrl_lookup_state(pl022->pinctrl,
+                                                PINCTRL_STATE_DEFAULT);
+       /* enable pins to be muxed in and configured */
+       if (!IS_ERR(pl022->pins_default)) {
+               status = pinctrl_select_state(pl022->pinctrl,
+                               pl022->pins_default);
+               if (status)
+                       dev_err(dev, "could not set default pins\n");
+       } else
+               dev_err(dev, "could not get default pinstate\n");
+
+       pl022->pins_sleep = pinctrl_lookup_state(pl022->pinctrl,
+                                              PINCTRL_STATE_SLEEP);
+       if (IS_ERR(pl022->pins_sleep))
+               dev_dbg(dev, "could not get sleep pinstate\n");
 
        /*
         * Bus Number Which has been Assigned to this SSP controller
         * on this board
         */
        master->bus_num = platform_info->bus_id;
-       master->num_chipselect = platform_info->num_chipselect;
+       master->num_chipselect = num_cs;
        master->cleanup = pl022_cleanup;
        master->setup = pl022_setup;
        master->prepare_transfer_hardware = pl022_prepare_transfer_hardware;
        master->transfer_one_message = pl022_transfer_one_message;
        master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
        master->rt = platform_info->rt;
+       master->dev.of_node = dev->of_node;
+
+       if (platform_info->num_chipselect && platform_info->chipselects) {
+               for (i = 0; i < num_cs; i++)
+                       pl022->chipselects[i] = platform_info->chipselects[i];
+       } else if (IS_ENABLED(CONFIG_OF)) {
+               for (i = 0; i < num_cs; i++) {
+                       int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
+
+                       if (cs_gpio == -EPROBE_DEFER) {
+                               status = -EPROBE_DEFER;
+                               goto err_no_gpio;
+                       }
+
+                       pl022->chipselects[i] = cs_gpio;
+
+                       if (gpio_is_valid(cs_gpio)) {
+                               if (devm_gpio_request(dev, cs_gpio, "ssp-pl022"))
+                                       dev_err(&adev->dev,
+                                               "could not request %d gpio\n",
+                                               cs_gpio);
+                               else if (gpio_direction_output(cs_gpio, 1))
+                                       dev_err(&adev->dev,
+                                               "could set gpio %d as output\n",
+                                               cs_gpio);
+                       }
+               }
+       }
 
        /*
         * Supports mode 0-3, loopback, and active low CS. Transfers are
@@ -2045,7 +2177,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
                goto err_no_ioregion;
 
        pl022->phybase = adev->res.start;
-       pl022->virtbase = ioremap(adev->res.start, resource_size(&adev->res));
+       pl022->virtbase = devm_ioremap(dev, adev->res.start,
+                                      resource_size(&adev->res));
        if (pl022->virtbase == NULL) {
                status = -ENOMEM;
                goto err_no_ioremap;
@@ -2055,7 +2188,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
 
        pm_runtime_resume(dev);
 
-       pl022->clk = clk_get(&adev->dev, NULL);
+       pl022->clk = devm_clk_get(&adev->dev, NULL);
        if (IS_ERR(pl022->clk)) {
                status = PTR_ERR(pl022->clk);
                dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n");
@@ -2083,8 +2216,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
               SSP_CR1(pl022->virtbase));
        load_ssp_default_config(pl022);
 
-       status = request_irq(adev->irq[0], pl022_interrupt_handler, 0, "pl022",
-                            pl022);
+       status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler,
+                                 0, "pl022", pl022);
        if (status < 0) {
                dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
                goto err_no_irq;
@@ -2124,22 +2257,18 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
  err_spi_register:
        if (platform_info->enable_dma)
                pl022_dma_remove(pl022);
-
-       free_irq(adev->irq[0], pl022);
  err_no_irq:
        clk_disable(pl022->clk);
  err_no_clk_en:
        clk_unprepare(pl022->clk);
  err_clk_prep:
-       clk_put(pl022->clk);
  err_no_clk:
-       iounmap(pl022->virtbase);
  err_no_ioremap:
        amba_release_regions(adev);
  err_no_ioregion:
+ err_no_gpio:
+ err_no_pinctrl:
        spi_master_put(master);
- err_no_master:
- err_no_pdata:
        return status;
 }
 
@@ -2161,20 +2290,55 @@ pl022_remove(struct amba_device *adev)
        if (pl022->master_info->enable_dma)
                pl022_dma_remove(pl022);
 
-       free_irq(adev->irq[0], pl022);
        clk_disable(pl022->clk);
        clk_unprepare(pl022->clk);
-       clk_put(pl022->clk);
        pm_runtime_disable(&adev->dev);
-       iounmap(pl022->virtbase);
        amba_release_regions(adev);
        tasklet_disable(&pl022->pump_transfers);
        spi_unregister_master(pl022->master);
-       spi_master_put(pl022->master);
        amba_set_drvdata(adev, NULL);
        return 0;
 }
 
+#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME)
+/*
+ * These two functions are used from both suspend/resume and
+ * the runtime counterparts to handle external resources like
+ * clocks, pins and regulators when going to sleep.
+ */
+static void pl022_suspend_resources(struct pl022 *pl022)
+{
+       int ret;
+
+       clk_disable(pl022->clk);
+
+       /* Optionally let pins go into sleep states */
+       if (!IS_ERR(pl022->pins_sleep)) {
+               ret = pinctrl_select_state(pl022->pinctrl,
+                                          pl022->pins_sleep);
+               if (ret)
+                       dev_err(&pl022->adev->dev,
+                               "could not set pins to sleep state\n");
+       }
+}
+
+static void pl022_resume_resources(struct pl022 *pl022)
+{
+       int ret;
+
+       /* Optionaly enable pins to be muxed in and configured */
+       if (!IS_ERR(pl022->pins_default)) {
+               ret = pinctrl_select_state(pl022->pinctrl,
+                                          pl022->pins_default);
+               if (ret)
+                       dev_err(&pl022->adev->dev,
+                               "could not set default pins\n");
+       }
+
+       clk_enable(pl022->clk);
+}
+#endif
+
 #ifdef CONFIG_SUSPEND
 static int pl022_suspend(struct device *dev)
 {
@@ -2186,6 +2350,7 @@ static int pl022_suspend(struct device *dev)
                dev_warn(dev, "cannot suspend master\n");
                return ret;
        }
+       pl022_suspend_resources(pl022);
 
        dev_dbg(dev, "suspended\n");
        return 0;
@@ -2196,6 +2361,8 @@ static int pl022_resume(struct device *dev)
        struct pl022 *pl022 = dev_get_drvdata(dev);
        int ret;
 
+       pl022_resume_resources(pl022);
+
        /* Start the queue running */
        ret = spi_master_resume(pl022->master);
        if (ret)
@@ -2212,8 +2379,7 @@ static int pl022_runtime_suspend(struct device *dev)
 {
        struct pl022 *pl022 = dev_get_drvdata(dev);
 
-       clk_disable(pl022->clk);
-
+       pl022_suspend_resources(pl022);
        return 0;
 }
 
@@ -2221,8 +2387,7 @@ static int pl022_runtime_resume(struct device *dev)
 {
        struct pl022 *pl022 = dev_get_drvdata(dev);
 
-       clk_enable(pl022->clk);
-
+       pl022_resume_resources(pl022);
        return 0;
 }
 #endif
index 8ee7d790ce49424d4595321f6abc068bfcf7eb02..a2a080b7f42b2d9cb515b940301509466a92c7af 100644 (file)
@@ -611,6 +611,7 @@ static int __devinit s3c24xx_spi_probe(struct platform_device *pdev)
        if (!pdata->set_cs) {
                if (pdata->pin_cs < 0) {
                        dev_err(&pdev->dev, "No chipselect pin\n");
+                       err = -EINVAL;
                        goto err_register;
                }
 
index d1c8441f638c39e6ad18d3930cd90b22efe268e5..a2062b231a6bbc45699441b890432cc86f3fbedf 100644 (file)
@@ -976,7 +976,8 @@ err_msgq:
        spi_set_ctldata(spi, NULL);
 
 err_gpio_req:
-       kfree(cs);
+       if (spi->dev.of_node)
+               kfree(cs);
 
        return err;
 }
@@ -1409,7 +1410,7 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM
 static int s3c64xx_spi_suspend(struct device *dev)
 {
-       struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+       struct spi_master *master = dev_get_drvdata(dev);
        struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
 
        spi_master_suspend(master);
@@ -1428,7 +1429,7 @@ static int s3c64xx_spi_suspend(struct device *dev)
 
 static int s3c64xx_spi_resume(struct device *dev)
 {
-       struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+       struct spi_master *master = dev_get_drvdata(dev);
        struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
        struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
 
@@ -1452,7 +1453,7 @@ static int s3c64xx_spi_resume(struct device *dev)
 #ifdef CONFIG_PM_RUNTIME
 static int s3c64xx_spi_runtime_suspend(struct device *dev)
 {
-       struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+       struct spi_master *master = dev_get_drvdata(dev);
        struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
 
        clk_disable(sdd->clk);
@@ -1463,7 +1464,7 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev)
 
 static int s3c64xx_spi_runtime_resume(struct device *dev)
 {
-       struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+       struct spi_master *master = dev_get_drvdata(dev);
        struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
 
        clk_enable(sdd->src_clk);
diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c
new file mode 100644 (file)
index 0000000..9eda21d
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * NXP SC18IS602/603 SPI driver
+ *
+ * Copyright (C) Guenter Roeck <linux@roeck-us.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/platform_data/sc18is602.h>
+
+enum chips { sc18is602, sc18is602b, sc18is603 };
+
+#define SC18IS602_BUFSIZ               200
+#define SC18IS602_CLOCK                        7372000
+
+#define SC18IS602_MODE_CPHA            BIT(2)
+#define SC18IS602_MODE_CPOL            BIT(3)
+#define SC18IS602_MODE_LSB_FIRST       BIT(5)
+#define SC18IS602_MODE_CLOCK_DIV_4     0x0
+#define SC18IS602_MODE_CLOCK_DIV_16    0x1
+#define SC18IS602_MODE_CLOCK_DIV_64    0x2
+#define SC18IS602_MODE_CLOCK_DIV_128   0x3
+
+struct sc18is602 {
+       struct spi_master       *master;
+       struct device           *dev;
+       u8                      ctrl;
+       u32                     freq;
+       u32                     speed;
+
+       /* I2C data */
+       struct i2c_client       *client;
+       enum chips              id;
+       u8                      buffer[SC18IS602_BUFSIZ + 1];
+       int                     tlen;   /* Data queued for tx in buffer */
+       int                     rindex; /* Receive data index in buffer */
+};
+
+static int sc18is602_wait_ready(struct sc18is602 *hw, int len)
+{
+       int i, err;
+       int usecs = 1000000 * len / hw->speed + 1;
+       u8 dummy[1];
+
+       for (i = 0; i < 10; i++) {
+               err = i2c_master_recv(hw->client, dummy, 1);
+               if (err >= 0)
+                       return 0;
+               usleep_range(usecs, usecs * 2);
+       }
+       return -ETIMEDOUT;
+}
+
+static int sc18is602_txrx(struct sc18is602 *hw, struct spi_message *msg,
+                         struct spi_transfer *t, bool do_transfer)
+{
+       unsigned int len = t->len;
+       int ret;
+
+       if (hw->tlen == 0) {
+               /* First byte (I2C command) is chip select */
+               hw->buffer[0] = 1 << msg->spi->chip_select;
+               hw->tlen = 1;
+               hw->rindex = 0;
+       }
+       /*
+        * We can not immediately send data to the chip, since each I2C message
+        * resembles a full SPI message (from CS active to CS inactive).
+        * Enqueue messages up to the first read or until do_transfer is true.
+        */
+       if (t->tx_buf) {
+               memcpy(&hw->buffer[hw->tlen], t->tx_buf, len);
+               hw->tlen += len;
+               if (t->rx_buf)
+                       do_transfer = true;
+               else
+                       hw->rindex = hw->tlen - 1;
+       } else if (t->rx_buf) {
+               /*
+                * For receive-only transfers we still need to perform a dummy
+                * write to receive data from the SPI chip.
+                * Read data starts at the end of transmit data (minus 1 to
+                * account for CS).
+                */
+               hw->rindex = hw->tlen - 1;
+               memset(&hw->buffer[hw->tlen], 0, len);
+               hw->tlen += len;
+               do_transfer = true;
+       }
+
+       if (do_transfer && hw->tlen > 1) {
+               ret = sc18is602_wait_ready(hw, SC18IS602_BUFSIZ);
+               if (ret < 0)
+                       return ret;
+               ret = i2c_master_send(hw->client, hw->buffer, hw->tlen);
+               if (ret < 0)
+                       return ret;
+               if (ret != hw->tlen)
+                       return -EIO;
+
+               if (t->rx_buf) {
+                       int rlen = hw->rindex + len;
+
+                       ret = sc18is602_wait_ready(hw, hw->tlen);
+                       if (ret < 0)
+                               return ret;
+                       ret = i2c_master_recv(hw->client, hw->buffer, rlen);
+                       if (ret < 0)
+                               return ret;
+                       if (ret != rlen)
+                               return -EIO;
+                       memcpy(t->rx_buf, &hw->buffer[hw->rindex], len);
+               }
+               hw->tlen = 0;
+       }
+       return len;
+}
+
+static int sc18is602_setup_transfer(struct sc18is602 *hw, u32 hz, u8 mode)
+{
+       u8 ctrl = 0;
+       int ret;
+
+       if (mode & SPI_CPHA)
+               ctrl |= SC18IS602_MODE_CPHA;
+       if (mode & SPI_CPOL)
+               ctrl |= SC18IS602_MODE_CPOL;
+       if (mode & SPI_LSB_FIRST)
+               ctrl |= SC18IS602_MODE_LSB_FIRST;
+
+       /* Find the closest clock speed */
+       if (hz >= hw->freq / 4) {
+               ctrl |= SC18IS602_MODE_CLOCK_DIV_4;
+               hw->speed = hw->freq / 4;
+       } else if (hz >= hw->freq / 16) {
+               ctrl |= SC18IS602_MODE_CLOCK_DIV_16;
+               hw->speed = hw->freq / 16;
+       } else if (hz >= hw->freq / 64) {
+               ctrl |= SC18IS602_MODE_CLOCK_DIV_64;
+               hw->speed = hw->freq / 64;
+       } else {
+               ctrl |= SC18IS602_MODE_CLOCK_DIV_128;
+               hw->speed = hw->freq / 128;
+       }
+
+       /*
+        * Don't do anything if the control value did not change. The initial
+        * value of 0xff for hw->ctrl ensures that the correct mode will be set
+        * with the first call to this function.
+        */
+       if (ctrl == hw->ctrl)
+               return 0;
+
+       ret = i2c_smbus_write_byte_data(hw->client, 0xf0, ctrl);
+       if (ret < 0)
+               return ret;
+
+       hw->ctrl = ctrl;
+
+       return 0;
+}
+
+static int sc18is602_check_transfer(struct spi_device *spi,
+                                   struct spi_transfer *t, int tlen)
+{
+       int bpw;
+       uint32_t hz;
+
+       if (t && t->len + tlen > SC18IS602_BUFSIZ)
+               return -EINVAL;
+
+       bpw = spi->bits_per_word;
+       if (t && t->bits_per_word)
+               bpw = t->bits_per_word;
+       if (bpw != 8)
+               return -EINVAL;
+
+       hz = spi->max_speed_hz;
+       if (t && t->speed_hz)
+               hz = t->speed_hz;
+       if (hz == 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int sc18is602_transfer_one(struct spi_master *master,
+                                 struct spi_message *m)
+{
+       struct sc18is602 *hw = spi_master_get_devdata(master);
+       struct spi_device *spi = m->spi;
+       struct spi_transfer *t;
+       int status = 0;
+
+       /* SC18IS602 does not support CS2 */
+       if (hw->id == sc18is602 && spi->chip_select == 2) {
+               status = -ENXIO;
+               goto error;
+       }
+
+       hw->tlen = 0;
+       list_for_each_entry(t, &m->transfers, transfer_list) {
+               u32 hz = t->speed_hz ? : spi->max_speed_hz;
+               bool do_transfer;
+
+               status = sc18is602_check_transfer(spi, t, hw->tlen);
+               if (status < 0)
+                       break;
+
+               status = sc18is602_setup_transfer(hw, hz, spi->mode);
+               if (status < 0)
+                       break;
+
+               do_transfer = t->cs_change || list_is_last(&t->transfer_list,
+                                                          &m->transfers);
+
+               if (t->len) {
+                       status = sc18is602_txrx(hw, m, t, do_transfer);
+                       if (status < 0)
+                               break;
+                       m->actual_length += status;
+               }
+               status = 0;
+
+               if (t->delay_usecs)
+                       udelay(t->delay_usecs);
+       }
+error:
+       m->status = status;
+       spi_finalize_current_message(master);
+
+       return status;
+}
+
+static int sc18is602_setup(struct spi_device *spi)
+{
+       if (!spi->bits_per_word)
+               spi->bits_per_word = 8;
+
+       if (spi->mode & ~(SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST))
+               return -EINVAL;
+
+       return sc18is602_check_transfer(spi, NULL, 0);
+}
+
+static int sc18is602_probe(struct i2c_client *client,
+                          const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       struct device_node *np = dev->of_node;
+       struct sc18is602_platform_data *pdata = dev_get_platdata(dev);
+       struct sc18is602 *hw;
+       struct spi_master *master;
+       int error;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+                                    I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+               return -EINVAL;
+
+       master = spi_alloc_master(dev, sizeof(struct sc18is602));
+       if (!master)
+               return -ENOMEM;
+
+       hw = spi_master_get_devdata(master);
+       i2c_set_clientdata(client, hw);
+
+       hw->master = master;
+       hw->client = client;
+       hw->dev = dev;
+       hw->ctrl = 0xff;
+
+       hw->id = id->driver_data;
+
+       switch (hw->id) {
+       case sc18is602:
+       case sc18is602b:
+               master->num_chipselect = 4;
+               hw->freq = SC18IS602_CLOCK;
+               break;
+       case sc18is603:
+               master->num_chipselect = 2;
+               if (pdata) {
+                       hw->freq = pdata->clock_frequency;
+               } else {
+                       const __be32 *val;
+                       int len;
+
+                       val = of_get_property(np, "clock-frequency", &len);
+                       if (val && len >= sizeof(__be32))
+                               hw->freq = be32_to_cpup(val);
+               }
+               if (!hw->freq)
+                       hw->freq = SC18IS602_CLOCK;
+               break;
+       }
+       master->bus_num = client->adapter->nr;
+       master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST;
+       master->setup = sc18is602_setup;
+       master->transfer_one_message = sc18is602_transfer_one;
+       master->dev.of_node = np;
+
+       error = spi_register_master(master);
+       if (error)
+               goto error_reg;
+
+       return 0;
+
+error_reg:
+       spi_master_put(master);
+       return error;
+}
+
+static int sc18is602_remove(struct i2c_client *client)
+{
+       struct sc18is602 *hw = i2c_get_clientdata(client);
+       struct spi_master *master = hw->master;
+
+       spi_unregister_master(master);
+
+       return 0;
+}
+
+static const struct i2c_device_id sc18is602_id[] = {
+       { "sc18is602", sc18is602 },
+       { "sc18is602b", sc18is602b },
+       { "sc18is603", sc18is603 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, sc18is602_id);
+
+static struct i2c_driver sc18is602_driver = {
+       .driver = {
+               .name = "sc18is602",
+       },
+       .probe = sc18is602_probe,
+       .remove = sc18is602_remove,
+       .id_table = sc18is602_id,
+};
+
+module_i2c_driver(sc18is602_driver);
+
+MODULE_DESCRIPTION("SC18IC602/603 SPI Master Driver");
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_LICENSE("GPL");
index 934138c7b3d3c34f94ce4aebe0a33b8b32a02034..796c077ef439dacd3e22fd85415724f72316276d 100644 (file)
@@ -283,7 +283,7 @@ static int __devinit hspi_probe(struct platform_device *pdev)
        ret = spi_register_master(master);
        if (ret < 0) {
                dev_err(&pdev->dev, "spi_register_master error.\n");
-               goto error2;
+               goto error1;
        }
 
        pm_runtime_enable(&pdev->dev);
@@ -292,8 +292,6 @@ static int __devinit hspi_probe(struct platform_device *pdev)
 
        return 0;
 
- error2:
-       devm_iounmap(hspi->dev, hspi->addr);
  error1:
        clk_put(clk);
  error0:
@@ -310,7 +308,6 @@ static int __devexit hspi_remove(struct platform_device *pdev)
 
        clk_put(hspi->clk);
        spi_unregister_master(hspi->master);
-       devm_iounmap(hspi->dev, hspi->addr);
 
        return 0;
 }
index 58e38528532393f89c0b8688469ad3af8a982b21..911e904b3c84db0052eb9d4f028e99d3f5f8c017 100644 (file)
@@ -594,9 +594,7 @@ static int __devexit stmp_spi_remove(struct platform_device *dev)
        struct stmp_spi *ss;
        struct spi_master *master;
 
-       master = platform_get_drvdata(dev);
-       if (master == NULL)
-               goto out0;
+       master = spi_master_get(platform_get_drvdata(dev));
        ss = spi_master_get_devdata(master);
 
        spi_unregister_master(master);
@@ -609,8 +607,6 @@ static int __devexit stmp_spi_remove(struct platform_device *dev)
        destroy_workqueue(ss->workqueue);
        iounmap(ss->regs);
        spi_master_put(master);
-       platform_set_drvdata(dev, NULL);
-out0:
        return 0;
 }
 
index ef52c1c6f5c5e6c78759149986ff0bf2af6b6c06..e28445d8d27f2404e58e6a11a2f251fcdd799c1f 100644 (file)
@@ -652,7 +652,7 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev)
        struct spi_tegra_data   *tspi;
        struct resource         *r;
 
-       master = dev_get_drvdata(&pdev->dev);
+       master = spi_master_get(dev_get_drvdata(&pdev->dev));
        tspi = spi_master_get_devdata(master);
 
        spi_unregister_master(master);
@@ -668,6 +668,8 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev)
        clk_put(tspi->clk);
        iounmap(tspi->base);
 
+       spi_master_put(master);
+
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        release_mem_region(r->start, resource_size(r));
 
index 0ce5c12aab5520d3761f33c7e3cf34530aabd8e2..24421024deaf6d6bd19b3da4063ba932f5a3ddb8 100644 (file)
@@ -316,18 +316,7 @@ static struct spi_driver tle62x0_driver = {
        .remove         = __devexit_p(tle62x0_remove),
 };
 
-static __init int tle62x0_init(void)
-{
-       return spi_register_driver(&tle62x0_driver);
-}
-
-static __exit void tle62x0_exit(void)
-{
-       spi_unregister_driver(&tle62x0_driver);
-}
-
-module_init(tle62x0_init);
-module_exit(tle62x0_exit);
+module_spi_driver(tle62x0_driver);
 
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
 MODULE_DESCRIPTION("TLE62x0 SPI driver");
index cd56dcf46320bb57f598ac50321c214d6133824c..159dafd2f9080953ecfecca74a0b676da22e73f7 100644 (file)
@@ -1536,8 +1536,6 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
 
        pci_iounmap(board_dat->pdev, data->io_remap_addr);
        spi_unregister_master(data->master);
-       spi_master_put(data->master);
-       platform_set_drvdata(plat_dev, NULL);
 
        return 0;
 }
index fe1d7b283cb633c4be48d465f5fa572333929fe8..854b7294f6c64f77b636c38188897a9621e7bbff 100644 (file)
@@ -244,6 +244,7 @@ struct dma_chan;
  *     indicates no delay and the device will be suspended immediately.
  * @rt: indicates the controller should run the message pump with realtime
  *     priority to minimise the transfer latency on the bus.
+ * @chipselects: list of <num_chipselects> chip select gpios
  */
 struct pl022_ssp_controller {
        u16 bus_id;
@@ -254,6 +255,7 @@ struct pl022_ssp_controller {
        void *dma_tx_param;
        int autosuspend_delay;
        bool rt;
+       int *chipselects;
 };
 
 /**
diff --git a/include/linux/platform_data/sc18is602.h b/include/linux/platform_data/sc18is602.h
new file mode 100644 (file)
index 0000000..997b066
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Platform data for NXP SC18IS602/603
+ *
+ * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * For further information, see the Documentation/spi/sc18is602 file.
+ */
+
+/**
+ * struct sc18is602_platform_data - sc18is602 info
+ * @clock_frequency            SC18IS603 oscillator frequency
+ */
+struct sc18is602_platform_data {
+       u32 clock_frequency;
+};
diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h
new file mode 100644 (file)
index 0000000..61ae130
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * include/linux/spi/mxs-spi.h
+ *
+ * Freescale i.MX233/i.MX28 SPI controller register definition
+ *
+ * Copyright 2008 Embedded Alley Solutions, Inc.
+ * Copyright 2009-2011 Freescale Semiconductor, Inc.
+ *
+ * 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_MXS_SPI_H__
+#define __LINUX_SPI_MXS_SPI_H__
+
+#include <linux/fsl/mxs-dma.h>
+
+#define ssp_is_old(host)       ((host)->devid == IMX23_SSP)
+
+/* SSP registers */
+#define HW_SSP_CTRL0                           0x000
+#define  BM_SSP_CTRL0_RUN                      (1 << 29)
+#define  BM_SSP_CTRL0_SDIO_IRQ_CHECK           (1 << 28)
+#define  BM_SSP_CTRL0_LOCK_CS                  (1 << 27)
+#define  BM_SSP_CTRL0_IGNORE_CRC               (1 << 26)
+#define  BM_SSP_CTRL0_READ                     (1 << 25)
+#define  BM_SSP_CTRL0_DATA_XFER                        (1 << 24)
+#define  BP_SSP_CTRL0_BUS_WIDTH                        22
+#define  BM_SSP_CTRL0_BUS_WIDTH                        (0x3 << 22)
+#define  BM_SSP_CTRL0_WAIT_FOR_IRQ             (1 << 21)
+#define  BM_SSP_CTRL0_WAIT_FOR_CMD             (1 << 20)
+#define  BM_SSP_CTRL0_LONG_RESP                        (1 << 19)
+#define  BM_SSP_CTRL0_GET_RESP                 (1 << 17)
+#define  BM_SSP_CTRL0_ENABLE                   (1 << 16)
+#define  BP_SSP_CTRL0_XFER_COUNT               0
+#define  BM_SSP_CTRL0_XFER_COUNT               0xffff
+#define HW_SSP_CMD0                            0x010
+#define  BM_SSP_CMD0_DBL_DATA_RATE_EN          (1 << 25)
+#define  BM_SSP_CMD0_SLOW_CLKING_EN            (1 << 22)
+#define  BM_SSP_CMD0_CONT_CLKING_EN            (1 << 21)
+#define  BM_SSP_CMD0_APPEND_8CYC               (1 << 20)
+#define  BP_SSP_CMD0_BLOCK_SIZE                        16
+#define  BM_SSP_CMD0_BLOCK_SIZE                        (0xf << 16)
+#define  BP_SSP_CMD0_BLOCK_COUNT               8
+#define  BM_SSP_CMD0_BLOCK_COUNT               (0xff << 8)
+#define  BP_SSP_CMD0_CMD                       0
+#define  BM_SSP_CMD0_CMD                       0xff
+#define HW_SSP_CMD1                            0x020
+#define HW_SSP_XFER_SIZE                       0x030
+#define HW_SSP_BLOCK_SIZE                      0x040
+#define  BP_SSP_BLOCK_SIZE_BLOCK_COUNT         4
+#define  BM_SSP_BLOCK_SIZE_BLOCK_COUNT         (0xffffff << 4)
+#define  BP_SSP_BLOCK_SIZE_BLOCK_SIZE          0
+#define  BM_SSP_BLOCK_SIZE_BLOCK_SIZE          0xf
+#define HW_SSP_TIMING(h)                       (ssp_is_old(h) ? 0x050 : 0x070)
+#define  BP_SSP_TIMING_TIMEOUT                 16
+#define  BM_SSP_TIMING_TIMEOUT                 (0xffff << 16)
+#define  BP_SSP_TIMING_CLOCK_DIVIDE            8
+#define  BM_SSP_TIMING_CLOCK_DIVIDE            (0xff << 8)
+#define  BF_SSP_TIMING_CLOCK_DIVIDE(v)         \
+                       (((v) << 8) & BM_SSP_TIMING_CLOCK_DIVIDE)
+#define  BP_SSP_TIMING_CLOCK_RATE              0
+#define  BM_SSP_TIMING_CLOCK_RATE              0xff
+#define BF_SSP_TIMING_CLOCK_RATE(v)            \
+                       (((v) << 0) & BM_SSP_TIMING_CLOCK_RATE)
+#define HW_SSP_CTRL1(h)                                (ssp_is_old(h) ? 0x060 : 0x080)
+#define  BM_SSP_CTRL1_SDIO_IRQ                 (1 << 31)
+#define  BM_SSP_CTRL1_SDIO_IRQ_EN              (1 << 30)
+#define  BM_SSP_CTRL1_RESP_ERR_IRQ             (1 << 29)
+#define  BM_SSP_CTRL1_RESP_ERR_IRQ_EN          (1 << 28)
+#define  BM_SSP_CTRL1_RESP_TIMEOUT_IRQ         (1 << 27)
+#define  BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN      (1 << 26)
+#define  BM_SSP_CTRL1_DATA_TIMEOUT_IRQ         (1 << 25)
+#define  BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN      (1 << 24)
+#define  BM_SSP_CTRL1_DATA_CRC_IRQ             (1 << 23)
+#define  BM_SSP_CTRL1_DATA_CRC_IRQ_EN          (1 << 22)
+#define  BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ                (1 << 21)
+#define  BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ_EN     (1 << 20)
+#define  BM_SSP_CTRL1_RECV_TIMEOUT_IRQ         (1 << 17)
+#define  BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN      (1 << 16)
+#define  BM_SSP_CTRL1_FIFO_OVERRUN_IRQ         (1 << 15)
+#define  BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN      (1 << 14)
+#define  BM_SSP_CTRL1_DMA_ENABLE               (1 << 13)
+#define  BM_SSP_CTRL1_PHASE                    (1 << 10)
+#define  BM_SSP_CTRL1_POLARITY                 (1 << 9)
+#define  BP_SSP_CTRL1_WORD_LENGTH              4
+#define  BM_SSP_CTRL1_WORD_LENGTH              (0xf << 4)
+#define  BF_SSP_CTRL1_WORD_LENGTH(v)           \
+                       (((v) << 4) & BM_SSP_CTRL1_WORD_LENGTH)
+#define  BV_SSP_CTRL1_WORD_LENGTH__FOUR_BITS   0x3
+#define  BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS  0x7
+#define  BV_SSP_CTRL1_WORD_LENGTH__SIXTEEN_BITS        0xF
+#define  BP_SSP_CTRL1_SSP_MODE                 0
+#define  BM_SSP_CTRL1_SSP_MODE                 0xf
+#define  BF_SSP_CTRL1_SSP_MODE(v)              \
+                       (((v) << 0) & BM_SSP_CTRL1_SSP_MODE)
+#define  BV_SSP_CTRL1_SSP_MODE__SPI            0x0
+#define  BV_SSP_CTRL1_SSP_MODE__SSI            0x1
+#define  BV_SSP_CTRL1_SSP_MODE__SD_MMC         0x3
+#define  BV_SSP_CTRL1_SSP_MODE__MS             0x4
+
+#define HW_SSP_DATA(h)                         (ssp_is_old(h) ? 0x070 : 0x090)
+
+#define HW_SSP_SDRESP0(h)                      (ssp_is_old(h) ? 0x080 : 0x0a0)
+#define HW_SSP_SDRESP1(h)                      (ssp_is_old(h) ? 0x090 : 0x0b0)
+#define HW_SSP_SDRESP2(h)                      (ssp_is_old(h) ? 0x0a0 : 0x0c0)
+#define HW_SSP_SDRESP3(h)                      (ssp_is_old(h) ? 0x0b0 : 0x0d0)
+#define HW_SSP_STATUS(h)                       (ssp_is_old(h) ? 0x0c0 : 0x100)
+#define  BM_SSP_STATUS_CARD_DETECT             (1 << 28)
+#define  BM_SSP_STATUS_SDIO_IRQ                        (1 << 17)
+#define  BM_SSP_STATUS_FIFO_EMPTY              (1 << 5)
+
+#define BF_SSP(value, field)   (((value) << BP_SSP_##field) & BM_SSP_##field)
+
+#define SSP_PIO_NUM    3
+
+enum mxs_ssp_id {
+       IMX23_SSP,
+       IMX28_SSP,
+};
+
+struct mxs_ssp {
+       struct device                   *dev;
+       void __iomem                    *base;
+       struct clk                      *clk;
+       unsigned int                    clk_rate;
+       enum mxs_ssp_id                 devid;
+
+       int                             dma_channel;
+       struct dma_chan                 *dmach;
+       struct mxs_dma_data             dma_data;
+       unsigned int                    dma_dir;
+       enum dma_transfer_direction     slave_dirn;
+       u32                             ssp_pio_words[SSP_PIO_NUM];
+};
+
+void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate);
+
+#endif /* __LINUX_SPI_MXS_SPI_H__ */