Merge branch 'for-viro' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
[cascardo/linux.git] / drivers / spi / spi-orion.c
index a87cfd4..ded3702 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/clk.h>
 #include <linux/sizes.h>
@@ -43,6 +44,9 @@
 #define ORION_SPI_INT_CAUSE_REG                0x10
 #define ORION_SPI_TIMING_PARAMS_REG    0x18
 
+/* Register for the "Direct Mode" */
+#define SPI_DIRECT_WRITE_CONFIG_REG    0x20
+
 #define ORION_SPI_TMISO_SAMPLE_MASK    (0x3 << 6)
 #define ORION_SPI_TMISO_SAMPLE_1       (1 << 6)
 #define ORION_SPI_TMISO_SAMPLE_2       (2 << 6)
@@ -78,11 +82,18 @@ struct orion_spi_dev {
        bool                    is_errata_50mhz_ac;
 };
 
+struct orion_direct_acc {
+       void __iomem            *vaddr;
+       u32                     size;
+};
+
 struct orion_spi {
        struct spi_master       *master;
        void __iomem            *base;
        struct clk              *clk;
        const struct orion_spi_dev *devdata;
+
+       struct orion_direct_acc direct_access[ORION_NUM_CHIPSELECTS];
 };
 
 static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
@@ -372,10 +383,39 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
 {
        unsigned int count;
        int word_len;
+       struct orion_spi *orion_spi;
+       int cs = spi->chip_select;
 
        word_len = spi->bits_per_word;
        count = xfer->len;
 
+       orion_spi = spi_master_get_devdata(spi->master);
+
+       /*
+        * Use SPI direct write mode if base address is available. Otherwise
+        * fall back to PIO mode for this transfer.
+        */
+       if ((orion_spi->direct_access[cs].vaddr) && (xfer->tx_buf) &&
+           (word_len == 8)) {
+               unsigned int cnt = count / 4;
+               unsigned int rem = count % 4;
+
+               /*
+                * Send the TX-data to the SPI device via the direct
+                * mapped address window
+                */
+               iowrite32_rep(orion_spi->direct_access[cs].vaddr,
+                             xfer->tx_buf, cnt);
+               if (rem) {
+                       u32 *buf = (u32 *)xfer->tx_buf;
+
+                       iowrite8_rep(orion_spi->direct_access[cs].vaddr,
+                                    &buf[cnt], rem);
+               }
+
+               return count;
+       }
+
        if (word_len == 8) {
                const u8 *tx = xfer->tx_buf;
                u8 *rx = xfer->rx_buf;
@@ -425,6 +465,10 @@ static int orion_spi_reset(struct orion_spi *orion_spi)
 {
        /* Verify that the CS is deasserted */
        orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1);
+
+       /* Don't deassert CS between the direct mapped SPI transfers */
+       writel(0, spi_reg(orion_spi, SPI_DIRECT_WRITE_CONFIG_REG));
+
        return 0;
 }
 
@@ -504,6 +548,7 @@ static int orion_spi_probe(struct platform_device *pdev)
        struct resource *r;
        unsigned long tclk_hz;
        int status = 0;
+       struct device_node *np;
 
        master = spi_alloc_master(&pdev->dev, sizeof(*spi));
        if (master == NULL) {
@@ -576,6 +621,49 @@ static int orion_spi_probe(struct platform_device *pdev)
                goto out_rel_clk;
        }
 
+       /* Scan all SPI devices of this controller for direct mapped devices */
+       for_each_available_child_of_node(pdev->dev.of_node, np) {
+               u32 cs;
+
+               /* Get chip-select number from the "reg" property */
+               status = of_property_read_u32(np, "reg", &cs);
+               if (status) {
+                       dev_err(&pdev->dev,
+                               "%s has no valid 'reg' property (%d)\n",
+                               np->full_name, status);
+                       status = 0;
+                       continue;
+               }
+
+               /*
+                * Check if an address is configured for this SPI device. If
+                * not, the MBus mapping via the 'ranges' property in the 'soc'
+                * node is not configured and this device should not use the
+                * direct mode. In this case, just continue with the next
+                * device.
+                */
+               status = of_address_to_resource(pdev->dev.of_node, cs + 1, r);
+               if (status)
+                       continue;
+
+               /*
+                * Only map one page for direct access. This is enough for the
+                * simple TX transfer which only writes to the first word.
+                * This needs to get extended for the direct SPI-NOR / SPI-NAND
+                * support, once this gets implemented.
+                */
+               spi->direct_access[cs].vaddr = devm_ioremap(&pdev->dev,
+                                                           r->start,
+                                                           PAGE_SIZE);
+               if (!spi->direct_access[cs].vaddr) {
+                       status = -ENOMEM;
+                       goto out_rel_clk;
+               }
+               spi->direct_access[cs].size = PAGE_SIZE;
+
+               dev_info(&pdev->dev, "CS%d configured for direct access\n", cs);
+       }
+
        pm_runtime_set_active(&pdev->dev);
        pm_runtime_use_autosuspend(&pdev->dev);
        pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);