From patchwork Wed Aug 24 22:04:29 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kamal Dasu X-Patchwork-Id: 9298421 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 6035860459 for ; Wed, 24 Aug 2016 22:07:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4CB6C29139 for ; Wed, 24 Aug 2016 22:07:05 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 418BC2913B; Wed, 24 Aug 2016 22:07:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 521172913A for ; Wed, 24 Aug 2016 22:07:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757262AbcHXWHE (ORCPT ); Wed, 24 Aug 2016 18:07:04 -0400 Received: from mail-pf0-f193.google.com ([209.85.192.193]:35965 "EHLO mail-pf0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757257AbcHXWHD (ORCPT ); Wed, 24 Aug 2016 18:07:03 -0400 Received: by mail-pf0-f193.google.com with SMTP id y134so1945498pfg.3 for ; Wed, 24 Aug 2016 15:07:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=rWKickZFbDg/lsspsF8L+URay+wYMhVfMPFke04Zp+A=; b=zC938rr4+OU3jZa62RctGgZKWYYFH8W8G48ri4U6utVERIwgBk89tJhlkXNmYUMytr W6sDdu5d8e7tsll8nUDSTmpP+2r9nPCwR2Yogg26x9LUlOco5Zlwd/EkPbodo5YksUaX 7hdZk7aSujc6RWBkuATZzCBZNrroS18aEpoB30tmJYqgZNnz5TDqyPeA0jn1Arn4wl34 lL0n9V3VZ4RDcDSmA0rIEZ1cZzDw+A7xOBCHVlSRJMCHKr/aa0BQMknhuG/RGAUTGHy+ yV+7Fve5hHVvsTQdqYuYSK94oOVM93Lizp06LhbFQd0ykHHy44s+CT7RF4jjnttJL2Kv O5+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=rWKickZFbDg/lsspsF8L+URay+wYMhVfMPFke04Zp+A=; b=CXqmTApAf8Tz9xof+ptvBnZKIQ2Q8546XzXq44Rbjxczc1d2TfhoFEwVYjO5YAeqsu NUJWk884UfozZgfdu3kTteP5xJCMaDmf62iX98qoQIOgZ5CNg0lbEoZF3U/NJBdgQxtK a2H2lvJxX2hqhES4+g95mUzXyC4tlbFDBNpZkhcHbv3TpOKV7t6VwQGqcxMLgTwO9sqi P42qGwYrxxCebjis4dMpZpdICBnER8hDRROG+4IPviJumERAjO8tW3cfeB591AXQMHpw BBGpRfcT7na2UvF1iCvk2mgOc2PoYKeME4YFqrZt49ejBeNVCi5S+kMj4HguZp8R5Yvy R04Q== X-Gm-Message-State: AE9vXwPi099J6Pp1zTkZennk4IiDf/zhHGLPbit3XjNTYo5osGRS6K1CFswHjV4XI9Q0sQ== X-Received: by 10.98.49.198 with SMTP id x189mr10028808pfx.135.1472076422555; Wed, 24 Aug 2016 15:07:02 -0700 (PDT) Received: from mail.broadcom.com ([216.31.219.19]) by smtp.gmail.com with ESMTPSA id wa9sm15966968pac.35.2016.08.24.15.07.00 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 24 Aug 2016 15:07:01 -0700 (PDT) From: Kamal Dasu To: broonie@kernel.org, linux-spi@vger.kernel.org, computersforpeace@gmail.com, linux-mtd@lists.infradead.org, vigneshr@ti.com, f.fainelli@gmail.com, yendapally.reddy@broadcom.com Cc: bcm-kernel-feedback-list@broadcom.com, jon.mason@broadcom.com, Kamal Dasu Subject: [PATCH v6, 8/8] spi: iproc-qspi: Add Broadcom iProc SoCs support Date: Wed, 24 Aug 2016 18:04:29 -0400 Message-Id: <1472076269-4731-9-git-send-email-kdasu.kdev@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1472076269-4731-1-git-send-email-kdasu.kdev@gmail.com> References: <1472076269-4731-1-git-send-email-kdasu.kdev@gmail.com> Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Signed-off-by: Yendapally Reddy Dhananjaya Reddy --- 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 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3e753db..2dc1b71 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -21,7 +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-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 diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 8fff43e..19995c5 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -176,9 +176,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; }; @@ -199,6 +205,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; @@ -807,6 +817,7 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi, 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) @@ -851,6 +862,15 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi, 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(); @@ -953,9 +973,12 @@ static irqreturn_t bcm_qspi_mspi_l2_isr(int irq, void *dev_id) 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; } @@ -967,20 +990,33 @@ 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; - 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); @@ -991,13 +1027,39 @@ 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", @@ -1037,6 +1099,13 @@ static const struct bcm_qspi_irq qspi_irq_tab[] = { .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) @@ -1183,7 +1252,13 @@ int bcm_qspi_probe(struct platform_device *pdev, 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_device *pdev, 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 bcm_qspi_resume(struct device *dev) 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); diff --git a/drivers/spi/spi-bcm-qspi.h b/drivers/spi/spi-bcm-qspi.h index 65c363b..7abfc75 100644 --- 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 be, 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); diff --git a/drivers/spi/spi-iproc-qspi.c b/drivers/spi/spi-iproc-qspi.c new file mode 100644 index 0000000..be6ccb2 --- /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 +#include +#include +#include +#include +#include +#include +#include + +#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");