Merge branch 'next-spi' of git://git.secretlab.ca/git/linux-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 14 Dec 2009 18:22:11 +0000 (10:22 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 14 Dec 2009 18:22:11 +0000 (10:22 -0800)
* 'next-spi' of git://git.secretlab.ca/git/linux-2.6: (23 commits)
  spi: fix probe/remove section markings
  Add OMAP spi100k driver
  spi-imx: don't access struct device directly but use dev_get_platdata
  spi-imx: Add mx25 support
  spi-imx: use positive logic to distinguish cpu variants
  spi-imx: correct check for platform_get_irq failing
  ARM: NUC900: Add spi driver support for nuc900
  spi: SuperH MSIOF SPI Master driver V2
  spi: fix spidev compilation failure when VERBOSE is defined
  spi/au1550_spi: fix setupxfer not to override cfg with zeros
  spi/mpc8xxx: don't use __exit_p to wrap plat_mpc8xxx_spi_remove
  spi/i.MX: fix broken error handling for gpio_request
  spi/i.mx: drain MXC SPI transfer buffer when probing device
  MAINTAINERS: add SPI co-maintainer.
  spi/xilinx_spi: fix incorrect casting
  spi/mpc52xx-spi: minor cleanups
  xilinx_spi: add a platform driver using the xilinx_spi common module.
  xilinx_spi: add support for the DS570 IP.
  xilinx_spi: Switch to iomem functions and support little endian.
  xilinx_spi: Split into of driver and generic part.
  ...

19 files changed:
MAINTAINERS
arch/arm/mach-w90x900/include/mach/nuc900_spi.h [new file with mode: 0644]
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/au1550_spi.c
drivers/spi/mpc52xx_spi.c
drivers/spi/omap_spi_100k.c [new file with mode: 0644]
drivers/spi/spi_imx.c
drivers/spi/spi_mpc8xxx.c
drivers/spi/spi_nuc900.c [new file with mode: 0644]
drivers/spi/spi_sh_msiof.c [new file with mode: 0644]
drivers/spi/spidev.c
drivers/spi/xilinx_spi.c
drivers/spi/xilinx_spi.h [new file with mode: 0644]
drivers/spi/xilinx_spi_of.c [new file with mode: 0644]
drivers/spi/xilinx_spi_pltfm.c [new file with mode: 0644]
include/linux/spi/mpc52xx_spi.h [deleted file]
include/linux/spi/sh_msiof.h [new file with mode: 0644]
include/linux/spi/xilinx_spi.h [new file with mode: 0644]

index cff133b..1f21c34 100644 (file)
@@ -5080,6 +5080,7 @@ F:        drivers/char/specialix*
 
 SPI SUBSYSTEM
 M:     David Brownell <dbrownell@users.sourceforge.net>
+M:     Grant Likely <grant.likely@secretlab.ca>
 L:     spi-devel-general@lists.sourceforge.net
 S:     Maintained
 F:     Documentation/spi/
diff --git a/arch/arm/mach-w90x900/include/mach/nuc900_spi.h b/arch/arm/mach-w90x900/include/mach/nuc900_spi.h
new file mode 100644 (file)
index 0000000..bd94819
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/nuc900_spi.h
+ *
+ * Copyright (c) 2009 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.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;version 2 of the License.
+ *
+ */
+
+#ifndef __ASM_ARCH_SPI_H
+#define __ASM_ARCH_SPI_H
+
+extern void mfp_set_groupg(struct device *dev);
+
+struct nuc900_spi_info {
+       unsigned int num_cs;
+       unsigned int lsb;
+       unsigned int txneg;
+       unsigned int rxneg;
+       unsigned int divider;
+       unsigned int sleep;
+       unsigned int txnum;
+       unsigned int txbitlen;
+       int bus_num;
+};
+
+struct nuc900_spi_chip {
+       unsigned char bits_per_word;
+};
+
+#endif /* __ASM_ARCH_SPI_H */
index 28fce65..2d9d703 100644 (file)
@@ -169,6 +169,12 @@ config SPI_OMAP24XX
          SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI
          (McSPI) modules.
 
+config SPI_OMAP_100K
+       tristate "OMAP SPI 100K"
+       depends on SPI_MASTER && (ARCH_OMAP850 || ARCH_OMAP730)
+       help
+         OMAP SPI 100K master controller for omap7xx boards.
+
 config SPI_ORION
        tristate "Orion SPI master (EXPERIMENTAL)"
        depends on PLAT_ORION && EXPERIMENTAL
@@ -220,6 +226,13 @@ config SPI_S3C24XX_GPIO
          the inbuilt hardware cannot provide the transfer mode, or
          where the board is using non hardware connected pins.
 
+config SPI_SH_MSIOF
+       tristate "SuperH MSIOF SPI controller"
+       depends on SUPERH && HAVE_CLK
+       select SPI_BITBANG
+       help
+         SPI driver for SuperH MSIOF blocks.
+
 config SPI_SH_SCI
        tristate "SuperH SCI SPI controller"
        depends on SUPERH
@@ -240,15 +253,38 @@ config SPI_TXX9
          SPI driver for Toshiba TXx9 MIPS SoCs
 
 config SPI_XILINX
-       tristate "Xilinx SPI controller"
-       depends on (XILINX_VIRTEX || MICROBLAZE) && EXPERIMENTAL
+       tristate "Xilinx SPI controller common module"
+       depends on HAS_IOMEM && EXPERIMENTAL
        select SPI_BITBANG
+       select SPI_XILINX_OF if (XILINX_VIRTEX || MICROBLAZE)
        help
          This exposes the SPI controller IP from the Xilinx EDK.
 
          See the "OPB Serial Peripheral Interface (SPI) (v1.00e)"
          Product Specification document (DS464) for hardware details.
 
+         Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)"
+
+config SPI_XILINX_OF
+       tristate "Xilinx SPI controller OF device"
+       depends on SPI_XILINX && (XILINX_VIRTEX || MICROBLAZE)
+       help
+         This is the OF driver for the SPI controller IP from the Xilinx EDK.
+
+config SPI_XILINX_PLTFM
+       tristate "Xilinx SPI controller platform device"
+       depends on SPI_XILINX
+       help
+         This is the platform driver for the SPI controller IP
+         from the Xilinx EDK.
+
+config SPI_NUC900
+       tristate "Nuvoton NUC900 series SPI"
+       depends on ARCH_W90X900 && EXPERIMENTAL
+       select SPI_BITBANG
+       help
+         SPI driver for Nuvoton NUC900 series ARM SoCs
+
 #
 # Add new SPI master controllers in alphabetical order above this line
 #
index e3f092a..ed8c167 100644 (file)
@@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_LM70_LLP)            += spi_lm70llp.o
 obj-$(CONFIG_SPI_PXA2XX)               += pxa2xx_spi.o
 obj-$(CONFIG_SPI_OMAP_UWIRE)           += omap_uwire.o
 obj-$(CONFIG_SPI_OMAP24XX)             += omap2_mcspi.o
+obj-$(CONFIG_SPI_OMAP_100K)            += omap_spi_100k.o
 obj-$(CONFIG_SPI_ORION)                        += orion_spi.o
 obj-$(CONFIG_SPI_PL022)                        += amba-pl022.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)          += mpc52xx_psc_spi.o
@@ -32,8 +33,12 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO)               += spi_s3c24xx_gpio.o
 obj-$(CONFIG_SPI_S3C24XX)              += spi_s3c24xx.o
 obj-$(CONFIG_SPI_TXX9)                 += spi_txx9.o
 obj-$(CONFIG_SPI_XILINX)               += xilinx_spi.o
+obj-$(CONFIG_SPI_XILINX_OF)            += xilinx_spi_of.o
+obj-$(CONFIG_SPI_XILINX_PLTFM)         += xilinx_spi_pltfm.o
 obj-$(CONFIG_SPI_SH_SCI)               += spi_sh_sci.o
+obj-$(CONFIG_SPI_SH_MSIOF)             += spi_sh_msiof.o
 obj-$(CONFIG_SPI_STMP3XXX)             += spi_stmp.o
+obj-$(CONFIG_SPI_NUC900)               += spi_nuc900.o
 #      ... add above this line ...
 
 # SPI protocol drivers (device/link on bus)
index 76cbc1a..cfd5ff9 100644 (file)
@@ -237,8 +237,14 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
        unsigned bpw, hz;
        u32 cfg, stat;
 
-       bpw = t ? t->bits_per_word : spi->bits_per_word;
-       hz = t ? t->speed_hz : spi->max_speed_hz;
+       bpw = spi->bits_per_word;
+       hz = spi->max_speed_hz;
+       if (t) {
+               if (t->bits_per_word)
+                       bpw = t->bits_per_word;
+               if (t->speed_hz)
+                       hz = t->speed_hz;
+       }
 
        if (bpw < 4 || bpw > 24) {
                dev_err(&spi->dev, "setupxfer: invalid bits_per_word=%d\n",
index ef8379b..45bfe64 100644 (file)
@@ -18,9 +18,9 @@
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/spi/spi.h>
-#include <linux/spi/mpc52xx_spi.h>
 #include <linux/of_spi.h>
 #include <linux/io.h>
+#include <linux/of_gpio.h>
 #include <asm/time.h>
 #include <asm/mpc52xx.h>
 
@@ -53,7 +53,7 @@ MODULE_LICENSE("GPL");
 /* FSM state return values */
 #define FSM_STOP       0       /* Nothing more for the state machine to */
                                /* do.  If something interesting happens */
-                               /* then and IRQ will be received */
+                               /* then an IRQ will be received */
 #define FSM_POLL       1       /* need to poll for completion, an IRQ is */
                                /* not expected */
 #define FSM_CONTINUE   2       /* Keep iterating the state machine */
@@ -61,13 +61,12 @@ MODULE_LICENSE("GPL");
 /* Driver internal data */
 struct mpc52xx_spi {
        struct spi_master *master;
-       u32 sysclk;
        void __iomem *regs;
        int irq0;       /* MODF irq */
        int irq1;       /* SPIF irq */
-       int ipb_freq;
+       unsigned int ipb_freq;
 
-       /* Statistics */
+       /* Statistics; not used now, but will be reintroduced for debugfs */
        int msg_count;
        int wcol_count;
        int wcol_ticks;
@@ -79,7 +78,6 @@ struct mpc52xx_spi {
        spinlock_t lock;
        struct work_struct work;
 
-
        /* Details of current transfer (length, and buffer pointers) */
        struct spi_message *message;    /* current message */
        struct spi_transfer *transfer;  /* current transfer */
@@ -89,6 +87,8 @@ struct mpc52xx_spi {
        u8 *rx_buf;
        const u8 *tx_buf;
        int cs_change;
+       int gpio_cs_count;
+       unsigned int *gpio_cs;
 };
 
 /*
@@ -96,7 +96,13 @@ struct mpc52xx_spi {
  */
 static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value)
 {
-       out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08);
+       int cs;
+
+       if (ms->gpio_cs_count > 0) {
+               cs = ms->message->spi->chip_select;
+               gpio_set_value(ms->gpio_cs[cs], value ? 0 : 1);
+       } else
+               out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08);
 }
 
 /*
@@ -221,7 +227,7 @@ static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms,
                ms->wcol_tx_timestamp = get_tbl();
                data = 0;
                if (ms->tx_buf)
-                       data = *(ms->tx_buf-1);
+                       data = *(ms->tx_buf - 1);
                out_8(ms->regs + SPI_DATA, data); /* try again */
                return FSM_CONTINUE;
        } else if (status & SPI_STATUS_MODF) {
@@ -390,7 +396,9 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
        struct spi_master *master;
        struct mpc52xx_spi *ms;
        void __iomem *regs;
-       int rc;
+       u8 ctrl1;
+       int rc, i = 0;
+       int gpio_cs;
 
        /* MMIO registers */
        dev_dbg(&op->dev, "probing mpc5200 SPI device\n");
@@ -399,7 +407,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
                return -ENODEV;
 
        /* initialize the device */
-       out_8(regs+SPI_CTRL1, SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR);
+       ctrl1 = SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR;
+       out_8(regs + SPI_CTRL1, ctrl1);
        out_8(regs + SPI_CTRL2, 0x0);
        out_8(regs + SPI_DATADIR, 0xe); /* Set output pins */
        out_8(regs + SPI_PORTDATA, 0x8);        /* Deassert /SS signal */
@@ -409,6 +418,8 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
         * on the SPI bus.  This fault will also occur if the SPI signals
         * are not connected to any pins (port_config setting) */
        in_8(regs + SPI_STATUS);
+       out_8(regs + SPI_CTRL1, ctrl1);
+
        in_8(regs + SPI_DATA);
        if (in_8(regs + SPI_STATUS) & SPI_STATUS_MODF) {
                dev_err(&op->dev, "mode fault; is port_config correct?\n");
@@ -422,10 +433,12 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
                rc = -ENOMEM;
                goto err_alloc;
        }
+
        master->bus_num = -1;
-       master->num_chipselect = 1;
        master->setup = mpc52xx_spi_setup;
        master->transfer = mpc52xx_spi_transfer;
+       master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
+
        dev_set_drvdata(&op->dev, master);
 
        ms = spi_master_get_devdata(master);
@@ -435,16 +448,51 @@ static int __devinit mpc52xx_spi_probe(struct of_device *op,
        ms->irq1 = irq_of_parse_and_map(op->node, 1);
        ms->state = mpc52xx_spi_fsmstate_idle;
        ms->ipb_freq = mpc5xxx_get_bus_frequency(op->node);
+       ms->gpio_cs_count = of_gpio_count(op->node);
+       if (ms->gpio_cs_count > 0) {
+               master->num_chipselect = ms->gpio_cs_count;
+               ms->gpio_cs = kmalloc(ms->gpio_cs_count * sizeof(unsigned int),
+                               GFP_KERNEL);
+               if (!ms->gpio_cs) {
+                       rc = -ENOMEM;
+                       goto err_alloc;
+               }
+
+               for (i = 0; i < ms->gpio_cs_count; i++) {
+                       gpio_cs = of_get_gpio(op->node, i);
+                       if (gpio_cs < 0) {
+                               dev_err(&op->dev,
+                                       "could not parse the gpio field "
+                                       "in oftree\n");
+                               rc = -ENODEV;
+                               goto err_gpio;
+                       }
+
+                       rc = gpio_request(gpio_cs, dev_name(&op->dev));
+                       if (rc) {
+                               dev_err(&op->dev,
+                                       "can't request spi cs gpio #%d "
+                                       "on gpio line %d\n", i, gpio_cs);
+                               goto err_gpio;
+                       }
+
+                       gpio_direction_output(gpio_cs, 1);
+                       ms->gpio_cs[i] = gpio_cs;
+               }
+       } else {
+               master->num_chipselect = 1;
+       }
+
        spin_lock_init(&ms->lock);
        INIT_LIST_HEAD(&ms->queue);
        INIT_WORK(&ms->work, mpc52xx_spi_wq);
 
        /* Decide if interrupts can be used */
        if (ms->irq0 && ms->irq1) {
-               rc = request_irq(ms->irq0, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM,
+               rc = request_irq(ms->irq0, mpc52xx_spi_irq, 0,
                                  "mpc5200-spi-modf", ms);
-               rc |= request_irq(ms->irq1, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM,
-                                 "mpc5200-spi-spiF", ms);
+               rc |= request_irq(ms->irq1, mpc52xx_spi_irq, 0,
+                                 "mpc5200-spi-spif", ms);
                if (rc) {
                        free_irq(ms->irq0, ms);
                        free_irq(ms->irq1, ms);
@@ -471,6 +519,11 @@ static int __devinit mpc52xx_spi_probe(struct of_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:
  err_init:
        iounmap(regs);
@@ -481,10 +534,15 @@ static int __devexit mpc52xx_spi_remove(struct of_device *op)
 {
        struct spi_master *master = dev_get_drvdata(&op->dev);
        struct mpc52xx_spi *ms = spi_master_get_devdata(master);
+       int i;
 
        free_irq(ms->irq0, ms);
        free_irq(ms->irq1, ms);
 
+       for (i = 0; i < ms->gpio_cs_count; i++)
+               gpio_free(ms->gpio_cs[i]);
+
+       kfree(ms->gpio_cs);
        spi_unregister_master(master);
        spi_master_put(master);
        iounmap(ms->regs);
diff --git a/drivers/spi/omap_spi_100k.c b/drivers/spi/omap_spi_100k.c
new file mode 100644 (file)
index 0000000..5355d90
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * OMAP7xx SPI 100k controller driver
+ * Author: Fabrice Crohas <fcrohas@gmail.com>
+ * from original omap1_mcspi driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ * Author:      Samuel Ortiz <samuel.ortiz@nokia.com> and
+ *              Juha Yrj�l� <juha.yrjola@nokia.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.
+ *
+ * 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/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+
+#include <linux/spi/spi.h>
+
+#include <plat/clock.h>
+
+#define OMAP1_SPI100K_MAX_FREQ          48000000
+
+#define ICR_SPITAS      (OMAP7XX_ICR_BASE + 0x12)
+
+#define SPI_SETUP1      0x00
+#define SPI_SETUP2      0x02
+#define SPI_CTRL        0x04
+#define SPI_STATUS      0x06
+#define SPI_TX_LSB      0x08
+#define SPI_TX_MSB      0x0a
+#define SPI_RX_LSB      0x0c
+#define SPI_RX_MSB      0x0e
+
+#define SPI_SETUP1_INT_READ_ENABLE      (1UL << 5)
+#define SPI_SETUP1_INT_WRITE_ENABLE     (1UL << 4)
+#define SPI_SETUP1_CLOCK_DIVISOR(x)     ((x) << 1)
+#define SPI_SETUP1_CLOCK_ENABLE         (1UL << 0)
+
+#define SPI_SETUP2_ACTIVE_EDGE_FALLING  (0UL << 0)
+#define SPI_SETUP2_ACTIVE_EDGE_RISING   (1UL << 0)
+#define SPI_SETUP2_NEGATIVE_LEVEL       (0UL << 5)
+#define SPI_SETUP2_POSITIVE_LEVEL       (1UL << 5)
+#define SPI_SETUP2_LEVEL_TRIGGER        (0UL << 10)
+#define SPI_SETUP2_EDGE_TRIGGER         (1UL << 10)
+
+#define SPI_CTRL_SEN(x)                 ((x) << 7)
+#define SPI_CTRL_WORD_SIZE(x)           (((x) - 1) << 2)
+#define SPI_CTRL_WR                     (1UL << 1)
+#define SPI_CTRL_RD                     (1UL << 0)
+
+#define SPI_STATUS_WE                   (1UL << 1)
+#define SPI_STATUS_RD                   (1UL << 0)
+
+#define WRITE 0
+#define READ  1
+
+
+/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
+ * cache operations; better heuristics consider wordsize and bitrate.
+ */
+#define DMA_MIN_BYTES                   8
+
+#define SPI_RUNNING    0
+#define SPI_SHUTDOWN   1
+
+struct omap1_spi100k {
+       struct work_struct      work;
+
+       /* lock protects queue and registers */
+       spinlock_t              lock;
+       struct list_head        msg_queue;
+       struct spi_master       *master;
+       struct clk              *ick;
+       struct clk              *fck;
+
+       /* Virtual base address of the controller */
+       void __iomem            *base;
+
+       /* State of the SPI */
+       unsigned int            state;
+};
+
+struct omap1_spi100k_cs {
+       void __iomem            *base;
+       int                     word_len;
+};
+
+static struct workqueue_struct *omap1_spi100k_wq;
+
+#define MOD_REG_BIT(val, mask, set) do { \
+       if (set) \
+               val |= mask; \
+       else \
+               val &= ~mask; \
+} while (0)
+
+static void spi100k_enable_clock(struct spi_master *master)
+{
+       unsigned int val;
+       struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
+
+       /* enable SPI */
+       val = readw(spi100k->base + SPI_SETUP1);
+       val |= SPI_SETUP1_CLOCK_ENABLE;
+       writew(val, spi100k->base + SPI_SETUP1);
+}
+
+static void spi100k_disable_clock(struct spi_master *master)
+{
+       unsigned int val;
+       struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
+
+       /* disable SPI */
+       val = readw(spi100k->base + SPI_SETUP1);
+       val &= ~SPI_SETUP1_CLOCK_ENABLE;
+       writew(val, spi100k->base + SPI_SETUP1);
+}
+
+static void spi100k_write_data(struct spi_master *master, int len, int data)
+{
+       struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
+
+       /* write 16-bit word */
+       spi100k_enable_clock(master);
+       writew( data , spi100k->base + SPI_TX_MSB);
+
+       writew(SPI_CTRL_SEN(0) |
+              SPI_CTRL_WORD_SIZE(len) |
+              SPI_CTRL_WR,
+              spi100k->base + SPI_CTRL);
+
+       /* Wait for bit ack send change */
+       while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE);
+       udelay(1000);
+
+       spi100k_disable_clock(master);
+}
+
+static int spi100k_read_data(struct spi_master *master, int len)
+{
+       int dataH,dataL;
+       struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
+
+       spi100k_enable_clock(master);
+       writew(SPI_CTRL_SEN(0) |
+              SPI_CTRL_WORD_SIZE(len) |
+              SPI_CTRL_RD,
+              spi100k->base + SPI_CTRL);
+
+       while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD);
+       udelay(1000);
+
+       dataL = readw(spi100k->base + SPI_RX_LSB);
+       dataH = readw(spi100k->base + SPI_RX_MSB);
+       spi100k_disable_clock(master);
+
+       return dataL;
+}
+
+static void spi100k_open(struct spi_master *master)
+{
+       /* get control of SPI */
+       struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
+
+       writew(SPI_SETUP1_INT_READ_ENABLE |
+              SPI_SETUP1_INT_WRITE_ENABLE |
+              SPI_SETUP1_CLOCK_DIVISOR(0), spi100k->base + SPI_SETUP1);
+
+       /* configure clock and interrupts */
+       writew(SPI_SETUP2_ACTIVE_EDGE_FALLING |
+              SPI_SETUP2_NEGATIVE_LEVEL |
+              SPI_SETUP2_LEVEL_TRIGGER, spi100k->base + SPI_SETUP2);
+}
+
+static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable)
+{
+       if (enable)
+               writew(0x05fc, spi100k->base + SPI_CTRL);
+       else
+               writew(0x05fd, spi100k->base + SPI_CTRL);
+}
+
+static unsigned
+omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
+{
+       struct omap1_spi100k    *spi100k;
+       struct omap1_spi100k_cs *cs = spi->controller_state;
+       unsigned int            count, c;
+       int                     word_len;
+
+       spi100k = spi_master_get_devdata(spi->master);
+       count = xfer->len;
+       c = count;
+       word_len = cs->word_len;
+
+       /* RX_ONLY mode needs dummy data in TX reg */
+       if (xfer->tx_buf == NULL)
+               spi100k_write_data(spi->master,word_len, 0);
+
+       if (word_len <= 8) {
+               u8              *rx;
+               const u8        *tx;
+
+               rx = xfer->rx_buf;
+               tx = xfer->tx_buf;
+               do {
+                       c-=1;
+                       if (xfer->tx_buf != NULL)
+                               spi100k_write_data(spi->master,word_len, *tx);
+                       if (xfer->rx_buf != NULL)
+                               *rx = spi100k_read_data(spi->master,word_len);
+               } while(c);
+       } else if (word_len <= 16) {
+               u16             *rx;
+               const u16       *tx;
+
+               rx = xfer->rx_buf;
+               tx = xfer->tx_buf;
+               do {
+                       c-=2;
+                       if (xfer->tx_buf != NULL)
+                               spi100k_write_data(spi->master,word_len, *tx++);
+                       if (xfer->rx_buf != NULL)
+                               *rx++ = spi100k_read_data(spi->master,word_len);
+               } while(c);
+       } else if (word_len <= 32) {
+               u32             *rx;
+               const u32       *tx;
+
+               rx = xfer->rx_buf;
+               tx = xfer->tx_buf;
+               do {
+                       c-=4;
+                       if (xfer->tx_buf != NULL)
+                               spi100k_write_data(spi->master,word_len, *tx);
+                       if (xfer->rx_buf != NULL)
+                               *rx = spi100k_read_data(spi->master,word_len);
+               } while(c);
+       }
+       return count - c;
+}
+
+/* called only when no transfer is active to this device */
+static int omap1_spi100k_setup_transfer(struct spi_device *spi,
+               struct spi_transfer *t)
+{
+       struct omap1_spi100k *spi100k = spi_master_get_devdata(spi->master);
+       struct omap1_spi100k_cs *cs = spi->controller_state;
+       u8 word_len = spi->bits_per_word;
+
+       if (t != NULL && t->bits_per_word)
+               word_len = t->bits_per_word;
+       if (!word_len)
+               word_len = 8;
+
+       if (spi->bits_per_word > 32)
+               return -EINVAL;
+       cs->word_len = word_len;
+
+       /* SPI init before transfer */
+       writew(0x3e , spi100k->base + SPI_SETUP1);
+       writew(0x00 , spi100k->base + SPI_STATUS);
+       writew(0x3e , spi100k->base + SPI_CTRL);
+
+       return 0;
+}
+
+/* the spi->mode bits understood by this driver: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
+
+static int omap1_spi100k_setup(struct spi_device *spi)
+{
+       int                     ret;
+       struct omap1_spi100k    *spi100k;
+       struct omap1_spi100k_cs *cs = spi->controller_state;
+
+       if (spi->bits_per_word < 4 || spi->bits_per_word > 32) {
+                dev_dbg(&spi->dev, "setup: unsupported %d bit words\n",
+                       spi->bits_per_word);
+                return -EINVAL;
+       }
+
+       spi100k = spi_master_get_devdata(spi->master);
+
+       if (!cs) {
+               cs = kzalloc(sizeof *cs, GFP_KERNEL);
+               if (!cs)
+                       return -ENOMEM;
+               cs->base = spi100k->base + spi->chip_select * 0x14;
+               spi->controller_state = cs;
+       }
+
+       spi100k_open(spi->master);
+
+       clk_enable(spi100k->ick);
+       clk_enable(spi100k->fck);
+
+       ret = omap1_spi100k_setup_transfer(spi, NULL);
+
+       clk_disable(spi100k->ick);
+       clk_disable(spi100k->fck);
+
+       return ret;
+}
+
+static void omap1_spi100k_work(struct work_struct *work)
+{
+       struct omap1_spi100k    *spi100k;
+       int status = 0;
+
+       spi100k = container_of(work, struct omap1_spi100k, work);
+       spin_lock_irq(&spi100k->lock);
+
+       clk_enable(spi100k->ick);
+       clk_enable(spi100k->fck);
+
+       /* We only enable one channel at a time -- the one whose message is
+        * at the head of the queue -- although this controller would gladly
+        * arbitrate among multiple channels.  This corresponds to "single
+        * channel" master mode.  As a side effect, we need to manage the
+        * chipselect with the FORCE bit ... CS != channel enable.
+        */
+        while (!list_empty(&spi100k->msg_queue)) {
+               struct spi_message              *m;
+               struct spi_device               *spi;
+               struct spi_transfer             *t = NULL;
+               int                             cs_active = 0;
+               struct omap1_spi100k_cs         *cs;
+               int                             par_override = 0;
+
+               m = container_of(spi100k->msg_queue.next, struct spi_message,
+                                queue);
+
+               list_del_init(&m->queue);
+               spin_unlock_irq(&spi100k->lock);
+
+               spi = m->spi;
+               cs = spi->controller_state;
+
+               list_for_each_entry(t, &m->transfers, transfer_list) {
+                       if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
+                               status = -EINVAL;
+                               break;
+                       }
+                       if (par_override || t->speed_hz || t->bits_per_word) {
+                               par_override = 1;
+                               status = omap1_spi100k_setup_transfer(spi, t);
+                               if (status < 0)
+                                       break;
+                               if (!t->speed_hz && !t->bits_per_word)
+                                       par_override = 0;
+                       }
+
+                       if (!cs_active) {
+                               omap1_spi100k_force_cs(spi100k, 1);
+                               cs_active = 1;
+                       }
+
+                       if (t->len) {
+                               unsigned count;
+
+                               /* RX_ONLY mode needs dummy data in TX reg */
+                               if (t->tx_buf == NULL)
+                                       spi100k_write_data(spi->master, 8, 0);
+
+                               count = omap1_spi100k_txrx_pio(spi, t);
+                               m->actual_length += count;
+
+                               if (count != t->len) {
+                                       status = -EIO;
+                                       break;
+                               }
+                       }
+
+                       if (t->delay_usecs)
+                               udelay(t->delay_usecs);
+
+                       /* ignore the "leave it on after last xfer" hint */
+
+                       if (t->cs_change) {
+                               omap1_spi100k_force_cs(spi100k, 0);
+                               cs_active = 0;
+                       }
+               }
+
+               /* Restore defaults if they were overriden */
+               if (par_override) {
+                       par_override = 0;
+                       status = omap1_spi100k_setup_transfer(spi, NULL);
+               }
+
+               if (cs_active)
+                       omap1_spi100k_force_cs(spi100k, 0);
+
+               m->status = status;
+               m->complete(m->context);
+
+               spin_lock_irq(&spi100k->lock);
+       }
+
+       clk_disable(spi100k->ick);
+       clk_disable(spi100k->fck);
+       spin_unlock_irq(&spi100k->lock);
+
+       if (status < 0)
+               printk(KERN_WARNING "spi transfer failed with %d\n", status);
+}
+
+static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m)
+{
+       struct omap1_spi100k    *spi100k;
+       unsigned long           flags;
+       struct spi_transfer     *t;
+
+       m->actual_length = 0;
+       m->status = -EINPROGRESS;
+
+       spi100k = spi_master_get_devdata(spi->master);
+
+       /* Don't accept new work if we're shutting down */
+       if (spi100k->state == SPI_SHUTDOWN)
+               return -ESHUTDOWN;
+
+       /* reject invalid messages and transfers */
+       if (list_empty(&m->transfers) || !m->complete)
+               return -EINVAL;
+
+       list_for_each_entry(t, &m->transfers, transfer_list) {
+               const void      *tx_buf = t->tx_buf;
+               void            *rx_buf = t->rx_buf;
+               unsigned        len = t->len;
+
+               if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ
+                               || (len && !(rx_buf || tx_buf))
+                               || (t->bits_per_word &&
+                                       (  t->bits_per_word < 4
+                                       || t->bits_per_word > 32))) {
+                       dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
+                                       t->speed_hz,
+                                       len,
+                                       tx_buf ? "tx" : "",
+                                       rx_buf ? "rx" : "",
+                                       t->bits_per_word);
+                       return -EINVAL;
+               }
+
+               if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) {
+                       dev_dbg(&spi->dev, "%d Hz max exceeds %d\n",
+                                       t->speed_hz,
+                                       OMAP1_SPI100K_MAX_FREQ/(1<<16));
+                       return -EINVAL;
+               }
+
+       }
+
+       spin_lock_irqsave(&spi100k->lock, flags);
+       list_add_tail(&m->queue, &spi100k->msg_queue);
+       queue_work(omap1_spi100k_wq, &spi100k->work);
+       spin_unlock_irqrestore(&spi100k->lock, flags);
+
+       return 0;
+}
+
+static int __init omap1_spi100k_reset(struct omap1_spi100k *spi100k)
+{
+       return 0;
+}
+
+static int __devinit omap1_spi100k_probe(struct platform_device *pdev)
+{
+       struct spi_master       *master;
+       struct omap1_spi100k    *spi100k;
+       int                     status = 0;
+
+       if (!pdev->id)
+               return -EINVAL;
+
+       master = spi_alloc_master(&pdev->dev, sizeof *spi100k);
+       if (master == NULL) {
+               dev_dbg(&pdev->dev, "master allocation failed\n");
+               return -ENOMEM;
+       }
+
+       if (pdev->id != -1)
+              master->bus_num = pdev->id;
+
+       master->setup = omap1_spi100k_setup;
+       master->transfer = omap1_spi100k_transfer;
+       master->cleanup = NULL;
+       master->num_chipselect = 2;
+       master->mode_bits = MODEBITS;
+
+       dev_set_drvdata(&pdev->dev, master);
+
+       spi100k = spi_master_get_devdata(master);
+       spi100k->master = master;
+
+       /*
+        * The memory region base address is taken as the platform_data.
+        * You should allocate this with ioremap() before initializing
+        * the SPI.
+        */
+       spi100k->base = (void __iomem *) pdev->dev.platform_data;
+
+       INIT_WORK(&spi100k->work, omap1_spi100k_work);
+
+       spin_lock_init(&spi100k->lock);
+       INIT_LIST_HEAD(&spi100k->msg_queue);
+       spi100k->ick = clk_get(&pdev->dev, "ick");
+       if (IS_ERR(spi100k->ick)) {
+               dev_dbg(&pdev->dev, "can't get spi100k_ick\n");
+               status = PTR_ERR(spi100k->ick);
+               goto err1;
+       }
+
+       spi100k->fck = clk_get(&pdev->dev, "fck");
+       if (IS_ERR(spi100k->fck)) {
+               dev_dbg(&pdev->dev, "can't get spi100k_fck\n");
+               status = PTR_ERR(spi100k->fck);
+               goto err2;
+       }
+
+       if (omap1_spi100k_reset(spi100k) < 0)
+               goto err3;
+
+       status = spi_register_master(master);
+       if (status < 0)
+               goto err3;
+
+       spi100k->state = SPI_RUNNING;
+
+       return status;
+
+err3:
+       clk_put(spi100k->fck);
+err2:
+       clk_put(spi100k->ick);
+err1:
+       spi_master_put(master);
+       return status;
+}
+
+static int __exit omap1_spi100k_remove(struct platform_device *pdev)
+{
+       struct spi_master       *master;
+       struct omap1_spi100k    *spi100k;
+       struct resource         *r;
+       unsigned                limit = 500;
+       unsigned long           flags;
+       int                     status = 0;
+
+       master = dev_get_drvdata(&pdev->dev);
+       spi100k = spi_master_get_devdata(master);
+
+       spin_lock_irqsave(&spi100k->lock, flags);
+
+       spi100k->state = SPI_SHUTDOWN;
+       while (!list_empty(&spi100k->msg_queue) && limit--) {
+               spin_unlock_irqrestore(&spi100k->lock, flags);
+               msleep(10);
+               spin_lock_irqsave(&spi100k->lock, flags);
+       }
+
+       if (!list_empty(&spi100k->msg_queue))
+               status = -EBUSY;
+
+       spin_unlock_irqrestore(&spi100k->lock, flags);
+
+       if (status != 0)
+               return status;
+
+       clk_put(spi100k->fck);
+       clk_put(spi100k->ick);
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       spi_unregister_master(master);
+
+       return 0;
+}
+
+static struct platform_driver omap1_spi100k_driver = {
+       .driver = {
+               .name           = "omap1_spi100k",
+               .owner          = THIS_MODULE,
+       },
+       .remove         = __exit_p(omap1_spi100k_remove),
+};
+
+
+static int __init omap1_spi100k_init(void)
+{
+       omap1_spi100k_wq = create_singlethread_workqueue(
+                       omap1_spi100k_driver.driver.name);
+
+       if (omap1_spi100k_wq == NULL)
+               return -1;
+
+       return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe);
+}
+
+static void __exit omap1_spi100k_exit(void)
+{
+       platform_driver_unregister(&omap1_spi100k_driver);
+
+       destroy_workqueue(omap1_spi100k_wq);
+}
+
+module_init(omap1_spi100k_init);
+module_exit(omap1_spi100k_exit);
+
+MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver");
+MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>");
+MODULE_LICENSE("GPL");
+
index 89c22ef..1893f1e 100644 (file)
@@ -44,6 +44,9 @@
 #define MXC_CSPIINT            0x0c
 #define MXC_RESET              0x1c
 
+#define MX3_CSPISTAT           0x14
+#define MX3_CSPISTAT_RR                (1 << 3)
+
 /* generic defines to abstract from the different register layouts */
 #define MXC_INT_RR     (1 << 0) /* Receive data ready interrupt */
 #define MXC_INT_TE     (1 << 1) /* Transmit FIFO empty interrupt */
@@ -205,7 +208,7 @@ static int mx31_config(struct spi_imx_data *spi_imx,
 
        if (cpu_is_mx31())
                reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT;
-       else if (cpu_is_mx35()) {
+       else if (cpu_is_mx25() || cpu_is_mx35()) {
                reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT;
                reg |= MX31_CSPICTRL_SSCTL;
        }
@@ -219,7 +222,7 @@ static int mx31_config(struct spi_imx_data *spi_imx,
        if (config->cs < 0) {
                if (cpu_is_mx31())
                        reg |= (config->cs + 32) << MX31_CSPICTRL_CS_SHIFT;
-               else if (cpu_is_mx35())
+               else if (cpu_is_mx25() || cpu_is_mx35())
                        reg |= (config->cs + 32) << MX35_CSPICTRL_CS_SHIFT;
        }
 
@@ -481,7 +484,7 @@ static void spi_imx_cleanup(struct spi_device *spi)
 {
 }
 
-static int __init spi_imx_probe(struct platform_device *pdev)
+static int __devinit spi_imx_probe(struct platform_device *pdev)
 {
        struct spi_imx_master *mxc_platform_info;
        struct spi_master *master;
@@ -489,7 +492,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
        struct resource *res;
        int i, ret;
 
-       mxc_platform_info = (struct spi_imx_master *)pdev->dev.platform_data;
+       mxc_platform_info = dev_get_platdata(&pdev->dev);
        if (!mxc_platform_info) {
                dev_err(&pdev->dev, "can't get the platform data\n");
                return -EINVAL;
@@ -513,11 +516,12 @@ static int __init spi_imx_probe(struct platform_device *pdev)
                        continue;
                ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
                if (ret) {
-                       i--;
-                       while (i > 0)
+                       while (i > 0) {
+                               i--;
                                if (spi_imx->chipselect[i] >= 0)
-                                       gpio_free(spi_imx->chipselect[i--]);
-                       dev_err(&pdev->dev, "can't get cs gpios");
+                                       gpio_free(spi_imx->chipselect[i]);
+                       }
+                       dev_err(&pdev->dev, "can't get cs gpios\n");
                        goto out_master_put;
                }
        }
@@ -551,7 +555,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
        }
 
        spi_imx->irq = platform_get_irq(pdev, 0);
-       if (!spi_imx->irq) {
+       if (spi_imx->irq <= 0) {
                ret = -EINVAL;
                goto out_iounmap;
        }
@@ -562,7 +566,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
                goto out_iounmap;
        }
 
-       if (cpu_is_mx31() || cpu_is_mx35()) {
+       if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35()) {
                spi_imx->intctrl = mx31_intctrl;
                spi_imx->config = mx31_config;
                spi_imx->trigger = mx31_trigger;
@@ -590,9 +594,14 @@ static int __init spi_imx_probe(struct platform_device *pdev)
        clk_enable(spi_imx->clk);
        spi_imx->spi_clk = clk_get_rate(spi_imx->clk);
 
-       if (!cpu_is_mx31() || !cpu_is_mx35())
+       if (cpu_is_mx1() || cpu_is_mx21() || cpu_is_mx27())
                writel(1, spi_imx->base + MXC_RESET);
 
+       /* drain receive buffer */
+       if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35())
+               while (readl(spi_imx->base + MX3_CSPISTAT) & MX3_CSPISTAT_RR)
+                       readl(spi_imx->base + MXC_CSPIRXDATA);
+
        spi_imx->intctrl(spi_imx, 0);
 
        ret = spi_bitbang_start(&spi_imx->bitbang);
@@ -625,7 +634,7 @@ out_master_put:
        return ret;
 }
 
-static int __exit spi_imx_remove(struct platform_device *pdev)
+static int __devexit spi_imx_remove(struct platform_device *pdev)
 {
        struct spi_master *master = platform_get_drvdata(pdev);
        struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -659,7 +668,7 @@ static struct platform_driver spi_imx_driver = {
                   .owner = THIS_MODULE,
                   },
        .probe = spi_imx_probe,
-       .remove = __exit_p(spi_imx_remove),
+       .remove = __devexit_p(spi_imx_remove),
 };
 
 static int __init spi_imx_init(void)
index 930135d..e9390d7 100644 (file)
@@ -1356,7 +1356,7 @@ static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev)
 MODULE_ALIAS("platform:mpc8xxx_spi");
 static struct platform_driver mpc8xxx_spi_driver = {
        .probe = plat_mpc8xxx_spi_probe,
-       .remove = __exit_p(plat_mpc8xxx_spi_remove),
+       .remove = __devexit_p(plat_mpc8xxx_spi_remove),
        .driver = {
                .name = "mpc8xxx_spi",
                .owner = THIS_MODULE,
diff --git a/drivers/spi/spi_nuc900.c b/drivers/spi/spi_nuc900.c
new file mode 100644 (file)
index 0000000..b319f9b
--- /dev/null
@@ -0,0 +1,504 @@
+/* linux/drivers/spi/spi_nuc900.c
+ *
+ * Copyright (c) 2009 Nuvoton technology.
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+
+#include <mach/nuc900_spi.h>
+
+/* usi registers offset */
+#define USI_CNT                0x00
+#define USI_DIV                0x04
+#define USI_SSR                0x08
+#define USI_RX0                0x10
+#define USI_TX0                0x10
+
+/* usi register bit */
+#define ENINT          (0x01 << 17)
+#define ENFLG          (0x01 << 16)
+#define TXNUM          (0x03 << 8)
+#define TXNEG          (0x01 << 2)
+#define RXNEG          (0x01 << 1)
+#define LSB            (0x01 << 10)
+#define SELECTLEV      (0x01 << 2)
+#define SELECTPOL      (0x01 << 31)
+#define SELECTSLAVE    0x01
+#define GOBUSY         0x01
+
+struct nuc900_spi {
+       struct spi_bitbang       bitbang;
+       struct completion        done;
+       void __iomem            *regs;
+       int                      irq;
+       int                      len;
+       int                      count;
+       const unsigned char     *tx;
+       unsigned char           *rx;
+       struct clk              *clk;
+       struct resource         *ioarea;
+       struct spi_master       *master;
+       struct spi_device       *curdev;
+       struct device           *dev;
+       struct nuc900_spi_info *pdata;
+       spinlock_t              lock;
+       struct resource         *res;
+};
+
+static inline struct nuc900_spi *to_hw(struct spi_device *sdev)
+{
+       return spi_master_get_devdata(sdev->master);
+}
+
+static void nuc900_slave_select(struct spi_device *spi, unsigned int ssr)
+{
+       struct nuc900_spi *hw = to_hw(spi);
+       unsigned int val;
+       unsigned int cs = spi->mode & SPI_CS_HIGH ? 1 : 0;
+       unsigned int cpol = spi->mode & SPI_CPOL ? 1 : 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hw->lock, flags);
+
+       val = __raw_readl(hw->regs + USI_SSR);
+
+       if (!cs)
+               val &= ~SELECTLEV;
+       else
+               val |= SELECTLEV;
+
+       if (!ssr)
+               val &= ~SELECTSLAVE;
+       else
+               val |= SELECTSLAVE;
+
+       __raw_writel(val, hw->regs + USI_SSR);
+
+       val = __raw_readl(hw->regs + USI_CNT);
+
+       if (!cpol)
+               val &= ~SELECTPOL;
+       else
+               val |= SELECTPOL;
+
+       __raw_writel(val, hw->regs + USI_CNT);
+
+       spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_spi_chipsel(struct spi_device *spi, int value)
+{
+       switch (value) {
+       case BITBANG_CS_INACTIVE:
+               nuc900_slave_select(spi, 0);
+               break;
+
+       case BITBANG_CS_ACTIVE:
+               nuc900_slave_select(spi, 1);
+               break;
+       }
+}
+
+static void nuc900_spi_setup_txnum(struct nuc900_spi *hw,
+                                                       unsigned int txnum)
+{
+       unsigned int val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hw->lock, flags);
+
+       val = __raw_readl(hw->regs + USI_CNT);
+
+       if (!txnum)
+               val &= ~TXNUM;
+       else
+               val |= txnum << 0x08;
+
+       __raw_writel(val, hw->regs + USI_CNT);
+
+       spin_unlock_irqrestore(&hw->lock, flags);
+
+}
+
+static void nuc900_spi_setup_txbitlen(struct nuc900_spi *hw,
+                                                       unsigned int txbitlen)
+{
+       unsigned int val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hw->lock, flags);
+
+       val = __raw_readl(hw->regs + USI_CNT);
+
+       val |= (txbitlen << 0x03);
+
+       __raw_writel(val, hw->regs + USI_CNT);
+
+       spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_spi_gobusy(struct nuc900_spi *hw)
+{
+       unsigned int val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hw->lock, flags);
+
+       val = __raw_readl(hw->regs + USI_CNT);
+
+       val |= GOBUSY;
+
+       __raw_writel(val, hw->regs + USI_CNT);
+
+       spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static int nuc900_spi_setupxfer(struct spi_device *spi,
+                                struct spi_transfer *t)
+{
+       return 0;
+}
+
+static int nuc900_spi_setup(struct spi_device *spi)
+{
+       return 0;
+}
+
+static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count)
+{
+       return hw->tx ? hw->tx[count] : 0;
+}
+
+static int nuc900_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
+{
+       struct nuc900_spi *hw = to_hw(spi);
+
+       hw->tx = t->tx_buf;
+       hw->rx = t->rx_buf;
+       hw->len = t->len;
+       hw->count = 0;
+
+       __raw_writel(hw_txbyte(hw, 0x0), hw->regs + USI_TX0);
+
+       nuc900_spi_gobusy(hw);
+
+       wait_for_completion(&hw->done);
+
+       return hw->count;
+}
+
+static irqreturn_t nuc900_spi_irq(int irq, void *dev)
+{
+       struct nuc900_spi *hw = dev;
+       unsigned int status;
+       unsigned int count = hw->count;
+
+       status = __raw_readl(hw->regs + USI_CNT);
+       __raw_writel(status, hw->regs + USI_CNT);
+
+       if (status & ENFLG) {
+               hw->count++;
+
+               if (hw->rx)
+                       hw->rx[count] = __raw_readl(hw->regs + USI_RX0);
+               count++;
+
+               if (count < hw->len) {
+                       __raw_writel(hw_txbyte(hw, count), hw->regs + USI_TX0);
+                       nuc900_spi_gobusy(hw);
+               } else {
+                       complete(&hw->done);
+               }
+
+               return IRQ_HANDLED;
+       }
+
+       complete(&hw->done);
+       return IRQ_HANDLED;
+}
+
+static void nuc900_tx_edge(struct nuc900_spi *hw, unsigned int edge)
+{
+       unsigned int val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hw->lock, flags);
+
+       val = __raw_readl(hw->regs + USI_CNT);
+
+       if (edge)
+               val |= TXNEG;
+       else
+               val &= ~TXNEG;
+       __raw_writel(val, hw->regs + USI_CNT);
+
+       spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_rx_edge(struct nuc900_spi *hw, unsigned int edge)
+{
+       unsigned int val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hw->lock, flags);
+
+       val = __raw_readl(hw->regs + USI_CNT);
+
+       if (edge)
+               val |= RXNEG;
+       else
+               val &= ~RXNEG;
+       __raw_writel(val, hw->regs + USI_CNT);
+
+       spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_send_first(struct nuc900_spi *hw, unsigned int lsb)
+{
+       unsigned int val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hw->lock, flags);
+
+       val = __raw_readl(hw->regs + USI_CNT);
+
+       if (lsb)
+               val |= LSB;
+       else
+               val &= ~LSB;
+       __raw_writel(val, hw->regs + USI_CNT);
+
+       spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_set_sleep(struct nuc900_spi *hw, unsigned int sleep)
+{
+       unsigned int val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hw->lock, flags);
+
+       val = __raw_readl(hw->regs + USI_CNT);
+
+       if (sleep)
+               val |= (sleep << 12);
+       else
+               val &= ~(0x0f << 12);
+       __raw_writel(val, hw->regs + USI_CNT);
+
+       spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_enable_int(struct nuc900_spi *hw)
+{
+       unsigned int val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hw->lock, flags);
+
+       val = __raw_readl(hw->regs + USI_CNT);
+
+       val |= ENINT;
+
+       __raw_writel(val, hw->regs + USI_CNT);
+
+       spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+static void nuc900_set_divider(struct nuc900_spi *hw)
+{
+       __raw_writel(hw->pdata->divider, hw->regs + USI_DIV);
+}
+
+static void nuc900_init_spi(struct nuc900_spi *hw)
+{
+       clk_enable(hw->clk);
+       spin_lock_init(&hw->lock);
+
+       nuc900_tx_edge(hw, hw->pdata->txneg);
+       nuc900_rx_edge(hw, hw->pdata->rxneg);
+       nuc900_send_first(hw, hw->pdata->lsb);
+       nuc900_set_sleep(hw, hw->pdata->sleep);
+       nuc900_spi_setup_txbitlen(hw, hw->pdata->txbitlen);
+       nuc900_spi_setup_txnum(hw, hw->pdata->txnum);
+       nuc900_set_divider(hw);
+       nuc900_enable_int(hw);
+}
+
+static int __devinit nuc900_spi_probe(struct platform_device *pdev)
+{
+       struct nuc900_spi *hw;
+       struct spi_master *master;
+       int err = 0;
+
+       master = spi_alloc_master(&pdev->dev, sizeof(struct nuc900_spi));
+       if (master == NULL) {
+               dev_err(&pdev->dev, "No memory for spi_master\n");
+               err = -ENOMEM;
+               goto err_nomem;
+       }
+
+       hw = spi_master_get_devdata(master);
+       memset(hw, 0, sizeof(struct nuc900_spi));
+
+       hw->master = spi_master_get(master);
+       hw->pdata  = pdev->dev.platform_data;
+       hw->dev = &pdev->dev;
+
+       if (hw->pdata == NULL) {
+               dev_err(&pdev->dev, "No platform data supplied\n");
+               err = -ENOENT;
+               goto err_pdata;
+       }
+
+       platform_set_drvdata(pdev, hw);
+       init_completion(&hw->done);
+
+       master->mode_bits          = SPI_MODE_0;
+       master->num_chipselect     = hw->pdata->num_cs;
+       master->bus_num            = hw->pdata->bus_num;
+       hw->bitbang.master         = hw->master;
+       hw->bitbang.setup_transfer = nuc900_spi_setupxfer;
+       hw->bitbang.chipselect     = nuc900_spi_chipsel;
+       hw->bitbang.txrx_bufs      = nuc900_spi_txrx;
+       hw->bitbang.master->setup  = nuc900_spi_setup;
+
+       hw->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (hw->res == NULL) {
+               dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
+               err = -ENOENT;
+               goto err_pdata;
+       }
+
+       hw->ioarea = request_mem_region(hw->res->start,
+                                       resource_size(hw->res), pdev->name);
+
+       if (hw->ioarea == NULL) {
+               dev_err(&pdev->dev, "Cannot reserve region\n");
+               err = -ENXIO;
+               goto err_pdata;
+       }
+
+       hw->regs = ioremap(hw->res->start, resource_size(hw->res));
+       if (hw->regs == NULL) {
+               dev_err(&pdev->dev, "Cannot map IO\n");
+               err = -ENXIO;
+               goto err_iomap;
+       }
+
+       hw->irq = platform_get_irq(pdev, 0);
+       if (hw->irq < 0) {
+               dev_err(&pdev->dev, "No IRQ specified\n");
+               err = -ENOENT;
+               goto err_irq;
+       }
+
+       err = request_irq(hw->irq, nuc900_spi_irq, 0, pdev->name, hw);
+       if (err) {
+               dev_err(&pdev->dev, "Cannot claim IRQ\n");
+               goto err_irq;
+       }
+
+       hw->clk = clk_get(&pdev->dev, "spi");
+       if (IS_ERR(hw->clk)) {
+               dev_err(&pdev->dev, "No clock for device\n");
+               err = PTR_ERR(hw->clk);
+               goto err_clk;
+       }
+
+       mfp_set_groupg(&pdev->dev);
+       nuc900_init_spi(hw);
+
+       err = spi_bitbang_start(&hw->bitbang);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to register SPI master\n");
+               goto err_register;
+       }
+
+       return 0;
+
+err_register:
+       clk_disable(hw->clk);
+       clk_put(hw->clk);
+err_clk:
+       free_irq(hw->irq, hw);
+err_irq:
+       iounmap(hw->regs);
+err_iomap:
+       release_mem_region(hw->res->start, resource_size(hw->res));
+       kfree(hw->ioarea);
+err_pdata:
+       spi_master_put(hw->master);;
+
+err_nomem:
+       return err;
+}
+
+static int __devexit nuc900_spi_remove(struct platform_device *dev)
+{
+       struct nuc900_spi *hw = platform_get_drvdata(dev);
+
+       free_irq(hw->irq, hw);
+
+       platform_set_drvdata(dev, NULL);
+
+       spi_unregister_master(hw->master);
+
+       clk_disable(hw->clk);
+       clk_put(hw->clk);
+
+       iounmap(hw->regs);
+
+       release_mem_region(hw->res->start, resource_size(hw->res));
+       kfree(hw->ioarea);
+
+       spi_master_put(hw->master);
+       return 0;
+}
+
+static struct platform_driver nuc900_spi_driver = {
+       .probe          = nuc900_spi_probe,
+       .remove         = __devexit_p(nuc900_spi_remove),
+       .driver         = {
+               .name   = "nuc900-spi",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init nuc900_spi_init(void)
+{
+       return platform_driver_register(&nuc900_spi_driver);
+}
+
+static void __exit nuc900_spi_exit(void)
+{
+       platform_driver_unregister(&nuc900_spi_driver);
+}
+
+module_init(nuc900_spi_init);
+module_exit(nuc900_spi_exit);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("nuc900 spi driver!");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nuc900-spi");
diff --git a/drivers/spi/spi_sh_msiof.c b/drivers/spi/spi_sh_msiof.c
new file mode 100644 (file)
index 0000000..51e5e1d
--- /dev/null
@@ -0,0 +1,691 @@
+/*
+ * SuperH MSIOF SPI Master Interface
+ *
+ * Copyright (c) 2009 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/pm_runtime.h>
+#include <linux/gpio.h>
+#include <linux/bitmap.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/spi/sh_msiof.h>
+
+#include <asm/spi.h>
+#include <asm/unaligned.h>
+
+struct sh_msiof_spi_priv {
+       struct spi_bitbang bitbang; /* must be first for spi_bitbang.c */
+       void __iomem *mapbase;
+       struct clk *clk;
+       struct platform_device *pdev;
+       struct sh_msiof_spi_info *info;
+       struct completion done;
+       unsigned long flags;
+       int tx_fifo_size;
+       int rx_fifo_size;
+};
+
+#define TMDR1  0x00
+#define TMDR2  0x04
+#define TMDR3  0x08
+#define RMDR1  0x10
+#define RMDR2  0x14
+#define RMDR3  0x18
+#define TSCR   0x20
+#define RSCR   0x22
+#define CTR    0x28
+#define FCTR   0x30
+#define STR    0x40
+#define IER    0x44
+#define TDR1   0x48
+#define TDR2   0x4c
+#define TFDR   0x50
+#define RDR1   0x58
+#define RDR2   0x5c
+#define RFDR   0x60
+
+#define CTR_TSCKE (1 << 15)
+#define CTR_TFSE  (1 << 14)
+#define CTR_TXE   (1 << 9)
+#define CTR_RXE   (1 << 8)
+
+#define STR_TEOF  (1 << 23)
+#define STR_REOF  (1 << 7)
+
+static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
+{
+       switch (reg_offs) {
+       case TSCR:
+       case RSCR:
+               return ioread16(p->mapbase + reg_offs);
+       default:
+               return ioread32(p->mapbase + reg_offs);
+       }
+}
+
+static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs,
+                          unsigned long value)
+{
+       switch (reg_offs) {
+       case TSCR:
+       case RSCR:
+               iowrite16(value, p->mapbase + reg_offs);
+               break;
+       default:
+               iowrite32(value, p->mapbase + reg_offs);
+               break;
+       }
+}
+
+static int sh_msiof_modify_ctr_wait(struct sh_msiof_spi_priv *p,
+                                   unsigned long clr, unsigned long set)
+{
+       unsigned long mask = clr | set;
+       unsigned long data;
+       int k;
+
+       data = sh_msiof_read(p, CTR);
+       data &= ~clr;
+       data |= set;
+       sh_msiof_write(p, CTR, data);
+
+       for (k = 100; k > 0; k--) {
+               if ((sh_msiof_read(p, CTR) & mask) == set)
+                       break;
+
+               udelay(10);
+       }
+
+       return k > 0 ? 0 : -ETIMEDOUT;
+}
+
+static irqreturn_t sh_msiof_spi_irq(int irq, void *data)
+{
+       struct sh_msiof_spi_priv *p = data;
+
+       /* just disable the interrupt and wake up */
+       sh_msiof_write(p, IER, 0);
+       complete(&p->done);
+
+       return IRQ_HANDLED;
+}
+
+static struct {
+       unsigned short div;
+       unsigned short scr;
+} const sh_msiof_spi_clk_table[] = {
+       { 1, 0x0007 },
+       { 2, 0x0000 },
+       { 4, 0x0001 },
+       { 8, 0x0002 },
+       { 16, 0x0003 },
+       { 32, 0x0004 },
+       { 64, 0x1f00 },
+       { 128, 0x1f01 },
+       { 256, 0x1f02 },
+       { 512, 0x1f03 },
+       { 1024, 0x1f04 },
+};
+
+static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
+                                     unsigned long parent_rate,
+                                     unsigned long spi_hz)
+{
+       unsigned long div = 1024;
+       size_t k;
+
+       if (!WARN_ON(!spi_hz || !parent_rate))
+               div = parent_rate / spi_hz;
+
+       /* TODO: make more fine grained */
+
+       for (k = 0; k < ARRAY_SIZE(sh_msiof_spi_clk_table); k++) {
+               if (sh_msiof_spi_clk_table[k].div >= div)
+                       break;
+       }
+
+       k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_clk_table) - 1);
+
+       sh_msiof_write(p, TSCR, sh_msiof_spi_clk_table[k].scr);
+       sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr);
+}
+
+static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
+                                     int cpol, int cpha,
+                                     int tx_hi_z, int lsb_first)
+{
+       unsigned long tmp;
+       int edge;
+
+       /*
+        * CPOL CPHA     TSCKIZ RSCKIZ TEDG REDG(!)
+        *    0    0         10     10    1    0
+        *    0    1         10     10    0    1
+        *    1    0         11     11    0    1
+        *    1    1         11     11    1    0
+        *
+        * (!) Note: REDG is inverted recommended data sheet setting
+        */
+
+       sh_msiof_write(p, FCTR, 0);
+       sh_msiof_write(p, TMDR1, 0xe2000005 | (lsb_first << 24));
+       sh_msiof_write(p, RMDR1, 0x22000005 | (lsb_first << 24));
+
+       tmp = 0xa0000000;
+       tmp |= cpol << 30; /* TSCKIZ */
+       tmp |= cpol << 28; /* RSCKIZ */
+
+       edge = cpol ? cpha : !cpha;
+
+       tmp |= edge << 27; /* TEDG */
+       tmp |= !edge << 26; /* REDG */
+       tmp |= (tx_hi_z ? 2 : 0) << 22; /* TXDIZ */
+       sh_msiof_write(p, CTR, tmp);
+}
+
+static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p,
+                                      const void *tx_buf, void *rx_buf,
+                                      int bits, int words)
+{
+       unsigned long dr2;
+
+       dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
+
+       if (tx_buf)
+               sh_msiof_write(p, TMDR2, dr2);
+       else
+               sh_msiof_write(p, TMDR2, dr2 | 1);
+
+       if (rx_buf)
+               sh_msiof_write(p, RMDR2, dr2);
+
+       sh_msiof_write(p, IER, STR_TEOF | STR_REOF);
+}
+
+static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p)
+{
+       sh_msiof_write(p, STR, sh_msiof_read(p, STR));
+}
+
+static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p,
+                                     const void *tx_buf, int words, int fs)
+{
+       const unsigned char *buf_8 = tx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               sh_msiof_write(p, TFDR, buf_8[k] << fs);
+}
+
+static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p,
+                                      const void *tx_buf, int words, int fs)
+{
+       const unsigned short *buf_16 = tx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               sh_msiof_write(p, TFDR, buf_16[k] << fs);
+}
+
+static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p,
+                                       const void *tx_buf, int words, int fs)
+{
+       const unsigned short *buf_16 = tx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               sh_msiof_write(p, TFDR, get_unaligned(&buf_16[k]) << fs);
+}
+
+static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p,
+                                      const void *tx_buf, int words, int fs)
+{
+       const unsigned int *buf_32 = tx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               sh_msiof_write(p, TFDR, buf_32[k] << fs);
+}
+
+static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p,
+                                       const void *tx_buf, int words, int fs)
+{
+       const unsigned int *buf_32 = tx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               sh_msiof_write(p, TFDR, get_unaligned(&buf_32[k]) << fs);
+}
+
+static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p,
+                                    void *rx_buf, int words, int fs)
+{
+       unsigned char *buf_8 = rx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               buf_8[k] = sh_msiof_read(p, RFDR) >> fs;
+}
+
+static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p,
+                                     void *rx_buf, int words, int fs)
+{
+       unsigned short *buf_16 = rx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               buf_16[k] = sh_msiof_read(p, RFDR) >> fs;
+}
+
+static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p,
+                                      void *rx_buf, int words, int fs)
+{
+       unsigned short *buf_16 = rx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_16[k]);
+}
+
+static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p,
+                                     void *rx_buf, int words, int fs)
+{
+       unsigned int *buf_32 = rx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               buf_32[k] = sh_msiof_read(p, RFDR) >> fs;
+}
+
+static void sh_msiof_spi_read_fifo_32u(struct sh_msiof_spi_priv *p,
+                                      void *rx_buf, int words, int fs)
+{
+       unsigned int *buf_32 = rx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_32[k]);
+}
+
+static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t)
+{
+       int bits;
+
+       bits = t ? t->bits_per_word : 0;
+       bits = bits ? bits : spi->bits_per_word;
+       return bits;
+}
+
+static unsigned long sh_msiof_spi_hz(struct spi_device *spi,
+                                    struct spi_transfer *t)
+{
+       unsigned long hz;
+
+       hz = t ? t->speed_hz : 0;
+       hz = hz ? hz : spi->max_speed_hz;
+       return hz;
+}
+
+static int sh_msiof_spi_setup_transfer(struct spi_device *spi,
+                                      struct spi_transfer *t)
+{
+       int bits;
+
+       /* noting to check hz values against since parent clock is disabled */
+
+       bits = sh_msiof_spi_bits(spi, t);
+       if (bits < 8)
+               return -EINVAL;
+       if (bits > 32)
+               return -EINVAL;
+
+       return spi_bitbang_setup_transfer(spi, t);
+}
+
+static void sh_msiof_spi_chipselect(struct spi_device *spi, int is_on)
+{
+       struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
+       int value;
+
+       /* chip select is active low unless SPI_CS_HIGH is set */
+       if (spi->mode & SPI_CS_HIGH)
+               value = (is_on == BITBANG_CS_ACTIVE) ? 1 : 0;
+       else
+               value = (is_on == BITBANG_CS_ACTIVE) ? 0 : 1;
+
+       if (is_on == BITBANG_CS_ACTIVE) {
+               if (!test_and_set_bit(0, &p->flags)) {
+                       pm_runtime_get_sync(&p->pdev->dev);
+                       clk_enable(p->clk);
+               }
+
+               /* Configure pins before asserting CS */
+               sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL),
+                                         !!(spi->mode & SPI_CPHA),
+                                         !!(spi->mode & SPI_3WIRE),
+                                         !!(spi->mode & SPI_LSB_FIRST));
+       }
+
+       /* use spi->controller data for CS (same strategy as spi_gpio) */
+       gpio_set_value((unsigned)spi->controller_data, value);
+
+       if (is_on == BITBANG_CS_INACTIVE) {
+               if (test_and_clear_bit(0, &p->flags)) {
+                       clk_disable(p->clk);
+                       pm_runtime_put(&p->pdev->dev);
+               }
+       }
+}
+
+static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
+                                 void (*tx_fifo)(struct sh_msiof_spi_priv *,
+                                                 const void *, int, int),
+                                 void (*rx_fifo)(struct sh_msiof_spi_priv *,
+                                                 void *, int, int),
+                                 const void *tx_buf, void *rx_buf,
+                                 int words, int bits)
+{
+       int fifo_shift;
+       int ret;
+
+       /* limit maximum word transfer to rx/tx fifo size */
+       if (tx_buf)
+               words = min_t(int, words, p->tx_fifo_size);
+       if (rx_buf)
+               words = min_t(int, words, p->rx_fifo_size);
+
+       /* the fifo contents need shifting */
+       fifo_shift = 32 - bits;
+
+       /* setup msiof transfer mode registers */
+       sh_msiof_spi_set_mode_regs(p, tx_buf, rx_buf, bits, words);
+
+       /* write tx fifo */
+       if (tx_buf)
+               tx_fifo(p, tx_buf, words, fifo_shift);
+
+       /* setup clock and rx/tx signals */
+       ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
+       if (rx_buf)
+               ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_RXE);
+       ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TXE);
+
+       /* start by setting frame bit */
+       INIT_COMPLETION(p->done);
+       ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE);
+       if (ret) {
+               dev_err(&p->pdev->dev, "failed to start hardware\n");
+               goto err;
+       }
+
+       /* wait for tx fifo to be emptied / rx fifo to be filled */
+       wait_for_completion(&p->done);
+
+       /* read rx fifo */
+       if (rx_buf)
+               rx_fifo(p, rx_buf, words, fifo_shift);
+
+       /* clear status bits */
+       sh_msiof_reset_str(p);
+
+       /* shut down frame, tx/tx and clock signals */
+       ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
+       ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
+       if (rx_buf)
+               ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_RXE, 0);
+       ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TSCKE, 0);
+       if (ret) {
+               dev_err(&p->pdev->dev, "failed to shut down hardware\n");
+               goto err;
+       }
+
+       return words;
+
+ err:
+       sh_msiof_write(p, IER, 0);
+       return ret;
+}
+
+static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
+{
+       struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master);
+       void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int);
+       void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int);
+       int bits;
+       int bytes_per_word;
+       int bytes_done;
+       int words;
+       int n;
+
+       bits = sh_msiof_spi_bits(spi, t);
+
+       /* setup bytes per word and fifo read/write functions */
+       if (bits <= 8) {
+               bytes_per_word = 1;
+               tx_fifo = sh_msiof_spi_write_fifo_8;
+               rx_fifo = sh_msiof_spi_read_fifo_8;
+       } else if (bits <= 16) {
+               bytes_per_word = 2;
+               if ((unsigned long)t->tx_buf & 0x01)
+                       tx_fifo = sh_msiof_spi_write_fifo_16u;
+               else
+                       tx_fifo = sh_msiof_spi_write_fifo_16;
+
+               if ((unsigned long)t->rx_buf & 0x01)
+                       rx_fifo = sh_msiof_spi_read_fifo_16u;
+               else
+                       rx_fifo = sh_msiof_spi_read_fifo_16;
+       } else {
+               bytes_per_word = 4;
+               if ((unsigned long)t->tx_buf & 0x03)
+                       tx_fifo = sh_msiof_spi_write_fifo_32u;
+               else
+                       tx_fifo = sh_msiof_spi_write_fifo_32;
+
+               if ((unsigned long)t->rx_buf & 0x03)
+                       rx_fifo = sh_msiof_spi_read_fifo_32u;
+               else
+                       rx_fifo = sh_msiof_spi_read_fifo_32;
+       }
+
+       /* setup clocks (clock already enabled in chipselect()) */
+       sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk),
+                                 sh_msiof_spi_hz(spi, t));
+
+       /* transfer in fifo sized chunks */
+       words = t->len / bytes_per_word;
+       bytes_done = 0;
+
+       while (bytes_done < t->len) {
+               n = sh_msiof_spi_txrx_once(p, tx_fifo, rx_fifo,
+                                          t->tx_buf + bytes_done,
+                                          t->rx_buf + bytes_done,
+                                          words, bits);
+               if (n < 0)
+                       break;
+
+               bytes_done += n * bytes_per_word;
+               words -= n;
+       }
+
+       return bytes_done;
+}
+
+static u32 sh_msiof_spi_txrx_word(struct spi_device *spi, unsigned nsecs,
+                                 u32 word, u8 bits)
+{
+       BUG(); /* unused but needed by bitbang code */
+       return 0;
+}
+
+static int sh_msiof_spi_probe(struct platform_device *pdev)
+{
+       struct resource *r;
+       struct spi_master *master;
+       struct sh_msiof_spi_priv *p;
+       char clk_name[16];
+       int i;
+       int ret;
+
+       master = spi_alloc_master(&pdev->dev, sizeof(struct sh_msiof_spi_priv));
+       if (master == NULL) {
+               dev_err(&pdev->dev, "failed to allocate spi master\n");
+               ret = -ENOMEM;
+               goto err0;
+       }
+
+       p = spi_master_get_devdata(master);
+
+       platform_set_drvdata(pdev, p);
+       p->info = pdev->dev.platform_data;
+       init_completion(&p->done);
+
+       snprintf(clk_name, sizeof(clk_name), "msiof%d", pdev->id);
+       p->clk = clk_get(&pdev->dev, clk_name);
+       if (IS_ERR(p->clk)) {
+               dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+               ret = PTR_ERR(p->clk);
+               goto err1;
+       }
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       i = platform_get_irq(pdev, 0);
+       if (!r || i < 0) {
+               dev_err(&pdev->dev, "cannot get platform resources\n");
+               ret = -ENOENT;
+               goto err2;
+       }
+       p->mapbase = ioremap_nocache(r->start, resource_size(r));
+       if (!p->mapbase) {
+               dev_err(&pdev->dev, "unable to ioremap\n");
+               ret = -ENXIO;
+               goto err2;
+       }
+
+       ret = request_irq(i, sh_msiof_spi_irq, IRQF_DISABLED,
+                         dev_name(&pdev->dev), p);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to request irq\n");
+               goto err3;
+       }
+
+       p->pdev = pdev;
+       pm_runtime_enable(&pdev->dev);
+
+       /* The standard version of MSIOF use 64 word FIFOs */
+       p->tx_fifo_size = 64;
+       p->rx_fifo_size = 64;
+
+       /* Platform data may override FIFO sizes */
+       if (p->info->tx_fifo_override)
+               p->tx_fifo_size = p->info->tx_fifo_override;
+       if (p->info->rx_fifo_override)
+               p->rx_fifo_size = p->info->rx_fifo_override;
+
+       /* init master and bitbang code */
+       master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+       master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE;
+       master->flags = 0;
+       master->bus_num = pdev->id;
+       master->num_chipselect = p->info->num_chipselect;
+       master->setup = spi_bitbang_setup;
+       master->cleanup = spi_bitbang_cleanup;
+
+       p->bitbang.master = master;
+       p->bitbang.chipselect = sh_msiof_spi_chipselect;
+       p->bitbang.setup_transfer = sh_msiof_spi_setup_transfer;
+       p->bitbang.txrx_bufs = sh_msiof_spi_txrx;
+       p->bitbang.txrx_word[SPI_MODE_0] = sh_msiof_spi_txrx_word;
+       p->bitbang.txrx_word[SPI_MODE_1] = sh_msiof_spi_txrx_word;
+       p->bitbang.txrx_word[SPI_MODE_2] = sh_msiof_spi_txrx_word;
+       p->bitbang.txrx_word[SPI_MODE_3] = sh_msiof_spi_txrx_word;
+
+       ret = spi_bitbang_start(&p->bitbang);
+       if (ret == 0)
+               return 0;
+
+       pm_runtime_disable(&pdev->dev);
+ err3:
+       iounmap(p->mapbase);
+ err2:
+       clk_put(p->clk);
+ err1:
+       spi_master_put(master);
+ err0:
+       return ret;
+}
+
+static int sh_msiof_spi_remove(struct platform_device *pdev)
+{
+       struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev);
+       int ret;
+
+       ret = spi_bitbang_stop(&p->bitbang);
+       if (!ret) {
+               pm_runtime_disable(&pdev->dev);
+               free_irq(platform_get_irq(pdev, 0), sh_msiof_spi_irq);
+               iounmap(p->mapbase);
+               clk_put(p->clk);
+               spi_master_put(p->bitbang.master);
+       }
+       return ret;
+}
+
+static int sh_msiof_spi_runtime_nop(struct device *dev)
+{
+       /* Runtime PM callback shared between ->runtime_suspend()
+        * and ->runtime_resume(). Simply returns success.
+        *
+        * This driver re-initializes all registers after
+        * pm_runtime_get_sync() anyway so there is no need
+        * to save and restore registers here.
+        */
+       return 0;
+}
+
+static struct dev_pm_ops sh_msiof_spi_dev_pm_ops = {
+       .runtime_suspend = sh_msiof_spi_runtime_nop,
+       .runtime_resume = sh_msiof_spi_runtime_nop,
+};
+
+static struct platform_driver sh_msiof_spi_drv = {
+       .probe          = sh_msiof_spi_probe,
+       .remove         = sh_msiof_spi_remove,
+       .driver         = {
+               .name           = "spi_sh_msiof",
+               .owner          = THIS_MODULE,
+               .pm             = &sh_msiof_spi_dev_pm_ops,
+       },
+};
+
+static int __init sh_msiof_spi_init(void)
+{
+       return platform_driver_register(&sh_msiof_spi_drv);
+}
+module_init(sh_msiof_spi_init);
+
+static void __exit sh_msiof_spi_exit(void)
+{
+       platform_driver_unregister(&sh_msiof_spi_drv);
+}
+module_exit(sh_msiof_spi_exit);
+
+MODULE_DESCRIPTION("SuperH MSIOF SPI Master Interface Driver");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:spi_sh_msiof");
index 20d7322..9c446e6 100644 (file)
@@ -266,15 +266,15 @@ static int spidev_message(struct spidev_data *spidev,
                k_tmp->delay_usecs = u_tmp->delay_usecs;
                k_tmp->speed_hz = u_tmp->speed_hz;
 #ifdef VERBOSE
-               dev_dbg(&spi->dev,
+               dev_dbg(&spidev->spi->dev,
                        "  xfer len %zd %s%s%s%dbits %u usec %uHz\n",
                        u_tmp->len,
                        u_tmp->rx_buf ? "rx " : "",
                        u_tmp->tx_buf ? "tx " : "",
                        u_tmp->cs_change ? "cs " : "",
-                       u_tmp->bits_per_word ? : spi->bits_per_word,
+                       u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
                        u_tmp->delay_usecs,
-                       u_tmp->speed_hz ? : spi->max_speed_hz);
+                       u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
 #endif
                spi_message_add_tail(k_tmp, &msg);
        }
index 5a143b9..9f38637 100644 (file)
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/platform_device.h>
-
-#include <linux/of_platform.h>
-#include <linux/of_device.h>
-#include <linux/of_spi.h>
 
 #include <linux/spi/spi.h>
 #include <linux/spi/spi_bitbang.h>
 #include <linux/io.h>
 
+#include "xilinx_spi.h"
+#include <linux/spi/xilinx_spi.h>
+
 #define XILINX_SPI_NAME "xilinx_spi"
 
 /* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e)
  * Product Specification", DS464
  */
-#define XSPI_CR_OFFSET         0x62    /* 16-bit Control Register */
+#define XSPI_CR_OFFSET         0x60    /* Control Register */
 
 #define XSPI_CR_ENABLE         0x02
 #define XSPI_CR_MASTER_MODE    0x04
@@ -40,8 +38,9 @@
 #define XSPI_CR_RXFIFO_RESET   0x40
 #define XSPI_CR_MANUAL_SSELECT 0x80
 #define XSPI_CR_TRANS_INHIBIT  0x100
+#define XSPI_CR_LSB_FIRST      0x200
 
-#define XSPI_SR_OFFSET         0x67    /* 8-bit Status Register */
+#define XSPI_SR_OFFSET         0x64    /* Status Register */
 
 #define XSPI_SR_RX_EMPTY_MASK  0x01    /* Receive FIFO is empty */
 #define XSPI_SR_RX_FULL_MASK   0x02    /* Receive FIFO is full */
@@ -49,8 +48,8 @@
 #define XSPI_SR_TX_FULL_MASK   0x08    /* Transmit FIFO is full */
 #define XSPI_SR_MODE_FAULT_MASK        0x10    /* Mode fault error */
 
-#define XSPI_TXD_OFFSET                0x6b    /* 8-bit Data Transmit Register */
-#define XSPI_RXD_OFFSET                0x6f    /* 8-bit Data Receive Register */
+#define XSPI_TXD_OFFSET                0x68    /* Data Transmit Register */
+#define XSPI_RXD_OFFSET                0x6c    /* Data Receive Register */
 
 #define XSPI_SSR_OFFSET                0x70    /* 32-bit Slave Select Register */
 
@@ -70,6 +69,7 @@
 #define XSPI_INTR_TX_UNDERRUN          0x08    /* TxFIFO was underrun */
 #define XSPI_INTR_RX_FULL              0x10    /* RxFIFO is full */
 #define XSPI_INTR_RX_OVERRUN           0x20    /* RxFIFO was overrun */
+#define XSPI_INTR_TX_HALF_EMPTY                0x40    /* TxFIFO is half empty */
 
 #define XIPIF_V123B_RESETR_OFFSET      0x40    /* IPIF reset register */
 #define XIPIF_V123B_RESET_MASK         0x0a    /* the value to write */
@@ -78,35 +78,85 @@ struct xilinx_spi {
        /* bitbang has to be first */
        struct spi_bitbang bitbang;
        struct completion done;
-
+       struct resource mem; /* phys mem */
        void __iomem    *regs;  /* virt. address of the control registers */
 
        u32             irq;
 
-       u32             speed_hz; /* SCK has a fixed frequency of speed_hz Hz */
-
        u8 *rx_ptr;             /* pointer in the Tx buffer */
        const u8 *tx_ptr;       /* pointer in the Rx buffer */
        int remaining_bytes;    /* the number of bytes left to transfer */
+       u8 bits_per_word;
+       unsigned int (*read_fn) (void __iomem *);
+       void (*write_fn) (u32, void __iomem *);
+       void (*tx_fn) (struct xilinx_spi *);
+       void (*rx_fn) (struct xilinx_spi *);
 };
 
-static void xspi_init_hw(void __iomem *regs_base)
+static void xspi_tx8(struct xilinx_spi *xspi)
+{
+       xspi->write_fn(*xspi->tx_ptr, xspi->regs + XSPI_TXD_OFFSET);
+       xspi->tx_ptr++;
+}
+
+static void xspi_tx16(struct xilinx_spi *xspi)
+{
+       xspi->write_fn(*(u16 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET);
+       xspi->tx_ptr += 2;
+}
+
+static void xspi_tx32(struct xilinx_spi *xspi)
+{
+       xspi->write_fn(*(u32 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET);
+       xspi->tx_ptr += 4;
+}
+
+static void xspi_rx8(struct xilinx_spi *xspi)
+{
+       u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET);
+       if (xspi->rx_ptr) {
+               *xspi->rx_ptr = data & 0xff;
+               xspi->rx_ptr++;
+       }
+}
+
+static void xspi_rx16(struct xilinx_spi *xspi)
 {
+       u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET);
+       if (xspi->rx_ptr) {
+               *(u16 *)(xspi->rx_ptr) = data & 0xffff;
+               xspi->rx_ptr += 2;
+       }
+}
+
+static void xspi_rx32(struct xilinx_spi *xspi)
+{
+       u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET);
+       if (xspi->rx_ptr) {
+               *(u32 *)(xspi->rx_ptr) = data;
+               xspi->rx_ptr += 4;
+       }
+}
+
+static void xspi_init_hw(struct xilinx_spi *xspi)
+{
+       void __iomem *regs_base = xspi->regs;
+
        /* Reset the SPI device */
-       out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET,
-                XIPIF_V123B_RESET_MASK);
+       xspi->write_fn(XIPIF_V123B_RESET_MASK,
+               regs_base + XIPIF_V123B_RESETR_OFFSET);
        /* Disable all the interrupts just in case */
-       out_be32(regs_base + XIPIF_V123B_IIER_OFFSET, 0);
+       xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET);
        /* Enable the global IPIF interrupt */
-       out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET,
-                XIPIF_V123B_GINTR_ENABLE);
+       xspi->write_fn(XIPIF_V123B_GINTR_ENABLE,
+               regs_base + XIPIF_V123B_DGIER_OFFSET);
        /* Deselect the slave on the SPI bus */
-       out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff);
+       xspi->write_fn(0xffff, regs_base + XSPI_SSR_OFFSET);
        /* Disable the transmitter, enable Manual Slave Select Assertion,
         * put SPI controller into master mode, and enable it */
-       out_be16(regs_base + XSPI_CR_OFFSET,
-                XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
-                | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE);
+       xspi->write_fn(XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT |
+               XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET |
+               XSPI_CR_RXFIFO_RESET, regs_base + XSPI_CR_OFFSET);
 }
 
 static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
@@ -115,16 +165,16 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
 
        if (is_on == BITBANG_CS_INACTIVE) {
                /* Deselect the slave on the SPI bus */
-               out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff);
+               xspi->write_fn(0xffff, xspi->regs + XSPI_SSR_OFFSET);
        } else if (is_on == BITBANG_CS_ACTIVE) {
                /* Set the SPI clock phase and polarity */
-               u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET)
+               u16 cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET)
                         & ~XSPI_CR_MODE_MASK;
                if (spi->mode & SPI_CPHA)
                        cr |= XSPI_CR_CPHA;
                if (spi->mode & SPI_CPOL)
                        cr |= XSPI_CR_CPOL;
-               out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+               xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
 
                /* We do not check spi->max_speed_hz here as the SPI clock
                 * frequency is not software programmable (the IP block design
@@ -132,25 +182,27 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
                 */
 
                /* Activate the chip select */
-               out_be32(xspi->regs + XSPI_SSR_OFFSET,
-                        ~(0x0001 << spi->chip_select));
+               xspi->write_fn(~(0x0001 << spi->chip_select),
+                       xspi->regs + XSPI_SSR_OFFSET);
        }
 }
 
 /* spi_bitbang requires custom setup_transfer() to be defined if there is a
  * custom txrx_bufs(). We have nothing to setup here as the SPI IP block
- * supports just 8 bits per word, and SPI clock can't be changed in software.
- * Check for 8 bits per word. Chip select delay calculations could be
+ * supports 8 or 16 bits per word which cannot be changed in software.
+ * SPI clock can't be changed in software either.
+ * Check for correct bits per word. Chip select delay calculations could be
  * added here as soon as bitbang_work() can be made aware of the delay value.
  */
 static int xilinx_spi_setup_transfer(struct spi_device *spi,
                struct spi_transfer *t)
 {
+       struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
        u8 bits_per_word;
 
        bits_per_word = (t && t->bits_per_word)
                         ? t->bits_per_word : spi->bits_per_word;
-       if (bits_per_word != 8) {
+       if (bits_per_word != xspi->bits_per_word) {
                dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
                        __func__, bits_per_word);
                return -EINVAL;
@@ -161,17 +213,16 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi,
 
 static int xilinx_spi_setup(struct spi_device *spi)
 {
-       struct spi_bitbang *bitbang;
-       struct xilinx_spi *xspi;
-       int retval;
-
-       xspi = spi_master_get_devdata(spi->master);
-       bitbang = &xspi->bitbang;
-
-       retval = xilinx_spi_setup_transfer(spi, NULL);
-       if (retval < 0)
-               return retval;
-
+       /* always return 0, we can not check the number of bits.
+        * There are cases when SPI setup is called before any driver is
+        * there, in that case the SPI core defaults to 8 bits, which we
+        * do not support in some cases. But if we return an error, the
+        * SPI device would not be registered and no driver can get hold of it
+        * When the driver is there, it will call SPI setup again with the
+        * correct number of bits per transfer.
+        * If a driver setups with the wrong bit number, it will fail when
+        * it tries to do a transfer
+        */
        return 0;
 }
 
@@ -180,15 +231,14 @@ static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi)
        u8 sr;
 
        /* Fill the Tx FIFO with as many bytes as possible */
-       sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+       sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
        while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) {
-               if (xspi->tx_ptr) {
-                       out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++);
-               } else {
-                       out_8(xspi->regs + XSPI_TXD_OFFSET, 0);
-               }
-               xspi->remaining_bytes--;
-               sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+               if (xspi->tx_ptr)
+                       xspi->tx_fn(xspi);
+               else
+                       xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET);
+               xspi->remaining_bytes -= xspi->bits_per_word / 8;
+               sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
        }
 }
 
@@ -210,18 +260,19 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
        /* Enable the transmit empty interrupt, which we use to determine
         * progress on the transmission.
         */
-       ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET);
-       out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET,
-                ipif_ier | XSPI_INTR_TX_EMPTY);
+       ipif_ier = xspi->read_fn(xspi->regs + XIPIF_V123B_IIER_OFFSET);
+       xspi->write_fn(ipif_ier | XSPI_INTR_TX_EMPTY,
+               xspi->regs + XIPIF_V123B_IIER_OFFSET);
 
        /* Start the transfer by not inhibiting the transmitter any longer */
-       cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT;
-       out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+       cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) &
+               ~XSPI_CR_TRANS_INHIBIT;
+       xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
 
        wait_for_completion(&xspi->done);
 
        /* Disable the transmit empty interrupt */
-       out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, ipif_ier);
+       xspi->write_fn(ipif_ier, xspi->regs + XIPIF_V123B_IIER_OFFSET);
 
        return t->len - xspi->remaining_bytes;
 }
@@ -238,8 +289,8 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
        u32 ipif_isr;
 
        /* Get the IPIF interrupts, and clear them immediately */
-       ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET);
-       out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr);
+       ipif_isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET);
+       xspi->write_fn(ipif_isr, xspi->regs + XIPIF_V123B_IISR_OFFSET);
 
        if (ipif_isr & XSPI_INTR_TX_EMPTY) {    /* Transmission completed */
                u16 cr;
@@ -250,20 +301,15 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
                 * transmitter while the Isr refills the transmit register/FIFO,
                 * or make sure it is stopped if we're done.
                 */
-               cr = in_be16(xspi->regs + XSPI_CR_OFFSET);
-               out_be16(xspi->regs + XSPI_CR_OFFSET,
-                        cr | XSPI_CR_TRANS_INHIBIT);
+               cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
+               xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT,
+                       xspi->regs + XSPI_CR_OFFSET);
 
                /* Read out all the data from the Rx FIFO */
-               sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+               sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
                while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
-                       u8 data;
-
-                       data = in_8(xspi->regs + XSPI_RXD_OFFSET);
-                       if (xspi->rx_ptr) {
-                               *xspi->rx_ptr++ = data;
-                       }
-                       sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+                       xspi->rx_fn(xspi);
+                       sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
                }
 
                /* See if there is more data to send */
@@ -272,7 +318,7 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
                        /* Start the transfer by not inhibiting the
                         * transmitter any longer
                         */
-                       out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+                       xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
                } else {
                        /* No more data to send.
                         * Indicate the transfer is completed.
@@ -284,40 +330,22 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int __init xilinx_spi_of_probe(struct of_device *ofdev,
-                                       const struct of_device_id *match)
+struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
+       u32 irq, s16 bus_num)
 {
        struct spi_master *master;
        struct xilinx_spi *xspi;
-       struct resource r_irq_struct;
-       struct resource r_mem_struct;
-
-       struct resource *r_irq = &r_irq_struct;
-       struct resource *r_mem = &r_mem_struct;
-       int rc = 0;
-       const u32 *prop;
-       int len;
-
-       /* Get resources(memory, IRQ) associated with the device */
-       master = spi_alloc_master(&ofdev->dev, sizeof(struct xilinx_spi));
+       struct xspi_platform_data *pdata = dev->platform_data;
+       int ret;
 
-       if (master == NULL) {
-               return -ENOMEM;
+       if (!pdata) {
+               dev_err(dev, "No platform data attached\n");
+               return NULL;
        }
 
-       dev_set_drvdata(&ofdev->dev, master);
-
-       rc = of_address_to_resource(ofdev->node, 0, r_mem);
-       if (rc) {
-               dev_warn(&ofdev->dev, "invalid address\n");
-               goto put_master;
-       }
-
-       rc = of_irq_to_resource(ofdev->node, 0, r_irq);
-       if (rc == NO_IRQ) {
-               dev_warn(&ofdev->dev, "no IRQ found\n");
-               goto put_master;
-       }
+       master = spi_alloc_master(dev, sizeof(struct xilinx_spi));
+       if (!master)
+               return NULL;
 
        /* the spi->mode bits understood by this driver: */
        master->mode_bits = SPI_CPOL | SPI_CPHA;
@@ -330,128 +358,87 @@ static int __init xilinx_spi_of_probe(struct of_device *ofdev,
        xspi->bitbang.master->setup = xilinx_spi_setup;
        init_completion(&xspi->done);
 
-       xspi->irq = r_irq->start;
-
-       if (!request_mem_region(r_mem->start,
-                       r_mem->end - r_mem->start + 1, XILINX_SPI_NAME)) {
-               rc = -ENXIO;
-               dev_warn(&ofdev->dev, "memory request failure\n");
+       if (!request_mem_region(mem->start, resource_size(mem),
+               XILINX_SPI_NAME))
                goto put_master;
-       }
 
-       xspi->regs = ioremap(r_mem->start, r_mem->end - r_mem->start + 1);
+       xspi->regs = ioremap(mem->start, resource_size(mem));
        if (xspi->regs == NULL) {
-               rc = -ENOMEM;
-               dev_warn(&ofdev->dev, "ioremap failure\n");
-               goto release_mem;
+               dev_warn(dev, "ioremap failure\n");
+               goto map_failed;
        }
-       xspi->irq = r_irq->start;
-
-       /* dynamic bus assignment */
-       master->bus_num = -1;
 
-       /* number of slave select bits is required */
-       prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len);
-       if (!prop || len < sizeof(*prop)) {
-               dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n");
-               goto unmap_io;
+       master->bus_num = bus_num;
+       master->num_chipselect = pdata->num_chipselect;
+
+       xspi->mem = *mem;
+       xspi->irq = irq;
+       if (pdata->little_endian) {
+               xspi->read_fn = ioread32;
+               xspi->write_fn = iowrite32;
+       } else {
+               xspi->read_fn = ioread32be;
+               xspi->write_fn = iowrite32be;
        }
-       master->num_chipselect = *prop;
+       xspi->bits_per_word = pdata->bits_per_word;
+       if (xspi->bits_per_word == 8) {
+               xspi->tx_fn = xspi_tx8;
+               xspi->rx_fn = xspi_rx8;
+       } else if (xspi->bits_per_word == 16) {
+               xspi->tx_fn = xspi_tx16;
+               xspi->rx_fn = xspi_rx16;
+       } else if (xspi->bits_per_word == 32) {
+               xspi->tx_fn = xspi_tx32;
+               xspi->rx_fn = xspi_rx32;
+       } else
+               goto unmap_io;
+
 
        /* SPI controller initializations */
-       xspi_init_hw(xspi->regs);
+       xspi_init_hw(xspi);
 
        /* Register for SPI Interrupt */
-       rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
-       if (rc != 0) {
-               dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq);
+       ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
+       if (ret)
                goto unmap_io;
-       }
 
-       rc = spi_bitbang_start(&xspi->bitbang);
-       if (rc != 0) {
-               dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n");
+       ret = spi_bitbang_start(&xspi->bitbang);
+       if (ret) {
+               dev_err(dev, "spi_bitbang_start FAILED\n");
                goto free_irq;
        }
 
-       dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n",
-                       (unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq);
-
-       /* Add any subnodes on the SPI bus */
-       of_register_spi_devices(master, ofdev->node);
-
-       return rc;
+       dev_info(dev, "at 0x%08llX mapped to 0x%p, irq=%d\n",
+               (unsigned long long)mem->start, xspi->regs, xspi->irq);
+       return master;
 
 free_irq:
        free_irq(xspi->irq, xspi);
 unmap_io:
        iounmap(xspi->regs);
-release_mem:
-       release_mem_region(r_mem->start, resource_size(r_mem));
+map_failed:
+       release_mem_region(mem->start, resource_size(mem));
 put_master:
        spi_master_put(master);
-       return rc;
+       return NULL;
 }
+EXPORT_SYMBOL(xilinx_spi_init);
 
-static int __devexit xilinx_spi_remove(struct of_device *ofdev)
+void xilinx_spi_deinit(struct spi_master *master)
 {
        struct xilinx_spi *xspi;
-       struct spi_master *master;
-       struct resource r_mem;
 
-       master = platform_get_drvdata(ofdev);
        xspi = spi_master_get_devdata(master);
 
        spi_bitbang_stop(&xspi->bitbang);
        free_irq(xspi->irq, xspi);
        iounmap(xspi->regs);
-       if (!of_address_to_resource(ofdev->node, 0, &r_mem))
-               release_mem_region(r_mem.start, resource_size(&r_mem));
-       dev_set_drvdata(&ofdev->dev, 0);
-       spi_master_put(xspi->bitbang.master);
-
-       return 0;
-}
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:" XILINX_SPI_NAME);
-
-static int __exit xilinx_spi_of_remove(struct of_device *op)
-{
-       return xilinx_spi_remove(op);
-}
 
-static struct of_device_id xilinx_spi_of_match[] = {
-       { .compatible = "xlnx,xps-spi-2.00.a", },
-       { .compatible = "xlnx,xps-spi-2.00.b", },
-       {}
-};
-
-MODULE_DEVICE_TABLE(of, xilinx_spi_of_match);
-
-static struct of_platform_driver xilinx_spi_of_driver = {
-       .owner = THIS_MODULE,
-       .name = "xilinx-xps-spi",
-       .match_table = xilinx_spi_of_match,
-       .probe = xilinx_spi_of_probe,
-       .remove = __exit_p(xilinx_spi_of_remove),
-       .driver = {
-               .name = "xilinx-xps-spi",
-               .owner = THIS_MODULE,
-       },
-};
-
-static int __init xilinx_spi_init(void)
-{
-       return of_register_platform_driver(&xilinx_spi_of_driver);
+       release_mem_region(xspi->mem.start, resource_size(&xspi->mem));
+       spi_master_put(xspi->bitbang.master);
 }
-module_init(xilinx_spi_init);
+EXPORT_SYMBOL(xilinx_spi_deinit);
 
-static void __exit xilinx_spi_exit(void)
-{
-       of_unregister_platform_driver(&xilinx_spi_of_driver);
-}
-module_exit(xilinx_spi_exit);
 MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
 MODULE_DESCRIPTION("Xilinx SPI driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/spi/xilinx_spi.h b/drivers/spi/xilinx_spi.h
new file mode 100644 (file)
index 0000000..d211acc
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Xilinx SPI device driver API and platform data header file
+ *
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _XILINX_SPI_H_
+#define _XILINX_SPI_H_
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+
+#define XILINX_SPI_NAME "xilinx_spi"
+
+struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
+       u32 irq, s16 bus_num);
+
+void xilinx_spi_deinit(struct spi_master *master);
+#endif
diff --git a/drivers/spi/xilinx_spi_of.c b/drivers/spi/xilinx_spi_of.c
new file mode 100644 (file)
index 0000000..71dc3ad
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Xilinx SPI OF device driver
+ *
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Xilinx SPI devices as OF devices
+ *
+ * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/of_spi.h>
+
+#include <linux/spi/xilinx_spi.h>
+#include "xilinx_spi.h"
+
+
+static int __devinit xilinx_spi_of_probe(struct of_device *ofdev,
+       const struct of_device_id *match)
+{
+       struct spi_master *master;
+       struct xspi_platform_data *pdata;
+       struct resource r_mem;
+       struct resource r_irq;
+       int rc = 0;
+       const u32 *prop;
+       int len;
+
+       rc = of_address_to_resource(ofdev->node, 0, &r_mem);
+       if (rc) {
+               dev_warn(&ofdev->dev, "invalid address\n");
+               return rc;
+       }
+
+       rc = of_irq_to_resource(ofdev->node, 0, &r_irq);
+       if (rc == NO_IRQ) {
+               dev_warn(&ofdev->dev, "no IRQ found\n");
+               return -ENODEV;
+       }
+
+       ofdev->dev.platform_data =
+               kzalloc(sizeof(struct xspi_platform_data), GFP_KERNEL);
+       pdata = ofdev->dev.platform_data;
+       if (!pdata)
+               return -ENOMEM;
+
+       /* number of slave select bits is required */
+       prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len);
+       if (!prop || len < sizeof(*prop)) {
+               dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n");
+               return -EINVAL;
+       }
+       pdata->num_chipselect = *prop;
+       pdata->bits_per_word = 8;
+       master = xilinx_spi_init(&ofdev->dev, &r_mem, r_irq.start, -1);
+       if (!master)
+               return -ENODEV;
+
+       dev_set_drvdata(&ofdev->dev, master);
+
+       /* Add any subnodes on the SPI bus */
+       of_register_spi_devices(master, ofdev->node);
+
+       return 0;
+}
+
+static int __devexit xilinx_spi_remove(struct of_device *ofdev)
+{
+       xilinx_spi_deinit(dev_get_drvdata(&ofdev->dev));
+       dev_set_drvdata(&ofdev->dev, 0);
+       kfree(ofdev->dev.platform_data);
+       ofdev->dev.platform_data = NULL;
+       return 0;
+}
+
+static int __exit xilinx_spi_of_remove(struct of_device *op)
+{
+       return xilinx_spi_remove(op);
+}
+
+static struct of_device_id xilinx_spi_of_match[] = {
+       { .compatible = "xlnx,xps-spi-2.00.a", },
+       { .compatible = "xlnx,xps-spi-2.00.b", },
+       {}
+};
+
+MODULE_DEVICE_TABLE(of, xilinx_spi_of_match);
+
+static struct of_platform_driver xilinx_spi_of_driver = {
+       .match_table = xilinx_spi_of_match,
+       .probe = xilinx_spi_of_probe,
+       .remove = __exit_p(xilinx_spi_of_remove),
+       .driver = {
+               .name = "xilinx-xps-spi",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init xilinx_spi_of_init(void)
+{
+       return of_register_platform_driver(&xilinx_spi_of_driver);
+}
+module_init(xilinx_spi_of_init);
+
+static void __exit xilinx_spi_of_exit(void)
+{
+       of_unregister_platform_driver(&xilinx_spi_of_driver);
+}
+module_exit(xilinx_spi_of_exit);
+
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_DESCRIPTION("Xilinx SPI platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/xilinx_spi_pltfm.c b/drivers/spi/xilinx_spi_pltfm.c
new file mode 100644 (file)
index 0000000..24debac
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Support for Xilinx SPI platform devices
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Xilinx SPI devices as platform devices
+ *
+ * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/spi/xilinx_spi.h>
+
+#include "xilinx_spi.h"
+
+static int __devinit xilinx_spi_probe(struct platform_device *dev)
+{
+       struct xspi_platform_data *pdata;
+       struct resource *r;
+       int irq;
+       struct spi_master *master;
+       u8 i;
+
+       pdata = dev->dev.platform_data;
+       if (!pdata)
+               return -ENODEV;
+
+       r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       if (!r)
+               return -ENODEV;
+
+       irq = platform_get_irq(dev, 0);
+       if (irq < 0)
+               return -ENXIO;
+
+       master = xilinx_spi_init(&dev->dev, r, irq, dev->id);
+       if (!master)
+               return -ENODEV;
+
+       for (i = 0; i < pdata->num_devices; i++)
+               spi_new_device(master, pdata->devices + i);
+
+       platform_set_drvdata(dev, master);
+       return 0;
+}
+
+static int __devexit xilinx_spi_remove(struct platform_device *dev)
+{
+       xilinx_spi_deinit(platform_get_drvdata(dev));
+       platform_set_drvdata(dev, 0);
+
+       return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:" XILINX_SPI_NAME);
+
+static struct platform_driver xilinx_spi_driver = {
+       .probe  = xilinx_spi_probe,
+       .remove = __devexit_p(xilinx_spi_remove),
+       .driver = {
+               .name = XILINX_SPI_NAME,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init xilinx_spi_pltfm_init(void)
+{
+       return platform_driver_register(&xilinx_spi_driver);
+}
+module_init(xilinx_spi_pltfm_init);
+
+static void __exit xilinx_spi_pltfm_exit(void)
+{
+       platform_driver_unregister(&xilinx_spi_driver);
+}
+module_exit(xilinx_spi_pltfm_exit);
+
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_DESCRIPTION("Xilinx SPI platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/spi/mpc52xx_spi.h b/include/linux/spi/mpc52xx_spi.h
deleted file mode 100644 (file)
index d1004cf..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-
-#ifndef INCLUDE_MPC5200_SPI_H
-#define INCLUDE_MPC5200_SPI_H
-
-extern void mpc52xx_spi_set_premessage_hook(struct spi_master *master,
-                                           void (*hook)(struct spi_message *m,
-                                                        void *context),
-                                           void *hook_context);
-
-#endif
diff --git a/include/linux/spi/sh_msiof.h b/include/linux/spi/sh_msiof.h
new file mode 100644 (file)
index 0000000..2e8db3d
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __SPI_SH_MSIOF_H__
+#define __SPI_SH_MSIOF_H__
+
+struct sh_msiof_spi_info {
+       int tx_fifo_override;
+       int rx_fifo_override;
+       u16 num_chipselect;
+};
+
+#endif /* __SPI_SH_MSIOF_H__ */
diff --git a/include/linux/spi/xilinx_spi.h b/include/linux/spi/xilinx_spi.h
new file mode 100644 (file)
index 0000000..6f17278
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __LINUX_SPI_XILINX_SPI_H
+#define __LINUX_SPI_XILINX_SPI_H
+
+/**
+ * struct xspi_platform_data - Platform data of the Xilinx SPI driver
+ * @num_chipselect:    Number of chip select by the IP.
+ * @little_endian:     If registers should be accessed little endian or not.
+ * @bits_per_word:     Number of bits per word.
+ * @devices:           Devices to add when the driver is probed.
+ * @num_devices:       Number of devices in the devices array.
+ */
+struct xspi_platform_data {
+       u16 num_chipselect;
+       bool little_endian;
+       u8 bits_per_word;
+       struct spi_board_info *devices;
+       u8 num_devices;
+};
+
+#endif /* __LINUX_SPI_XILINX_SPI_H */