diff options
author | Kamal Dasu <kdasu.kdev@gmail.com> | 2016-08-24 18:04:29 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-09-24 20:03:25 +0100 |
commit | cc20a38612dbc87dc7396affc9758e3bfbe92340 (patch) | |
tree | 3c8764494d7345d8ba1ef847a42000392727e135 /drivers/spi/spi-bcm-qspi.c | |
parent | 71b8f350a4f03730f3024bfa7dc2414904a21bcb (diff) |
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>
Diffstat (limited to 'drivers/spi/spi-bcm-qspi.c')
-rw-r--r-- | drivers/spi/spi-bcm-qspi.c | 97 |
1 files changed, 94 insertions, 3 deletions
diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 2c121ba8f0cb..14f9dea3173f 100644 --- 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(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) @@ -850,6 +861,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(); @@ -952,9 +972,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; } @@ -966,20 +989,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); @@ -990,13 +1026,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", @@ -1036,6 +1098,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) @@ -1182,7 +1251,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 __maybe_unused 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); |