]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge remote-tracking branch 'origin' into spi/next
authorGrant Likely <grant.likely@secretlab.ca>
Thu, 17 Mar 2011 19:46:58 +0000 (13:46 -0600)
committerGrant Likely <grant.likely@secretlab.ca>
Thu, 17 Mar 2011 19:48:06 +0000 (13:48 -0600)
Pull in Linus' tree to pick up changes required for the langwell gpio fixes

29 files changed:
Documentation/devicetree/bindings/mmc/mmc-spi-slot.txt
Documentation/devicetree/bindings/spi/spi_altera.txt [new file with mode: 0644]
Documentation/devicetree/bindings/spi/spi_oc_tiny.txt [new file with mode: 0644]
drivers/gpio/74x164.c
drivers/gpio/Kconfig
drivers/gpio/cs5535-gpio.c
drivers/gpio/mc33880.c
drivers/gpio/mcp23s08.c
drivers/gpio/pca953x.c
drivers/gpio/sx150x.c
drivers/mmc/host/of_mmc_spi.c
drivers/rtc/rtc-ds1390.c
drivers/rtc/rtc-ds3234.c
drivers/rtc/rtc-m41t94.c
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/amba-pl022.c
drivers/spi/davinci_spi.c
drivers/spi/omap2_mcspi.c
drivers/spi/spi_altera.c [new file with mode: 0644]
drivers/spi/spi_bfin5xx.c
drivers/spi/spi_bitbang.c
drivers/spi/spi_imx.c
drivers/spi/spi_oc_tiny.c [new file with mode: 0644]
drivers/spi/spi_sh.c [new file with mode: 0644]
drivers/spi/spi_sh_msiof.c
drivers/spi/spidev.c
include/linux/spi/mcp23s08.h
include/linux/spi/spi_oc_tiny.h [new file with mode: 0644]

index c39ac2891951bf413935f2ab12daf3f46310e5e8..89a0084df2f76e625a2cdfcccf4e6c0f4281bc12 100644 (file)
@@ -7,8 +7,13 @@ Required properties:
 - voltage-ranges : two cells are required, first cell specifies minimum
   slot voltage (mV), second cell specifies maximum slot voltage (mV).
   Several ranges could be specified.
-- gpios : (optional) may specify GPIOs in this order: Card-Detect GPIO,
+
+Optional properties:
+- gpios : may specify GPIOs in this order: Card-Detect GPIO,
   Write-Protect GPIO.
+- interrupts : the interrupt of a card detect interrupt.
+- interrupt-parent : the phandle for the interrupt controller that
+  services interrupts for this device.
 
 Example:
 
@@ -20,4 +25,6 @@ Example:
                         &qe_pio_d 15 0>;
                voltage-ranges = <3300 3300>;
                spi-max-frequency = <50000000>;
+               interrupts = <42>;
+               interrupt-parent = <&PIC>;
        };
diff --git a/Documentation/devicetree/bindings/spi/spi_altera.txt b/Documentation/devicetree/bindings/spi/spi_altera.txt
new file mode 100644 (file)
index 0000000..dda3759
--- /dev/null
@@ -0,0 +1,4 @@
+Altera SPI
+
+Required properties:
+- compatible : should be "ALTR,spi-1.0".
diff --git a/Documentation/devicetree/bindings/spi/spi_oc_tiny.txt b/Documentation/devicetree/bindings/spi/spi_oc_tiny.txt
new file mode 100644 (file)
index 0000000..d95c0b3
--- /dev/null
@@ -0,0 +1,12 @@
+OpenCores tiny SPI
+
+Required properties:
+- compatible : should be "opencores,tiny-spi-rtlsvn2".
+- gpios : should specify GPIOs used for chipselect.
+Optional properties:
+- clock-frequency : input clock frequency to the core.
+- baud-width: width, in bits, of the programmable divider used to scale
+       the input clock to SCLK.
+
+The clock-frequency and baud-width properties are needed only if the divider
+is programmable. They are not needed if the divider is fixed.
index d91ff4c282e9e177ab1c3109fbd587d2ba8b8406..84e070219839266e054f22fab48ce991ed83ccaa 100644 (file)
@@ -133,7 +133,7 @@ exit_destroy:
        return ret;
 }
 
-static int gen_74x164_remove(struct spi_device *spi)
+static int __devexit gen_74x164_remove(struct spi_device *spi)
 {
        struct gen_74x164_chip *chip;
        int ret;
index 664660e563350a56e962f91fa8a1c78a079c4d52..9573b330f64768032c3ebb5c7458f3810b61bec2 100644 (file)
@@ -368,11 +368,11 @@ config GPIO_MAX7301
          GPIO driver for Maxim MAX7301 SPI-based GPIO expander.
 
 config GPIO_MCP23S08
-       tristate "Microchip MCP23S08 I/O expander"
+       tristate "Microchip MCP23Sxx I/O expander"
        depends on SPI_MASTER
        help
-         SPI driver for Microchip MCP23S08 I/O expander.  This provides
-         a GPIO interface supporting inputs and outputs.
+         SPI driver for Microchip MCP23S08/MPC23S17 I/O expanders.
+         This provides a GPIO interface supporting inputs and outputs.
 
 config GPIO_MC33880
        tristate "Freescale MC33880 high-side/low-side switch"
index 0d05ea7d499b8cb60932807c4861bfd8a1c03be2..6e16cba56ad2e291ca39fdeeadb4844c968b4029 100644 (file)
@@ -373,7 +373,7 @@ static int __devexit cs5535_gpio_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_driver cs5535_gpio_drv = {
+static struct platform_driver cs5535_gpio_driver = {
        .driver = {
                .name = DRV_NAME,
                .owner = THIS_MODULE,
@@ -384,12 +384,12 @@ static struct platform_driver cs5535_gpio_drv = {
 
 static int __init cs5535_gpio_init(void)
 {
-       return platform_driver_register(&cs5535_gpio_drv);
+       return platform_driver_register(&cs5535_gpio_driver);
 }
 
 static void __exit cs5535_gpio_exit(void)
 {
-       platform_driver_unregister(&cs5535_gpio_drv);
+       platform_driver_unregister(&cs5535_gpio_driver);
 }
 
 module_init(cs5535_gpio_init);
index 935479da6705b3161213521b8791f2ebf7633193..00f6d24c669d096b437bde1c410f68ac2c10f691 100644 (file)
@@ -146,7 +146,7 @@ exit_destroy:
        return ret;
 }
 
-static int mc33880_remove(struct spi_device *spi)
+static int __devexit mc33880_remove(struct spi_device *spi)
 {
        struct mc33880 *mc;
        int ret;
index 69f6f1955a31a650f31a9d9113c031a18b44a901..40e076083ec0f6f5438e5dcd9a0d5b64c30a7eae 100644 (file)
 #include <linux/spi/spi.h>
 #include <linux/spi/mcp23s08.h>
 #include <linux/slab.h>
+#include <asm/byteorder.h>
 
+/**
+ * MCP types supported by driver
+ */
+#define MCP_TYPE_S08   0
+#define MCP_TYPE_S17   1
 
 /* Registers are all 8 bits wide.
  *
 #define MCP_GPIO       0x09
 #define MCP_OLAT       0x0a
 
+struct mcp23s08;
+
+struct mcp23s08_ops {
+       int     (*read)(struct mcp23s08 *mcp, unsigned reg);
+       int     (*write)(struct mcp23s08 *mcp, unsigned reg, unsigned val);
+       int     (*read_regs)(struct mcp23s08 *mcp, unsigned reg,
+                            u16 *vals, unsigned n);
+};
+
 struct mcp23s08 {
        struct spi_device       *spi;
        u8                      addr;
 
-       u                     cache[11];
+       u16                     cache[11];
        /* lock protects the cached values */
        struct mutex            lock;
 
        struct gpio_chip        chip;
 
        struct work_struct      work;
+
+       const struct mcp23s08_ops       *ops;
 };
 
-/* A given spi_device can represent up to four mcp23s08 chips
+/* A given spi_device can represent up to eight mcp23sxx chips
  * sharing the same chipselect but using different addresses
  * (e.g. chips #0 and #3 might be populated, but not #1 or $2).
  * Driver data holds all the per-chip data.
  */
 struct mcp23s08_driver_data {
        unsigned                ngpio;
-       struct mcp23s08         *mcp[4];
+       struct mcp23s08         *mcp[8];
        struct mcp23s08         chip[];
 };
 
@@ -70,7 +87,7 @@ static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
        return (status < 0) ? status : rx[0];
 }
 
-static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val)
+static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
 {
        u8      tx[3];
 
@@ -81,17 +98,81 @@ static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val)
 }
 
 static int
-mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u8 *vals, unsigned n)
+mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
 {
-       u8      tx[2];
+       u8      tx[2], *tmp;
+       int     status;
 
        if ((n + reg) > sizeof mcp->cache)
                return -EINVAL;
        tx[0] = mcp->addr | 0x01;
        tx[1] = reg;
-       return spi_write_then_read(mcp->spi, tx, sizeof tx, vals, n);
+
+       tmp = (u8 *)vals;
+       status = spi_write_then_read(mcp->spi, tx, sizeof tx, tmp, n);
+       if (status >= 0) {
+               while (n--)
+                       vals[n] = tmp[n]; /* expand to 16bit */
+       }
+       return status;
+}
+
+static int mcp23s17_read(struct mcp23s08 *mcp, unsigned reg)
+{
+       u8      tx[2], rx[2];
+       int     status;
+
+       tx[0] = mcp->addr | 0x01;
+       tx[1] = reg << 1;
+       status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx);
+       return (status < 0) ? status : (rx[0] | (rx[1] << 8));
+}
+
+static int mcp23s17_write(struct mcp23s08 *mcp, unsigned reg, unsigned val)
+{
+       u8      tx[4];
+
+       tx[0] = mcp->addr;
+       tx[1] = reg << 1;
+       tx[2] = val;
+       tx[3] = val >> 8;
+       return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
+}
+
+static int
+mcp23s17_read_regs(struct mcp23s08 *mcp, unsigned reg, u16 *vals, unsigned n)
+{
+       u8      tx[2];
+       int     status;
+
+       if ((n + reg) > sizeof mcp->cache)
+               return -EINVAL;
+       tx[0] = mcp->addr | 0x01;
+       tx[1] = reg << 1;
+
+       status = spi_write_then_read(mcp->spi, tx, sizeof tx,
+                                    (u8 *)vals, n * 2);
+       if (status >= 0) {
+               while (n--)
+                       vals[n] = __le16_to_cpu((__le16)vals[n]);
+       }
+
+       return status;
 }
 
+static const struct mcp23s08_ops mcp23s08_ops = {
+       .read           = mcp23s08_read,
+       .write          = mcp23s08_write,
+       .read_regs      = mcp23s08_read_regs,
+};
+
+static const struct mcp23s08_ops mcp23s17_ops = {
+       .read           = mcp23s17_read,
+       .write          = mcp23s17_write,
+       .read_regs      = mcp23s17_read_regs,
+};
+
+
 /*----------------------------------------------------------------------*/
 
 static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -101,7 +182,7 @@ static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset)
 
        mutex_lock(&mcp->lock);
        mcp->cache[MCP_IODIR] |= (1 << offset);
-       status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
+       status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
        mutex_unlock(&mcp->lock);
        return status;
 }
@@ -114,7 +195,7 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset)
        mutex_lock(&mcp->lock);
 
        /* REVISIT reading this clears any IRQ ... */
-       status = mcp23s08_read(mcp, MCP_GPIO);
+       status = mcp->ops->read(mcp, MCP_GPIO);
        if (status < 0)
                status = 0;
        else {
@@ -127,20 +208,20 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset)
 
 static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value)
 {
-       u8 olat = mcp->cache[MCP_OLAT];
+       unsigned olat = mcp->cache[MCP_OLAT];
 
        if (value)
                olat |= mask;
        else
                olat &= ~mask;
        mcp->cache[MCP_OLAT] = olat;
-       return mcp23s08_write(mcp, MCP_OLAT, olat);
+       return mcp->ops->write(mcp, MCP_OLAT, olat);
 }
 
 static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value)
 {
        struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
-       u8 mask = 1 << offset;
+       unsigned mask = 1 << offset;
 
        mutex_lock(&mcp->lock);
        __mcp23s08_set(mcp, mask, value);
@@ -151,14 +232,14 @@ static int
 mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value)
 {
        struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip);
-       u8 mask = 1 << offset;
+       unsigned mask = 1 << offset;
        int status;
 
        mutex_lock(&mcp->lock);
        status = __mcp23s08_set(mcp, mask, value);
        if (status == 0) {
                mcp->cache[MCP_IODIR] &= ~mask;
-               status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
+               status = mcp->ops->write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]);
        }
        mutex_unlock(&mcp->lock);
        return status;
@@ -184,16 +265,16 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
        mcp = container_of(chip, struct mcp23s08, chip);
 
        /* NOTE: we only handle one bank for now ... */
-       bank = '0' + ((mcp->addr >> 1) & 0x3);
+       bank = '0' + ((mcp->addr >> 1) & 0x7);
 
        mutex_lock(&mcp->lock);
-       t = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache);
+       t = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
        if (t < 0) {
                seq_printf(s, " I/O ERROR %d\n", t);
                goto done;
        }
 
-       for (t = 0, mask = 1; t < 8; t++, mask <<= 1) {
+       for (t = 0, mask = 1; t < chip->ngpio; t++, mask <<= 1) {
                const char      *label;
 
                label = gpiochip_is_requested(chip, t);
@@ -219,28 +300,33 @@ done:
 /*----------------------------------------------------------------------*/
 
 static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
-               unsigned base, unsigned pullups)
+                             unsigned type, unsigned base, unsigned pullups)
 {
        struct mcp23s08_driver_data     *data = spi_get_drvdata(spi);
        struct mcp23s08                 *mcp = data->mcp[addr];
        int                             status;
-       int                             do_update = 0;
 
        mutex_init(&mcp->lock);
 
        mcp->spi = spi;
        mcp->addr = 0x40 | (addr << 1);
 
-       mcp->chip.label = "mcp23s08",
-
        mcp->chip.direction_input = mcp23s08_direction_input;
        mcp->chip.get = mcp23s08_get;
        mcp->chip.direction_output = mcp23s08_direction_output;
        mcp->chip.set = mcp23s08_set;
        mcp->chip.dbg_show = mcp23s08_dbg_show;
 
+       if (type == MCP_TYPE_S17) {
+               mcp->ops = &mcp23s17_ops;
+               mcp->chip.ngpio = 16;
+               mcp->chip.label = "mcp23s17";
+       } else {
+               mcp->ops = &mcp23s08_ops;
+               mcp->chip.ngpio = 8;
+               mcp->chip.label = "mcp23s08";
+       }
        mcp->chip.base = base;
-       mcp->chip.ngpio = 8;
        mcp->chip.can_sleep = 1;
        mcp->chip.dev = &spi->dev;
        mcp->chip.owner = THIS_MODULE;
@@ -248,45 +334,39 @@ static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
        /* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
         * and MCP_IOCON.HAEN = 1, so we work with all chips.
         */
-       status = mcp23s08_read(mcp, MCP_IOCON);
+       status = mcp->ops->read(mcp, MCP_IOCON);
        if (status < 0)
                goto fail;
        if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) {
-               status &= ~IOCON_SEQOP;
-               status |= IOCON_HAEN;
-               status = mcp23s08_write(mcp, MCP_IOCON, (u8) status);
+               /* mcp23s17 has IOCON twice, make sure they are in sync */
+               status &= ~(IOCON_SEQOP | (IOCON_SEQOP << 8));
+               status |= IOCON_HAEN | (IOCON_HAEN << 8);
+               status = mcp->ops->write(mcp, MCP_IOCON, status);
                if (status < 0)
                        goto fail;
        }
 
        /* configure ~100K pullups */
-       status = mcp23s08_write(mcp, MCP_GPPU, pullups);
+       status = mcp->ops->write(mcp, MCP_GPPU, pullups);
        if (status < 0)
                goto fail;
 
-       status = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache);
+       status = mcp->ops->read_regs(mcp, 0, mcp->cache, ARRAY_SIZE(mcp->cache));
        if (status < 0)
                goto fail;
 
        /* disable inverter on input */
        if (mcp->cache[MCP_IPOL] != 0) {
                mcp->cache[MCP_IPOL] = 0;
-               do_update = 1;
+               status = mcp->ops->write(mcp, MCP_IPOL, 0);
+               if (status < 0)
+                       goto fail;
        }
 
        /* disable irqs */
        if (mcp->cache[MCP_GPINTEN] != 0) {
                mcp->cache[MCP_GPINTEN] = 0;
-               do_update = 1;
-       }
-
-       if (do_update) {
-               u8 tx[4];
-
-               tx[0] = mcp->addr;
-               tx[1] = MCP_IPOL;
-               memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2);
-               status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
+               status = mcp->ops->write(mcp, MCP_GPINTEN, 0);
                if (status < 0)
                        goto fail;
        }
@@ -305,19 +385,26 @@ static int mcp23s08_probe(struct spi_device *spi)
        unsigned                        addr;
        unsigned                        chips = 0;
        struct mcp23s08_driver_data     *data;
-       int                             status;
+       int                             status, type;
        unsigned                        base;
 
+       type = spi_get_device_id(spi)->driver_data;
+
        pdata = spi->dev.platform_data;
        if (!pdata || !gpio_is_valid(pdata->base)) {
                dev_dbg(&spi->dev, "invalid or missing platform data\n");
                return -EINVAL;
        }
 
-       for (addr = 0; addr < 4; addr++) {
+       for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
                if (!pdata->chip[addr].is_present)
                        continue;
                chips++;
+               if ((type == MCP_TYPE_S08) && (addr > 3)) {
+                       dev_err(&spi->dev,
+                               "mcp23s08 only supports address 0..3\n");
+                       return -EINVAL;
+               }
        }
        if (!chips)
                return -ENODEV;
@@ -329,16 +416,17 @@ static int mcp23s08_probe(struct spi_device *spi)
        spi_set_drvdata(spi, data);
 
        base = pdata->base;
-       for (addr = 0; addr < 4; addr++) {
+       for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
                if (!pdata->chip[addr].is_present)
                        continue;
                chips--;
                data->mcp[addr] = &data->chip[chips];
-               status = mcp23s08_probe_one(spi, addr, base,
-                               pdata->chip[addr].pullups);
+               status = mcp23s08_probe_one(spi, addr, type, base,
+                                           pdata->chip[addr].pullups);
                if (status < 0)
                        goto fail;
-               base += 8;
+
+               base += (type == MCP_TYPE_S17) ? 16 : 8;
        }
        data->ngpio = base - pdata->base;
 
@@ -358,7 +446,7 @@ static int mcp23s08_probe(struct spi_device *spi)
        return 0;
 
 fail:
-       for (addr = 0; addr < 4; addr++) {
+       for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
                int tmp;
 
                if (!data->mcp[addr])
@@ -388,7 +476,7 @@ static int mcp23s08_remove(struct spi_device *spi)
                }
        }
 
-       for (addr = 0; addr < 4; addr++) {
+       for (addr = 0; addr < ARRAY_SIZE(data->mcp); addr++) {
                int tmp;
 
                if (!data->mcp[addr])
@@ -405,9 +493,17 @@ static int mcp23s08_remove(struct spi_device *spi)
        return status;
 }
 
+static const struct spi_device_id mcp23s08_ids[] = {
+       { "mcp23s08", MCP_TYPE_S08 },
+       { "mcp23s17", MCP_TYPE_S17 },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, mcp23s08_ids);
+
 static struct spi_driver mcp23s08_driver = {
        .probe          = mcp23s08_probe,
        .remove         = mcp23s08_remove,
+       .id_table       = mcp23s08_ids,
        .driver = {
                .name   = "mcp23s08",
                .owner  = THIS_MODULE,
@@ -432,4 +528,3 @@ static void __exit mcp23s08_exit(void)
 module_exit(mcp23s08_exit);
 
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("spi:mcp23s08");
index b473429eee75157a105c7144bb3cbc9dcd4d2cde..2fc25dec7cf5371ccd1a405fae65d622af5352e8 100644 (file)
@@ -462,7 +462,8 @@ pca953x_get_alt_pdata(struct i2c_client *client)
 {
        struct pca953x_platform_data *pdata;
        struct device_node *node;
-       const uint16_t *val;
+       const __be32 *val;
+       int size;
 
        node = client->dev.of_node;
        if (node == NULL)
@@ -475,13 +476,13 @@ pca953x_get_alt_pdata(struct i2c_client *client)
        }
 
        pdata->gpio_base = -1;
-       val = of_get_property(node, "linux,gpio-base", NULL);
+       val = of_get_property(node, "linux,gpio-base", &size);
        if (val) {
-               if (*val < 0)
-                       dev_warn(&client->dev,
-                                "invalid gpio-base in device tree\n");
+               if (size != sizeof(*val))
+                       dev_warn(&client->dev, "%s: wrong linux,gpio-base\n",
+                                node->full_name);
                else
-                       pdata->gpio_base = *val;
+                       pdata->gpio_base = be32_to_cpup(val);
        }
 
        val = of_get_property(node, "polarity", NULL);
index e60be0015c9bf59883a5a8ae1efaa71151c8ff5d..d2f874c3d3d59ff3f8c095ca5d93a33c769131fe 100644 (file)
@@ -25,6 +25,8 @@
 #include <linux/workqueue.h>
 #include <linux/i2c/sx150x.h>
 
+#define NO_UPDATE_PENDING      -1
+
 struct sx150x_device_data {
        u8 reg_pullup;
        u8 reg_pulldn;
@@ -47,8 +49,11 @@ struct sx150x_chip {
        const struct sx150x_device_data *dev_cfg;
        int                              irq_summary;
        int                              irq_base;
+       int                              irq_update;
        u32                              irq_sense;
-       unsigned long                    irq_set_type_pending;
+       u32                              irq_masked;
+       u32                              dev_sense;
+       u32                              dev_masked;
        struct irq_chip                  irq_chip;
        struct mutex                     lock;
 };
@@ -312,9 +317,8 @@ static void sx150x_irq_mask(struct irq_data *d)
 
        chip = container_of(ic, struct sx150x_chip, irq_chip);
        n = d->irq - chip->irq_base;
-
-       sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
-       sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
+       chip->irq_masked |= (1 << n);
+       chip->irq_update = n;
 }
 
 static void sx150x_irq_unmask(struct irq_data *d)
@@ -326,9 +330,8 @@ static void sx150x_irq_unmask(struct irq_data *d)
        chip = container_of(ic, struct sx150x_chip, irq_chip);
        n = d->irq - chip->irq_base;
 
-       sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
-       sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
-                        chip->irq_sense >> (n * 2));
+       chip->irq_masked &= ~(1 << n);
+       chip->irq_update = n;
 }
 
 static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
@@ -350,7 +353,7 @@ static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
 
        chip->irq_sense &= ~(3UL << (n * 2));
        chip->irq_sense |= val << (n * 2);
-       chip->irq_set_type_pending |= BIT(n);
+       chip->irq_update = n;
        return 0;
 }
 
@@ -404,15 +407,29 @@ static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
 
        chip = container_of(ic, struct sx150x_chip, irq_chip);
 
-       while (chip->irq_set_type_pending) {
-               n = __ffs(chip->irq_set_type_pending);
-               chip->irq_set_type_pending &= ~BIT(n);
-               if (!(irq_to_desc(n + chip->irq_base)->status & IRQ_MASKED))
-                       sx150x_write_cfg(chip, n, 2,
-                                       chip->dev_cfg->reg_sense,
-                                       chip->irq_sense >> (n * 2));
-       }
+       if (chip->irq_update == NO_UPDATE_PENDING)
+               goto out;
+
+       n = chip->irq_update;
+       chip->irq_update = NO_UPDATE_PENDING;
 
+       /* Avoid updates if nothing changed */
+       if (chip->dev_sense == chip->irq_sense &&
+           chip->dev_sense == chip->irq_masked)
+               goto out;
+
+       chip->dev_sense = chip->irq_sense;
+       chip->dev_masked = chip->irq_masked;
+
+       if (chip->irq_masked & (1 << n)) {
+               sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
+               sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
+       } else {
+               sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
+               sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
+                                chip->irq_sense >> (n * 2));
+       }
+out:
        mutex_unlock(&chip->lock);
 }
 
@@ -445,8 +462,11 @@ static void sx150x_init_chip(struct sx150x_chip *chip,
        chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
        chip->irq_summary                  = -1;
        chip->irq_base                     = -1;
+       chip->irq_masked                   = ~0;
        chip->irq_sense                    = 0;
-       chip->irq_set_type_pending         = 0;
+       chip->dev_masked                   = ~0;
+       chip->dev_sense                    = 0;
+       chip->irq_update                   = NO_UPDATE_PENDING;
 }
 
 static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
index 1247e5de9faa84afb6f2b9be923fcd5ce5053f97..5530def54e5bbf8a08be34e3ce4ea2534a8802da 100644 (file)
@@ -34,6 +34,7 @@ enum {
 struct of_mmc_spi {
        int gpios[NUM_GPIOS];
        bool alow_gpios[NUM_GPIOS];
+       int detect_irq;
        struct mmc_spi_platform_data pdata;
 };
 
@@ -61,6 +62,22 @@ static int of_mmc_spi_get_ro(struct device *dev)
        return of_mmc_spi_read_gpio(dev, WP_GPIO);
 }
 
+static int of_mmc_spi_init(struct device *dev,
+                          irqreturn_t (*irqhandler)(int, void *), void *mmc)
+{
+       struct of_mmc_spi *oms = to_of_mmc_spi(dev);
+
+       return request_threaded_irq(oms->detect_irq, NULL, irqhandler, 0,
+                                   dev_name(dev), mmc);
+}
+
+static void of_mmc_spi_exit(struct device *dev, void *mmc)
+{
+       struct of_mmc_spi *oms = to_of_mmc_spi(dev);
+
+       free_irq(oms->detect_irq, mmc);
+}
+
 struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
 {
        struct device *dev = &spi->dev;
@@ -121,8 +138,13 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
        if (gpio_is_valid(oms->gpios[WP_GPIO]))
                oms->pdata.get_ro = of_mmc_spi_get_ro;
 
-       /* We don't support interrupts yet, let's poll. */
-       oms->pdata.caps |= MMC_CAP_NEEDS_POLL;
+       oms->detect_irq = irq_of_parse_and_map(np, 0);
+       if (oms->detect_irq != NO_IRQ) {
+               oms->pdata.init = of_mmc_spi_init;
+               oms->pdata.exit = of_mmc_spi_exit;
+       } else {
+               oms->pdata.caps |= MMC_CAP_NEEDS_POLL;
+       }
 
        dev->platform_data = &oms->pdata;
        return dev->platform_data;
index 26a86d235051ff283115c34324ae2605a860a42f..b038d2cfef267163699fb502a4c3d2d533a58e75 100644 (file)
@@ -158,7 +158,7 @@ static int __devinit ds1390_probe(struct spi_device *spi)
 
 static int __devexit ds1390_remove(struct spi_device *spi)
 {
-       struct ds1390 *chip = platform_get_drvdata(spi);
+       struct ds1390 *chip = spi_get_drvdata(spi);
 
        rtc_device_unregister(chip->rtc);
        kfree(chip);
index a774ca35b5f728540f8895f435f0c3d878af6f06..bbd26228f5326f337e86af8c624736d5c9c82ede 100644 (file)
@@ -158,7 +158,7 @@ static int __devinit ds3234_probe(struct spi_device *spi)
 
 static int __devexit ds3234_remove(struct spi_device *spi)
 {
-       struct rtc_device *rtc = platform_get_drvdata(spi);
+       struct rtc_device *rtc = spi_get_drvdata(spi);
 
        rtc_device_unregister(rtc);
        return 0;
index c8c97a4169d4e22e46ed3aa6a7b0a72fa65ec4ba..e259ed76ae856d5b2cc8f354522f39e70d5474dc 100644 (file)
@@ -136,7 +136,7 @@ static int __devinit m41t94_probe(struct spi_device *spi)
 
 static int __devexit m41t94_remove(struct spi_device *spi)
 {
-       struct rtc_device *rtc = platform_get_drvdata(spi);
+       struct rtc_device *rtc = spi_get_drvdata(spi);
 
        if (rtc)
                rtc_device_unregister(rtc);
index bb233a9cbad2fef09da95aef7225cad4e78c518d..cdeb01f45bc232f2ca07371ac1d4ac95869cf85e 100644 (file)
@@ -53,6 +53,12 @@ if SPI_MASTER
 
 comment "SPI Master Controller Drivers"
 
+config SPI_ALTERA
+       tristate "Altera SPI Controller"
+       select SPI_BITBANG
+       help
+         This is the driver for the Altera SPI Controller.
+
 config SPI_ATH79
        tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver"
        depends on ATH79 && GENERIC_GPIO
@@ -231,6 +237,13 @@ config SPI_FSL_ESPI
          From MPC8536, 85xx platform uses the controller, and all P10xx,
          P20xx, P30xx,P40xx, P50xx uses this controller.
 
+config SPI_OC_TINY
+       tristate "OpenCores tiny SPI"
+       depends on GENERIC_GPIO
+       select SPI_BITBANG
+       help
+         This is the driver for OpenCores tiny SPI master controller.
+
 config SPI_OMAP_UWIRE
        tristate "OMAP1 MicroWire"
        depends on ARCH_OMAP1
@@ -330,6 +343,12 @@ config SPI_SH_MSIOF
        help
          SPI driver for SuperH MSIOF blocks.
 
+config SPI_SH
+       tristate "SuperH SPI controller"
+       depends on SUPERH
+       help
+         SPI driver for SuperH SPI blocks.
+
 config SPI_SH_SCI
        tristate "SuperH SCI SPI controller"
        depends on SUPERH
index 86d1b5f9bbd9a9b93c91db63ce7af659b5f6c74c..8b5a315045a6b3fc8e7aa787d1005a62f0447415 100644 (file)
@@ -9,6 +9,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
 obj-$(CONFIG_SPI_MASTER)               += spi.o
 
 # SPI master controller drivers (bus)
+obj-$(CONFIG_SPI_ALTERA)               += spi_altera.o
 obj-$(CONFIG_SPI_ATMEL)                        += atmel_spi.o
 obj-$(CONFIG_SPI_ATH79)                        += ath79_spi.o
 obj-$(CONFIG_SPI_BFIN)                 += spi_bfin5xx.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_SPI_IMX)                 += spi_imx.o
 obj-$(CONFIG_SPI_LM70_LLP)             += spi_lm70llp.o
 obj-$(CONFIG_SPI_PXA2XX)               += pxa2xx_spi.o
 obj-$(CONFIG_SPI_PXA2XX_PCI)           += pxa2xx_spi_pci.o
+obj-$(CONFIG_SPI_OC_TINY)              += spi_oc_tiny.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
@@ -46,6 +48,7 @@ obj-$(CONFIG_SPI_TEGRA)                       += spi_tegra.o
 obj-$(CONFIG_SPI_TOPCLIFF_PCH)         += spi_topcliff_pch.o
 obj-$(CONFIG_SPI_TXX9)                 += spi_txx9.o
 obj-$(CONFIG_SPI_XILINX)               += xilinx_spi.o
+obj-$(CONFIG_SPI_SH)                   += spi_sh.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
index 71a1219a995d12fa0f33fc45f8602a5681f4d6a8..14a451b2ad63b59450ed08a12d36862f58fc8608 100644 (file)
@@ -329,15 +329,16 @@ struct vendor_data {
 /**
  * struct pl022 - This is the private SSP driver data structure
  * @adev: AMBA device model hookup
- * @vendor: Vendor data for the IP block
- * @phybase: The physical memory where the SSP device resides
- * @virtbase: The virtual memory where the SSP is mapped
+ * @vendor: vendor data for the IP block
+ * @phybase: the physical memory where the SSP device resides
+ * @virtbase: the virtual memory where the SSP is mapped
+ * @clk: outgoing clock "SPICLK" for the SPI bus
  * @master: SPI framework hookup
  * @master_info: controller-specific data from machine setup
- * @regs: SSP controller register's virtual address
- * @pump_messages: Work struct for scheduling work to the workqueue
- * @lock: spinlock to syncronise access to driver data
  * @workqueue: a workqueue on which any spi_message request is queued
+ * @pump_messages: work struct for scheduling work to the workqueue
+ * @queue_lock: spinlock to syncronise access to message queue
+ * @queue: message queue
  * @busy: workqueue is busy
  * @running: workqueue is running
  * @pump_transfers: Tasklet used in Interrupt Transfer mode
@@ -348,8 +349,14 @@ struct vendor_data {
  * @tx_end: end position in TX buffer to be read
  * @rx: current position in RX buffer to be written
  * @rx_end: end position in RX buffer to be written
- * @readingtype: the type of read currently going on
- * @writingtype: the type or write currently going on
+ * @read: the type of read currently going on
+ * @write: the type of write currently going on
+ * @exp_fifo_level: expected FIFO level
+ * @dma_rx_channel: optional channel for RX DMA
+ * @dma_tx_channel: optional channel for TX DMA
+ * @sgt_rx: scattertable for the RX transfer
+ * @sgt_tx: scattertable for the TX transfer
+ * @dummypage: a dummy page used for driving data on the bus with DMA
  */
 struct pl022 {
        struct amba_device              *adev;
@@ -397,8 +404,8 @@ struct pl022 {
  * @cpsr: Value of Clock prescale register
  * @n_bytes: how many bytes(power of 2) reqd for a given data width of client
  * @enable_dma: Whether to enable DMA or not
- * @write: function ptr to be used to write when doing xfer for this chip
  * @read: function ptr to be used to read when doing xfer for this chip
+ * @write: function ptr to be used to write when doing xfer for this chip
  * @cs_control: chip select callback provided by chip
  * @xfer_type: polling/interrupt/DMA
  *
@@ -508,9 +515,10 @@ static void giveback(struct pl022 *pl022)
        msg->state = NULL;
        if (msg->complete)
                msg->complete(msg->context);
-       /* This message is completed, so let's turn off the clocks! */
+       /* This message is completed, so let's turn off the clocks & power */
        clk_disable(pl022->clk);
        amba_pclk_disable(pl022->adev);
+       amba_vcore_disable(pl022->adev);
 }
 
 /**
@@ -917,7 +925,6 @@ static int configure_dma(struct pl022 *pl022)
        struct dma_chan *txchan = pl022->dma_tx_channel;
        struct dma_async_tx_descriptor *rxdesc;
        struct dma_async_tx_descriptor *txdesc;
-       dma_cookie_t cookie;
 
        /* Check that the channels are available */
        if (!rxchan || !txchan)
@@ -962,10 +969,8 @@ static int configure_dma(struct pl022 *pl022)
                tx_conf.dst_addr_width = rx_conf.src_addr_width;
        BUG_ON(rx_conf.src_addr_width != tx_conf.dst_addr_width);
 
-       rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG,
-                                      (unsigned long) &rx_conf);
-       txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
-                                      (unsigned long) &tx_conf);
+       dmaengine_slave_config(rxchan, &rx_conf);
+       dmaengine_slave_config(txchan, &tx_conf);
 
        /* Create sglists for the transfers */
        pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1;
@@ -1018,23 +1023,17 @@ static int configure_dma(struct pl022 *pl022)
        rxdesc->callback_param = pl022;
 
        /* Submit and fire RX and TX with TX last so we're ready to read! */
-       cookie = rxdesc->tx_submit(rxdesc);
-       if (dma_submit_error(cookie))
-               goto err_submit_rx;
-       cookie = txdesc->tx_submit(txdesc);
-       if (dma_submit_error(cookie))
-               goto err_submit_tx;
-       rxchan->device->device_issue_pending(rxchan);
-       txchan->device->device_issue_pending(txchan);
+       dmaengine_submit(rxdesc);
+       dmaengine_submit(txdesc);
+       dma_async_issue_pending(rxchan);
+       dma_async_issue_pending(txchan);
 
        return 0;
 
-err_submit_tx:
-err_submit_rx:
 err_txdesc:
-       txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0);
+       dmaengine_terminate_all(txchan);
 err_rxdesc:
-       rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0);
+       dmaengine_terminate_all(rxchan);
        dma_unmap_sg(txchan->device->dev, pl022->sgt_tx.sgl,
                     pl022->sgt_tx.nents, DMA_TO_DEVICE);
 err_tx_sgmap:
@@ -1101,8 +1100,8 @@ static void terminate_dma(struct pl022 *pl022)
        struct dma_chan *rxchan = pl022->dma_rx_channel;
        struct dma_chan *txchan = pl022->dma_tx_channel;
 
-       rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0);
-       txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0);
+       dmaengine_terminate_all(rxchan);
+       dmaengine_terminate_all(txchan);
        unmap_free_dma_scatter(pl022);
 }
 
@@ -1482,9 +1481,11 @@ static void pump_messages(struct work_struct *work)
        /* Setup the SPI using the per chip configuration */
        pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi);
        /*
-        * We enable the clocks here, then the clocks will be disabled when
-        * giveback() is called in each method (poll/interrupt/DMA)
+        * We enable the core voltage and clocks here, then the clocks
+        * and core will be disabled when giveback() is called in each method
+        * (poll/interrupt/DMA)
         */
+       amba_vcore_enable(pl022->adev);
        amba_pclk_enable(pl022->adev);
        clk_enable(pl022->clk);
        restore_state(pl022);
@@ -1910,8 +1911,6 @@ static int pl022_setup(struct spi_device *spi)
            && ((pl022->master_info)->enable_dma)) {
                chip->enable_dma = true;
                dev_dbg(&spi->dev, "DMA mode set in controller state\n");
-               if (status < 0)
-                       goto err_config_params;
                SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
                               SSP_DMACR_MASK_RXDMAE, 0);
                SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
@@ -2130,8 +2129,12 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
                goto err_spi_register;
        }
        dev_dbg(dev, "probe succeded\n");
-       /* Disable the silicon block pclk and clock it when needed */
+       /*
+        * Disable the silicon block pclk and any voltage domain and just
+        * power it up and clock it when it's needed
+        */
        amba_pclk_disable(adev);
+       amba_vcore_disable(adev);
        return 0;
 
  err_spi_register:
@@ -2196,9 +2199,11 @@ static int pl022_suspend(struct amba_device *adev, pm_message_t state)
                return status;
        }
 
+       amba_vcore_enable(adev);
        amba_pclk_enable(adev);
        load_ssp_default_config(pl022);
        amba_pclk_disable(adev);
+       amba_vcore_disable(adev);
        dev_dbg(&adev->dev, "suspended\n");
        return 0;
 }
index 6beab99bf95b82ceacada96d20117678995eeded..e90c2d6a84b3c8bae536d897b24218f826af2c2a 100644 (file)
@@ -571,6 +571,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
                unsigned long tx_reg, rx_reg;
                struct edmacc_param param;
                void *rx_buf;
+               int b, c;
 
                dma = &dspi->dma;
 
@@ -591,22 +592,38 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
 
                if (t->tx_buf) {
                        t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf,
-                                               dspi->wcount, DMA_TO_DEVICE);
+                                               t->len, DMA_TO_DEVICE);
                        if (dma_mapping_error(&spi->dev, t->tx_dma)) {
                                dev_dbg(sdev, "Unable to DMA map %d bytes"
-                                               "TX buffer\n", dspi->wcount);
+                                               "TX buffer\n", t->len);
                                return -ENOMEM;
                        }
                }
 
+               /*
+                * If number of words is greater than 65535, then we need
+                * to configure a 3 dimension transfer.  Use the BCNTRLD
+                * feature to allow for transfers that aren't even multiples
+                * of 65535 (or any other possible b size) by first transferring
+                * the remainder amount then grabbing the next N blocks of
+                * 65535 words.
+                */
+
+               c = dspi->wcount / (SZ_64K - 1);        /* N 65535 Blocks */
+               b = dspi->wcount - c * (SZ_64K - 1);    /* Remainder */
+               if (b)
+                       c++;
+               else
+                       b = SZ_64K - 1;
+
                param.opt = TCINTEN | EDMA_TCC(dma->tx_channel);
                param.src = t->tx_buf ? t->tx_dma : tx_reg;
-               param.a_b_cnt = dspi->wcount << 16 | data_type;
+               param.a_b_cnt = b << 16 | data_type;
                param.dst = tx_reg;
                param.src_dst_bidx = t->tx_buf ? data_type : 0;
-               param.link_bcntrld = 0xffff;
-               param.src_dst_cidx = 0;
-               param.ccnt = 1;
+               param.link_bcntrld = 0xffffffff;
+               param.src_dst_cidx = t->tx_buf ? data_type : 0;
+               param.ccnt = c;
                edma_write_slot(dma->tx_channel, &param);
                edma_link(dma->tx_channel, dma->dummy_param_slot);
 
@@ -624,7 +641,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
 
                if (t->rx_buf) {
                        rx_buf = t->rx_buf;
-                       rx_buf_count = dspi->rcount;
+                       rx_buf_count = t->len;
                } else {
                        rx_buf = dspi->rx_tmp_buf;
                        rx_buf_count = sizeof(dspi->rx_tmp_buf);
@@ -636,19 +653,19 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
                        dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n",
                                                                rx_buf_count);
                        if (t->tx_buf)
-                               dma_unmap_single(NULL, t->tx_dma, dspi->wcount,
+                               dma_unmap_single(NULL, t->tx_dma, t->len,
                                                                DMA_TO_DEVICE);
                        return -ENOMEM;
                }
 
                param.opt = TCINTEN | EDMA_TCC(dma->rx_channel);
                param.src = rx_reg;
-               param.a_b_cnt = dspi->rcount << 16 | data_type;
+               param.a_b_cnt = b << 16 | data_type;
                param.dst = t->rx_dma;
                param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16;
-               param.link_bcntrld = 0xffff;
-               param.src_dst_cidx = 0;
-               param.ccnt = 1;
+               param.link_bcntrld = 0xffffffff;
+               param.src_dst_cidx = (t->rx_buf ? data_type : 0) << 16;
+               param.ccnt = c;
                edma_write_slot(dma->rx_channel, &param);
 
                if (pdata->cshold_bug)
@@ -675,7 +692,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
        if (spicfg->io_type == SPI_IO_TYPE_DMA) {
 
                if (t->tx_buf)
-                       dma_unmap_single(NULL, t->tx_dma, dspi->wcount,
+                       dma_unmap_single(NULL, t->tx_dma, t->len,
                                                                DMA_TO_DEVICE);
 
                dma_unmap_single(NULL, t->rx_dma, rx_buf_count,
index abb1ffbf3d2019154855c29028418c0f3e85beae..6a982a25b7c3fe16db15d45e0345cbf5bf0eec86 100644 (file)
@@ -487,6 +487,9 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
        rx_reg          = base + OMAP2_MCSPI_RX0;
        chstat_reg      = base + OMAP2_MCSPI_CHSTAT0;
 
+       if (c < (word_len>>3))
+               return 0;
+
        if (word_len <= 8) {
                u8              *rx;
                const u8        *tx;
@@ -534,7 +537,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
                                dev_vdbg(&spi->dev, "read-%d %02x\n",
                                                word_len, *(rx - 1));
                        }
-               } while (c);
+               } while (c > (word_len>>3));
        } else if (word_len <= 16) {
                u16             *rx;
                const u16       *tx;
@@ -581,7 +584,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
                                dev_vdbg(&spi->dev, "read-%d %04x\n",
                                                word_len, *(rx - 1));
                        }
-               } while (c);
+               } while (c > (word_len>>3));
        } else if (word_len <= 32) {
                u32             *rx;
                const u32       *tx;
@@ -628,7 +631,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
                                dev_vdbg(&spi->dev, "read-%d %08x\n",
                                                word_len, *(rx - 1));
                        }
-               } while (c);
+               } while (c > (word_len>>3));
        }
 
        /* for TX_ONLY mode, be sure all words have shifted out */
@@ -651,6 +654,17 @@ out:
        return count - c;
 }
 
+static u32 omap2_mcspi_calc_divisor(u32 speed_hz)
+{
+       u32 div;
+
+       for (div = 0; div < 15; div++)
+               if (speed_hz >= (OMAP2_MCSPI_MAX_FREQ >> div))
+                       return div;
+
+       return 15;
+}
+
 /* called only when no transfer is active to this device */
 static int omap2_mcspi_setup_transfer(struct spi_device *spi,
                struct spi_transfer *t)
@@ -673,12 +687,8 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
        if (t && t->speed_hz)
                speed_hz = t->speed_hz;
 
-       if (speed_hz) {
-               while (div <= 15 && (OMAP2_MCSPI_MAX_FREQ / (1 << div))
-                                       > speed_hz)
-                       div++;
-       } else
-               div = 15;
+       speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ);
+       div = omap2_mcspi_calc_divisor(speed_hz);
 
        l = mcspi_cached_chconf0(spi);
 
@@ -715,7 +725,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
        mcspi_write_chconf0(spi, l);
 
        dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n",
-                       OMAP2_MCSPI_MAX_FREQ / (1 << div),
+                       OMAP2_MCSPI_MAX_FREQ >> div,
                        (spi->mode & SPI_CPHA) ? "trailing" : "leading",
                        (spi->mode & SPI_CPOL) ? "inverted" : "normal");
 
@@ -1015,10 +1025,10 @@ static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m)
                                        t->bits_per_word);
                        return -EINVAL;
                }
-               if (t->speed_hz && t->speed_hz < OMAP2_MCSPI_MAX_FREQ/(1<<16)) {
-                       dev_dbg(&spi->dev, "%d Hz max exceeds %d\n",
-                                       t->speed_hz,
-                                       OMAP2_MCSPI_MAX_FREQ/(1<<16));
+               if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) {
+                       dev_dbg(&spi->dev, "speed_hz %d below minimum %d Hz\n",
+                               t->speed_hz,
+                               OMAP2_MCSPI_MAX_FREQ >> 15);
                        return -EINVAL;
                }
 
diff --git a/drivers/spi/spi_altera.c b/drivers/spi/spi_altera.c
new file mode 100644 (file)
index 0000000..4813a63
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Altera SPI driver
+ *
+ * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * Based on spi_s3c24xx.c, which is:
+ * Copyright (c) 2006 Ben Dooks
+ * Copyright (c) 2006 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#define DRV_NAME "spi_altera"
+
+#define ALTERA_SPI_RXDATA      0
+#define ALTERA_SPI_TXDATA      4
+#define ALTERA_SPI_STATUS      8
+#define ALTERA_SPI_CONTROL     12
+#define ALTERA_SPI_SLAVE_SEL   20
+
+#define ALTERA_SPI_STATUS_ROE_MSK      0x8
+#define ALTERA_SPI_STATUS_TOE_MSK      0x10
+#define ALTERA_SPI_STATUS_TMT_MSK      0x20
+#define ALTERA_SPI_STATUS_TRDY_MSK     0x40
+#define ALTERA_SPI_STATUS_RRDY_MSK     0x80
+#define ALTERA_SPI_STATUS_E_MSK                0x100
+
+#define ALTERA_SPI_CONTROL_IROE_MSK    0x8
+#define ALTERA_SPI_CONTROL_ITOE_MSK    0x10
+#define ALTERA_SPI_CONTROL_ITRDY_MSK   0x40
+#define ALTERA_SPI_CONTROL_IRRDY_MSK   0x80
+#define ALTERA_SPI_CONTROL_IE_MSK      0x100
+#define ALTERA_SPI_CONTROL_SSO_MSK     0x400
+
+struct altera_spi {
+       /* bitbang has to be first */
+       struct spi_bitbang bitbang;
+       struct completion done;
+
+       void __iomem *base;
+       int irq;
+       int len;
+       int count;
+       int bytes_per_word;
+       unsigned long imr;
+
+       /* data buffers */
+       const unsigned char *tx;
+       unsigned char *rx;
+};
+
+static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev)
+{
+       return spi_master_get_devdata(sdev->master);
+}
+
+static void altera_spi_chipsel(struct spi_device *spi, int value)
+{
+       struct altera_spi *hw = altera_spi_to_hw(spi);
+
+       if (spi->mode & SPI_CS_HIGH) {
+               switch (value) {
+               case BITBANG_CS_INACTIVE:
+                       writel(1 << spi->chip_select,
+                              hw->base + ALTERA_SPI_SLAVE_SEL);
+                       hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
+                       writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
+                       break;
+
+               case BITBANG_CS_ACTIVE:
+                       hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
+                       writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
+                       writel(0, hw->base + ALTERA_SPI_SLAVE_SEL);
+                       break;
+               }
+       } else {
+               switch (value) {
+               case BITBANG_CS_INACTIVE:
+                       hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
+                       writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
+                       break;
+
+               case BITBANG_CS_ACTIVE:
+                       writel(1 << spi->chip_select,
+                              hw->base + ALTERA_SPI_SLAVE_SEL);
+                       hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
+                       writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
+                       break;
+               }
+       }
+}
+
+static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
+{
+       return 0;
+}
+
+static int altera_spi_setup(struct spi_device *spi)
+{
+       return 0;
+}
+
+static inline unsigned int hw_txbyte(struct altera_spi *hw, int count)
+{
+       if (hw->tx) {
+               switch (hw->bytes_per_word) {
+               case 1:
+                       return hw->tx[count];
+               case 2:
+                       return (hw->tx[count * 2]
+                               | (hw->tx[count * 2 + 1] << 8));
+               }
+       }
+       return 0;
+}
+
+static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
+{
+       struct altera_spi *hw = altera_spi_to_hw(spi);
+
+       hw->tx = t->tx_buf;
+       hw->rx = t->rx_buf;
+       hw->count = 0;
+       hw->bytes_per_word = (t->bits_per_word ? : spi->bits_per_word) / 8;
+       hw->len = t->len / hw->bytes_per_word;
+
+       if (hw->irq >= 0) {
+               /* enable receive interrupt */
+               hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK;
+               writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
+
+               /* send the first byte */
+               writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA);
+
+               wait_for_completion(&hw->done);
+               /* disable receive interrupt */
+               hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
+               writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
+       } else {
+               /* send the first byte */
+               writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA);
+
+               while (1) {
+                       unsigned int rxd;
+
+                       while (!(readl(hw->base + ALTERA_SPI_STATUS) &
+                                ALTERA_SPI_STATUS_RRDY_MSK))
+                               cpu_relax();
+
+                       rxd = readl(hw->base + ALTERA_SPI_RXDATA);
+                       if (hw->rx) {
+                               switch (hw->bytes_per_word) {
+                               case 1:
+                                       hw->rx[hw->count] = rxd;
+                                       break;
+                               case 2:
+                                       hw->rx[hw->count * 2] = rxd;
+                                       hw->rx[hw->count * 2 + 1] = rxd >> 8;
+                                       break;
+                               }
+                       }
+
+                       hw->count++;
+
+                       if (hw->count < hw->len)
+                               writel(hw_txbyte(hw, hw->count),
+                                      hw->base + ALTERA_SPI_TXDATA);
+                       else
+                               break;
+               }
+
+       }
+
+       return hw->count * hw->bytes_per_word;
+}
+
+static irqreturn_t altera_spi_irq(int irq, void *dev)
+{
+       struct altera_spi *hw = dev;
+       unsigned int rxd;
+
+       rxd = readl(hw->base + ALTERA_SPI_RXDATA);
+       if (hw->rx) {
+               switch (hw->bytes_per_word) {
+               case 1:
+                       hw->rx[hw->count] = rxd;
+                       break;
+               case 2:
+                       hw->rx[hw->count * 2] = rxd;
+                       hw->rx[hw->count * 2 + 1] = rxd >> 8;
+                       break;
+               }
+       }
+
+       hw->count++;
+
+       if (hw->count < hw->len)
+               writel(hw_txbyte(hw, hw->count), hw->base + ALTERA_SPI_TXDATA);
+       else
+               complete(&hw->done);
+
+       return IRQ_HANDLED;
+}
+
+static int __devinit altera_spi_probe(struct platform_device *pdev)
+{
+       struct altera_spi_platform_data *platp = pdev->dev.platform_data;
+       struct altera_spi *hw;
+       struct spi_master *master;
+       struct resource *res;
+       int err = -ENODEV;
+
+       master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi));
+       if (!master)
+               return err;
+
+       /* setup the master state. */
+       master->bus_num = pdev->id;
+       master->num_chipselect = 16;
+       master->mode_bits = SPI_CS_HIGH;
+       master->setup = altera_spi_setup;
+
+       hw = spi_master_get_devdata(master);
+       platform_set_drvdata(pdev, hw);
+
+       /* setup the state for the bitbang driver */
+       hw->bitbang.master = spi_master_get(master);
+       if (!hw->bitbang.master)
+               return err;
+       hw->bitbang.setup_transfer = altera_spi_setupxfer;
+       hw->bitbang.chipselect = altera_spi_chipsel;
+       hw->bitbang.txrx_bufs = altera_spi_txrx;
+
+       /* find and map our resources */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               goto exit_busy;
+       if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+                                    pdev->name))
+               goto exit_busy;
+       hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
+                                       resource_size(res));
+       if (!hw->base)
+               goto exit_busy;
+       /* program defaults into the registers */
+       hw->imr = 0;            /* disable spi interrupts */
+       writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
+       writel(0, hw->base + ALTERA_SPI_STATUS);        /* clear status reg */
+       if (readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK)
+               readl(hw->base + ALTERA_SPI_RXDATA);    /* flush rxdata */
+       /* irq is optional */
+       hw->irq = platform_get_irq(pdev, 0);
+       if (hw->irq >= 0) {
+               init_completion(&hw->done);
+               err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0,
+                                      pdev->name, hw);
+               if (err)
+                       goto exit;
+       }
+       /* find platform data */
+       if (!platp)
+               hw->bitbang.master->dev.of_node = pdev->dev.of_node;
+
+       /* register our spi controller */
+       err = spi_bitbang_start(&hw->bitbang);
+       if (err)
+               goto exit;
+       dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
+
+       return 0;
+
+exit_busy:
+       err = -EBUSY;
+exit:
+       platform_set_drvdata(pdev, NULL);
+       spi_master_put(master);
+       return err;
+}
+
+static int __devexit altera_spi_remove(struct platform_device *dev)
+{
+       struct altera_spi *hw = platform_get_drvdata(dev);
+       struct spi_master *master = hw->bitbang.master;
+
+       spi_bitbang_stop(&hw->bitbang);
+       platform_set_drvdata(dev, NULL);
+       spi_master_put(master);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id altera_spi_match[] = {
+       { .compatible = "ALTR,spi-1.0", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, altera_spi_match);
+#else /* CONFIG_OF */
+#define altera_spi_match NULL
+#endif /* CONFIG_OF */
+
+static struct platform_driver altera_spi_driver = {
+       .probe = altera_spi_probe,
+       .remove = __devexit_p(altera_spi_remove),
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .pm = NULL,
+               .of_match_table = altera_spi_match,
+       },
+};
+
+static int __init altera_spi_init(void)
+{
+       return platform_driver_register(&altera_spi_driver);
+}
+module_init(altera_spi_init);
+
+static void __exit altera_spi_exit(void)
+{
+       platform_driver_unregister(&altera_spi_driver);
+}
+module_exit(altera_spi_exit);
+
+MODULE_DESCRIPTION("Altera SPI driver");
+MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
index 3f223511127b56c9a6800743991aea342e5e2e92..a28462486df8c6f53eabd609a01e18cc8d361314 100644 (file)
@@ -425,6 +425,7 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
        struct bfin_spi_slave_data *chip = drv_data->cur_chip;
        struct spi_message *msg = drv_data->cur_msg;
        int n_bytes = drv_data->n_bytes;
+       int loop = 0;
 
        /* wait until transfer finished. */
        while (!(read_STAT(drv_data) & BIT_STAT_RXS))
@@ -435,10 +436,15 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
                /* last read */
                if (drv_data->rx) {
                        dev_dbg(&drv_data->pdev->dev, "last read\n");
-                       if (n_bytes == 2)
-                               *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
-                       else if (n_bytes == 1)
-                               *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
+                       if (n_bytes % 2) {
+                               u16 *buf = (u16 *)drv_data->rx;
+                               for (loop = 0; loop < n_bytes / 2; loop++)
+                                       *buf++ = read_RDBR(drv_data);
+                       } else {
+                               u8 *buf = (u8 *)drv_data->rx;
+                               for (loop = 0; loop < n_bytes; loop++)
+                                       *buf++ = read_RDBR(drv_data);
+                       }
                        drv_data->rx += n_bytes;
                }
 
@@ -458,29 +464,53 @@ static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
        if (drv_data->rx && drv_data->tx) {
                /* duplex */
                dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n");
-               if (drv_data->n_bytes == 2) {
-                       *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
-                       write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
-               } else if (drv_data->n_bytes == 1) {
-                       *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
-                       write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
+               if (n_bytes % 2) {
+                       u16 *buf = (u16 *)drv_data->rx;
+                       u16 *buf2 = (u16 *)drv_data->tx;
+                       for (loop = 0; loop < n_bytes / 2; loop++) {
+                               *buf++ = read_RDBR(drv_data);
+                               write_TDBR(drv_data, *buf2++);
+                       }
+               } else {
+                       u8 *buf = (u8 *)drv_data->rx;
+                       u8 *buf2 = (u8 *)drv_data->tx;
+                       for (loop = 0; loop < n_bytes; loop++) {
+                               *buf++ = read_RDBR(drv_data);
+                               write_TDBR(drv_data, *buf2++);
+                       }
                }
        } else if (drv_data->rx) {
                /* read */
                dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n");
-               if (drv_data->n_bytes == 2)
-                       *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
-               else if (drv_data->n_bytes == 1)
-                       *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
-               write_TDBR(drv_data, chip->idle_tx_val);
+               if (n_bytes % 2) {
+                       u16 *buf = (u16 *)drv_data->rx;
+                       for (loop = 0; loop < n_bytes / 2; loop++) {
+                               *buf++ = read_RDBR(drv_data);
+                               write_TDBR(drv_data, chip->idle_tx_val);
+                       }
+               } else {
+                       u8 *buf = (u8 *)drv_data->rx;
+                       for (loop = 0; loop < n_bytes; loop++) {
+                               *buf++ = read_RDBR(drv_data);
+                               write_TDBR(drv_data, chip->idle_tx_val);
+                       }
+               }
        } else if (drv_data->tx) {
                /* write */
                dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n");
-               bfin_spi_dummy_read(drv_data);
-               if (drv_data->n_bytes == 2)
-                       write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
-               else if (drv_data->n_bytes == 1)
-                       write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
+               if (n_bytes % 2) {
+                       u16 *buf = (u16 *)drv_data->tx;
+                       for (loop = 0; loop < n_bytes / 2; loop++) {
+                               read_RDBR(drv_data);
+                               write_TDBR(drv_data, *buf++);
+                       }
+               } else {
+                       u8 *buf = (u8 *)drv_data->tx;
+                       for (loop = 0; loop < n_bytes; loop++) {
+                               read_RDBR(drv_data);
+                               write_TDBR(drv_data, *buf++);
+                       }
+               }
        }
 
        if (drv_data->tx)
@@ -623,6 +653,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
                message->state = bfin_spi_next_transfer(drv_data);
                /* Schedule next transfer tasklet */
                tasklet_schedule(&drv_data->pump_transfers);
+               return;
        }
 
        if (transfer->tx_buf != NULL) {
@@ -651,16 +682,16 @@ static void bfin_spi_pump_transfers(unsigned long data)
 
        /* Bits per word setup */
        bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word;
-       if (bits_per_word == 8) {
-               drv_data->n_bytes = 1;
-               drv_data->len = transfer->len;
-               cr_width = 0;
-               drv_data->ops = &bfin_bfin_spi_transfer_ops_u8;
-       } else if (bits_per_word == 16) {
-               drv_data->n_bytes = 2;
+       if ((bits_per_word > 0) && (bits_per_word % 16 == 0)) {
+               drv_data->n_bytes = bits_per_word/8;
                drv_data->len = (transfer->len) >> 1;
                cr_width = BIT_CTL_WORDSIZE;
                drv_data->ops = &bfin_bfin_spi_transfer_ops_u16;
+       } else if ((bits_per_word > 0) && (bits_per_word % 8 == 0)) {
+               drv_data->n_bytes = bits_per_word/8;
+               drv_data->len = transfer->len;
+               cr_width = 0;
+               drv_data->ops = &bfin_bfin_spi_transfer_ops_u8;
        } else {
                dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word\n");
                message->status = -EINVAL;
@@ -815,10 +846,19 @@ static void bfin_spi_pump_transfers(unsigned long data)
                if (drv_data->tx == NULL)
                        write_TDBR(drv_data, chip->idle_tx_val);
                else {
-                       if (bits_per_word == 8)
-                               write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
-                       else
-                               write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
+                       int loop;
+                       if (bits_per_word % 16 == 0) {
+                               u16 *buf = (u16 *)drv_data->tx;
+                               for (loop = 0; loop < bits_per_word / 16;
+                                               loop++) {
+                                       write_TDBR(drv_data, *buf++);
+                               }
+                       } else if (bits_per_word % 8 == 0) {
+                               u8 *buf = (u8 *)drv_data->tx;
+                               for (loop = 0; loop < bits_per_word / 8; loop++)
+                                       write_TDBR(drv_data, *buf++);
+                       }
+
                        drv_data->tx += drv_data->n_bytes;
                }
 
@@ -1031,7 +1071,7 @@ static int bfin_spi_setup(struct spi_device *spi)
                chip->ctl_reg &= bfin_ctl_reg;
        }
 
-       if (spi->bits_per_word != 8 && spi->bits_per_word != 16) {
+       if (spi->bits_per_word % 8) {
                dev_err(&spi->dev, "%d bits_per_word is not supported\n",
                                spi->bits_per_word);
                goto error;
index 8b55724d5f390152b7005d15b667f4f18f8ac788..14a63f6010d1bfda52ea7b125c8571fab457615b 100644 (file)
@@ -259,10 +259,6 @@ static void bitbang_work(struct work_struct *work)
        struct spi_bitbang      *bitbang =
                container_of(work, struct spi_bitbang, work);
        unsigned long           flags;
-       int                     (*setup_transfer)(struct spi_device *,
-                                       struct spi_transfer *);
-
-       setup_transfer = bitbang->setup_transfer;
 
        spin_lock_irqsave(&bitbang->lock, flags);
        bitbang->busy = 1;
@@ -300,11 +296,7 @@ static void bitbang_work(struct work_struct *work)
 
                        /* init (-1) or override (1) transfer params */
                        if (do_setup != 0) {
-                               if (!setup_transfer) {
-                                       status = -ENOPROTOOPT;
-                                       break;
-                               }
-                               status = setup_transfer(spi, t);
+                               status = bitbang->setup_transfer(spi, t);
                                if (status < 0)
                                        break;
                                if (do_setup == -1)
@@ -465,6 +457,9 @@ int spi_bitbang_start(struct spi_bitbang *bitbang)
                }
        } else if (!bitbang->master->setup)
                return -EINVAL;
+       if (bitbang->master->transfer == spi_bitbang_transfer &&
+                       !bitbang->setup_transfer)
+               return -EINVAL;
 
        /* this task is the only thing to touch the SPI bits */
        bitbang->busy = 0;
index 1cf9d5faabf4ac1f9347a62628ba48e31bede889..69d6dba67c194b9b76d88b459703a651cadc2392 100644 (file)
@@ -174,7 +174,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
 #define SPI_IMX2_3_CTRL                0x08
 #define SPI_IMX2_3_CTRL_ENABLE         (1 <<  0)
 #define SPI_IMX2_3_CTRL_XCH            (1 <<  2)
-#define SPI_IMX2_3_CTRL_MODE(cs)       (1 << ((cs) +  4))
+#define SPI_IMX2_3_CTRL_MODE_MASK      (0xf << 4)
 #define SPI_IMX2_3_CTRL_POSTDIV_OFFSET 8
 #define SPI_IMX2_3_CTRL_PREDIV_OFFSET  12
 #define SPI_IMX2_3_CTRL_CS(cs)         ((cs) << 18)
@@ -253,8 +253,14 @@ static int __maybe_unused spi_imx2_3_config(struct spi_imx_data *spi_imx,
 {
        u32 ctrl = SPI_IMX2_3_CTRL_ENABLE, cfg = 0;
 
-       /* set master mode */
-       ctrl |= SPI_IMX2_3_CTRL_MODE(config->cs);
+       /*
+        * The hardware seems to have a race condition when changing modes. The
+        * current assumption is that the selection of the channel arrives
+        * earlier in the hardware than the mode bits when they are written at
+        * the same time.
+        * So set master mode for all channels as we do not support slave mode.
+        */
+       ctrl |= SPI_IMX2_3_CTRL_MODE_MASK;
 
        /* set clock speed */
        ctrl |= spi_imx2_3_clkdiv(spi_imx->spi_clk, config->speed_hz);
diff --git a/drivers/spi/spi_oc_tiny.c b/drivers/spi/spi_oc_tiny.c
new file mode 100644 (file)
index 0000000..f1bde66
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * OpenCores tiny SPI master driver
+ *
+ * http://opencores.org/project,tiny_spi
+ *
+ * Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * Based on spi_s3c24xx.c, which is:
+ * Copyright (c) 2006 Ben Dooks
+ * Copyright (c) 2006 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/spi/spi_oc_tiny.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+
+#define DRV_NAME "spi_oc_tiny"
+
+#define TINY_SPI_RXDATA 0
+#define TINY_SPI_TXDATA 4
+#define TINY_SPI_STATUS 8
+#define TINY_SPI_CONTROL 12
+#define TINY_SPI_BAUD 16
+
+#define TINY_SPI_STATUS_TXE 0x1
+#define TINY_SPI_STATUS_TXR 0x2
+
+struct tiny_spi {
+       /* bitbang has to be first */
+       struct spi_bitbang bitbang;
+       struct completion done;
+
+       void __iomem *base;
+       int irq;
+       unsigned int freq;
+       unsigned int baudwidth;
+       unsigned int baud;
+       unsigned int speed_hz;
+       unsigned int mode;
+       unsigned int len;
+       unsigned int txc, rxc;
+       const u8 *txp;
+       u8 *rxp;
+       unsigned int gpio_cs_count;
+       int *gpio_cs;
+};
+
+static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev)
+{
+       return spi_master_get_devdata(sdev->master);
+}
+
+static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz)
+{
+       struct tiny_spi *hw = tiny_spi_to_hw(spi);
+
+       return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1;
+}
+
+static void tiny_spi_chipselect(struct spi_device *spi, int is_active)
+{
+       struct tiny_spi *hw = tiny_spi_to_hw(spi);
+
+       if (hw->gpio_cs_count) {
+               gpio_set_value(hw->gpio_cs[spi->chip_select],
+                       (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
+       }
+}
+
+static int tiny_spi_setup_transfer(struct spi_device *spi,
+                                  struct spi_transfer *t)
+{
+       struct tiny_spi *hw = tiny_spi_to_hw(spi);
+       unsigned int baud = hw->baud;
+
+       if (t) {
+               if (t->speed_hz && t->speed_hz != hw->speed_hz)
+                       baud = tiny_spi_baud(spi, t->speed_hz);
+       }
+       writel(baud, hw->base + TINY_SPI_BAUD);
+       writel(hw->mode, hw->base + TINY_SPI_CONTROL);
+       return 0;
+}
+
+static int tiny_spi_setup(struct spi_device *spi)
+{
+       struct tiny_spi *hw = tiny_spi_to_hw(spi);
+
+       if (spi->max_speed_hz != hw->speed_hz) {
+               hw->speed_hz = spi->max_speed_hz;
+               hw->baud = tiny_spi_baud(spi, hw->speed_hz);
+       }
+       hw->mode = spi->mode & (SPI_CPOL | SPI_CPHA);
+       return 0;
+}
+
+static inline void tiny_spi_wait_txr(struct tiny_spi *hw)
+{
+       while (!(readb(hw->base + TINY_SPI_STATUS) &
+                TINY_SPI_STATUS_TXR))
+               cpu_relax();
+}
+
+static inline void tiny_spi_wait_txe(struct tiny_spi *hw)
+{
+       while (!(readb(hw->base + TINY_SPI_STATUS) &
+                TINY_SPI_STATUS_TXE))
+               cpu_relax();
+}
+
+static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
+{
+       struct tiny_spi *hw = tiny_spi_to_hw(spi);
+       const u8 *txp = t->tx_buf;
+       u8 *rxp = t->rx_buf;
+       unsigned int i;
+
+       if (hw->irq >= 0) {
+               /* use intrrupt driven data transfer */
+               hw->len = t->len;
+               hw->txp = t->tx_buf;
+               hw->rxp = t->rx_buf;
+               hw->txc = 0;
+               hw->rxc = 0;
+
+               /* send the first byte */
+               if (t->len > 1) {
+                       writeb(hw->txp ? *hw->txp++ : 0,
+                              hw->base + TINY_SPI_TXDATA);
+                       hw->txc++;
+                       writeb(hw->txp ? *hw->txp++ : 0,
+                              hw->base + TINY_SPI_TXDATA);
+                       hw->txc++;
+                       writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS);
+               } else {
+                       writeb(hw->txp ? *hw->txp++ : 0,
+                              hw->base + TINY_SPI_TXDATA);
+                       hw->txc++;
+                       writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS);
+               }
+
+               wait_for_completion(&hw->done);
+       } else if (txp && rxp) {
+               /* we need to tighten the transfer loop */
+               writeb(*txp++, hw->base + TINY_SPI_TXDATA);
+               if (t->len > 1) {
+                       writeb(*txp++, hw->base + TINY_SPI_TXDATA);
+                       for (i = 2; i < t->len; i++) {
+                               u8 rx, tx = *txp++;
+                               tiny_spi_wait_txr(hw);
+                               rx = readb(hw->base + TINY_SPI_TXDATA);
+                               writeb(tx, hw->base + TINY_SPI_TXDATA);
+                               *rxp++ = rx;
+                       }
+                       tiny_spi_wait_txr(hw);
+                       *rxp++ = readb(hw->base + TINY_SPI_TXDATA);
+               }
+               tiny_spi_wait_txe(hw);
+               *rxp++ = readb(hw->base + TINY_SPI_RXDATA);
+       } else if (rxp) {
+               writeb(0, hw->base + TINY_SPI_TXDATA);
+               if (t->len > 1) {
+                       writeb(0,
+                              hw->base + TINY_SPI_TXDATA);
+                       for (i = 2; i < t->len; i++) {
+                               u8 rx;
+                               tiny_spi_wait_txr(hw);
+                               rx = readb(hw->base + TINY_SPI_TXDATA);
+                               writeb(0, hw->base + TINY_SPI_TXDATA);
+                               *rxp++ = rx;
+                       }
+                       tiny_spi_wait_txr(hw);
+                       *rxp++ = readb(hw->base + TINY_SPI_TXDATA);
+               }
+               tiny_spi_wait_txe(hw);
+               *rxp++ = readb(hw->base + TINY_SPI_RXDATA);
+       } else if (txp) {
+               writeb(*txp++, hw->base + TINY_SPI_TXDATA);
+               if (t->len > 1) {
+                       writeb(*txp++, hw->base + TINY_SPI_TXDATA);
+                       for (i = 2; i < t->len; i++) {
+                               u8 tx = *txp++;
+                               tiny_spi_wait_txr(hw);
+                               writeb(tx, hw->base + TINY_SPI_TXDATA);
+                       }
+               }
+               tiny_spi_wait_txe(hw);
+       } else {
+               writeb(0, hw->base + TINY_SPI_TXDATA);
+               if (t->len > 1) {
+                       writeb(0, hw->base + TINY_SPI_TXDATA);
+                       for (i = 2; i < t->len; i++) {
+                               tiny_spi_wait_txr(hw);
+                               writeb(0, hw->base + TINY_SPI_TXDATA);
+                       }
+               }
+               tiny_spi_wait_txe(hw);
+       }
+       return t->len;
+}
+
+static irqreturn_t tiny_spi_irq(int irq, void *dev)
+{
+       struct tiny_spi *hw = dev;
+
+       writeb(0, hw->base + TINY_SPI_STATUS);
+       if (hw->rxc + 1 == hw->len) {
+               if (hw->rxp)
+                       *hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA);
+               hw->rxc++;
+               complete(&hw->done);
+       } else {
+               if (hw->rxp)
+                       *hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA);
+               hw->rxc++;
+               if (hw->txc < hw->len) {
+                       writeb(hw->txp ? *hw->txp++ : 0,
+                              hw->base + TINY_SPI_TXDATA);
+                       hw->txc++;
+                       writeb(TINY_SPI_STATUS_TXR,
+                              hw->base + TINY_SPI_STATUS);
+               } else {
+                       writeb(TINY_SPI_STATUS_TXE,
+                              hw->base + TINY_SPI_STATUS);
+               }
+       }
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_OF
+#include <linux/of_gpio.h>
+
+static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
+{
+       struct tiny_spi *hw = platform_get_drvdata(pdev);
+       struct device_node *np = pdev->dev.of_node;
+       unsigned int i;
+       const __be32 *val;
+       int len;
+
+       if (!np)
+               return 0;
+       hw->gpio_cs_count = of_gpio_count(np);
+       if (hw->gpio_cs_count) {
+               hw->gpio_cs = devm_kzalloc(&pdev->dev,
+                               hw->gpio_cs_count * sizeof(unsigned int),
+                               GFP_KERNEL);
+               if (!hw->gpio_cs)
+                       return -ENOMEM;
+       }
+       for (i = 0; i < hw->gpio_cs_count; i++) {
+               hw->gpio_cs[i] = of_get_gpio_flags(np, i, NULL);
+               if (hw->gpio_cs[i] < 0)
+                       return -ENODEV;
+       }
+       hw->bitbang.master->dev.of_node = pdev->dev.of_node;
+       val = of_get_property(pdev->dev.of_node,
+                             "clock-frequency", &len);
+       if (val && len >= sizeof(__be32))
+               hw->freq = be32_to_cpup(val);
+       val = of_get_property(pdev->dev.of_node, "baud-width", &len);
+       if (val && len >= sizeof(__be32))
+               hw->baudwidth = be32_to_cpup(val);
+       return 0;
+}
+#else /* !CONFIG_OF */
+static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
+{
+       return 0;
+}
+#endif /* CONFIG_OF */
+
+static int __devinit tiny_spi_probe(struct platform_device *pdev)
+{
+       struct tiny_spi_platform_data *platp = pdev->dev.platform_data;
+       struct tiny_spi *hw;
+       struct spi_master *master;
+       struct resource *res;
+       unsigned int i;
+       int err = -ENODEV;
+
+       master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi));
+       if (!master)
+               return err;
+
+       /* setup the master state. */
+       master->bus_num = pdev->id;
+       master->num_chipselect = 255;
+       master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+       master->setup = tiny_spi_setup;
+
+       hw = spi_master_get_devdata(master);
+       platform_set_drvdata(pdev, hw);
+
+       /* setup the state for the bitbang driver */
+       hw->bitbang.master = spi_master_get(master);
+       if (!hw->bitbang.master)
+               return err;
+       hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
+       hw->bitbang.chipselect = tiny_spi_chipselect;
+       hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
+
+       /* find and map our resources */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               goto exit_busy;
+       if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+                                    pdev->name))
+               goto exit_busy;
+       hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
+                                       resource_size(res));
+       if (!hw->base)
+               goto exit_busy;
+       /* irq is optional */
+       hw->irq = platform_get_irq(pdev, 0);
+       if (hw->irq >= 0) {
+               init_completion(&hw->done);
+               err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0,
+                                      pdev->name, hw);
+               if (err)
+                       goto exit;
+       }
+       /* find platform data */
+       if (platp) {
+               hw->gpio_cs_count = platp->gpio_cs_count;
+               hw->gpio_cs = platp->gpio_cs;
+               if (platp->gpio_cs_count && !platp->gpio_cs)
+                       goto exit_busy;
+               hw->freq = platp->freq;
+               hw->baudwidth = platp->baudwidth;
+       } else {
+               err = tiny_spi_of_probe(pdev);
+               if (err)
+                       goto exit;
+       }
+       for (i = 0; i < hw->gpio_cs_count; i++) {
+               err = gpio_request(hw->gpio_cs[i], dev_name(&pdev->dev));
+               if (err)
+                       goto exit_gpio;
+               gpio_direction_output(hw->gpio_cs[i], 1);
+       }
+       hw->bitbang.master->num_chipselect = max(1U, hw->gpio_cs_count);
+
+       /* register our spi controller */
+       err = spi_bitbang_start(&hw->bitbang);
+       if (err)
+               goto exit;
+       dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
+
+       return 0;
+
+exit_gpio:
+       while (i-- > 0)
+               gpio_free(hw->gpio_cs[i]);
+exit_busy:
+       err = -EBUSY;
+exit:
+       platform_set_drvdata(pdev, NULL);
+       spi_master_put(master);
+       return err;
+}
+
+static int __devexit tiny_spi_remove(struct platform_device *pdev)
+{
+       struct tiny_spi *hw = platform_get_drvdata(pdev);
+       struct spi_master *master = hw->bitbang.master;
+       unsigned int i;
+
+       spi_bitbang_stop(&hw->bitbang);
+       for (i = 0; i < hw->gpio_cs_count; i++)
+               gpio_free(hw->gpio_cs[i]);
+       platform_set_drvdata(pdev, NULL);
+       spi_master_put(master);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id tiny_spi_match[] = {
+       { .compatible = "opencores,tiny-spi-rtlsvn2", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, tiny_spi_match);
+#else /* CONFIG_OF */
+#define tiny_spi_match NULL
+#endif /* CONFIG_OF */
+
+static struct platform_driver tiny_spi_driver = {
+       .probe = tiny_spi_probe,
+       .remove = __devexit_p(tiny_spi_remove),
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .pm = NULL,
+               .of_match_table = tiny_spi_match,
+       },
+};
+
+static int __init tiny_spi_init(void)
+{
+       return platform_driver_register(&tiny_spi_driver);
+}
+module_init(tiny_spi_init);
+
+static void __exit tiny_spi_exit(void)
+{
+       platform_driver_unregister(&tiny_spi_driver);
+}
+module_exit(tiny_spi_exit);
+
+MODULE_DESCRIPTION("OpenCores tiny SPI driver");
+MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/spi/spi_sh.c b/drivers/spi/spi_sh.c
new file mode 100644 (file)
index 0000000..869a07d
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * SH SPI bus driver
+ *
+ * Copyright (C) 2011  Renesas Solutions Corp.
+ *
+ * Based on pxa2xx_spi.c:
+ * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/spi/spi.h>
+
+#define SPI_SH_TBR             0x00
+#define SPI_SH_RBR             0x00
+#define SPI_SH_CR1             0x08
+#define SPI_SH_CR2             0x10
+#define SPI_SH_CR3             0x18
+#define SPI_SH_CR4             0x20
+#define SPI_SH_CR5             0x28
+
+/* CR1 */
+#define SPI_SH_TBE             0x80
+#define SPI_SH_TBF             0x40
+#define SPI_SH_RBE             0x20
+#define SPI_SH_RBF             0x10
+#define SPI_SH_PFONRD          0x08
+#define SPI_SH_SSDB            0x04
+#define SPI_SH_SSD             0x02
+#define SPI_SH_SSA             0x01
+
+/* CR2 */
+#define SPI_SH_RSTF            0x80
+#define SPI_SH_LOOPBK          0x40
+#define SPI_SH_CPOL            0x20
+#define SPI_SH_CPHA            0x10
+#define SPI_SH_L1M0            0x08
+
+/* CR3 */
+#define SPI_SH_MAX_BYTE                0xFF
+
+/* CR4 */
+#define SPI_SH_TBEI            0x80
+#define SPI_SH_TBFI            0x40
+#define SPI_SH_RBEI            0x20
+#define SPI_SH_RBFI            0x10
+#define SPI_SH_WPABRT          0x04
+#define SPI_SH_SSS             0x01
+
+/* CR8 */
+#define SPI_SH_P1L0            0x80
+#define SPI_SH_PP1L0           0x40
+#define SPI_SH_MUXI            0x20
+#define SPI_SH_MUXIRQ          0x10
+
+#define SPI_SH_FIFO_SIZE       32
+#define SPI_SH_SEND_TIMEOUT    (3 * HZ)
+#define SPI_SH_RECEIVE_TIMEOUT (HZ >> 3)
+
+#undef DEBUG
+
+struct spi_sh_data {
+       void __iomem *addr;
+       int irq;
+       struct spi_master *master;
+       struct list_head queue;
+       struct workqueue_struct *workqueue;
+       struct work_struct ws;
+       unsigned long cr1;
+       wait_queue_head_t wait;
+       spinlock_t lock;
+};
+
+static void spi_sh_write(struct spi_sh_data *ss, unsigned long data,
+                            unsigned long offset)
+{
+       writel(data, ss->addr + offset);
+}
+
+static unsigned long spi_sh_read(struct spi_sh_data *ss, unsigned long offset)
+{
+       return readl(ss->addr + offset);
+}
+
+static void spi_sh_set_bit(struct spi_sh_data *ss, unsigned long val,
+                               unsigned long offset)
+{
+       unsigned long tmp;
+
+       tmp = spi_sh_read(ss, offset);
+       tmp |= val;
+       spi_sh_write(ss, tmp, offset);
+}
+
+static void spi_sh_clear_bit(struct spi_sh_data *ss, unsigned long val,
+                               unsigned long offset)
+{
+       unsigned long tmp;
+
+       tmp = spi_sh_read(ss, offset);
+       tmp &= ~val;
+       spi_sh_write(ss, tmp, offset);
+}
+
+static void clear_fifo(struct spi_sh_data *ss)
+{
+       spi_sh_set_bit(ss, SPI_SH_RSTF, SPI_SH_CR2);
+       spi_sh_clear_bit(ss, SPI_SH_RSTF, SPI_SH_CR2);
+}
+
+static int spi_sh_wait_receive_buffer(struct spi_sh_data *ss)
+{
+       int timeout = 100000;
+
+       while (spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_RBE) {
+               udelay(10);
+               if (timeout-- < 0)
+                       return -ETIMEDOUT;
+       }
+       return 0;
+}
+
+static int spi_sh_wait_write_buffer_empty(struct spi_sh_data *ss)
+{
+       int timeout = 100000;
+
+       while (!(spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_TBE)) {
+               udelay(10);
+               if (timeout-- < 0)
+                       return -ETIMEDOUT;
+       }
+       return 0;
+}
+
+static int spi_sh_send(struct spi_sh_data *ss, struct spi_message *mesg,
+                       struct spi_transfer *t)
+{
+       int i, retval = 0;
+       int remain = t->len;
+       int cur_len;
+       unsigned char *data;
+       unsigned long tmp;
+       long ret;
+
+       if (t->len)
+               spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
+
+       data = (unsigned char *)t->tx_buf;
+       while (remain > 0) {
+               cur_len = min(SPI_SH_FIFO_SIZE, remain);
+               for (i = 0; i < cur_len &&
+                               !(spi_sh_read(ss, SPI_SH_CR4) &
+                                                       SPI_SH_WPABRT) &&
+                               !(spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_TBF);
+                               i++)
+                       spi_sh_write(ss, (unsigned long)data[i], SPI_SH_TBR);
+
+               if (spi_sh_read(ss, SPI_SH_CR4) & SPI_SH_WPABRT) {
+                       /* Abort SPI operation */
+                       spi_sh_set_bit(ss, SPI_SH_WPABRT, SPI_SH_CR4);
+                       retval = -EIO;
+                       break;
+               }
+
+               cur_len = i;
+
+               remain -= cur_len;
+               data += cur_len;
+
+               if (remain > 0) {
+                       ss->cr1 &= ~SPI_SH_TBE;
+                       spi_sh_set_bit(ss, SPI_SH_TBE, SPI_SH_CR4);
+                       ret = wait_event_interruptible_timeout(ss->wait,
+                                                ss->cr1 & SPI_SH_TBE,
+                                                SPI_SH_SEND_TIMEOUT);
+                       if (ret == 0 && !(ss->cr1 & SPI_SH_TBE)) {
+                               printk(KERN_ERR "%s: timeout\n", __func__);
+                               return -ETIMEDOUT;
+                       }
+               }
+       }
+
+       if (list_is_last(&t->transfer_list, &mesg->transfers)) {
+               tmp = spi_sh_read(ss, SPI_SH_CR1);
+               tmp = tmp & ~(SPI_SH_SSD | SPI_SH_SSDB);
+               spi_sh_write(ss, tmp, SPI_SH_CR1);
+               spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
+
+               ss->cr1 &= ~SPI_SH_TBE;
+               spi_sh_set_bit(ss, SPI_SH_TBE, SPI_SH_CR4);
+               ret = wait_event_interruptible_timeout(ss->wait,
+                                        ss->cr1 & SPI_SH_TBE,
+                                        SPI_SH_SEND_TIMEOUT);
+               if (ret == 0 && (ss->cr1 & SPI_SH_TBE)) {
+                       printk(KERN_ERR "%s: timeout\n", __func__);
+                       return -ETIMEDOUT;
+               }
+       }
+
+       return retval;
+}
+
+static int spi_sh_receive(struct spi_sh_data *ss, struct spi_message *mesg,
+                         struct spi_transfer *t)
+{
+       int i;
+       int remain = t->len;
+       int cur_len;
+       unsigned char *data;
+       unsigned long tmp;
+       long ret;
+
+       if (t->len > SPI_SH_MAX_BYTE)
+               spi_sh_write(ss, SPI_SH_MAX_BYTE, SPI_SH_CR3);
+       else
+               spi_sh_write(ss, t->len, SPI_SH_CR3);
+
+       tmp = spi_sh_read(ss, SPI_SH_CR1);
+       tmp = tmp & ~(SPI_SH_SSD | SPI_SH_SSDB);
+       spi_sh_write(ss, tmp, SPI_SH_CR1);
+       spi_sh_set_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
+
+       spi_sh_wait_write_buffer_empty(ss);
+
+       data = (unsigned char *)t->rx_buf;
+       while (remain > 0) {
+               if (remain >= SPI_SH_FIFO_SIZE) {
+                       ss->cr1 &= ~SPI_SH_RBF;
+                       spi_sh_set_bit(ss, SPI_SH_RBF, SPI_SH_CR4);
+                       ret = wait_event_interruptible_timeout(ss->wait,
+                                                ss->cr1 & SPI_SH_RBF,
+                                                SPI_SH_RECEIVE_TIMEOUT);
+                       if (ret == 0 &&
+                           spi_sh_read(ss, SPI_SH_CR1) & SPI_SH_RBE) {
+                               printk(KERN_ERR "%s: timeout\n", __func__);
+                               return -ETIMEDOUT;
+                       }
+               }
+
+               cur_len = min(SPI_SH_FIFO_SIZE, remain);
+               for (i = 0; i < cur_len; i++) {
+                       if (spi_sh_wait_receive_buffer(ss))
+                               break;
+                       data[i] = (unsigned char)spi_sh_read(ss, SPI_SH_RBR);
+               }
+
+               remain -= cur_len;
+               data += cur_len;
+       }
+
+       /* deassert CS when SPI is receiving. */
+       if (t->len > SPI_SH_MAX_BYTE) {
+               clear_fifo(ss);
+               spi_sh_write(ss, 1, SPI_SH_CR3);
+       } else {
+               spi_sh_write(ss, 0, SPI_SH_CR3);
+       }
+
+       return 0;
+}
+
+static void spi_sh_work(struct work_struct *work)
+{
+       struct spi_sh_data *ss = container_of(work, struct spi_sh_data, ws);
+       struct spi_message *mesg;
+       struct spi_transfer *t;
+       unsigned long flags;
+       int ret;
+
+       pr_debug("%s: enter\n", __func__);
+
+       spin_lock_irqsave(&ss->lock, flags);
+       while (!list_empty(&ss->queue)) {
+               mesg = list_entry(ss->queue.next, struct spi_message, queue);
+               list_del_init(&mesg->queue);
+
+               spin_unlock_irqrestore(&ss->lock, flags);
+               list_for_each_entry(t, &mesg->transfers, transfer_list) {
+                       pr_debug("tx_buf = %p, rx_buf = %p\n",
+                                       t->tx_buf, t->rx_buf);
+                       pr_debug("len = %d, delay_usecs = %d\n",
+                                       t->len, t->delay_usecs);
+
+                       if (t->tx_buf) {
+                               ret = spi_sh_send(ss, mesg, t);
+                               if (ret < 0)
+                                       goto error;
+                       }
+                       if (t->rx_buf) {
+                               ret = spi_sh_receive(ss, mesg, t);
+                               if (ret < 0)
+                                       goto error;
+                       }
+                       mesg->actual_length += t->len;
+               }
+               spin_lock_irqsave(&ss->lock, flags);
+
+               mesg->status = 0;
+               mesg->complete(mesg->context);
+       }
+
+       clear_fifo(ss);
+       spi_sh_set_bit(ss, SPI_SH_SSD, SPI_SH_CR1);
+       udelay(100);
+
+       spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD,
+                        SPI_SH_CR1);
+
+       clear_fifo(ss);
+
+       spin_unlock_irqrestore(&ss->lock, flags);
+
+       return;
+
+ error:
+       mesg->status = ret;
+       mesg->complete(mesg->context);
+
+       spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD,
+                        SPI_SH_CR1);
+       clear_fifo(ss);
+
+}
+
+static int spi_sh_setup(struct spi_device *spi)
+{
+       struct spi_sh_data *ss = spi_master_get_devdata(spi->master);
+
+       if (!spi->bits_per_word)
+               spi->bits_per_word = 8;
+
+       pr_debug("%s: enter\n", __func__);
+
+       spi_sh_write(ss, 0xfe, SPI_SH_CR1);     /* SPI sycle stop */
+       spi_sh_write(ss, 0x00, SPI_SH_CR1);     /* CR1 init */
+       spi_sh_write(ss, 0x00, SPI_SH_CR3);     /* CR3 init */
+
+       clear_fifo(ss);
+
+       /* 1/8 clock */
+       spi_sh_write(ss, spi_sh_read(ss, SPI_SH_CR2) | 0x07, SPI_SH_CR2);
+       udelay(10);
+
+       return 0;
+}
+
+static int spi_sh_transfer(struct spi_device *spi, struct spi_message *mesg)
+{
+       struct spi_sh_data *ss = spi_master_get_devdata(spi->master);
+       unsigned long flags;
+
+       pr_debug("%s: enter\n", __func__);
+       pr_debug("\tmode = %02x\n", spi->mode);
+
+       spin_lock_irqsave(&ss->lock, flags);
+
+       mesg->actual_length = 0;
+       mesg->status = -EINPROGRESS;
+
+       spi_sh_clear_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
+
+       list_add_tail(&mesg->queue, &ss->queue);
+       queue_work(ss->workqueue, &ss->ws);
+
+       spin_unlock_irqrestore(&ss->lock, flags);
+
+       return 0;
+}
+
+static void spi_sh_cleanup(struct spi_device *spi)
+{
+       struct spi_sh_data *ss = spi_master_get_devdata(spi->master);
+
+       pr_debug("%s: enter\n", __func__);
+
+       spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD,
+                        SPI_SH_CR1);
+}
+
+static irqreturn_t spi_sh_irq(int irq, void *_ss)
+{
+       struct spi_sh_data *ss = (struct spi_sh_data *)_ss;
+       unsigned long cr1;
+
+       cr1 = spi_sh_read(ss, SPI_SH_CR1);
+       if (cr1 & SPI_SH_TBE)
+               ss->cr1 |= SPI_SH_TBE;
+       if (cr1 & SPI_SH_TBF)
+               ss->cr1 |= SPI_SH_TBF;
+       if (cr1 & SPI_SH_RBE)
+               ss->cr1 |= SPI_SH_RBE;
+       if (cr1 & SPI_SH_RBF)
+               ss->cr1 |= SPI_SH_RBF;
+
+       if (ss->cr1) {
+               spi_sh_clear_bit(ss, ss->cr1, SPI_SH_CR4);
+               wake_up(&ss->wait);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int __devexit spi_sh_remove(struct platform_device *pdev)
+{
+       struct spi_sh_data *ss = dev_get_drvdata(&pdev->dev);
+
+       destroy_workqueue(ss->workqueue);
+       free_irq(ss->irq, ss);
+       iounmap(ss->addr);
+       spi_master_put(ss->master);
+
+       return 0;
+}
+
+static int __devinit spi_sh_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct spi_master *master;
+       struct spi_sh_data *ss;
+       int ret, irq;
+
+       /* get base addr */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (unlikely(res == NULL)) {
+               dev_err(&pdev->dev, "invalid resource\n");
+               return -EINVAL;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "platform_get_irq error\n");
+               return -ENODEV;
+       }
+
+       master = spi_alloc_master(&pdev->dev, sizeof(struct spi_sh_data));
+       if (master == NULL) {
+               dev_err(&pdev->dev, "spi_alloc_master error.\n");
+               return -ENOMEM;
+       }
+
+       ss = spi_master_get_devdata(master);
+       dev_set_drvdata(&pdev->dev, ss);
+
+       ss->irq = irq;
+       ss->master = master;
+       ss->addr = ioremap(res->start, resource_size(res));
+       if (ss->addr == NULL) {
+               dev_err(&pdev->dev, "ioremap error.\n");
+               ret = -ENOMEM;
+               goto error1;
+       }
+       INIT_LIST_HEAD(&ss->queue);
+       spin_lock_init(&ss->lock);
+       INIT_WORK(&ss->ws, spi_sh_work);
+       init_waitqueue_head(&ss->wait);
+       ss->workqueue = create_singlethread_workqueue(
+                                       dev_name(master->dev.parent));
+       if (ss->workqueue == NULL) {
+               dev_err(&pdev->dev, "create workqueue error\n");
+               ret = -EBUSY;
+               goto error2;
+       }
+
+       ret = request_irq(irq, spi_sh_irq, IRQF_DISABLED, "spi_sh", ss);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "request_irq error\n");
+               goto error3;
+       }
+
+       master->num_chipselect = 2;
+       master->bus_num = pdev->id;
+       master->setup = spi_sh_setup;
+       master->transfer = spi_sh_transfer;
+       master->cleanup = spi_sh_cleanup;
+
+       ret = spi_register_master(master);
+       if (ret < 0) {
+               printk(KERN_ERR "spi_register_master error.\n");
+               goto error4;
+       }
+
+       return 0;
+
+ error4:
+       free_irq(irq, ss);
+ error3:
+       destroy_workqueue(ss->workqueue);
+ error2:
+       iounmap(ss->addr);
+ error1:
+       spi_master_put(master);
+
+       return ret;
+}
+
+static struct platform_driver spi_sh_driver = {
+       .probe = spi_sh_probe,
+       .remove = __devexit_p(spi_sh_remove),
+       .driver = {
+               .name = "sh_spi",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init spi_sh_init(void)
+{
+       return platform_driver_register(&spi_sh_driver);
+}
+module_init(spi_sh_init);
+
+static void __exit spi_sh_exit(void)
+{
+       platform_driver_unregister(&spi_sh_driver);
+}
+module_exit(spi_sh_exit);
+
+MODULE_DESCRIPTION("SH SPI bus driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yoshihiro Shimoda");
+MODULE_ALIAS("platform:sh_spi");
index 2c665fceaac7b5ef957b5bf50ea9016c4b1edf1a..e00d94b22250a5d368146fa28c87aa3be9e5a565 100644 (file)
@@ -9,22 +9,22 @@
  *
  */
 
-#include <linux/kernel.h>
-#include <linux/init.h>
+#include <linux/bitmap.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
 #include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.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/err.h>
 
+#include <linux/spi/sh_msiof.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/spi_bitbang.h>
-#include <linux/spi/sh_msiof.h>
 
 #include <asm/unaligned.h>
 
@@ -67,7 +67,7 @@ struct sh_msiof_spi_priv {
 #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)
+static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
 {
        switch (reg_offs) {
        case TSCR:
@@ -79,7 +79,7 @@ static unsigned long sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
 }
 
 static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs,
-                          unsigned long value)
+                          u32 value)
 {
        switch (reg_offs) {
        case TSCR:
@@ -93,10 +93,10 @@ static void sh_msiof_write(struct sh_msiof_spi_priv *p, int reg_offs,
 }
 
 static int sh_msiof_modify_ctr_wait(struct sh_msiof_spi_priv *p,
-                                   unsigned long clr, unsigned long set)
+                                   u32 clr, u32 set)
 {
-       unsigned long mask = clr | set;
-       unsigned long data;
+       u32 mask = clr | set;
+       u32 data;
        int k;
 
        data = sh_msiof_read(p, CTR);
@@ -166,10 +166,10 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
 }
 
 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)
+                                     u32 cpol, u32 cpha,
+                                     u32 tx_hi_z, u32 lsb_first)
 {
-       unsigned long tmp;
+       u32 tmp;
        int edge;
 
        /*
@@ -187,7 +187,7 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
        tmp |= cpol << 30; /* TSCKIZ */
        tmp |= cpol << 28; /* RSCKIZ */
 
-       edge = cpol ? cpha : !cpha;
+       edge = cpol ^ !cpha;
 
        tmp |= edge << 27; /* TEDG */
        tmp |= edge << 26; /* REDG */
@@ -197,11 +197,9 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
 
 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)
+                                      u32 bits, u32 words)
 {
-       unsigned long dr2;
-
-       dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
+       u32 dr2 = ((bits - 1) << 24) | ((words - 1) << 16);
 
        if (tx_buf)
                sh_msiof_write(p, TMDR2, dr2);
@@ -222,7 +220,7 @@ static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p)
 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;
+       const u8 *buf_8 = tx_buf;
        int k;
 
        for (k = 0; k < words; k++)
@@ -232,7 +230,7 @@ static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p,
 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;
+       const u16 *buf_16 = tx_buf;
        int k;
 
        for (k = 0; k < words; k++)
@@ -242,7 +240,7 @@ static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p,
 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;
+       const u16 *buf_16 = tx_buf;
        int k;
 
        for (k = 0; k < words; k++)
@@ -252,7 +250,7 @@ static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p,
 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;
+       const u32 *buf_32 = tx_buf;
        int k;
 
        for (k = 0; k < words; k++)
@@ -262,17 +260,37 @@ static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p,
 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;
+       const u32 *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_write_fifo_s32(struct sh_msiof_spi_priv *p,
+                                       const void *tx_buf, int words, int fs)
+{
+       const u32 *buf_32 = tx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               sh_msiof_write(p, TFDR, swab32(buf_32[k] << fs));
+}
+
+static void sh_msiof_spi_write_fifo_s32u(struct sh_msiof_spi_priv *p,
+                                        const void *tx_buf, int words, int fs)
+{
+       const u32 *buf_32 = tx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               sh_msiof_write(p, TFDR, swab32(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;
+       u8 *buf_8 = rx_buf;
        int k;
 
        for (k = 0; k < words; k++)
@@ -282,7 +300,7 @@ static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p,
 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;
+       u16 *buf_16 = rx_buf;
        int k;
 
        for (k = 0; k < words; k++)
@@ -292,7 +310,7 @@ static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p,
 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;
+       u16 *buf_16 = rx_buf;
        int k;
 
        for (k = 0; k < words; k++)
@@ -302,7 +320,7 @@ static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p,
 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;
+       u32 *buf_32 = rx_buf;
        int k;
 
        for (k = 0; k < words; k++)
@@ -312,19 +330,40 @@ static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p,
 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;
+       u32 *buf_32 = rx_buf;
        int k;
 
        for (k = 0; k < words; k++)
                put_unaligned(sh_msiof_read(p, RFDR) >> fs, &buf_32[k]);
 }
 
+static void sh_msiof_spi_read_fifo_s32(struct sh_msiof_spi_priv *p,
+                                      void *rx_buf, int words, int fs)
+{
+       u32 *buf_32 = rx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               buf_32[k] = swab32(sh_msiof_read(p, RFDR) >> fs);
+}
+
+static void sh_msiof_spi_read_fifo_s32u(struct sh_msiof_spi_priv *p,
+                                      void *rx_buf, int words, int fs)
+{
+       u32 *buf_32 = rx_buf;
+       int k;
+
+       for (k = 0; k < words; k++)
+               put_unaligned(swab32(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;
+       if (!bits)
+               bits = spi->bits_per_word;
        return bits;
 }
 
@@ -334,7 +373,8 @@ static unsigned long sh_msiof_spi_hz(struct spi_device *spi,
        unsigned long hz;
 
        hz = t ? t->speed_hz : 0;
-       hz = hz ? hz : spi->max_speed_hz;
+       if (!hz)
+               hz = spi->max_speed_hz;
        return hz;
 }
 
@@ -468,9 +508,17 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
        int bytes_done;
        int words;
        int n;
+       bool swab;
 
        bits = sh_msiof_spi_bits(spi, t);
 
+       if (bits <= 8 && t->len > 15 && !(t->len & 3)) {
+               bits = 32;
+               swab = true;
+       } else {
+               swab = false;
+       }
+
        /* setup bytes per word and fifo read/write functions */
        if (bits <= 8) {
                bytes_per_word = 1;
@@ -487,6 +535,17 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
                        rx_fifo = sh_msiof_spi_read_fifo_16u;
                else
                        rx_fifo = sh_msiof_spi_read_fifo_16;
+       } else if (swab) {
+               bytes_per_word = 4;
+               if ((unsigned long)t->tx_buf & 0x03)
+                       tx_fifo = sh_msiof_spi_write_fifo_s32u;
+               else
+                       tx_fifo = sh_msiof_spi_write_fifo_s32;
+
+               if ((unsigned long)t->rx_buf & 0x03)
+                       rx_fifo = sh_msiof_spi_read_fifo_s32u;
+               else
+                       rx_fifo = sh_msiof_spi_read_fifo_s32;
        } else {
                bytes_per_word = 4;
                if ((unsigned long)t->tx_buf & 0x03)
index 603428213d2185d980f99db6850e89aee2e89355..d9fd86211365e729c4b940e87fe26f4f9d9b09c8 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/errno.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/compat.h>
 
 #include <linux/spi/spi.h>
 #include <linux/spi/spidev.h>
@@ -471,6 +472,16 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        return retval;
 }
 
+#ifdef CONFIG_COMPAT
+static long
+spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
+}
+#else
+#define spidev_compat_ioctl NULL
+#endif /* CONFIG_COMPAT */
+
 static int spidev_open(struct inode *inode, struct file *filp)
 {
        struct spidev_data      *spidev;
@@ -543,6 +554,7 @@ static const struct file_operations spidev_fops = {
        .write =        spidev_write,
        .read =         spidev_read,
        .unlocked_ioctl = spidev_ioctl,
+       .compat_ioctl = spidev_compat_ioctl,
        .open =         spidev_open,
        .release =      spidev_release,
        .llseek =       no_llseek,
index 22ef107d7704432b52287fd9fd4df95945c23aef..c42cff8ca1918ec84ba7b3371eeb93ad3718e3ff 100644 (file)
@@ -2,21 +2,24 @@
 /* FIXME driver should be able to handle IRQs...  */
 
 struct mcp23s08_chip_info {
-       bool    is_present;             /* true iff populated */
-       u8      pullups;                /* BIT(x) means enable pullup x */
+       bool            is_present;     /* true if populated */
+       unsigned        pullups;        /* BIT(x) means enable pullup x */
 };
 
 struct mcp23s08_platform_data {
-       /* Four slaves (numbered 0..3) can share one SPI chipselect, and
-        * will provide 8..32 GPIOs using 1..4 gpio_chip instances.
+       /* For mcp23s08, up to 4 slaves (numbered 0..3) can share one SPI
+        * chipselect, each providing 1 gpio_chip instance with 8 gpios.
+        * For mpc23s17, up to 8 slaves (numbered 0..7) can share one SPI
+        * chipselect, each providing 1 gpio_chip (port A + port B) with
+        * 16 gpios.
         */
-       struct mcp23s08_chip_info       chip[4];
+       struct mcp23s08_chip_info       chip[8];
 
        /* "base" is the number of the first GPIO.  Dynamic assignment is
         * not currently supported, and even if there are gaps in chip
         * addressing the GPIO numbers are sequential .. so for example
         * if only slaves 0 and 3 are present, their GPIOs range from
-        * base to base+15.
+        * base to base+15 (or base+31 for s17 variant).
         */
        unsigned        base;
 
diff --git a/include/linux/spi/spi_oc_tiny.h b/include/linux/spi/spi_oc_tiny.h
new file mode 100644 (file)
index 0000000..1ac529c
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef _LINUX_SPI_SPI_OC_TINY_H
+#define _LINUX_SPI_SPI_OC_TINY_H
+
+/**
+ * struct tiny_spi_platform_data - platform data of the OpenCores tiny SPI
+ * @freq:      input clock freq to the core.
+ * @baudwidth: baud rate divider width of the core.
+ * @gpio_cs_count:     number of gpio pins used for chipselect.
+ * @gpio_cs:   array of gpio pins used for chipselect.
+ *
+ * freq and baudwidth are used only if the divider is programmable.
+ */
+struct tiny_spi_platform_data {
+       unsigned int freq;
+       unsigned int baudwidth;
+       unsigned int gpio_cs_count;
+       int *gpio_cs;
+};
+
+#endif /* _LINUX_SPI_SPI_OC_TINY_H */