]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branches 'spi/topic/ti-qspi', 'spi/topic/xcomm' and 'spi/topic...
authorMark Brown <broonie@kernel.org>
Mon, 31 Aug 2015 13:45:45 +0000 (14:45 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 31 Aug 2015 13:45:45 +0000 (14:45 +0100)
Documentation/devicetree/bindings/spi/spi-xlp.txt [new file with mode: 0644]
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/spi-ti-qspi.c
drivers/spi/spi-xcomm.c
drivers/spi/spi-xlp.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/spi/spi-xlp.txt b/Documentation/devicetree/bindings/spi/spi-xlp.txt
new file mode 100644 (file)
index 0000000..40e82d5
--- /dev/null
@@ -0,0 +1,39 @@
+SPI Master controller for Netlogic XLP MIPS64 SOCs
+==================================================
+
+Currently this SPI controller driver is supported for the following
+Netlogic XLP SoCs:
+       XLP832, XLP316, XLP208, XLP980, XLP532
+
+Required properties:
+- compatible           : Should be "netlogic,xlp832-spi".
+- #address-cells       : Number of cells required to define a chip select address
+                         on the SPI bus.
+- #size-cells          : Should be zero.
+- reg                  : Should contain register location and length.
+- clocks               : Phandle of the spi clock
+- interrupts           : Interrupt number used by this controller.
+- interrupt-parent     : Phandle of the parent interrupt controller.
+
+SPI slave nodes must be children of the SPI master node and can contain
+properties described in Documentation/devicetree/bindings/spi/spi-bus.txt.
+
+Example:
+
+       spi: xlp_spi@3a100 {
+               compatible = "netlogic,xlp832-spi";
+               #address-cells = <1>;
+               #size-cells = <0>;
+               reg = <0 0x3a100 0x100>;
+               clocks = <&spi_clk>;
+               interrupts = <34>;
+               interrupt-parent = <&pic>;
+
+               spi_nor@1 {
+                       compatible = "spansion,s25sl12801";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       reg = <1>;      /* Chip Select */
+                       spi-max-frequency = <40000000>;
+               };
+};
index 43f6d3dc0fa0bbb253a4ad5c8b028e73d4b02e1b..4887f317ea58f7c5566fcb1481559831b7f81180 100644 (file)
@@ -607,6 +607,17 @@ config SPI_XILINX
 
          Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)"
 
+config SPI_XLP
+       tristate "Netlogic XLP SPI controller driver"
+       depends on CPU_XLP || COMPILE_TEST
+       help
+         Enable support for the SPI controller on the Netlogic XLP SoCs.
+         Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX, XLP9XX
+         and XLP5XX.
+
+         If you have a Netlogic XLP platform say Y here.
+         If unsure, say N.
+
 config SPI_XTENSA_XTFPGA
        tristate "Xtensa SPI controller for xtfpga"
        depends on (XTENSA && XTENSA_PLATFORM_XTFPGA) || COMPILE_TEST
index 9746beb21769458146ab723242d11f5aa64e9d8a..6a7f6f9d0d1c8cd7dbb89cbd1a902d2997264baa 100644 (file)
@@ -89,5 +89,6 @@ 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_XLP)                  += spi-xlp.o
 obj-$(CONFIG_SPI_XTENSA_XTFPGA)                += spi-xtensa-xtfpga.o
 obj-$(CONFIG_SPI_ZYNQMP_GQSPI)         += spi-zynqmp-gqspi.o
index 5c061687035886e5768bf6031616068906540b04..aa6d284131e0fd99fd19ef92dc0adff73ee18a02 100644 (file)
@@ -99,6 +99,8 @@ struct ti_qspi {
 #define QSPI_INVAL                     (4 << 16)
 #define QSPI_WC_CMD_INT_EN                     (1 << 14)
 #define QSPI_FLEN(n)                   ((n - 1) << 0)
+#define QSPI_WLEN_MAX_BITS             128
+#define QSPI_WLEN_MAX_BYTES            16
 
 /* STATUS REGISTER */
 #define BUSY                           0x01
@@ -217,14 +219,16 @@ static inline u32 qspi_is_busy(struct ti_qspi *qspi)
 
 static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
 {
-       int wlen, count;
+       int wlen, count, xfer_len;
        unsigned int cmd;
        const u8 *txbuf;
+       u32 data;
 
        txbuf = t->tx_buf;
        cmd = qspi->cmd | QSPI_WR_SNGL;
        count = t->len;
        wlen = t->bits_per_word >> 3;   /* in bytes */
+       xfer_len = wlen;
 
        while (count) {
                if (qspi_is_busy(qspi))
@@ -234,7 +238,29 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
                case 1:
                        dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %02x\n",
                                        cmd, qspi->dc, *txbuf);
-                       writeb(*txbuf, qspi->base + QSPI_SPI_DATA_REG);
+                       if (count >= QSPI_WLEN_MAX_BYTES) {
+                               u32 *txp = (u32 *)txbuf;
+
+                               data = cpu_to_be32(*txp++);
+                               writel(data, qspi->base +
+                                      QSPI_SPI_DATA_REG_3);
+                               data = cpu_to_be32(*txp++);
+                               writel(data, qspi->base +
+                                      QSPI_SPI_DATA_REG_2);
+                               data = cpu_to_be32(*txp++);
+                               writel(data, qspi->base +
+                                      QSPI_SPI_DATA_REG_1);
+                               data = cpu_to_be32(*txp++);
+                               writel(data, qspi->base +
+                                      QSPI_SPI_DATA_REG);
+                               xfer_len = QSPI_WLEN_MAX_BYTES;
+                               cmd |= QSPI_WLEN(QSPI_WLEN_MAX_BITS);
+                       } else {
+                               writeb(*txbuf, qspi->base + QSPI_SPI_DATA_REG);
+                               cmd = qspi->cmd | QSPI_WR_SNGL;
+                               xfer_len = wlen;
+                               cmd |= QSPI_WLEN(wlen);
+                       }
                        break;
                case 2:
                        dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %04x\n",
@@ -254,8 +280,8 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
                        dev_err(qspi->dev, "write timed out\n");
                        return -ETIMEDOUT;
                }
-               txbuf += wlen;
-               count -= wlen;
+               txbuf += xfer_len;
+               count -= xfer_len;
        }
 
        return 0;
index bb478dccf1d82dd5d9fb1316bd04cf1755f08f0a..3c28e24b10f59604ae95e53cd2d5df495ce4ef5e 100644 (file)
@@ -237,11 +237,11 @@ static const struct i2c_device_id spi_xcomm_ids[] = {
        { "spi-xcomm" },
        { },
 };
+MODULE_DEVICE_TABLE(i2c, spi_xcomm_ids);
 
 static struct i2c_driver spi_xcomm_driver = {
        .driver = {
                .name   = "spi-xcomm",
-               .owner  = THIS_MODULE,
        },
        .id_table       = spi_xcomm_ids,
        .probe          = spi_xcomm_probe,
diff --git a/drivers/spi/spi-xlp.c b/drivers/spi/spi-xlp.c
new file mode 100644 (file)
index 0000000..8f04fec
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2003-2015 Broadcom Corporation
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (GPL v2)
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+
+/* SPI Configuration Register */
+#define XLP_SPI_CONFIG                 0x00
+#define XLP_SPI_CPHA                   BIT(0)
+#define XLP_SPI_CPOL                   BIT(1)
+#define XLP_SPI_CS_POL                 BIT(2)
+#define XLP_SPI_TXMISO_EN              BIT(3)
+#define XLP_SPI_TXMOSI_EN              BIT(4)
+#define XLP_SPI_RXMISO_EN              BIT(5)
+#define XLP_SPI_CS_LSBFE               BIT(10)
+#define XLP_SPI_RXCAP_EN               BIT(11)
+
+/* SPI Frequency Divider Register */
+#define XLP_SPI_FDIV                   0x04
+
+/* SPI Command Register */
+#define XLP_SPI_CMD                    0x08
+#define XLP_SPI_CMD_IDLE_MASK          0x0
+#define XLP_SPI_CMD_TX_MASK            0x1
+#define XLP_SPI_CMD_RX_MASK            0x2
+#define XLP_SPI_CMD_TXRX_MASK          0x3
+#define XLP_SPI_CMD_CONT               BIT(4)
+#define XLP_SPI_XFR_BITCNT_SHIFT       16
+
+/* SPI Status Register */
+#define XLP_SPI_STATUS                 0x0c
+#define XLP_SPI_XFR_PENDING            BIT(0)
+#define XLP_SPI_XFR_DONE               BIT(1)
+#define XLP_SPI_TX_INT                 BIT(2)
+#define XLP_SPI_RX_INT                 BIT(3)
+#define XLP_SPI_TX_UF                  BIT(4)
+#define XLP_SPI_RX_OF                  BIT(5)
+#define XLP_SPI_STAT_MASK              0x3f
+
+/* SPI Interrupt Enable Register */
+#define XLP_SPI_INTR_EN                        0x10
+#define XLP_SPI_INTR_DONE              BIT(0)
+#define XLP_SPI_INTR_TXTH              BIT(1)
+#define XLP_SPI_INTR_RXTH              BIT(2)
+#define XLP_SPI_INTR_TXUF              BIT(3)
+#define XLP_SPI_INTR_RXOF              BIT(4)
+
+/* SPI FIFO Threshold Register */
+#define XLP_SPI_FIFO_THRESH            0x14
+
+/* SPI FIFO Word Count Register */
+#define XLP_SPI_FIFO_WCNT              0x18
+#define XLP_SPI_RXFIFO_WCNT_MASK       0xf
+#define XLP_SPI_TXFIFO_WCNT_MASK       0xf0
+#define XLP_SPI_TXFIFO_WCNT_SHIFT      4
+
+/* SPI Transmit Data FIFO Register */
+#define XLP_SPI_TXDATA_FIFO            0x1c
+
+/* SPI Receive Data FIFO Register */
+#define XLP_SPI_RXDATA_FIFO            0x20
+
+/* SPI System Control Register */
+#define XLP_SPI_SYSCTRL                        0x100
+#define XLP_SPI_SYS_RESET              BIT(0)
+#define XLP_SPI_SYS_CLKDIS             BIT(1)
+#define XLP_SPI_SYS_PMEN               BIT(8)
+
+#define SPI_CS_OFFSET                  0x40
+#define XLP_SPI_TXRXTH                 0x80
+#define XLP_SPI_FIFO_SIZE              8
+#define XLP_SPI_MAX_CS                 4
+#define XLP_SPI_DEFAULT_FREQ           133333333
+#define XLP_SPI_FDIV_MIN               4
+#define XLP_SPI_FDIV_MAX               65535
+/*
+ * SPI can transfer only 28 bytes properly at a time. So split the
+ * transfer into 28 bytes size.
+ */
+#define XLP_SPI_XFER_SIZE              28
+
+struct xlp_spi_priv {
+       struct device           dev;            /* device structure */
+       void __iomem            *base;          /* spi registers base address */
+       const u8                *tx_buf;        /* tx data buffer */
+       u8                      *rx_buf;        /* rx data buffer */
+       int                     tx_len;         /* tx xfer length */
+       int                     rx_len;         /* rx xfer length */
+       int                     txerrors;       /* TXFIFO underflow count */
+       int                     rxerrors;       /* RXFIFO overflow count */
+       int                     cs;             /* slave device chip select */
+       u32                     spi_clk;        /* spi clock frequency */
+       bool                    cmd_cont;       /* cs active */
+       struct completion       done;           /* completion notification */
+};
+
+static inline u32 xlp_spi_reg_read(struct xlp_spi_priv *priv,
+                               int cs, int regoff)
+{
+       return readl(priv->base + regoff + cs * SPI_CS_OFFSET);
+}
+
+static inline void xlp_spi_reg_write(struct xlp_spi_priv *priv, int cs,
+                               int regoff, u32 val)
+{
+       writel(val, priv->base + regoff + cs * SPI_CS_OFFSET);
+}
+
+static inline void xlp_spi_sysctl_write(struct xlp_spi_priv *priv,
+                               int regoff, u32 val)
+{
+       writel(val, priv->base + regoff);
+}
+
+/*
+ * Setup global SPI_SYSCTRL register for all SPI channels.
+ */
+static void xlp_spi_sysctl_setup(struct xlp_spi_priv *xspi)
+{
+       int cs;
+
+       for (cs = 0; cs < XLP_SPI_MAX_CS; cs++)
+               xlp_spi_sysctl_write(xspi, XLP_SPI_SYSCTRL,
+                               XLP_SPI_SYS_RESET << cs);
+       xlp_spi_sysctl_write(xspi, XLP_SPI_SYSCTRL, XLP_SPI_SYS_PMEN);
+}
+
+static int xlp_spi_setup(struct spi_device *spi)
+{
+       struct xlp_spi_priv *xspi;
+       u32 fdiv, cfg;
+       int cs;
+
+       xspi = spi_master_get_devdata(spi->master);
+       cs = spi->chip_select;
+       /*
+        * The value of fdiv must be between 4 and 65535.
+        */
+       fdiv = DIV_ROUND_UP(xspi->spi_clk, spi->max_speed_hz);
+       if (fdiv > XLP_SPI_FDIV_MAX)
+               fdiv = XLP_SPI_FDIV_MAX;
+       else if (fdiv < XLP_SPI_FDIV_MIN)
+               fdiv = XLP_SPI_FDIV_MIN;
+
+       xlp_spi_reg_write(xspi, cs, XLP_SPI_FDIV, fdiv);
+       xlp_spi_reg_write(xspi, cs, XLP_SPI_FIFO_THRESH, XLP_SPI_TXRXTH);
+       cfg = xlp_spi_reg_read(xspi, cs, XLP_SPI_CONFIG);
+       if (spi->mode & SPI_CPHA)
+               cfg |= XLP_SPI_CPHA;
+       else
+               cfg &= ~XLP_SPI_CPHA;
+       if (spi->mode & SPI_CPOL)
+               cfg |= XLP_SPI_CPOL;
+       else
+               cfg &= ~XLP_SPI_CPOL;
+       if (!(spi->mode & SPI_CS_HIGH))
+               cfg |= XLP_SPI_CS_POL;
+       else
+               cfg &= ~XLP_SPI_CS_POL;
+       if (spi->mode & SPI_LSB_FIRST)
+               cfg |= XLP_SPI_CS_LSBFE;
+       else
+               cfg &= ~XLP_SPI_CS_LSBFE;
+
+       cfg |= XLP_SPI_TXMOSI_EN | XLP_SPI_RXMISO_EN;
+       if (fdiv == 4)
+               cfg |= XLP_SPI_RXCAP_EN;
+       xlp_spi_reg_write(xspi, cs, XLP_SPI_CONFIG, cfg);
+
+       return 0;
+}
+
+static void xlp_spi_read_rxfifo(struct xlp_spi_priv *xspi)
+{
+       u32 rx_data, rxfifo_cnt;
+       int i, j, nbytes;
+
+       rxfifo_cnt = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_FIFO_WCNT);
+       rxfifo_cnt &= XLP_SPI_RXFIFO_WCNT_MASK;
+       while (rxfifo_cnt) {
+               rx_data = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_RXDATA_FIFO);
+               j = 0;
+               nbytes = min(xspi->rx_len, 4);
+               for (i = nbytes - 1; i >= 0; i--, j++)
+                       xspi->rx_buf[i] = (rx_data >> (j * 8)) & 0xff;
+
+               xspi->rx_len -= nbytes;
+               xspi->rx_buf += nbytes;
+               rxfifo_cnt--;
+       }
+}
+
+static void xlp_spi_fill_txfifo(struct xlp_spi_priv *xspi)
+{
+       u32 tx_data, txfifo_cnt;
+       int i, j, nbytes;
+
+       txfifo_cnt = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_FIFO_WCNT);
+       txfifo_cnt &= XLP_SPI_TXFIFO_WCNT_MASK;
+       txfifo_cnt >>= XLP_SPI_TXFIFO_WCNT_SHIFT;
+       while (xspi->tx_len && (txfifo_cnt < XLP_SPI_FIFO_SIZE)) {
+               j = 0;
+               tx_data = 0;
+               nbytes = min(xspi->tx_len, 4);
+               for (i = nbytes - 1; i >= 0; i--, j++)
+                       tx_data |= xspi->tx_buf[i] << (j * 8);
+
+               xlp_spi_reg_write(xspi, xspi->cs, XLP_SPI_TXDATA_FIFO, tx_data);
+               xspi->tx_len -= nbytes;
+               xspi->tx_buf += nbytes;
+               txfifo_cnt++;
+       }
+}
+
+static irqreturn_t xlp_spi_interrupt(int irq, void *dev_id)
+{
+       struct xlp_spi_priv *xspi = dev_id;
+       u32 stat;
+
+       stat = xlp_spi_reg_read(xspi, xspi->cs, XLP_SPI_STATUS) &
+               XLP_SPI_STAT_MASK;
+       if (!stat)
+               return IRQ_NONE;
+
+       if (stat & XLP_SPI_TX_INT) {
+               if (xspi->tx_len)
+                       xlp_spi_fill_txfifo(xspi);
+               if (stat & XLP_SPI_TX_UF)
+                       xspi->txerrors++;
+       }
+
+       if (stat & XLP_SPI_RX_INT) {
+               if (xspi->rx_len)
+                       xlp_spi_read_rxfifo(xspi);
+               if (stat & XLP_SPI_RX_OF)
+                       xspi->rxerrors++;
+       }
+
+       /* write status back to clear interrupts */
+       xlp_spi_reg_write(xspi, xspi->cs, XLP_SPI_STATUS, stat);
+       if (stat & XLP_SPI_XFR_DONE)
+               complete(&xspi->done);
+
+       return IRQ_HANDLED;
+}
+
+static void xlp_spi_send_cmd(struct xlp_spi_priv *xspi, int xfer_len,
+                       int cmd_cont)
+{
+       u32 cmd = 0;
+
+       if (xspi->tx_buf)
+               cmd |= XLP_SPI_CMD_TX_MASK;
+       if (xspi->rx_buf)
+               cmd |= XLP_SPI_CMD_RX_MASK;
+       if (cmd_cont)
+               cmd |= XLP_SPI_CMD_CONT;
+       cmd |= ((xfer_len * 8 - 1) << XLP_SPI_XFR_BITCNT_SHIFT);
+       xlp_spi_reg_write(xspi, xspi->cs, XLP_SPI_CMD, cmd);
+}
+
+static int xlp_spi_xfer_block(struct  xlp_spi_priv *xs,
+               const unsigned char *tx_buf,
+               unsigned char *rx_buf, int xfer_len, int cmd_cont)
+{
+       int timeout;
+       u32 intr_mask = 0;
+
+       xs->tx_buf = tx_buf;
+       xs->rx_buf = rx_buf;
+       xs->tx_len = (xs->tx_buf == NULL) ? 0 : xfer_len;
+       xs->rx_len = (xs->rx_buf == NULL) ? 0 : xfer_len;
+       xs->txerrors = xs->rxerrors = 0;
+
+       /* fill TXDATA_FIFO, then send the CMD */
+       if (xs->tx_len)
+               xlp_spi_fill_txfifo(xs);
+
+       xlp_spi_send_cmd(xs, xfer_len, cmd_cont);
+
+       /*
+        * We are getting some spurious tx interrupts, so avoid enabling
+        * tx interrupts when only rx is in process.
+        * Enable all the interrupts in tx case.
+        */
+       if (xs->tx_len)
+               intr_mask |= XLP_SPI_INTR_TXTH | XLP_SPI_INTR_TXUF |
+                               XLP_SPI_INTR_RXTH | XLP_SPI_INTR_RXOF;
+       else
+               intr_mask |= XLP_SPI_INTR_RXTH | XLP_SPI_INTR_RXOF;
+
+       intr_mask |= XLP_SPI_INTR_DONE;
+       xlp_spi_reg_write(xs, xs->cs, XLP_SPI_INTR_EN, intr_mask);
+
+       timeout = wait_for_completion_timeout(&xs->done,
+                               msecs_to_jiffies(1000));
+       /* Disable interrupts */
+       xlp_spi_reg_write(xs, xs->cs, XLP_SPI_INTR_EN, 0x0);
+       if (!timeout) {
+               dev_err(&xs->dev, "xfer timedout!\n");
+               goto out;
+       }
+       if (xs->txerrors || xs->rxerrors)
+               dev_err(&xs->dev, "Over/Underflow rx %d tx %d xfer %d!\n",
+                               xs->rxerrors, xs->txerrors, xfer_len);
+
+       return xfer_len;
+out:
+       return -ETIMEDOUT;
+}
+
+static int xlp_spi_txrx_bufs(struct xlp_spi_priv *xs, struct spi_transfer *t)
+{
+       int bytesleft, sz;
+       unsigned char *rx_buf;
+       const unsigned char *tx_buf;
+
+       tx_buf = t->tx_buf;
+       rx_buf = t->rx_buf;
+       bytesleft = t->len;
+       while (bytesleft) {
+               if (bytesleft > XLP_SPI_XFER_SIZE)
+                       sz = xlp_spi_xfer_block(xs, tx_buf, rx_buf,
+                                       XLP_SPI_XFER_SIZE, 1);
+               else
+                       sz = xlp_spi_xfer_block(xs, tx_buf, rx_buf,
+                                       bytesleft, xs->cmd_cont);
+               if (sz < 0)
+                       return sz;
+               bytesleft -= sz;
+               if (tx_buf)
+                       tx_buf += sz;
+               if (rx_buf)
+                       rx_buf += sz;
+       }
+       return bytesleft;
+}
+
+static int xlp_spi_transfer_one(struct spi_master *master,
+                                       struct spi_device *spi,
+                                       struct spi_transfer *t)
+{
+       struct xlp_spi_priv *xspi = spi_master_get_devdata(master);
+       int ret = 0;
+
+       xspi->cs = spi->chip_select;
+       xspi->dev = spi->dev;
+
+       if (spi_transfer_is_last(master, t))
+               xspi->cmd_cont = 0;
+       else
+               xspi->cmd_cont = 1;
+
+       if (xlp_spi_txrx_bufs(xspi, t))
+               ret = -EIO;
+
+       spi_finalize_current_transfer(master);
+       return ret;
+}
+
+static int xlp_spi_probe(struct platform_device *pdev)
+{
+       struct spi_master *master;
+       struct xlp_spi_priv *xspi;
+       struct resource *res;
+       struct clk *clk;
+       int irq, err;
+
+       xspi = devm_kzalloc(&pdev->dev, sizeof(*xspi), GFP_KERNEL);
+       if (!xspi)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xspi->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xspi->base))
+               return PTR_ERR(xspi->base);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "no IRQ resource found\n");
+               return -EINVAL;
+       }
+       err = devm_request_irq(&pdev->dev, irq, xlp_spi_interrupt, 0,
+                       pdev->name, xspi);
+       if (err) {
+               dev_err(&pdev->dev, "unable to request irq %d\n", irq);
+               return err;
+       }
+
+       clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "could not get spi clock\n");
+               return -ENODEV;
+       }
+       xspi->spi_clk = clk_get_rate(clk);
+
+       master = spi_alloc_master(&pdev->dev, 0);
+       if (!master) {
+               dev_err(&pdev->dev, "could not alloc master\n");
+               return -ENOMEM;
+       }
+
+       master->bus_num = 0;
+       master->num_chipselect = XLP_SPI_MAX_CS;
+       master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+       master->setup = xlp_spi_setup;
+       master->transfer_one = xlp_spi_transfer_one;
+       master->dev.of_node = pdev->dev.of_node;
+
+       init_completion(&xspi->done);
+       spi_master_set_devdata(master, xspi);
+       xlp_spi_sysctl_setup(xspi);
+
+       /* register spi controller */
+       err = devm_spi_register_master(&pdev->dev, master);
+       if (err) {
+               dev_err(&pdev->dev, "spi register master failed!\n");
+               spi_master_put(master);
+               return err;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id xlp_spi_dt_id[] = {
+       { .compatible = "netlogic,xlp832-spi" },
+       { },
+};
+
+static struct platform_driver xlp_spi_driver = {
+       .probe  = xlp_spi_probe,
+       .driver = {
+               .name   = "xlp-spi",
+               .of_match_table = xlp_spi_dt_id,
+       },
+};
+module_platform_driver(xlp_spi_driver);
+
+MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
+MODULE_DESCRIPTION("Netlogic XLP SPI controller driver");
+MODULE_LICENSE("GPL v2");