Merge remote-tracking branches 'spi/topic/bcm', 'spi/topic/dw' and 'spi/topic/fsl...
authorMark Brown <broonie@kernel.org>
Fri, 30 Sep 2016 16:14:08 +0000 (09:14 -0700)
committerMark Brown <broonie@kernel.org>
Fri, 30 Sep 2016 16:14:08 +0000 (09:14 -0700)
Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt [new file with mode: 0644]
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/spi-bcm-qspi.c [new file with mode: 0644]
drivers/spi/spi-bcm-qspi.h [new file with mode: 0644]
drivers/spi/spi-brcmstb-qspi.c [new file with mode: 0644]
drivers/spi/spi-dw.c
drivers/spi/spi-dw.h
drivers/spi/spi-fsl-dspi.c
drivers/spi/spi-iproc-qspi.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt b/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt
new file mode 100644 (file)
index 0000000..ad7ac80
--- /dev/null
@@ -0,0 +1,233 @@
+Broadcom SPI controller
+
+The Broadcom SPI controller is a SPI master found on various SOCs, including
+BRCMSTB (BCM7XXX), Cygnus, NSP and NS2. The Broadcom Master SPI hw IP consits
+of :
+ MSPI : SPI master controller can read and write to a SPI slave device
+ BSPI : Broadcom SPI in combination with the MSPI hw IP provides acceleration
+       for flash reads and be configured to do single, double, quad lane
+       io with 3-byte and 4-byte addressing support.
+
+ Supported Broadcom SoCs have one instance of MSPI+BSPI controller IP.
+ MSPI master can be used wihout BSPI. BRCMSTB SoCs have an additional instance
+ of a MSPI master without the BSPI to use with non flash slave devices that
+ use SPI protocol.
+
+Required properties:
+
+- #address-cells:
+    Must be <1>, as required by generic SPI binding.
+
+- #size-cells:
+    Must be <0>, also as required by generic SPI binding.
+
+- compatible:
+    Must be one of :
+    "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-qspi" : MSPI+BSPI on BRCMSTB SoCs
+    "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
+                                                  BRCMSTB  SoCs
+    "brcm,spi-bcm-qspi", "brcm,spi-nsp-qspi"     : MSPI+BSPI on Cygnus, NSP
+    "brcm,spi-bcm-qspi", "brcm,spi-ns2-qspi"     : NS2 SoCs
+
+- reg:
+    Define the bases and ranges of the associated I/O address spaces.
+    The required range is MSPI controller registers.
+
+- reg-names:
+    First name does not matter, but must be reserved for the MSPI controller
+    register range as mentioned in 'reg' above, and will typically contain
+    - "bspi_regs": BSPI register range, not required with compatible
+                  "spi-brcmstb-mspi"
+    - "mspi_regs": MSPI register range is required for compatible strings
+    - "intr_regs", "intr_status_reg" : Interrupt and status register for
+      NSP, NS2, Cygnus SoC
+
+- interrupts
+    The interrupts used by the MSPI and/or BSPI controller.
+
+- interrupt-names:
+    Names of interrupts associated with MSPI
+    - "mspi_halted" :
+    - "mspi_done": Indicates that the requested SPI operation is complete.
+    - "spi_lr_fullness_reached" : Linear read BSPI pipe full
+    - "spi_lr_session_aborted"  : Linear read BSPI pipe aborted
+    - "spi_lr_impatient" : Linear read BSPI requested when pipe empty
+    - "spi_lr_session_done" : Linear read BSPI session done
+
+- clocks:
+    A phandle to the reference clock for this block.
+
+Optional properties:
+
+
+- native-endian
+    Defined when using BE SoC and device uses BE register read/write
+
+Recommended optional m25p80 properties:
+- spi-rx-bus-width: Definition as per
+                    Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Examples:
+
+BRCMSTB SoC Example:
+
+  SPI Master (MSPI+BSPI) for SPI-NOR access:
+
+    spi@f03e3400 {
+               #address-cells = <0x1>;
+               #size-cells = <0x0>;
+               compatible = "brcm,spi-brcmstb-qspi", "brcm,spi-brcmstb-qspi";
+               reg = <0xf03e0920 0x4 0xf03e3400 0x188 0xf03e3200 0x50>;
+               reg-names = "cs_reg", "mspi", "bspi";
+               interrupts = <0x6 0x5 0x4 0x3 0x2 0x1 0x0>;
+               interrupt-parent = <0x1c>;
+               interrupt-names = "mspi_halted",
+                                 "mspi_done",
+                                 "spi_lr_overread",
+                                 "spi_lr_session_done",
+                                 "spi_lr_impatient",
+                                 "spi_lr_session_aborted",
+                                 "spi_lr_fullness_reached";
+
+               clocks = <&hif_spi>;
+               clock-names = "sw_spi";
+
+               m25p80@0 {
+                       #size-cells = <0x2>;
+                       #address-cells = <0x2>;
+                       compatible = "m25p80";
+                       reg = <0x0>;
+                       spi-max-frequency = <0x2625a00>;
+                       spi-cpol;
+                       spi-cpha;
+                       m25p,fast-read;
+
+                       flash0.bolt@0 {
+                               reg = <0x0 0x0 0x0 0x100000>;
+                       };
+
+                       flash0.macadr@100000 {
+                               reg = <0x0 0x100000 0x0 0x10000>;
+                       };
+
+                       flash0.nvram@110000 {
+                               reg = <0x0 0x110000 0x0 0x10000>;
+                       };
+
+                       flash0.kernel@120000 {
+                               reg = <0x0 0x120000 0x0 0x400000>;
+                       };
+
+                       flash0.devtree@520000 {
+                               reg = <0x0 0x520000 0x0 0x10000>;
+                       };
+
+                       flash0.splash@530000 {
+                               reg = <0x0 0x530000 0x0 0x80000>;
+                       };
+
+                       flash0@0 {
+                               reg = <0x0 0x0 0x0 0x4000000>;
+                       };
+               };
+       };
+
+
+    MSPI master for any SPI device :
+
+       spi@f0416000 {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               clocks = <&upg_fixed>;
+               compatible = "brcm,spi-brcmstb-qspi", "brcm,spi-brcmstb-mspi";
+               reg = <0xf0416000 0x180>;
+               reg-names = "mspi";
+               interrupts = <0x14>;
+               interrupt-parent = <&irq0_aon_intc>;
+               interrupt-names = "mspi_done";
+       };
+
+iProc SoC Example:
+
+    qspi: spi@18027200 {
+       compatible = "brcm,spi-bcm-qspi", "brcm,spi-nsp-qspi";
+       reg = <0x18027200 0x184>,
+             <0x18027000 0x124>,
+             <0x1811c408 0x004>,
+             <0x180273a0 0x01c>;
+       reg-names = "mspi_regs", "bspi_regs", "intr_regs", "intr_status_reg";
+       interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>,
+                    <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
+                    <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
+                    <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>,
+                    <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>,
+                    <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>,
+                    <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+       interrupt-names =
+                    "spi_lr_fullness_reached",
+                    "spi_lr_session_aborted",
+                    "spi_lr_impatient",
+                    "spi_lr_session_done",
+                    "mspi_done",
+                    "mspi_halted";
+       clocks = <&iprocmed>;
+       clock-names = "iprocmed";
+       num-cs = <2>;
+       #address-cells = <1>;
+       #size-cells = <0>;
+    };
+
+
+ NS2 SoC Example:
+
+              qspi: spi@66470200 {
+                      compatible = "brcm,spi-bcm-qspi", "brcm,spi-ns2-qspi";
+                      reg = <0x66470200 0x184>,
+                            <0x66470000 0x124>,
+                            <0x67017408 0x004>,
+                            <0x664703a0 0x01c>;
+                      reg-names = "mspi", "bspi", "intr_regs",
+                       "intr_status_reg";
+                      interrupts = <GIC_SPI 419 IRQ_TYPE_LEVEL_HIGH>;
+                      interrupt-names = "spi_l1_intr";
+                       clocks = <&iprocmed>;
+                       clock-names = "iprocmed";
+                       num-cs = <2>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+              };
+
+
+ m25p80 node for NSP, NS2
+
+        &qspi {
+                     flash: m25p80@0 {
+                     #address-cells = <1>;
+                     #size-cells = <1>;
+                     compatible = "m25p80";
+                     reg = <0x0>;
+                     spi-max-frequency = <12500000>;
+                     m25p,fast-read;
+                     spi-cpol;
+                     spi-cpha;
+
+                     partition@0 {
+                                 label = "boot";
+                                 reg = <0x00000000 0x000a0000>;
+                     };
+
+                     partition@a0000 {
+                                 label = "env";
+                                 reg = <0x000a0000 0x00060000>;
+                     };
+
+                     partition@100000 {
+                                 label = "system";
+                                 reg = <0x00100000 0x00600000>;
+                     };
+
+                     partition@700000 {
+                                 label = "rootfs";
+                                 reg = <0x00700000 0x01900000>;
+                     };
+       };
index d6fb8d4..a1c86bd 100644 (file)
@@ -153,6 +153,16 @@ config SPI_BCM63XX_HSSPI
          This enables support for the High Speed SPI controller present on
          newer Broadcom BCM63XX SoCs.
 
+config SPI_BCM_QSPI
+       tristate "Broadcom BSPI and MSPI controller support"
+       depends on ARCH_BRCMSTB || ARCH_BCM || ARCH_BCM_IPROC || COMPILE_TEST
+       default ARCH_BCM_IPROC
+       help
+         Enables support for the Broadcom SPI flash and MSPI controller.
+         Select this option for any one of BRCMSTB, iProc NSP and NS2 SoCs
+         based platforms. This driver works for both SPI master for spi-nor
+         flash device as well as MSPI device.
+
 config SPI_BITBANG
        tristate "Utilities for Bitbanging SPI masters"
        help
index 185367e..2dc1b71 100644 (file)
@@ -21,6 +21,7 @@ obj-$(CONFIG_SPI_BCM2835AUX)          += spi-bcm2835aux.o
 obj-$(CONFIG_SPI_BCM53XX)              += spi-bcm53xx.o
 obj-$(CONFIG_SPI_BCM63XX)              += spi-bcm63xx.o
 obj-$(CONFIG_SPI_BCM63XX_HSSPI)                += spi-bcm63xx-hsspi.o
+obj-$(CONFIG_SPI_BCM_QSPI)             += spi-iproc-qspi.o spi-brcmstb-qspi.o spi-bcm-qspi.o
 obj-$(CONFIG_SPI_BFIN5XX)              += spi-bfin5xx.o
 obj-$(CONFIG_SPI_ADI_V3)                += spi-adi-v3.o
 obj-$(CONFIG_SPI_BFIN_SPORT)           += spi-bfin-sport.o
diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c
new file mode 100644 (file)
index 0000000..14f9dea
--- /dev/null
@@ -0,0 +1,1397 @@
+/*
+ * Driver for Broadcom BRCMSTB, NSP,  NS2, Cygnus SPI Controllers
+ *
+ * Copyright 2016 Broadcom
+ *
+ * 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 (the "GPL").
+ *
+ * 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 version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/spi-nor.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include "spi-bcm-qspi.h"
+
+#define DRIVER_NAME "bcm_qspi"
+
+
+/* BSPI register offsets */
+#define BSPI_REVISION_ID                       0x000
+#define BSPI_SCRATCH                           0x004
+#define BSPI_MAST_N_BOOT_CTRL                  0x008
+#define BSPI_BUSY_STATUS                       0x00c
+#define BSPI_INTR_STATUS                       0x010
+#define BSPI_B0_STATUS                         0x014
+#define BSPI_B0_CTRL                           0x018
+#define BSPI_B1_STATUS                         0x01c
+#define BSPI_B1_CTRL                           0x020
+#define BSPI_STRAP_OVERRIDE_CTRL               0x024
+#define BSPI_FLEX_MODE_ENABLE                  0x028
+#define BSPI_BITS_PER_CYCLE                    0x02c
+#define BSPI_BITS_PER_PHASE                    0x030
+#define BSPI_CMD_AND_MODE_BYTE                 0x034
+#define BSPI_BSPI_FLASH_UPPER_ADDR_BYTE        0x038
+#define BSPI_BSPI_XOR_VALUE                    0x03c
+#define BSPI_BSPI_XOR_ENABLE                   0x040
+#define BSPI_BSPI_PIO_MODE_ENABLE              0x044
+#define BSPI_BSPI_PIO_IODIR                    0x048
+#define BSPI_BSPI_PIO_DATA                     0x04c
+
+/* RAF register offsets */
+#define BSPI_RAF_START_ADDR                    0x100
+#define BSPI_RAF_NUM_WORDS                     0x104
+#define BSPI_RAF_CTRL                          0x108
+#define BSPI_RAF_FULLNESS                      0x10c
+#define BSPI_RAF_WATERMARK                     0x110
+#define BSPI_RAF_STATUS                        0x114
+#define BSPI_RAF_READ_DATA                     0x118
+#define BSPI_RAF_WORD_CNT                      0x11c
+#define BSPI_RAF_CURR_ADDR                     0x120
+
+/* Override mode masks */
+#define BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE      BIT(0)
+#define BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL     BIT(1)
+#define BSPI_STRAP_OVERRIDE_CTRL_ADDR_4BYTE    BIT(2)
+#define BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD     BIT(3)
+#define BSPI_STRAP_OVERRIDE_CTRL_ENDAIN_MODE   BIT(4)
+
+#define BSPI_ADDRLEN_3BYTES                    3
+#define BSPI_ADDRLEN_4BYTES                    4
+
+#define BSPI_RAF_STATUS_FIFO_EMPTY_MASK        BIT(1)
+
+#define BSPI_RAF_CTRL_START_MASK               BIT(0)
+#define BSPI_RAF_CTRL_CLEAR_MASK               BIT(1)
+
+#define BSPI_BPP_MODE_SELECT_MASK              BIT(8)
+#define BSPI_BPP_ADDR_SELECT_MASK              BIT(16)
+
+#define BSPI_READ_LENGTH                       256
+
+/* MSPI register offsets */
+#define MSPI_SPCR0_LSB                         0x000
+#define MSPI_SPCR0_MSB                         0x004
+#define MSPI_SPCR1_LSB                         0x008
+#define MSPI_SPCR1_MSB                         0x00c
+#define MSPI_NEWQP                             0x010
+#define MSPI_ENDQP                             0x014
+#define MSPI_SPCR2                             0x018
+#define MSPI_MSPI_STATUS                       0x020
+#define MSPI_CPTQP                             0x024
+#define MSPI_SPCR3                             0x028
+#define MSPI_TXRAM                             0x040
+#define MSPI_RXRAM                             0x0c0
+#define MSPI_CDRAM                             0x140
+#define MSPI_WRITE_LOCK                        0x180
+
+#define MSPI_MASTER_BIT                        BIT(7)
+
+#define MSPI_NUM_CDRAM                         16
+#define MSPI_CDRAM_CONT_BIT                    BIT(7)
+#define MSPI_CDRAM_BITSE_BIT                   BIT(6)
+#define MSPI_CDRAM_PCS                         0xf
+
+#define MSPI_SPCR2_SPE                         BIT(6)
+#define MSPI_SPCR2_CONT_AFTER_CMD              BIT(7)
+
+#define MSPI_MSPI_STATUS_SPIF                  BIT(0)
+
+#define INTR_BASE_BIT_SHIFT                    0x02
+#define INTR_COUNT                             0x07
+
+#define NUM_CHIPSELECT                         4
+#define QSPI_SPBR_MIN                          8U
+#define QSPI_SPBR_MAX                          255U
+
+#define OPCODE_DIOR                            0xBB
+#define OPCODE_QIOR                            0xEB
+#define OPCODE_DIOR_4B                         0xBC
+#define OPCODE_QIOR_4B                         0xEC
+
+#define MAX_CMD_SIZE                           6
+
+#define ADDR_4MB_MASK                          GENMASK(22, 0)
+
+/* stop at end of transfer, no other reason */
+#define TRANS_STATUS_BREAK_NONE                0
+/* stop at end of spi_message */
+#define TRANS_STATUS_BREAK_EOM                 1
+/* stop at end of spi_transfer if delay */
+#define TRANS_STATUS_BREAK_DELAY               2
+/* stop at end of spi_transfer if cs_change */
+#define TRANS_STATUS_BREAK_CS_CHANGE           4
+/* stop if we run out of bytes */
+#define TRANS_STATUS_BREAK_NO_BYTES            8
+
+/* events that make us stop filling TX slots */
+#define TRANS_STATUS_BREAK_TX (TRANS_STATUS_BREAK_EOM |                \
+                              TRANS_STATUS_BREAK_DELAY |               \
+                              TRANS_STATUS_BREAK_CS_CHANGE)
+
+/* events that make us deassert CS */
+#define TRANS_STATUS_BREAK_DESELECT (TRANS_STATUS_BREAK_EOM |          \
+                                    TRANS_STATUS_BREAK_CS_CHANGE)
+
+struct bcm_qspi_parms {
+       u32 speed_hz;
+       u8 mode;
+       u8 bits_per_word;
+};
+
+struct bcm_xfer_mode {
+       bool flex_mode;
+       unsigned int width;
+       unsigned int addrlen;
+       unsigned int hp;
+};
+
+enum base_type {
+       MSPI,
+       BSPI,
+       CHIP_SELECT,
+       BASEMAX,
+};
+
+enum irq_source {
+       SINGLE_L2,
+       MUXED_L1,
+};
+
+struct bcm_qspi_irq {
+       const char *irq_name;
+       const irq_handler_t irq_handler;
+       int irq_source;
+       u32 mask;
+};
+
+struct bcm_qspi_dev_id {
+       const struct bcm_qspi_irq *irqp;
+       void *dev;
+};
+
+struct qspi_trans {
+       struct spi_transfer *trans;
+       int byte;
+};
+
+struct bcm_qspi {
+       struct platform_device *pdev;
+       struct spi_master *master;
+       struct clk *clk;
+       u32 base_clk;
+       u32 max_speed_hz;
+       void __iomem *base[BASEMAX];
+
+       /* Some SoCs provide custom interrupt status register(s) */
+       struct bcm_qspi_soc_intc        *soc_intc;
+
+       struct bcm_qspi_parms last_parms;
+       struct qspi_trans  trans_pos;
+       int curr_cs;
+       int bspi_maj_rev;
+       int bspi_min_rev;
+       int bspi_enabled;
+       struct spi_flash_read_message *bspi_rf_msg;
+       u32 bspi_rf_msg_idx;
+       u32 bspi_rf_msg_len;
+       u32 bspi_rf_msg_status;
+       struct bcm_xfer_mode xfer_mode;
+       u32 s3_strap_override_ctrl;
+       bool bspi_mode;
+       bool big_endian;
+       int num_irqs;
+       struct bcm_qspi_dev_id *dev_ids;
+       struct completion mspi_done;
+       struct completion bspi_done;
+};
+
+static inline bool has_bspi(struct bcm_qspi *qspi)
+{
+       return qspi->bspi_mode;
+}
+
+/* Read qspi controller register*/
+static inline u32 bcm_qspi_read(struct bcm_qspi *qspi, enum base_type type,
+                               unsigned int offset)
+{
+       return bcm_qspi_readl(qspi->big_endian, qspi->base[type] + offset);
+}
+
+/* Write qspi controller register*/
+static inline void bcm_qspi_write(struct bcm_qspi *qspi, enum base_type type,
+                                 unsigned int offset, unsigned int data)
+{
+       bcm_qspi_writel(qspi->big_endian, data, qspi->base[type] + offset);
+}
+
+/* BSPI helpers */
+static int bcm_qspi_bspi_busy_poll(struct bcm_qspi *qspi)
+{
+       int i;
+
+       /* this should normally finish within 10us */
+       for (i = 0; i < 1000; i++) {
+               if (!(bcm_qspi_read(qspi, BSPI, BSPI_BUSY_STATUS) & 1))
+                       return 0;
+               udelay(1);
+       }
+       dev_warn(&qspi->pdev->dev, "timeout waiting for !busy_status\n");
+       return -EIO;
+}
+
+static inline bool bcm_qspi_bspi_ver_three(struct bcm_qspi *qspi)
+{
+       if (qspi->bspi_maj_rev < 4)
+               return true;
+       return false;
+}
+
+static void bcm_qspi_bspi_flush_prefetch_buffers(struct bcm_qspi *qspi)
+{
+       bcm_qspi_bspi_busy_poll(qspi);
+       /* Force rising edge for the b0/b1 'flush' field */
+       bcm_qspi_write(qspi, BSPI, BSPI_B0_CTRL, 1);
+       bcm_qspi_write(qspi, BSPI, BSPI_B1_CTRL, 1);
+       bcm_qspi_write(qspi, BSPI, BSPI_B0_CTRL, 0);
+       bcm_qspi_write(qspi, BSPI, BSPI_B1_CTRL, 0);
+}
+
+static int bcm_qspi_bspi_lr_is_fifo_empty(struct bcm_qspi *qspi)
+{
+       return (bcm_qspi_read(qspi, BSPI, BSPI_RAF_STATUS) &
+                               BSPI_RAF_STATUS_FIFO_EMPTY_MASK);
+}
+
+static inline u32 bcm_qspi_bspi_lr_read_fifo(struct bcm_qspi *qspi)
+{
+       u32 data = bcm_qspi_read(qspi, BSPI, BSPI_RAF_READ_DATA);
+
+       /* BSPI v3 LR is LE only, convert data to host endianness */
+       if (bcm_qspi_bspi_ver_three(qspi))
+               data = le32_to_cpu(data);
+
+       return data;
+}
+
+static inline void bcm_qspi_bspi_lr_start(struct bcm_qspi *qspi)
+{
+       bcm_qspi_bspi_busy_poll(qspi);
+       bcm_qspi_write(qspi, BSPI, BSPI_RAF_CTRL,
+                      BSPI_RAF_CTRL_START_MASK);
+}
+
+static inline void bcm_qspi_bspi_lr_clear(struct bcm_qspi *qspi)
+{
+       bcm_qspi_write(qspi, BSPI, BSPI_RAF_CTRL,
+                      BSPI_RAF_CTRL_CLEAR_MASK);
+       bcm_qspi_bspi_flush_prefetch_buffers(qspi);
+}
+
+static void bcm_qspi_bspi_lr_data_read(struct bcm_qspi *qspi)
+{
+       u32 *buf = (u32 *)qspi->bspi_rf_msg->buf;
+       u32 data = 0;
+
+       dev_dbg(&qspi->pdev->dev, "xfer %p rx %p rxlen %d\n", qspi->bspi_rf_msg,
+               qspi->bspi_rf_msg->buf, qspi->bspi_rf_msg_len);
+       while (!bcm_qspi_bspi_lr_is_fifo_empty(qspi)) {
+               data = bcm_qspi_bspi_lr_read_fifo(qspi);
+               if (likely(qspi->bspi_rf_msg_len >= 4) &&
+                   IS_ALIGNED((uintptr_t)buf, 4)) {
+                       buf[qspi->bspi_rf_msg_idx++] = data;
+                       qspi->bspi_rf_msg_len -= 4;
+               } else {
+                       /* Read out remaining bytes, make sure*/
+                       u8 *cbuf = (u8 *)&buf[qspi->bspi_rf_msg_idx];
+
+                       data = cpu_to_le32(data);
+                       while (qspi->bspi_rf_msg_len) {
+                               *cbuf++ = (u8)data;
+                               data >>= 8;
+                               qspi->bspi_rf_msg_len--;
+                       }
+               }
+       }
+}
+
+static void bcm_qspi_bspi_set_xfer_params(struct bcm_qspi *qspi, u8 cmd_byte,
+                                         int bpp, int bpc, int flex_mode)
+{
+       bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, 0);
+       bcm_qspi_write(qspi, BSPI, BSPI_BITS_PER_CYCLE, bpc);
+       bcm_qspi_write(qspi, BSPI, BSPI_BITS_PER_PHASE, bpp);
+       bcm_qspi_write(qspi, BSPI, BSPI_CMD_AND_MODE_BYTE, cmd_byte);
+       bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, flex_mode);
+}
+
+static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width,
+                                      int addrlen, int hp)
+{
+       int bpc = 0, bpp = 0;
+       u8 command = SPINOR_OP_READ_FAST;
+       int flex_mode = 1, rv = 0;
+       bool spans_4byte = false;
+
+       dev_dbg(&qspi->pdev->dev, "set flex mode w %x addrlen %x hp %d\n",
+               width, addrlen, hp);
+
+       if (addrlen == BSPI_ADDRLEN_4BYTES) {
+               bpp = BSPI_BPP_ADDR_SELECT_MASK;
+               spans_4byte = true;
+       }
+
+       bpp |= 8;
+
+       switch (width) {
+       case SPI_NBITS_SINGLE:
+               if (addrlen == BSPI_ADDRLEN_3BYTES)
+                       /* default mode, does not need flex_cmd */
+                       flex_mode = 0;
+               else
+                       command = SPINOR_OP_READ4_FAST;
+               break;
+       case SPI_NBITS_DUAL:
+               bpc = 0x00000001;
+               if (hp) {
+                       bpc |= 0x00010100; /* address and mode are 2-bit */
+                       bpp = BSPI_BPP_MODE_SELECT_MASK;
+                       command = OPCODE_DIOR;
+                       if (spans_4byte)
+                               command = OPCODE_DIOR_4B;
+               } else {
+                       command = SPINOR_OP_READ_1_1_2;
+                       if (spans_4byte)
+                               command = SPINOR_OP_READ4_1_1_2;
+               }
+               break;
+       case SPI_NBITS_QUAD:
+               bpc = 0x00000002;
+               if (hp) {
+                       bpc |= 0x00020200; /* address and mode are 4-bit */
+                       bpp = 4; /* dummy cycles */
+                       bpp |= BSPI_BPP_ADDR_SELECT_MASK;
+                       command = OPCODE_QIOR;
+                       if (spans_4byte)
+                               command = OPCODE_QIOR_4B;
+               } else {
+                       command = SPINOR_OP_READ_1_1_4;
+                       if (spans_4byte)
+                               command = SPINOR_OP_READ4_1_1_4;
+               }
+               break;
+       default:
+               rv = -EINVAL;
+               break;
+       }
+
+       if (rv == 0)
+               bcm_qspi_bspi_set_xfer_params(qspi, command, bpp, bpc,
+                                             flex_mode);
+
+       return rv;
+}
+
+static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi, int width,
+                                     int addrlen, int hp)
+{
+       u32 data = bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL);
+
+       dev_dbg(&qspi->pdev->dev, "set override mode w %x addrlen %x hp %d\n",
+               width, addrlen, hp);
+
+       switch (width) {
+       case SPI_NBITS_SINGLE:
+               /* clear quad/dual mode */
+               data &= ~(BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD |
+                         BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL);
+               break;
+
+       case SPI_NBITS_QUAD:
+               /* clear dual mode and set quad mode */
+               data &= ~BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL;
+               data |= BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD;
+               break;
+       case SPI_NBITS_DUAL:
+               /* clear quad mode set dual mode */
+               data &= ~BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD;
+               data |= BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (addrlen == BSPI_ADDRLEN_4BYTES)
+               /* set 4byte mode*/
+               data |= BSPI_STRAP_OVERRIDE_CTRL_ADDR_4BYTE;
+       else
+               /* clear 4 byte mode */
+               data &= ~BSPI_STRAP_OVERRIDE_CTRL_ADDR_4BYTE;
+
+       /* set the override mode */
+       data |= BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE;
+       bcm_qspi_write(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL, data);
+       bcm_qspi_bspi_set_xfer_params(qspi, SPINOR_OP_READ_FAST, 0, 0, 0);
+
+       return 0;
+}
+
+static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi,
+                                 int width, int addrlen, int hp)
+{
+       int error = 0;
+
+       /* default mode */
+       qspi->xfer_mode.flex_mode = true;
+
+       if (!bcm_qspi_bspi_ver_three(qspi)) {
+               u32 val, mask;
+
+               val = bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL);
+               mask = BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE;
+               if (val & mask || qspi->s3_strap_override_ctrl & mask) {
+                       qspi->xfer_mode.flex_mode = false;
+                       bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE,
+                                      0);
+
+                       if ((val | qspi->s3_strap_override_ctrl) &
+                           BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL)
+                               width = SPI_NBITS_DUAL;
+                       else if ((val |  qspi->s3_strap_override_ctrl) &
+                                BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD)
+                               width = SPI_NBITS_QUAD;
+
+                       error = bcm_qspi_bspi_set_override(qspi, width, addrlen,
+                                                          hp);
+               }
+       }
+
+       if (qspi->xfer_mode.flex_mode)
+               error = bcm_qspi_bspi_set_flex_mode(qspi, width, addrlen, hp);
+
+       if (error) {
+               dev_warn(&qspi->pdev->dev,
+                        "INVALID COMBINATION: width=%d addrlen=%d hp=%d\n",
+                        width, addrlen, hp);
+       } else if (qspi->xfer_mode.width != width ||
+                  qspi->xfer_mode.addrlen != addrlen ||
+                  qspi->xfer_mode.hp != hp) {
+               qspi->xfer_mode.width = width;
+               qspi->xfer_mode.addrlen = addrlen;
+               qspi->xfer_mode.hp = hp;
+               dev_dbg(&qspi->pdev->dev,
+                       "cs:%d %d-lane output, %d-byte address%s\n",
+                       qspi->curr_cs,
+                       qspi->xfer_mode.width,
+                       qspi->xfer_mode.addrlen,
+                       qspi->xfer_mode.hp != -1 ? ", hp mode" : "");
+       }
+
+       return error;
+}
+
+static void bcm_qspi_enable_bspi(struct bcm_qspi *qspi)
+{
+       if (!has_bspi(qspi) || (qspi->bspi_enabled))
+               return;
+
+       qspi->bspi_enabled = 1;
+       if ((bcm_qspi_read(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL) & 1) == 0)
+               return;
+
+       bcm_qspi_bspi_flush_prefetch_buffers(qspi);
+       udelay(1);
+       bcm_qspi_write(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL, 0);
+       udelay(1);
+}
+
+static void bcm_qspi_disable_bspi(struct bcm_qspi *qspi)
+{
+       if (!has_bspi(qspi) || (!qspi->bspi_enabled))
+               return;
+
+       qspi->bspi_enabled = 0;
+       if ((bcm_qspi_read(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL) & 1))
+               return;
+
+       bcm_qspi_bspi_busy_poll(qspi);
+       bcm_qspi_write(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL, 1);
+       udelay(1);
+}
+
+static void bcm_qspi_chip_select(struct bcm_qspi *qspi, int cs)
+{
+       u32 data = 0;
+
+       if (qspi->curr_cs == cs)
+               return;
+       if (qspi->base[CHIP_SELECT]) {
+               data = bcm_qspi_read(qspi, CHIP_SELECT, 0);
+               data = (data & ~0xff) | (1 << cs);
+               bcm_qspi_write(qspi, CHIP_SELECT, 0, data);
+               usleep_range(10, 20);
+       }
+       qspi->curr_cs = cs;
+}
+
+/* MSPI helpers */
+static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi,
+                                 const struct bcm_qspi_parms *xp)
+{
+       u32 spcr, spbr = 0;
+
+       if (xp->speed_hz)
+               spbr = qspi->base_clk / (2 * xp->speed_hz);
+
+       spcr = clamp_val(spbr, QSPI_SPBR_MIN, QSPI_SPBR_MAX);
+       bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, spcr);
+
+       spcr = MSPI_MASTER_BIT;
+       /* for 16 bit the data should be zero */
+       if (xp->bits_per_word != 16)
+               spcr |= xp->bits_per_word << 2;
+       spcr |= xp->mode & 3;
+       bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_MSB, spcr);
+
+       qspi->last_parms = *xp;
+}
+
+static void bcm_qspi_update_parms(struct bcm_qspi *qspi,
+                                 struct spi_device *spi,
+                                 struct spi_transfer *trans)
+{
+       struct bcm_qspi_parms xp;
+
+       xp.speed_hz = trans->speed_hz;
+       xp.bits_per_word = trans->bits_per_word;
+       xp.mode = spi->mode;
+
+       bcm_qspi_hw_set_parms(qspi, &xp);
+}
+
+static int bcm_qspi_setup(struct spi_device *spi)
+{
+       struct bcm_qspi_parms *xp;
+
+       if (spi->bits_per_word > 16)
+               return -EINVAL;
+
+       xp = spi_get_ctldata(spi);
+       if (!xp) {
+               xp = kzalloc(sizeof(*xp), GFP_KERNEL);
+               if (!xp)
+                       return -ENOMEM;
+               spi_set_ctldata(spi, xp);
+       }
+       xp->speed_hz = spi->max_speed_hz;
+       xp->mode = spi->mode;
+
+       if (spi->bits_per_word)
+               xp->bits_per_word = spi->bits_per_word;
+       else
+               xp->bits_per_word = 8;
+
+       return 0;
+}
+
+static int update_qspi_trans_byte_count(struct bcm_qspi *qspi,
+                                       struct qspi_trans *qt, int flags)
+{
+       int ret = TRANS_STATUS_BREAK_NONE;
+
+       /* count the last transferred bytes */
+       if (qt->trans->bits_per_word <= 8)
+               qt->byte++;
+       else
+               qt->byte += 2;
+
+       if (qt->byte >= qt->trans->len) {
+               /* we're at the end of the spi_transfer */
+
+               /* in TX mode, need to pause for a delay or CS change */
+               if (qt->trans->delay_usecs &&
+                   (flags & TRANS_STATUS_BREAK_DELAY))
+                       ret |= TRANS_STATUS_BREAK_DELAY;
+               if (qt->trans->cs_change &&
+                   (flags & TRANS_STATUS_BREAK_CS_CHANGE))
+                       ret |= TRANS_STATUS_BREAK_CS_CHANGE;
+               if (ret)
+                       goto done;
+
+               dev_dbg(&qspi->pdev->dev, "advance msg exit\n");
+               if (spi_transfer_is_last(qspi->master, qt->trans))
+                       ret = TRANS_STATUS_BREAK_EOM;
+               else
+                       ret = TRANS_STATUS_BREAK_NO_BYTES;
+
+               qt->trans = NULL;
+       }
+
+done:
+       dev_dbg(&qspi->pdev->dev, "trans %p len %d byte %d ret %x\n",
+               qt->trans, qt->trans ? qt->trans->len : 0, qt->byte, ret);
+       return ret;
+}
+
+static inline u8 read_rxram_slot_u8(struct bcm_qspi *qspi, int slot)
+{
+       u32 slot_offset = MSPI_RXRAM + (slot << 3) + 0x4;
+
+       /* mask out reserved bits */
+       return bcm_qspi_read(qspi, MSPI, slot_offset) & 0xff;
+}
+
+static inline u16 read_rxram_slot_u16(struct bcm_qspi *qspi, int slot)
+{
+       u32 reg_offset = MSPI_RXRAM;
+       u32 lsb_offset = reg_offset + (slot << 3) + 0x4;
+       u32 msb_offset = reg_offset + (slot << 3);
+
+       return (bcm_qspi_read(qspi, MSPI, lsb_offset) & 0xff) |
+               ((bcm_qspi_read(qspi, MSPI, msb_offset) & 0xff) << 8);
+}
+
+static void read_from_hw(struct bcm_qspi *qspi, int slots)
+{
+       struct qspi_trans tp;
+       int slot;
+
+       bcm_qspi_disable_bspi(qspi);
+
+       if (slots > MSPI_NUM_CDRAM) {
+               /* should never happen */
+               dev_err(&qspi->pdev->dev, "%s: too many slots!\n", __func__);
+               return;
+       }
+
+       tp = qspi->trans_pos;
+
+       for (slot = 0; slot < slots; slot++) {
+               if (tp.trans->bits_per_word <= 8) {
+                       u8 *buf = tp.trans->rx_buf;
+
+                       if (buf)
+                               buf[tp.byte] = read_rxram_slot_u8(qspi, slot);
+                       dev_dbg(&qspi->pdev->dev, "RD %02x\n",
+                               buf ? buf[tp.byte] : 0xff);
+               } else {
+                       u16 *buf = tp.trans->rx_buf;
+
+                       if (buf)
+                               buf[tp.byte / 2] = read_rxram_slot_u16(qspi,
+                                                                     slot);
+                       dev_dbg(&qspi->pdev->dev, "RD %04x\n",
+                               buf ? buf[tp.byte] : 0xffff);
+               }
+
+               update_qspi_trans_byte_count(qspi, &tp,
+                                            TRANS_STATUS_BREAK_NONE);
+       }
+
+       qspi->trans_pos = tp;
+}
+
+static inline void write_txram_slot_u8(struct bcm_qspi *qspi, int slot,
+                                      u8 val)
+{
+       u32 reg_offset = MSPI_TXRAM + (slot << 3);
+
+       /* mask out reserved bits */
+       bcm_qspi_write(qspi, MSPI, reg_offset, val);
+}
+
+static inline void write_txram_slot_u16(struct bcm_qspi *qspi, int slot,
+                                       u16 val)
+{
+       u32 reg_offset = MSPI_TXRAM;
+       u32 msb_offset = reg_offset + (slot << 3);
+       u32 lsb_offset = reg_offset + (slot << 3) + 0x4;
+
+       bcm_qspi_write(qspi, MSPI, msb_offset, (val >> 8));
+       bcm_qspi_write(qspi, MSPI, lsb_offset, (val & 0xff));
+}
+
+static inline u32 read_cdram_slot(struct bcm_qspi *qspi, int slot)
+{
+       return bcm_qspi_read(qspi, MSPI, MSPI_CDRAM + (slot << 2));
+}
+
+static inline void write_cdram_slot(struct bcm_qspi *qspi, int slot, u32 val)
+{
+       bcm_qspi_write(qspi, MSPI, (MSPI_CDRAM + (slot << 2)), val);
+}
+
+/* Return number of slots written */
+static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi)
+{
+       struct qspi_trans tp;
+       int slot = 0, tstatus = 0;
+       u32 mspi_cdram = 0;
+
+       bcm_qspi_disable_bspi(qspi);
+       tp = qspi->trans_pos;
+       bcm_qspi_update_parms(qspi, spi, tp.trans);
+
+       /* Run until end of transfer or reached the max data */
+       while (!tstatus && slot < MSPI_NUM_CDRAM) {
+               if (tp.trans->bits_per_word <= 8) {
+                       const u8 *buf = tp.trans->tx_buf;
+                       u8 val = buf ? buf[tp.byte] : 0xff;
+
+                       write_txram_slot_u8(qspi, slot, val);
+                       dev_dbg(&qspi->pdev->dev, "WR %02x\n", val);
+               } else {
+                       const u16 *buf = tp.trans->tx_buf;
+                       u16 val = buf ? buf[tp.byte / 2] : 0xffff;
+
+                       write_txram_slot_u16(qspi, slot, val);
+                       dev_dbg(&qspi->pdev->dev, "WR %04x\n", val);
+               }
+               mspi_cdram = MSPI_CDRAM_CONT_BIT;
+               mspi_cdram |= (~(1 << spi->chip_select) &
+                              MSPI_CDRAM_PCS);
+               mspi_cdram |= ((tp.trans->bits_per_word <= 8) ? 0 :
+                               MSPI_CDRAM_BITSE_BIT);
+
+               write_cdram_slot(qspi, slot, mspi_cdram);
+
+               tstatus = update_qspi_trans_byte_count(qspi, &tp,
+                                                      TRANS_STATUS_BREAK_TX);
+               slot++;
+       }
+
+       if (!slot) {
+               dev_err(&qspi->pdev->dev, "%s: no data to send?", __func__);
+               goto done;
+       }
+
+       dev_dbg(&qspi->pdev->dev, "submitting %d slots\n", slot);
+       bcm_qspi_write(qspi, MSPI, MSPI_NEWQP, 0);
+       bcm_qspi_write(qspi, MSPI, MSPI_ENDQP, slot - 1);
+
+       if (tstatus & TRANS_STATUS_BREAK_DESELECT) {
+               mspi_cdram = read_cdram_slot(qspi, slot - 1) &
+                       ~MSPI_CDRAM_CONT_BIT;
+               write_cdram_slot(qspi, slot - 1, mspi_cdram);
+       }
+
+       if (has_bspi(qspi))
+               bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 1);
+
+       /* Must flush previous writes before starting MSPI operation */
+       mb();
+       /* Set cont | spe | spifie */
+       bcm_qspi_write(qspi, MSPI, MSPI_SPCR2, 0xe0);
+
+done:
+       return slot;
+}
+
+static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
+                                   struct spi_flash_read_message *msg)
+{
+       struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
+       u32 addr = 0, len, len_words;
+       int ret = 0;
+       unsigned long timeo = msecs_to_jiffies(100);
+       struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
+
+       if (bcm_qspi_bspi_ver_three(qspi))
+               if (msg->addr_width == BSPI_ADDRLEN_4BYTES)
+                       return -EIO;
+
+       bcm_qspi_chip_select(qspi, spi->chip_select);
+       bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 0);
+
+       /*
+        * when using flex mode mode we need to send
+        * the upper address byte to bspi
+        */
+       if (bcm_qspi_bspi_ver_three(qspi) == false) {
+               addr = msg->from & 0xff000000;
+               bcm_qspi_write(qspi, BSPI,
+                              BSPI_BSPI_FLASH_UPPER_ADDR_BYTE, addr);
+       }
+
+       if (!qspi->xfer_mode.flex_mode)
+               addr = msg->from;
+       else
+               addr = msg->from & 0x00ffffff;
+
+       /* set BSPI RAF buffer max read length */
+       len = msg->len;
+       if (len > BSPI_READ_LENGTH)
+               len = BSPI_READ_LENGTH;
+
+       if (bcm_qspi_bspi_ver_three(qspi) == true)
+               addr = (addr + 0xc00000) & 0xffffff;
+
+       reinit_completion(&qspi->bspi_done);
+       bcm_qspi_enable_bspi(qspi);
+       len_words = (len + 3) >> 2;
+       qspi->bspi_rf_msg = msg;
+       qspi->bspi_rf_msg_status = 0;
+       qspi->bspi_rf_msg_idx = 0;
+       qspi->bspi_rf_msg_len = len;
+       dev_dbg(&qspi->pdev->dev, "bspi xfr addr 0x%x len 0x%x", addr, len);
+
+       bcm_qspi_write(qspi, BSPI, BSPI_RAF_START_ADDR, addr);
+       bcm_qspi_write(qspi, BSPI, BSPI_RAF_NUM_WORDS, len_words);
+       bcm_qspi_write(qspi, BSPI, BSPI_RAF_WATERMARK, 0);
+
+       if (qspi->soc_intc) {
+               /*
+                * clear soc MSPI and BSPI interrupts and enable
+                * BSPI interrupts.
+                */
+               soc_intc->bcm_qspi_int_ack(soc_intc, MSPI_BSPI_DONE);
+               soc_intc->bcm_qspi_int_set(soc_intc, BSPI_DONE, true);
+       }
+
+       /* Must flush previous writes before starting BSPI operation */
+       mb();
+
+       bcm_qspi_bspi_lr_start(qspi);
+       if (!wait_for_completion_timeout(&qspi->bspi_done, timeo)) {
+               dev_err(&qspi->pdev->dev, "timeout waiting for BSPI\n");
+               ret = -ETIMEDOUT;
+       } else {
+               /* set the return length for the caller */
+               msg->retlen = len;
+       }
+
+       return ret;
+}
+
+static int bcm_qspi_flash_read(struct spi_device *spi,
+                              struct spi_flash_read_message *msg)
+{
+       struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
+       int ret = 0;
+       bool mspi_read = false;
+       u32 io_width, addrlen, addr, len;
+       u_char *buf;
+
+       buf = msg->buf;
+       addr = msg->from;
+       len = msg->len;
+
+       if (bcm_qspi_bspi_ver_three(qspi) == true) {
+               /*
+                * The address coming into this function is a raw flash offset.
+                * But for BSPI <= V3, we need to convert it to a remapped BSPI
+                * address. If it crosses a 4MB boundary, just revert back to
+                * using MSPI.
+                */
+               addr = (addr + 0xc00000) & 0xffffff;
+
+               if ((~ADDR_4MB_MASK & addr) ^
+                   (~ADDR_4MB_MASK & (addr + len - 1)))
+                       mspi_read = true;
+       }
+
+       /* non-aligned and very short transfers are handled by MSPI */
+       if (!IS_ALIGNED((uintptr_t)addr, 4) || !IS_ALIGNED((uintptr_t)buf, 4) ||
+           len < 4)
+               mspi_read = true;
+
+       if (mspi_read)
+               /* this will make the m25p80 read to fallback to mspi read */
+               return -EAGAIN;
+
+       io_width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
+       addrlen = msg->addr_width;
+       ret = bcm_qspi_bspi_set_mode(qspi, io_width, addrlen, -1);
+
+       if (!ret)
+               ret = bcm_qspi_bspi_flash_read(spi, msg);
+
+       return ret;
+}
+
+static int bcm_qspi_transfer_one(struct spi_master *master,
+                                struct spi_device *spi,
+                                struct spi_transfer *trans)
+{
+       struct bcm_qspi *qspi = spi_master_get_devdata(master);
+       int slots;
+       unsigned long timeo = msecs_to_jiffies(100);
+
+       bcm_qspi_chip_select(qspi, spi->chip_select);
+       qspi->trans_pos.trans = trans;
+       qspi->trans_pos.byte = 0;
+
+       while (qspi->trans_pos.byte < trans->len) {
+               reinit_completion(&qspi->mspi_done);
+
+               slots = write_to_hw(qspi, spi);
+               if (!wait_for_completion_timeout(&qspi->mspi_done, timeo)) {
+                       dev_err(&qspi->pdev->dev, "timeout waiting for MSPI\n");
+                       return -ETIMEDOUT;
+               }
+
+               read_from_hw(qspi, slots);
+       }
+
+       return 0;
+}
+
+static void bcm_qspi_cleanup(struct spi_device *spi)
+{
+       struct bcm_qspi_parms *xp = spi_get_ctldata(spi);
+
+       kfree(xp);
+}
+
+static irqreturn_t bcm_qspi_mspi_l2_isr(int irq, void *dev_id)
+{
+       struct bcm_qspi_dev_id *qspi_dev_id = dev_id;
+       struct bcm_qspi *qspi = qspi_dev_id->dev;
+       u32 status = bcm_qspi_read(qspi, MSPI, MSPI_MSPI_STATUS);
+
+       if (status & MSPI_MSPI_STATUS_SPIF) {
+               struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
+               /* clear interrupt */
+               status &= ~MSPI_MSPI_STATUS_SPIF;
+               bcm_qspi_write(qspi, MSPI, MSPI_MSPI_STATUS, status);
+               if (qspi->soc_intc)
+                       soc_intc->bcm_qspi_int_ack(soc_intc, MSPI_DONE);
+               complete(&qspi->mspi_done);
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
+static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id)
+{
+       struct bcm_qspi_dev_id *qspi_dev_id = dev_id;
+       struct bcm_qspi *qspi = qspi_dev_id->dev;
+       struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
+       u32 status = qspi_dev_id->irqp->mask;
+
+       if (qspi->bspi_enabled && qspi->bspi_rf_msg) {
+               bcm_qspi_bspi_lr_data_read(qspi);
+               if (qspi->bspi_rf_msg_len == 0) {
+                       qspi->bspi_rf_msg = NULL;
+                       if (qspi->soc_intc) {
+                               /* disable soc BSPI interrupt */
+                               soc_intc->bcm_qspi_int_set(soc_intc, BSPI_DONE,
+                                                          false);
+                               /* indicate done */
+                               status = INTR_BSPI_LR_SESSION_DONE_MASK;
+                       }
+
+                       if (qspi->bspi_rf_msg_status)
+                               bcm_qspi_bspi_lr_clear(qspi);
+                       else
+                               bcm_qspi_bspi_flush_prefetch_buffers(qspi);
+               }
+
+               if (qspi->soc_intc)
+                       /* clear soc BSPI interrupt */
+                       soc_intc->bcm_qspi_int_ack(soc_intc, BSPI_DONE);
+       }
+
+       status &= INTR_BSPI_LR_SESSION_DONE_MASK;
+       if (qspi->bspi_enabled && status && qspi->bspi_rf_msg_len == 0)
+               complete(&qspi->bspi_done);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t bcm_qspi_bspi_lr_err_l2_isr(int irq, void *dev_id)
+{
+       struct bcm_qspi_dev_id *qspi_dev_id = dev_id;
+       struct bcm_qspi *qspi = qspi_dev_id->dev;
+       struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
+
+       dev_err(&qspi->pdev->dev, "BSPI INT error\n");
+       qspi->bspi_rf_msg_status = -EIO;
+       if (qspi->soc_intc)
+               /* clear soc interrupt */
+               soc_intc->bcm_qspi_int_ack(soc_intc, BSPI_ERR);
+
+       complete(&qspi->bspi_done);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t bcm_qspi_l1_isr(int irq, void *dev_id)
+{
+       struct bcm_qspi_dev_id *qspi_dev_id = dev_id;
+       struct bcm_qspi *qspi = qspi_dev_id->dev;
+       struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
+       irqreturn_t ret = IRQ_NONE;
+
+       if (soc_intc) {
+               u32 status = soc_intc->bcm_qspi_get_int_status(soc_intc);
+
+               if (status & MSPI_DONE)
+                       ret = bcm_qspi_mspi_l2_isr(irq, dev_id);
+               else if (status & BSPI_DONE)
+                       ret = bcm_qspi_bspi_lr_l2_isr(irq, dev_id);
+               else if (status & BSPI_ERR)
+                       ret = bcm_qspi_bspi_lr_err_l2_isr(irq, dev_id);
+       }
+
+       return ret;
+}
+
+static const struct bcm_qspi_irq qspi_irq_tab[] = {
+       {
+               .irq_name = "spi_lr_fullness_reached",
+               .irq_handler = bcm_qspi_bspi_lr_l2_isr,
+               .mask = INTR_BSPI_LR_FULLNESS_REACHED_MASK,
+       },
+       {
+               .irq_name = "spi_lr_session_aborted",
+               .irq_handler = bcm_qspi_bspi_lr_err_l2_isr,
+               .mask = INTR_BSPI_LR_SESSION_ABORTED_MASK,
+       },
+       {
+               .irq_name = "spi_lr_impatient",
+               .irq_handler = bcm_qspi_bspi_lr_err_l2_isr,
+               .mask = INTR_BSPI_LR_IMPATIENT_MASK,
+       },
+       {
+               .irq_name = "spi_lr_session_done",
+               .irq_handler = bcm_qspi_bspi_lr_l2_isr,
+               .mask = INTR_BSPI_LR_SESSION_DONE_MASK,
+       },
+#ifdef QSPI_INT_DEBUG
+       /* this interrupt is for debug purposes only, dont request irq */
+       {
+               .irq_name = "spi_lr_overread",
+               .irq_handler = bcm_qspi_bspi_lr_err_l2_isr,
+               .mask = INTR_BSPI_LR_OVERREAD_MASK,
+       },
+#endif
+       {
+               .irq_name = "mspi_done",
+               .irq_handler = bcm_qspi_mspi_l2_isr,
+               .mask = INTR_MSPI_DONE_MASK,
+       },
+       {
+               .irq_name = "mspi_halted",
+               .irq_handler = bcm_qspi_mspi_l2_isr,
+               .mask = INTR_MSPI_HALTED_MASK,
+       },
+       {
+               /* single muxed L1 interrupt source */
+               .irq_name = "spi_l1_intr",
+               .irq_handler = bcm_qspi_l1_isr,
+               .irq_source = MUXED_L1,
+               .mask = QSPI_INTERRUPTS_ALL,
+       },
+};
+
+static void bcm_qspi_bspi_init(struct bcm_qspi *qspi)
+{
+       u32 val = 0;
+
+       val = bcm_qspi_read(qspi, BSPI, BSPI_REVISION_ID);
+       qspi->bspi_maj_rev = (val >> 8) & 0xff;
+       qspi->bspi_min_rev = val & 0xff;
+       if (!(bcm_qspi_bspi_ver_three(qspi))) {
+               /* Force mapping of BSPI address -> flash offset */
+               bcm_qspi_write(qspi, BSPI, BSPI_BSPI_XOR_VALUE, 0);
+               bcm_qspi_write(qspi, BSPI, BSPI_BSPI_XOR_ENABLE, 1);
+       }
+       qspi->bspi_enabled = 1;
+       bcm_qspi_disable_bspi(qspi);
+       bcm_qspi_write(qspi, BSPI, BSPI_B0_CTRL, 0);
+       bcm_qspi_write(qspi, BSPI, BSPI_B1_CTRL, 0);
+}
+
+static void bcm_qspi_hw_init(struct bcm_qspi *qspi)
+{
+       struct bcm_qspi_parms parms;
+
+       bcm_qspi_write(qspi, MSPI, MSPI_SPCR1_LSB, 0);
+       bcm_qspi_write(qspi, MSPI, MSPI_SPCR1_MSB, 0);
+       bcm_qspi_write(qspi, MSPI, MSPI_NEWQP, 0);
+       bcm_qspi_write(qspi, MSPI, MSPI_ENDQP, 0);
+       bcm_qspi_write(qspi, MSPI, MSPI_SPCR2, 0x20);
+
+       parms.mode = SPI_MODE_3;
+       parms.bits_per_word = 8;
+       parms.speed_hz = qspi->max_speed_hz;
+       bcm_qspi_hw_set_parms(qspi, &parms);
+
+       if (has_bspi(qspi))
+               bcm_qspi_bspi_init(qspi);
+}
+
+static void bcm_qspi_hw_uninit(struct bcm_qspi *qspi)
+{
+       bcm_qspi_write(qspi, MSPI, MSPI_SPCR2, 0);
+       if (has_bspi(qspi))
+               bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 0);
+
+}
+
+static const struct of_device_id bcm_qspi_of_match[] = {
+       { .compatible = "brcm,spi-bcm-qspi" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, bcm_qspi_of_match);
+
+int bcm_qspi_probe(struct platform_device *pdev,
+                  struct bcm_qspi_soc_intc *soc_intc)
+{
+       struct device *dev = &pdev->dev;
+       struct bcm_qspi *qspi;
+       struct spi_master *master;
+       struct resource *res;
+       int irq, ret = 0, num_ints = 0;
+       u32 val;
+       const char *name = NULL;
+       int num_irqs = ARRAY_SIZE(qspi_irq_tab);
+
+       /* We only support device-tree instantiation */
+       if (!dev->of_node)
+               return -ENODEV;
+
+       if (!of_match_node(bcm_qspi_of_match, dev->of_node))
+               return -ENODEV;
+
+       master = spi_alloc_master(dev, sizeof(struct bcm_qspi));
+       if (!master) {
+               dev_err(dev, "error allocating spi_master\n");
+               return -ENOMEM;
+       }
+
+       qspi = spi_master_get_devdata(master);
+       qspi->pdev = pdev;
+       qspi->trans_pos.trans = NULL;
+       qspi->trans_pos.byte = 0;
+       qspi->master = master;
+
+       master->bus_num = -1;
+       master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD;
+       master->setup = bcm_qspi_setup;
+       master->transfer_one = bcm_qspi_transfer_one;
+       master->spi_flash_read = bcm_qspi_flash_read;
+       master->cleanup = bcm_qspi_cleanup;
+       master->dev.of_node = dev->of_node;
+       master->num_chipselect = NUM_CHIPSELECT;
+
+       qspi->big_endian = of_device_is_big_endian(dev->of_node);
+
+       if (!of_property_read_u32(dev->of_node, "num-cs", &val))
+               master->num_chipselect = val;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hif_mspi");
+       if (!res)
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  "mspi");
+
+       if (res) {
+               qspi->base[MSPI]  = devm_ioremap_resource(dev, res);
+               if (IS_ERR(qspi->base[MSPI])) {
+                       ret = PTR_ERR(qspi->base[MSPI]);
+                       goto qspi_probe_err;
+               }
+       } else {
+               goto qspi_probe_err;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bspi");
+       if (res) {
+               qspi->base[BSPI]  = devm_ioremap_resource(dev, res);
+               if (IS_ERR(qspi->base[BSPI])) {
+                       ret = PTR_ERR(qspi->base[BSPI]);
+                       goto qspi_probe_err;
+               }
+               qspi->bspi_mode = true;
+       } else {
+               qspi->bspi_mode = false;
+       }
+
+       dev_info(dev, "using %smspi mode\n", qspi->bspi_mode ? "bspi-" : "");
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs_reg");
+       if (res) {
+               qspi->base[CHIP_SELECT]  = devm_ioremap_resource(dev, res);
+               if (IS_ERR(qspi->base[CHIP_SELECT])) {
+                       ret = PTR_ERR(qspi->base[CHIP_SELECT]);
+                       goto qspi_probe_err;
+               }
+       }
+
+       qspi->dev_ids = kcalloc(num_irqs, sizeof(struct bcm_qspi_dev_id),
+                               GFP_KERNEL);
+       if (!qspi->dev_ids) {
+               ret = -ENOMEM;
+               goto qspi_probe_err;
+       }
+
+       for (val = 0; val < num_irqs; val++) {
+               irq = -1;
+               name = qspi_irq_tab[val].irq_name;
+               if (qspi_irq_tab[val].irq_source == SINGLE_L2) {
+                       /* get the l2 interrupts */
+                       irq = platform_get_irq_byname(pdev, name);
+               } else if (!num_ints && soc_intc) {
+                       /* all mspi, bspi intrs muxed to one L1 intr */
+                       irq = platform_get_irq(pdev, 0);
+               }
+
+               if (irq  >= 0) {
+                       ret = devm_request_irq(&pdev->dev, irq,
+                                              qspi_irq_tab[val].irq_handler, 0,
+                                              name,
+                                              &qspi->dev_ids[val]);
+                       if (ret < 0) {
+                               dev_err(&pdev->dev, "IRQ %s not found\n", name);
+                               goto qspi_probe_err;
+                       }
+
+                       qspi->dev_ids[val].dev = qspi;
+                       qspi->dev_ids[val].irqp = &qspi_irq_tab[val];
+                       num_ints++;
+                       dev_dbg(&pdev->dev, "registered IRQ %s %d\n",
+                               qspi_irq_tab[val].irq_name,
+                               irq);
+               }
+       }
+
+       if (!num_ints) {
+               dev_err(&pdev->dev, "no IRQs registered, cannot init driver\n");
+               ret = -EINVAL;
+               goto qspi_probe_err;
+       }
+
+       /*
+        * Some SoCs integrate spi controller (e.g., its interrupt bits)
+        * in specific ways
+        */
+       if (soc_intc) {
+               qspi->soc_intc = soc_intc;
+               soc_intc->bcm_qspi_int_set(soc_intc, MSPI_DONE, true);
+       } else {
+               qspi->soc_intc = NULL;
+       }
+
+       qspi->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(qspi->clk)) {
+               dev_warn(dev, "unable to get clock\n");
+               ret = PTR_ERR(qspi->clk);
+               goto qspi_probe_err;
+       }
+
+       ret = clk_prepare_enable(qspi->clk);
+       if (ret) {
+               dev_err(dev, "failed to prepare clock\n");
+               goto qspi_probe_err;
+       }
+
+       qspi->base_clk = clk_get_rate(qspi->clk);
+       qspi->max_speed_hz = qspi->base_clk / (QSPI_SPBR_MIN * 2);
+
+       bcm_qspi_hw_init(qspi);
+       init_completion(&qspi->mspi_done);
+       init_completion(&qspi->bspi_done);
+       qspi->curr_cs = -1;
+
+       platform_set_drvdata(pdev, qspi);
+
+       qspi->xfer_mode.width = -1;
+       qspi->xfer_mode.addrlen = -1;
+       qspi->xfer_mode.hp = -1;
+
+       ret = devm_spi_register_master(&pdev->dev, master);
+       if (ret < 0) {
+               dev_err(dev, "can't register master\n");
+               goto qspi_reg_err;
+       }
+
+       return 0;
+
+qspi_reg_err:
+       bcm_qspi_hw_uninit(qspi);
+       clk_disable_unprepare(qspi->clk);
+qspi_probe_err:
+       spi_master_put(master);
+       kfree(qspi->dev_ids);
+       return ret;
+}
+/* probe function to be called by SoC specific platform driver probe */
+EXPORT_SYMBOL_GPL(bcm_qspi_probe);
+
+int bcm_qspi_remove(struct platform_device *pdev)
+{
+       struct bcm_qspi *qspi = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);
+       bcm_qspi_hw_uninit(qspi);
+       clk_disable_unprepare(qspi->clk);
+       kfree(qspi->dev_ids);
+       spi_unregister_master(qspi->master);
+
+       return 0;
+}
+/* function to be called by SoC specific platform driver remove() */
+EXPORT_SYMBOL_GPL(bcm_qspi_remove);
+
+static int __maybe_unused bcm_qspi_suspend(struct device *dev)
+{
+       struct bcm_qspi *qspi = dev_get_drvdata(dev);
+
+       spi_master_suspend(qspi->master);
+       clk_disable(qspi->clk);
+       bcm_qspi_hw_uninit(qspi);
+
+       return 0;
+};
+
+static int __maybe_unused bcm_qspi_resume(struct device *dev)
+{
+       struct bcm_qspi *qspi = dev_get_drvdata(dev);
+       int ret = 0;
+
+       bcm_qspi_hw_init(qspi);
+       bcm_qspi_chip_select(qspi, qspi->curr_cs);
+       if (qspi->soc_intc)
+               /* enable MSPI interrupt */
+               qspi->soc_intc->bcm_qspi_int_set(qspi->soc_intc, MSPI_DONE,
+                                                true);
+
+       ret = clk_enable(qspi->clk);
+       if (!ret)
+               spi_master_resume(qspi->master);
+
+       return ret;
+}
+
+SIMPLE_DEV_PM_OPS(bcm_qspi_pm_ops, bcm_qspi_suspend, bcm_qspi_resume);
+
+/* pm_ops to be called by SoC specific platform driver */
+EXPORT_SYMBOL_GPL(bcm_qspi_pm_ops);
+
+MODULE_AUTHOR("Kamal Dasu");
+MODULE_DESCRIPTION("Broadcom QSPI driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/spi/spi-bcm-qspi.h b/drivers/spi/spi-bcm-qspi.h
new file mode 100644 (file)
index 0000000..7abfc75
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2016 Broadcom
+ *
+ * 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 (the "GPL").
+ *
+ * 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 version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+#ifndef __SPI_BCM_QSPI_H__
+#define __SPI_BCM_QSPI_H__
+
+#include <linux/types.h>
+#include <linux/io.h>
+
+/* BSPI interrupt masks */
+#define INTR_BSPI_LR_OVERREAD_MASK             BIT(4)
+#define INTR_BSPI_LR_SESSION_DONE_MASK         BIT(3)
+#define INTR_BSPI_LR_IMPATIENT_MASK            BIT(2)
+#define INTR_BSPI_LR_SESSION_ABORTED_MASK      BIT(1)
+#define INTR_BSPI_LR_FULLNESS_REACHED_MASK     BIT(0)
+
+#define BSPI_LR_INTERRUPTS_DATA                       \
+       (INTR_BSPI_LR_SESSION_DONE_MASK |              \
+        INTR_BSPI_LR_FULLNESS_REACHED_MASK)
+
+#define BSPI_LR_INTERRUPTS_ERROR               \
+       (INTR_BSPI_LR_OVERREAD_MASK |          \
+        INTR_BSPI_LR_IMPATIENT_MASK |         \
+        INTR_BSPI_LR_SESSION_ABORTED_MASK)
+
+#define BSPI_LR_INTERRUPTS_ALL                 \
+       (BSPI_LR_INTERRUPTS_ERROR |            \
+        BSPI_LR_INTERRUPTS_DATA)
+
+/* MSPI Interrupt masks */
+#define INTR_MSPI_HALTED_MASK                  BIT(6)
+#define INTR_MSPI_DONE_MASK                    BIT(5)
+
+#define MSPI_INTERRUPTS_ALL                   \
+       (INTR_MSPI_DONE_MASK |                 \
+        INTR_MSPI_HALTED_MASK)
+
+#define QSPI_INTERRUPTS_ALL                    \
+       (MSPI_INTERRUPTS_ALL |                 \
+        BSPI_LR_INTERRUPTS_ALL)
+
+struct platform_device;
+struct dev_pm_ops;
+
+enum {
+       MSPI_DONE = 0x1,
+       BSPI_DONE = 0x2,
+       BSPI_ERR = 0x4,
+       MSPI_BSPI_DONE = 0x7
+};
+
+struct bcm_qspi_soc_intc {
+       void (*bcm_qspi_int_ack)(struct bcm_qspi_soc_intc *soc_intc, int type);
+       void (*bcm_qspi_int_set)(struct bcm_qspi_soc_intc *soc_intc, int type,
+                                bool en);
+       u32 (*bcm_qspi_get_int_status)(struct bcm_qspi_soc_intc *soc_intc);
+};
+
+/* Read controller register*/
+static inline u32 bcm_qspi_readl(bool be, void __iomem *addr)
+{
+       if (be)
+               return ioread32be(addr);
+       else
+               return readl_relaxed(addr);
+}
+
+/* Write controller register*/
+static inline void bcm_qspi_writel(bool be,
+                                  unsigned int data, void __iomem *addr)
+{
+       if (be)
+               iowrite32be(data, addr);
+       else
+               writel_relaxed(data, addr);
+}
+
+static inline u32 get_qspi_mask(int type)
+{
+       switch (type) {
+       case MSPI_DONE:
+               return INTR_MSPI_DONE_MASK;
+       case BSPI_DONE:
+               return BSPI_LR_INTERRUPTS_ALL;
+       case MSPI_BSPI_DONE:
+               return QSPI_INTERRUPTS_ALL;
+       case BSPI_ERR:
+               return BSPI_LR_INTERRUPTS_ERROR;
+       }
+
+       return 0;
+}
+
+/* The common driver functions to be called by the SoC platform driver */
+int bcm_qspi_probe(struct platform_device *pdev,
+                  struct bcm_qspi_soc_intc *soc_intc);
+int bcm_qspi_remove(struct platform_device *pdev);
+
+/* pm_ops used by the SoC platform driver called on PM suspend/resume */
+extern const struct dev_pm_ops bcm_qspi_pm_ops;
+
+#endif /* __SPI_BCM_QSPI_H__ */
diff --git a/drivers/spi/spi-brcmstb-qspi.c b/drivers/spi/spi-brcmstb-qspi.c
new file mode 100644 (file)
index 0000000..c7df92e
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Broadcom
+ *
+ * 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 (the "GPL").
+ *
+ * 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 version 2 (GPLv2) for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 (GPLv2) along with this source code.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include "spi-bcm-qspi.h"
+
+static const struct of_device_id brcmstb_qspi_of_match[] = {
+       { .compatible = "brcm,spi-brcmstb-qspi" },
+       { .compatible = "brcm,spi-brcmstb-mspi" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, brcmstb_qspi_of_match);
+
+static int brcmstb_qspi_probe(struct platform_device *pdev)
+{
+       return bcm_qspi_probe(pdev, NULL);
+}
+
+static int brcmstb_qspi_remove(struct platform_device *pdev)
+{
+       return bcm_qspi_remove(pdev);
+}
+
+static struct platform_driver brcmstb_qspi_driver = {
+       .probe                  = brcmstb_qspi_probe,
+       .remove                 = brcmstb_qspi_remove,
+       .driver = {
+               .name           = "brcmstb_qspi",
+               .pm             = &bcm_qspi_pm_ops,
+               .of_match_table = brcmstb_qspi_of_match,
+       }
+};
+module_platform_driver(brcmstb_qspi_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kamal Dasu");
+MODULE_DESCRIPTION("Broadcom SPI driver for settop SoC");
index c09bb74..27960e4 100644 (file)
@@ -283,7 +283,6 @@ static int dw_spi_transfer_one(struct spi_master *master,
        struct chip_data *chip = spi_get_ctldata(spi);
        u8 imask = 0;
        u16 txlevel = 0;
-       u16 clk_div;
        u32 cr0;
        int ret;
 
@@ -298,13 +297,13 @@ static int dw_spi_transfer_one(struct spi_master *master,
        spi_enable_chip(dws, 0);
 
        /* Handle per transfer options for bpw and speed */
-       if (transfer->speed_hz != chip->speed_hz) {
-               /* clk_div doesn't support odd number */
-               clk_div = (dws->max_freq / transfer->speed_hz + 1) & 0xfffe;
-
-               chip->speed_hz = transfer->speed_hz;
-               chip->clk_div = clk_div;
-
+       if (transfer->speed_hz != dws->current_freq) {
+               if (transfer->speed_hz != chip->speed_hz) {
+                       /* clk_div doesn't support odd number */
+                       chip->clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe;
+                       chip->speed_hz = transfer->speed_hz;
+               }
+               dws->current_freq = transfer->speed_hz;
                spi_set_clk(dws, chip->clk_div);
        }
        if (transfer->bits_per_word == 8) {
index 61bc3cb..c21ca02 100644 (file)
@@ -123,6 +123,7 @@ struct dw_spi {
        u8                      n_bytes;        /* current is a 1/2 bytes op */
        u32                     dma_width;
        irqreturn_t             (*transfer_handler)(struct dw_spi *dws);
+       u32                     current_freq;   /* frequency in hz */
 
        /* DMA info */
        int                     dma_inited;
index 9e9dadb..35c0dd9 100644 (file)
@@ -159,7 +159,7 @@ struct fsl_dspi {
        u8                      cs;
        u16                     void_write_data;
        u32                     cs_change;
-       struct fsl_dspi_devtype_data *devtype_data;
+       const struct fsl_dspi_devtype_data *devtype_data;
 
        wait_queue_head_t       waitq;
        u32                     waitflags;
@@ -624,10 +624,13 @@ static int dspi_resume(struct device *dev)
 {
        struct spi_master *master = dev_get_drvdata(dev);
        struct fsl_dspi *dspi = spi_master_get_devdata(master);
+       int ret;
 
        pinctrl_pm_select_default_state(dev);
 
-       clk_prepare_enable(dspi->clk);
+       ret = clk_prepare_enable(dspi->clk);
+       if (ret)
+               return ret;
        spi_master_resume(master);
 
        return 0;
@@ -651,8 +654,6 @@ static int dspi_probe(struct platform_device *pdev)
        struct resource *res;
        void __iomem *base;
        int ret = 0, cs_num, bus_num;
-       const struct of_device_id *of_id =
-                       of_match_device(fsl_dspi_dt_ids, &pdev->dev);
 
        master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi));
        if (!master)
@@ -686,7 +687,7 @@ static int dspi_probe(struct platform_device *pdev)
        }
        master->bus_num = bus_num;
 
-       dspi->devtype_data = (struct fsl_dspi_devtype_data *)of_id->data;
+       dspi->devtype_data = of_device_get_match_data(&pdev->dev);
        if (!dspi->devtype_data) {
                dev_err(&pdev->dev, "can't get devtype_data\n");
                ret = -EFAULT;
@@ -728,7 +729,9 @@ static int dspi_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "unable to get clock\n");
                goto out_master_put;
        }
-       clk_prepare_enable(dspi->clk);
+       ret = clk_prepare_enable(dspi->clk);
+       if (ret)
+               goto out_master_put;
 
        master->max_speed_hz =
                clk_get_rate(dspi->clk) / dspi->devtype_data->max_clock_factor;
@@ -760,7 +763,6 @@ static int dspi_remove(struct platform_device *pdev)
        /* Disconnect from the SPI framework */
        clk_disable_unprepare(dspi->clk);
        spi_unregister_master(dspi->master);
-       spi_master_put(dspi->master);
 
        return 0;
 }
diff --git a/drivers/spi/spi-iproc-qspi.c b/drivers/spi/spi-iproc-qspi.c
new file mode 100644 (file)
index 0000000..be6ccb2
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2016 Broadcom Limited
+ *
+ * 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.
+ *
+ * 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/device.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "spi-bcm-qspi.h"
+
+#define INTR_BASE_BIT_SHIFT                    0x02
+#define INTR_COUNT                             0x07
+
+struct bcm_iproc_intc {
+       struct bcm_qspi_soc_intc soc_intc;
+       struct platform_device *pdev;
+       void __iomem *int_reg;
+       void __iomem *int_status_reg;
+       spinlock_t soclock;
+       bool big_endian;
+};
+
+static u32 bcm_iproc_qspi_get_l2_int_status(struct bcm_qspi_soc_intc *soc_intc)
+{
+       struct bcm_iproc_intc *priv =
+                       container_of(soc_intc, struct bcm_iproc_intc, soc_intc);
+       void __iomem *mmio = priv->int_status_reg;
+       int i;
+       u32 val = 0, sts = 0;
+
+       for (i = 0; i < INTR_COUNT; i++) {
+               if (bcm_qspi_readl(priv->big_endian, mmio + (i * 4)))
+                       val |= 1UL << i;
+       }
+
+       if (val & INTR_MSPI_DONE_MASK)
+               sts |= MSPI_DONE;
+
+       if (val & BSPI_LR_INTERRUPTS_ALL)
+               sts |= BSPI_DONE;
+
+       if (val & BSPI_LR_INTERRUPTS_ERROR)
+               sts |= BSPI_ERR;
+
+       return sts;
+}
+
+static void bcm_iproc_qspi_int_ack(struct bcm_qspi_soc_intc *soc_intc, int type)
+{
+       struct bcm_iproc_intc *priv =
+                       container_of(soc_intc, struct bcm_iproc_intc, soc_intc);
+       void __iomem *mmio = priv->int_status_reg;
+       u32 mask = get_qspi_mask(type);
+       int i;
+
+       for (i = 0; i < INTR_COUNT; i++) {
+               if (mask & (1UL << i))
+                       bcm_qspi_writel(priv->big_endian, 1, mmio + (i * 4));
+       }
+}
+
+static void bcm_iproc_qspi_int_set(struct bcm_qspi_soc_intc *soc_intc, int type,
+                                  bool en)
+{
+       struct bcm_iproc_intc *priv =
+                       container_of(soc_intc, struct bcm_iproc_intc, soc_intc);
+       void __iomem *mmio = priv->int_reg;
+       u32 mask = get_qspi_mask(type);
+       u32 val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->soclock, flags);
+
+       val = bcm_qspi_readl(priv->big_endian, mmio);
+
+       if (en)
+               val = val | (mask << INTR_BASE_BIT_SHIFT);
+       else
+               val = val & ~(mask << INTR_BASE_BIT_SHIFT);
+
+       bcm_qspi_writel(priv->big_endian, val, mmio);
+
+       spin_unlock_irqrestore(&priv->soclock, flags);
+}
+
+static int bcm_iproc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct bcm_iproc_intc *priv;
+       struct bcm_qspi_soc_intc *soc_intc;
+       struct resource *res;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+       soc_intc = &priv->soc_intc;
+       priv->pdev = pdev;
+
+       spin_lock_init(&priv->soclock);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr_regs");
+       priv->int_reg = devm_ioremap_resource(dev, res);
+       if (IS_ERR(priv->int_reg))
+               return PTR_ERR(priv->int_reg);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                          "intr_status_reg");
+       priv->int_status_reg = devm_ioremap_resource(dev, res);
+       if (IS_ERR(priv->int_status_reg))
+               return PTR_ERR(priv->int_status_reg);
+
+       priv->big_endian = of_device_is_big_endian(dev->of_node);
+
+       bcm_iproc_qspi_int_ack(soc_intc, MSPI_BSPI_DONE);
+       bcm_iproc_qspi_int_set(soc_intc, MSPI_BSPI_DONE, false);
+
+       soc_intc->bcm_qspi_int_ack = bcm_iproc_qspi_int_ack;
+       soc_intc->bcm_qspi_int_set = bcm_iproc_qspi_int_set;
+       soc_intc->bcm_qspi_get_int_status = bcm_iproc_qspi_get_l2_int_status;
+
+       return bcm_qspi_probe(pdev, soc_intc);
+}
+
+static int bcm_iproc_remove(struct platform_device *pdev)
+{
+       return bcm_qspi_remove(pdev);
+}
+
+static const struct of_device_id bcm_iproc_of_match[] = {
+       { .compatible = "brcm,spi-nsp-qspi" },
+       { .compatible = "brcm,spi-ns2-qspi" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, bcm_iproc_of_match);
+
+static struct platform_driver bcm_iproc_driver = {
+       .probe                  = bcm_iproc_probe,
+       .remove                 = bcm_iproc_remove,
+       .driver = {
+               .name           = "bcm_iproc",
+               .pm             = &bcm_qspi_pm_ops,
+               .of_match_table = bcm_iproc_of_match,
+       }
+};
+module_platform_driver(bcm_iproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kamal Dasu");
+MODULE_DESCRIPTION("SPI flash driver for Broadcom iProc SoCs");