aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm53xx/patches-4.4/083-0008-spi-iproc-qspi-Add-Broadcom-iProc-SoCs-support.patch
diff options
context:
space:
mode:
authorRafał Miłecki <rafal@milecki.pl>2017-03-01 06:52:23 +0100
committerRafał Miłecki <rafal@milecki.pl>2017-03-01 07:02:17 +0100
commit9209511d61fce02b0af48b44d963150e076e0f7e (patch)
tree2791f6e6e7370c8c977bd51e88bfe94eb9b5a022 /target/linux/bcm53xx/patches-4.4/083-0008-spi-iproc-qspi-Add-Broadcom-iProc-SoCs-support.patch
parentebf846b00559a326d6ea5a1711d11335d89d1218 (diff)
downloadupstream-9209511d61fce02b0af48b44d963150e076e0f7e.tar.gz
upstream-9209511d61fce02b0af48b44d963150e076e0f7e.tar.bz2
upstream-9209511d61fce02b0af48b44d963150e076e0f7e.zip
bcm53xx: backport Broadcom's iProc QSPI driver
This driver has been added instead of improving spi-bcm53xx. It has some advantages: allows SPI speed control & hopefully doesn't have bug that was stopping us from using multiple SPI messages for writing flash data. Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
Diffstat (limited to 'target/linux/bcm53xx/patches-4.4/083-0008-spi-iproc-qspi-Add-Broadcom-iProc-SoCs-support.patch')
-rw-r--r--target/linux/bcm53xx/patches-4.4/083-0008-spi-iproc-qspi-Add-Broadcom-iProc-SoCs-support.patch452
1 files changed, 452 insertions, 0 deletions
diff --git a/target/linux/bcm53xx/patches-4.4/083-0008-spi-iproc-qspi-Add-Broadcom-iProc-SoCs-support.patch b/target/linux/bcm53xx/patches-4.4/083-0008-spi-iproc-qspi-Add-Broadcom-iProc-SoCs-support.patch
new file mode 100644
index 0000000000..836d629c15
--- /dev/null
+++ b/target/linux/bcm53xx/patches-4.4/083-0008-spi-iproc-qspi-Add-Broadcom-iProc-SoCs-support.patch
@@ -0,0 +1,452 @@
+From cc20a38612dbc87dc7396affc9758e3bfbe92340 Mon Sep 17 00:00:00 2001
+From: Kamal Dasu <kdasu.kdev@gmail.com>
+Date: Wed, 24 Aug 2016 18:04:29 -0400
+Subject: [PATCH] spi: iproc-qspi: Add Broadcom iProc SoCs support
+
+This spi driver uses the common spi-bcm-qspi driver and implements iProc
+SoCs specific interrupt controller. The common driver now calls the SoC
+handlers when present. Adding support for both muxed l1 and unmuxed interrupt
+sources.
+
+Signed-off-by: Kamal Dasu <kdasu.kdev@gmail.com>
+Signed-off-by: Yendapally Reddy Dhananjaya Reddy <yendapally.reddy@broadcom.com>
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/Makefile | 2 +-
+ drivers/spi/spi-bcm-qspi.c | 97 ++++++++++++++++++++++++-
+ drivers/spi/spi-bcm-qspi.h | 34 ++++++++-
+ drivers/spi/spi-iproc-qspi.c | 163 +++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 291 insertions(+), 5 deletions(-)
+ create mode 100644 drivers/spi/spi-iproc-qspi.c
+
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -19,7 +19,7 @@ obj-$(CONFIG_SPI_BCM2835AUX) += spi-bcm
+ 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-brcmstb-qspi.o spi-bcm-qspi.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
+--- a/drivers/spi/spi-bcm-qspi.c
++++ b/drivers/spi/spi-bcm-qspi.c
+@@ -175,9 +175,15 @@ enum base_type {
+ 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;
+ };
+
+@@ -198,6 +204,10 @@ struct bcm_qspi {
+ 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;
+@@ -806,6 +816,7 @@ static int bcm_qspi_bspi_flash_read(stru
+ 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)
+@@ -850,6 +861,15 @@ static int bcm_qspi_bspi_flash_read(stru
+ 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();
+
+@@ -952,9 +972,12 @@ static irqreturn_t bcm_qspi_mspi_l2_isr(
+ 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;
+ }
+@@ -966,20 +989,33 @@ static irqreturn_t bcm_qspi_bspi_lr_l2_i
+ {
+ struct bcm_qspi_dev_id *qspi_dev_id = dev_id;
+ struct bcm_qspi *qspi = qspi_dev_id->dev;
+- u32 status;
++ 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 = (qspi_dev_id->irqp->mask & INTR_BSPI_LR_SESSION_DONE_MASK);
++ status &= INTR_BSPI_LR_SESSION_DONE_MASK;
+ if (qspi->bspi_enabled && status && qspi->bspi_rf_msg_len == 0)
+ complete(&qspi->bspi_done);
+
+@@ -990,13 +1026,39 @@ static irqreturn_t bcm_qspi_bspi_lr_err_
+ {
+ 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",
+@@ -1036,6 +1098,13 @@ static const struct bcm_qspi_irq qspi_ir
+ .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)
+@@ -1182,7 +1251,13 @@ int bcm_qspi_probe(struct platform_devic
+ for (val = 0; val < num_irqs; val++) {
+ irq = -1;
+ name = qspi_irq_tab[val].irq_name;
+- irq = platform_get_irq_byname(pdev, 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,
+@@ -1209,6 +1284,17 @@ int bcm_qspi_probe(struct platform_devic
+ 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");
+@@ -1288,6 +1374,11 @@ static int __maybe_unused bcm_qspi_resum
+
+ 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);
+--- a/drivers/spi/spi-bcm-qspi.h
++++ b/drivers/spi/spi-bcm-qspi.h
+@@ -48,10 +48,26 @@
+ (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;
+
+-struct bcm_qspi_soc_intc;
++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)
+@@ -72,6 +88,22 @@ static inline void bcm_qspi_writel(bool
+ 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);
+--- /dev/null
++++ b/drivers/spi/spi-iproc-qspi.c
+@@ -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");