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