Merge branch 'for-4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq
[deliverable/linux.git] / drivers / spi / spi-ti-qspi.c
index 64318fcfacf275c989ebe19d157eb8ff9e523ebf..eac3c960b2decb8c4aa73f73894b16ac3021beba 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 
 #include <linux/spi/spi.h>
 
@@ -44,8 +46,9 @@ struct ti_qspi {
 
        struct spi_master       *master;
        void __iomem            *base;
-       void __iomem            *ctrl_base;
        void __iomem            *mmap_base;
+       struct regmap           *ctrl_base;
+       unsigned int            ctrl_reg;
        struct clk              *fclk;
        struct device           *dev;
 
@@ -55,7 +58,7 @@ struct ti_qspi {
        u32 cmd;
        u32 dc;
 
-       bool ctrl_mod;
+       bool mmap_enabled;
 };
 
 #define QSPI_PID                       (0x0)
@@ -65,11 +68,8 @@ struct ti_qspi {
 #define QSPI_SPI_CMD_REG               (0x48)
 #define QSPI_SPI_STATUS_REG            (0x4c)
 #define QSPI_SPI_DATA_REG              (0x50)
-#define QSPI_SPI_SETUP0_REG            (0x54)
+#define QSPI_SPI_SETUP_REG(n)          ((0x54 + 4 * n))
 #define QSPI_SPI_SWITCH_REG            (0x64)
-#define QSPI_SPI_SETUP1_REG            (0x58)
-#define QSPI_SPI_SETUP2_REG            (0x5c)
-#define QSPI_SPI_SETUP3_REG            (0x60)
 #define QSPI_SPI_DATA_REG_1            (0x68)
 #define QSPI_SPI_DATA_REG_2            (0x6c)
 #define QSPI_SPI_DATA_REG_3            (0x70)
@@ -109,6 +109,17 @@ struct ti_qspi {
 
 #define QSPI_AUTOSUSPEND_TIMEOUT         2000
 
+#define MEM_CS_EN(n)                   ((n + 1) << 8)
+#define MEM_CS_MASK                    (7 << 8)
+
+#define MM_SWITCH                      0x1
+
+#define QSPI_SETUP_RD_NORMAL           (0x0 << 12)
+#define QSPI_SETUP_RD_DUAL             (0x1 << 12)
+#define QSPI_SETUP_RD_QUAD             (0x3 << 12)
+#define QSPI_SETUP_ADDR_SHIFT          8
+#define QSPI_SETUP_DUMMY_SHIFT         10
+
 static inline unsigned long ti_qspi_read(struct ti_qspi *qspi,
                unsigned long reg)
 {
@@ -366,6 +377,72 @@ static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t)
        return 0;
 }
 
+static void ti_qspi_enable_memory_map(struct spi_device *spi)
+{
+       struct ti_qspi  *qspi = spi_master_get_devdata(spi->master);
+
+       ti_qspi_write(qspi, MM_SWITCH, QSPI_SPI_SWITCH_REG);
+       if (qspi->ctrl_base) {
+               regmap_update_bits(qspi->ctrl_base, qspi->ctrl_reg,
+                                  MEM_CS_EN(spi->chip_select),
+                                  MEM_CS_MASK);
+       }
+       qspi->mmap_enabled = true;
+}
+
+static void ti_qspi_disable_memory_map(struct spi_device *spi)
+{
+       struct ti_qspi  *qspi = spi_master_get_devdata(spi->master);
+
+       ti_qspi_write(qspi, 0, QSPI_SPI_SWITCH_REG);
+       if (qspi->ctrl_base)
+               regmap_update_bits(qspi->ctrl_base, qspi->ctrl_reg,
+                                  0, MEM_CS_MASK);
+       qspi->mmap_enabled = false;
+}
+
+static void ti_qspi_setup_mmap_read(struct spi_device *spi,
+                                   struct spi_flash_read_message *msg)
+{
+       struct ti_qspi  *qspi = spi_master_get_devdata(spi->master);
+       u32 memval = msg->read_opcode;
+
+       switch (msg->data_nbits) {
+       case SPI_NBITS_QUAD:
+               memval |= QSPI_SETUP_RD_QUAD;
+               break;
+       case SPI_NBITS_DUAL:
+               memval |= QSPI_SETUP_RD_DUAL;
+               break;
+       default:
+               memval |= QSPI_SETUP_RD_NORMAL;
+               break;
+       }
+       memval |= ((msg->addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
+                  msg->dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
+       ti_qspi_write(qspi, memval,
+                     QSPI_SPI_SETUP_REG(spi->chip_select));
+}
+
+static int ti_qspi_spi_flash_read(struct  spi_device *spi,
+                                 struct spi_flash_read_message *msg)
+{
+       struct ti_qspi *qspi = spi_master_get_devdata(spi->master);
+       int ret = 0;
+
+       mutex_lock(&qspi->list_lock);
+
+       if (!qspi->mmap_enabled)
+               ti_qspi_enable_memory_map(spi);
+       ti_qspi_setup_mmap_read(spi, msg);
+       memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len);
+       msg->retlen = msg->len;
+
+       mutex_unlock(&qspi->list_lock);
+
+       return ret;
+}
+
 static int ti_qspi_start_transfer_one(struct spi_master *master,
                struct spi_message *m)
 {
@@ -398,6 +475,9 @@ static int ti_qspi_start_transfer_one(struct spi_master *master,
 
        mutex_lock(&qspi->list_lock);
 
+       if (qspi->mmap_enabled)
+               ti_qspi_disable_memory_map(spi);
+
        list_for_each_entry(t, &m->transfers, transfer_list) {
                qspi->cmd |= QSPI_WLEN(t->bits_per_word);
 
@@ -441,7 +521,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
 {
        struct  ti_qspi *qspi;
        struct spi_master *master;
-       struct resource         *r, *res_ctrl, *res_mmap;
+       struct resource         *r, *res_mmap;
        struct device_node *np = pdev->dev.of_node;
        u32 max_freq;
        int ret = 0, num_cs, irq;
@@ -487,16 +567,6 @@ static int ti_qspi_probe(struct platform_device *pdev)
                }
        }
 
-       res_ctrl = platform_get_resource_byname(pdev,
-                       IORESOURCE_MEM, "qspi_ctrlmod");
-       if (res_ctrl == NULL) {
-               res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-               if (res_ctrl == NULL) {
-                       dev_dbg(&pdev->dev,
-                               "control module resources not required\n");
-               }
-       }
-
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(&pdev->dev, "no irq resource?\n");
@@ -511,20 +581,31 @@ static int ti_qspi_probe(struct platform_device *pdev)
                goto free_master;
        }
 
-       if (res_ctrl) {
-               qspi->ctrl_mod = true;
-               qspi->ctrl_base = devm_ioremap_resource(&pdev->dev, res_ctrl);
-               if (IS_ERR(qspi->ctrl_base)) {
-                       ret = PTR_ERR(qspi->ctrl_base);
-                       goto free_master;
-               }
-       }
-
        if (res_mmap) {
-               qspi->mmap_base = devm_ioremap_resource(&pdev->dev, res_mmap);
+               qspi->mmap_base = devm_ioremap_resource(&pdev->dev,
+                                                       res_mmap);
+               master->spi_flash_read = ti_qspi_spi_flash_read;
                if (IS_ERR(qspi->mmap_base)) {
-                       ret = PTR_ERR(qspi->mmap_base);
-                       goto free_master;
+                       dev_err(&pdev->dev,
+                               "falling back to PIO mode\n");
+                       master->spi_flash_read = NULL;
+               }
+       }
+       qspi->mmap_enabled = false;
+
+       if (of_property_read_bool(np, "syscon-chipselects")) {
+               qspi->ctrl_base =
+               syscon_regmap_lookup_by_phandle(np,
+                                               "syscon-chipselects");
+               if (IS_ERR(qspi->ctrl_base))
+                       return PTR_ERR(qspi->ctrl_base);
+               ret = of_property_read_u32_index(np,
+                                                "syscon-chipselects",
+                                                1, &qspi->ctrl_reg);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "couldn't get ctrl_mod reg index\n");
+                       return ret;
                }
        }
 
This page took 0.027467 seconds and 5 git commands to generate.