18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2016 Broadcom Limited 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/device.h> 78c2ecf20Sopenharmony_ci#include <linux/io.h> 88c2ecf20Sopenharmony_ci#include <linux/ioport.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci#include <linux/of_address.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "spi-bcm-qspi.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define INTR_BASE_BIT_SHIFT 0x02 188c2ecf20Sopenharmony_ci#define INTR_COUNT 0x07 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct bcm_iproc_intc { 218c2ecf20Sopenharmony_ci struct bcm_qspi_soc_intc soc_intc; 228c2ecf20Sopenharmony_ci struct platform_device *pdev; 238c2ecf20Sopenharmony_ci void __iomem *int_reg; 248c2ecf20Sopenharmony_ci void __iomem *int_status_reg; 258c2ecf20Sopenharmony_ci spinlock_t soclock; 268c2ecf20Sopenharmony_ci bool big_endian; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic u32 bcm_iproc_qspi_get_l2_int_status(struct bcm_qspi_soc_intc *soc_intc) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct bcm_iproc_intc *priv = 328c2ecf20Sopenharmony_ci container_of(soc_intc, struct bcm_iproc_intc, soc_intc); 338c2ecf20Sopenharmony_ci void __iomem *mmio = priv->int_status_reg; 348c2ecf20Sopenharmony_ci int i; 358c2ecf20Sopenharmony_ci u32 val = 0, sts = 0; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci for (i = 0; i < INTR_COUNT; i++) { 388c2ecf20Sopenharmony_ci if (bcm_qspi_readl(priv->big_endian, mmio + (i * 4))) 398c2ecf20Sopenharmony_ci val |= 1UL << i; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (val & INTR_MSPI_DONE_MASK) 438c2ecf20Sopenharmony_ci sts |= MSPI_DONE; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (val & BSPI_LR_INTERRUPTS_ALL) 468c2ecf20Sopenharmony_ci sts |= BSPI_DONE; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (val & BSPI_LR_INTERRUPTS_ERROR) 498c2ecf20Sopenharmony_ci sts |= BSPI_ERR; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return sts; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void bcm_iproc_qspi_int_ack(struct bcm_qspi_soc_intc *soc_intc, int type) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct bcm_iproc_intc *priv = 578c2ecf20Sopenharmony_ci container_of(soc_intc, struct bcm_iproc_intc, soc_intc); 588c2ecf20Sopenharmony_ci void __iomem *mmio = priv->int_status_reg; 598c2ecf20Sopenharmony_ci u32 mask = get_qspi_mask(type); 608c2ecf20Sopenharmony_ci int i; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci for (i = 0; i < INTR_COUNT; i++) { 638c2ecf20Sopenharmony_ci if (mask & (1UL << i)) 648c2ecf20Sopenharmony_ci bcm_qspi_writel(priv->big_endian, 1, mmio + (i * 4)); 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void bcm_iproc_qspi_int_set(struct bcm_qspi_soc_intc *soc_intc, int type, 698c2ecf20Sopenharmony_ci bool en) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct bcm_iproc_intc *priv = 728c2ecf20Sopenharmony_ci container_of(soc_intc, struct bcm_iproc_intc, soc_intc); 738c2ecf20Sopenharmony_ci void __iomem *mmio = priv->int_reg; 748c2ecf20Sopenharmony_ci u32 mask = get_qspi_mask(type); 758c2ecf20Sopenharmony_ci u32 val; 768c2ecf20Sopenharmony_ci unsigned long flags; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->soclock, flags); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci val = bcm_qspi_readl(priv->big_endian, mmio); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (en) 838c2ecf20Sopenharmony_ci val = val | (mask << INTR_BASE_BIT_SHIFT); 848c2ecf20Sopenharmony_ci else 858c2ecf20Sopenharmony_ci val = val & ~(mask << INTR_BASE_BIT_SHIFT); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci bcm_qspi_writel(priv->big_endian, val, mmio); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->soclock, flags); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int bcm_iproc_probe(struct platform_device *pdev) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 958c2ecf20Sopenharmony_ci struct bcm_iproc_intc *priv; 968c2ecf20Sopenharmony_ci struct bcm_qspi_soc_intc *soc_intc; 978c2ecf20Sopenharmony_ci struct resource *res; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 1008c2ecf20Sopenharmony_ci if (!priv) 1018c2ecf20Sopenharmony_ci return -ENOMEM; 1028c2ecf20Sopenharmony_ci soc_intc = &priv->soc_intc; 1038c2ecf20Sopenharmony_ci priv->pdev = pdev; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci spin_lock_init(&priv->soclock); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr_regs"); 1088c2ecf20Sopenharmony_ci priv->int_reg = devm_ioremap_resource(dev, res); 1098c2ecf20Sopenharmony_ci if (IS_ERR(priv->int_reg)) 1108c2ecf20Sopenharmony_ci return PTR_ERR(priv->int_reg); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 1138c2ecf20Sopenharmony_ci "intr_status_reg"); 1148c2ecf20Sopenharmony_ci priv->int_status_reg = devm_ioremap_resource(dev, res); 1158c2ecf20Sopenharmony_ci if (IS_ERR(priv->int_status_reg)) 1168c2ecf20Sopenharmony_ci return PTR_ERR(priv->int_status_reg); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci priv->big_endian = of_device_is_big_endian(dev->of_node); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci bcm_iproc_qspi_int_ack(soc_intc, MSPI_BSPI_DONE); 1218c2ecf20Sopenharmony_ci bcm_iproc_qspi_int_set(soc_intc, MSPI_BSPI_DONE, false); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci soc_intc->bcm_qspi_int_ack = bcm_iproc_qspi_int_ack; 1248c2ecf20Sopenharmony_ci soc_intc->bcm_qspi_int_set = bcm_iproc_qspi_int_set; 1258c2ecf20Sopenharmony_ci soc_intc->bcm_qspi_get_int_status = bcm_iproc_qspi_get_l2_int_status; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return bcm_qspi_probe(pdev, soc_intc); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int bcm_iproc_remove(struct platform_device *pdev) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci return bcm_qspi_remove(pdev); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic const struct of_device_id bcm_iproc_of_match[] = { 1368c2ecf20Sopenharmony_ci { .compatible = "brcm,spi-nsp-qspi" }, 1378c2ecf20Sopenharmony_ci { .compatible = "brcm,spi-ns2-qspi" }, 1388c2ecf20Sopenharmony_ci {}, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm_iproc_of_match); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic struct platform_driver bcm_iproc_driver = { 1438c2ecf20Sopenharmony_ci .probe = bcm_iproc_probe, 1448c2ecf20Sopenharmony_ci .remove = bcm_iproc_remove, 1458c2ecf20Sopenharmony_ci .driver = { 1468c2ecf20Sopenharmony_ci .name = "bcm_iproc", 1478c2ecf20Sopenharmony_ci .pm = &bcm_qspi_pm_ops, 1488c2ecf20Sopenharmony_ci .of_match_table = bcm_iproc_of_match, 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_cimodule_platform_driver(bcm_iproc_driver); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kamal Dasu"); 1558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SPI flash driver for Broadcom iProc SoCs"); 156