Merge remote-tracking branches 'spi/topic/xilinx' and 'spi/topic/xtfpga' into spi...
authorMark Brown <broonie@linaro.org>
Sun, 30 Mar 2014 00:51:48 +0000 (00:51 +0000)
committerMark Brown <broonie@linaro.org>
Sun, 30 Mar 2014 00:51:48 +0000 (00:51 +0000)
Documentation/devicetree/bindings/spi/spi-xtensa-xtfpga.txt [new file with mode: 0644]
MAINTAINERS
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/spi-xilinx.c
drivers/spi/spi-xtensa-xtfpga.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/spi/spi-xtensa-xtfpga.txt b/Documentation/devicetree/bindings/spi/spi-xtensa-xtfpga.txt
new file mode 100644 (file)
index 0000000..b6ebe2b
--- /dev/null
@@ -0,0 +1,9 @@
+Cadence Xtensa XTFPGA platform SPI controller.
+
+This simple SPI master controller is built into xtfpga bitstreams and is used
+to control daughterboard audio codec.
+
+Required properties:
+- compatible: should be "cdns,xtfpga-spi".
+- reg: physical base address of the controller and length of memory mapped
+  region.
index 82640e640f36be2f8dd98a54989d223a109b5a1f..338db2514585e66b0680af565140c8691a42834e 100644 (file)
@@ -9782,6 +9782,12 @@ L:       linux-serial@vger.kernel.org
 S:     Maintained
 F:     drivers/tty/serial/uartlite.c
 
+XTENSA XTFPGA PLATFORM SUPPORT
+M:     Max Filippov <jcmvbkbc@gmail.com>
+L:     linux-xtensa@linux-xtensa.org
+S:     Maintained
+F:     drivers/spi/spi-xtensa-xtfpga.c
+
 YAM DRIVER FOR AX.25
 M:     Jean-Paul Roubelat <jpr@f6fbb.org>
 L:     linux-hams@vger.kernel.org
index 756b5b3ca2008240b01bfbd365eacdb0c008bafd..efe1960af2b315704c39290617dbc2215f8aec48 100644 (file)
@@ -538,6 +538,19 @@ config SPI_XILINX
 
          Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)"
 
+config SPI_XTENSA_XTFPGA
+       tristate "Xtensa SPI controller for xtfpga"
+       depends on (XTENSA && XTENSA_PLATFORM_XTFPGA) || COMPILE_TEST
+       select SPI_BITBANG
+       help
+         SPI driver for xtfpga SPI master controller.
+
+         This simple SPI master controller is built into xtfpga bitstreams
+         and is used to control daughterboard audio codec. It always transfers
+         16 bit words in SPI mode 0, automatically asserting CS on transfer
+         start and deasserting on end.
+
+
 config SPI_NUC900
        tristate "Nuvoton NUC900 series SPI"
        depends on ARCH_W90X900
index e245b94d07d86287e489f8d7825498b2436dd864..bd792669e56395525cfa3aba6c3542cb54fc6dad 100644 (file)
@@ -81,3 +81,4 @@ obj-$(CONFIG_SPI_TOPCLIFF_PCH)                += spi-topcliff-pch.o
 obj-$(CONFIG_SPI_TXX9)                 += spi-txx9.o
 obj-$(CONFIG_SPI_XCOMM)                += spi-xcomm.o
 obj-$(CONFIG_SPI_XILINX)               += spi-xilinx.o
+obj-$(CONFIG_SPI_XTENSA_XTFPGA)                += spi-xtensa-xtfpga.o
index e6cd1112ae40d9afc072141840f936f4ee226fc3..a3b0b9944bf000c7b69afd868a1c8eed71308489 100644 (file)
@@ -87,10 +87,10 @@ struct xilinx_spi {
        const u8 *tx_ptr;       /* pointer in the Rx buffer */
        int remaining_bytes;    /* the number of bytes left to transfer */
        u8 bits_per_word;
-       unsigned int (*read_fn) (void __iomem *);
-       void (*write_fn) (u32, void __iomem *);
-       void (*tx_fn) (struct xilinx_spi *);
-       void (*rx_fn) (struct xilinx_spi *);
+       unsigned int (*read_fn)(void __iomem *);
+       void (*write_fn)(u32, void __iomem *);
+       void (*tx_fn)(struct xilinx_spi *);
+       void (*rx_fn)(struct xilinx_spi *);
 };
 
 static void xspi_write32(u32 val, void __iomem *addr)
@@ -208,26 +208,11 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
 }
 
 /* spi_bitbang requires custom setup_transfer() to be defined if there is a
- * custom txrx_bufs(). We have nothing to setup here as the SPI IP block
- * supports 8 or 16 bits per word which cannot be changed in software.
- * SPI clock can't be changed in software either.
- * Check for correct bits per word. Chip select delay calculations could be
- * added here as soon as bitbang_work() can be made aware of the delay value.
+ * custom txrx_bufs().
  */
 static int xilinx_spi_setup_transfer(struct spi_device *spi,
                struct spi_transfer *t)
 {
-       struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
-       u8 bits_per_word;
-
-       bits_per_word = (t && t->bits_per_word)
-                        ? t->bits_per_word : spi->bits_per_word;
-       if (bits_per_word != xspi->bits_per_word) {
-               dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
-                       __func__, bits_per_word);
-               return -EINVAL;
-       }
-
        return 0;
 }
 
@@ -406,6 +391,7 @@ static int xilinx_spi_probe(struct platform_device *pdev)
                xspi->write_fn = xspi_write32_be;
        }
 
+       master->bits_per_word_mask = SPI_BPW_MASK(bits_per_word);
        xspi->bits_per_word = bits_per_word;
        if (xspi->bits_per_word == 8) {
                xspi->tx_fn = xspi_tx8;
diff --git a/drivers/spi/spi-xtensa-xtfpga.c b/drivers/spi/spi-xtensa-xtfpga.c
new file mode 100644 (file)
index 0000000..41e1581
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Xtensa xtfpga SPI controller driver
+ *
+ * Copyright (c) 2014 Cadence Design Systems Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+
+#define XTFPGA_SPI_NAME "xtfpga_spi"
+
+#define XTFPGA_SPI_START       0x0
+#define XTFPGA_SPI_BUSY                0x4
+#define XTFPGA_SPI_DATA                0x8
+
+#define BUSY_WAIT_US           100
+
+struct xtfpga_spi {
+       struct spi_bitbang bitbang;
+       void __iomem *regs;
+       u32 data;
+       unsigned data_sz;
+};
+
+static inline void xtfpga_spi_write32(const struct xtfpga_spi *spi,
+                                     unsigned addr, u32 val)
+{
+       iowrite32(val, spi->regs + addr);
+}
+
+static inline unsigned int xtfpga_spi_read32(const struct xtfpga_spi *spi,
+                                            unsigned addr)
+{
+       return ioread32(spi->regs + addr);
+}
+
+static inline void xtfpga_spi_wait_busy(struct xtfpga_spi *xspi)
+{
+       unsigned i;
+       for (i = 0; xtfpga_spi_read32(xspi, XTFPGA_SPI_BUSY) &&
+            i < BUSY_WAIT_US; ++i)
+               udelay(1);
+       WARN_ON_ONCE(i == BUSY_WAIT_US);
+}
+
+static u32 xtfpga_spi_txrx_word(struct spi_device *spi, unsigned nsecs,
+                               u32 v, u8 bits)
+{
+       struct xtfpga_spi *xspi = spi_master_get_devdata(spi->master);
+
+       xspi->data = (xspi->data << bits) | (v & GENMASK(bits - 1, 0));
+       xspi->data_sz += bits;
+       if (xspi->data_sz >= 16) {
+               xtfpga_spi_write32(xspi, XTFPGA_SPI_DATA,
+                                  xspi->data >> (xspi->data_sz - 16));
+               xspi->data_sz -= 16;
+               xtfpga_spi_write32(xspi, XTFPGA_SPI_START, 1);
+               xtfpga_spi_wait_busy(xspi);
+               xtfpga_spi_write32(xspi, XTFPGA_SPI_START, 0);
+       }
+
+       return 0;
+}
+
+static void xtfpga_spi_chipselect(struct spi_device *spi, int is_on)
+{
+       struct xtfpga_spi *xspi = spi_master_get_devdata(spi->master);
+
+       WARN_ON(xspi->data_sz != 0);
+       xspi->data_sz = 0;
+}
+
+static int xtfpga_spi_probe(struct platform_device *pdev)
+{
+       struct xtfpga_spi *xspi;
+       struct resource *mem;
+       int ret;
+       struct spi_master *master;
+
+       master = spi_alloc_master(&pdev->dev, sizeof(struct xtfpga_spi));
+       if (!master)
+               return -ENOMEM;
+
+       master->flags = SPI_MASTER_NO_RX;
+       master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
+       master->bus_num = pdev->dev.id;
+       master->dev.of_node = pdev->dev.of_node;
+
+       xspi = spi_master_get_devdata(master);
+       xspi->bitbang.master = master;
+       xspi->bitbang.chipselect = xtfpga_spi_chipselect;
+       xspi->bitbang.txrx_word[SPI_MODE_0] = xtfpga_spi_txrx_word;
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(&pdev->dev, "No memory resource\n");
+               ret = -ENODEV;
+               goto err;
+       }
+       xspi->regs = devm_ioremap_resource(&pdev->dev, mem);
+       if (IS_ERR(xspi->regs)) {
+               ret = PTR_ERR(xspi->regs);
+               goto err;
+       }
+
+       xtfpga_spi_write32(xspi, XTFPGA_SPI_START, 0);
+       usleep_range(1000, 2000);
+       if (xtfpga_spi_read32(xspi, XTFPGA_SPI_BUSY)) {
+               dev_err(&pdev->dev, "Device stuck in busy state\n");
+               ret = -EBUSY;
+               goto err;
+       }
+
+       ret = spi_bitbang_start(&xspi->bitbang);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "spi_bitbang_start failed\n");
+               goto err;
+       }
+
+       platform_set_drvdata(pdev, master);
+       return 0;
+err:
+       spi_master_put(master);
+       return ret;
+}
+
+static int xtfpga_spi_remove(struct platform_device *pdev)
+{
+       struct spi_master *master = platform_get_drvdata(pdev);
+       struct xtfpga_spi *xspi = spi_master_get_devdata(master);
+
+       spi_bitbang_stop(&xspi->bitbang);
+       spi_master_put(master);
+
+       return 0;
+}
+
+MODULE_ALIAS("platform:" XTFPGA_SPI_NAME);
+
+#ifdef CONFIG_OF
+static const struct of_device_id xtfpga_spi_of_match[] = {
+       { .compatible = "cdns,xtfpga-spi", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, xtfpga_spi_of_match);
+#endif
+
+static struct platform_driver xtfpga_spi_driver = {
+       .probe = xtfpga_spi_probe,
+       .remove = xtfpga_spi_remove,
+       .driver = {
+               .name = XTFPGA_SPI_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(xtfpga_spi_of_match),
+       },
+};
+module_platform_driver(xtfpga_spi_driver);
+
+MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>");
+MODULE_DESCRIPTION("xtensa xtfpga SPI driver");
+MODULE_LICENSE("GPL");