18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  CLPS711X IRQ driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/irq.h>
108c2ecf20Sopenharmony_ci#include <linux/irqchip.h>
118c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
128c2ecf20Sopenharmony_ci#include <linux/of_address.h>
138c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <asm/exception.h>
178c2ecf20Sopenharmony_ci#include <asm/mach/irq.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define CLPS711X_INTSR1	(0x0240)
208c2ecf20Sopenharmony_ci#define CLPS711X_INTMR1	(0x0280)
218c2ecf20Sopenharmony_ci#define CLPS711X_BLEOI	(0x0600)
228c2ecf20Sopenharmony_ci#define CLPS711X_MCEOI	(0x0640)
238c2ecf20Sopenharmony_ci#define CLPS711X_TEOI	(0x0680)
248c2ecf20Sopenharmony_ci#define CLPS711X_TC1EOI	(0x06c0)
258c2ecf20Sopenharmony_ci#define CLPS711X_TC2EOI	(0x0700)
268c2ecf20Sopenharmony_ci#define CLPS711X_RTCEOI	(0x0740)
278c2ecf20Sopenharmony_ci#define CLPS711X_UMSEOI	(0x0780)
288c2ecf20Sopenharmony_ci#define CLPS711X_COEOI	(0x07c0)
298c2ecf20Sopenharmony_ci#define CLPS711X_INTSR2	(0x1240)
308c2ecf20Sopenharmony_ci#define CLPS711X_INTMR2	(0x1280)
318c2ecf20Sopenharmony_ci#define CLPS711X_SRXEOF	(0x1600)
328c2ecf20Sopenharmony_ci#define CLPS711X_KBDEOI	(0x1700)
338c2ecf20Sopenharmony_ci#define CLPS711X_INTSR3	(0x2240)
348c2ecf20Sopenharmony_ci#define CLPS711X_INTMR3	(0x2280)
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic const struct {
378c2ecf20Sopenharmony_ci#define CLPS711X_FLAG_EN	(1 << 0)
388c2ecf20Sopenharmony_ci#define CLPS711X_FLAG_FIQ	(1 << 1)
398c2ecf20Sopenharmony_ci	unsigned int	flags;
408c2ecf20Sopenharmony_ci	phys_addr_t	eoi;
418c2ecf20Sopenharmony_ci} clps711x_irqs[] = {
428c2ecf20Sopenharmony_ci	[1]	= { CLPS711X_FLAG_FIQ, CLPS711X_BLEOI, },
438c2ecf20Sopenharmony_ci	[3]	= { CLPS711X_FLAG_FIQ, CLPS711X_MCEOI, },
448c2ecf20Sopenharmony_ci	[4]	= { CLPS711X_FLAG_EN, CLPS711X_COEOI, },
458c2ecf20Sopenharmony_ci	[5]	= { CLPS711X_FLAG_EN, },
468c2ecf20Sopenharmony_ci	[6]	= { CLPS711X_FLAG_EN, },
478c2ecf20Sopenharmony_ci	[7]	= { CLPS711X_FLAG_EN, },
488c2ecf20Sopenharmony_ci	[8]	= { CLPS711X_FLAG_EN, CLPS711X_TC1EOI, },
498c2ecf20Sopenharmony_ci	[9]	= { CLPS711X_FLAG_EN, CLPS711X_TC2EOI, },
508c2ecf20Sopenharmony_ci	[10]	= { CLPS711X_FLAG_EN, CLPS711X_RTCEOI, },
518c2ecf20Sopenharmony_ci	[11]	= { CLPS711X_FLAG_EN, CLPS711X_TEOI, },
528c2ecf20Sopenharmony_ci	[12]	= { CLPS711X_FLAG_EN, },
538c2ecf20Sopenharmony_ci	[13]	= { CLPS711X_FLAG_EN, },
548c2ecf20Sopenharmony_ci	[14]	= { CLPS711X_FLAG_EN, CLPS711X_UMSEOI, },
558c2ecf20Sopenharmony_ci	[15]	= { CLPS711X_FLAG_EN, CLPS711X_SRXEOF, },
568c2ecf20Sopenharmony_ci	[16]	= { CLPS711X_FLAG_EN, CLPS711X_KBDEOI, },
578c2ecf20Sopenharmony_ci	[17]	= { CLPS711X_FLAG_EN, },
588c2ecf20Sopenharmony_ci	[18]	= { CLPS711X_FLAG_EN, },
598c2ecf20Sopenharmony_ci	[28]	= { CLPS711X_FLAG_EN, },
608c2ecf20Sopenharmony_ci	[29]	= { CLPS711X_FLAG_EN, },
618c2ecf20Sopenharmony_ci	[32]	= { CLPS711X_FLAG_FIQ, },
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic struct {
658c2ecf20Sopenharmony_ci	void __iomem		*base;
668c2ecf20Sopenharmony_ci	void __iomem		*intmr[3];
678c2ecf20Sopenharmony_ci	void __iomem		*intsr[3];
688c2ecf20Sopenharmony_ci	struct irq_domain	*domain;
698c2ecf20Sopenharmony_ci	struct irq_domain_ops	ops;
708c2ecf20Sopenharmony_ci} *clps711x_intc;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic asmlinkage void __exception_irq_entry clps711x_irqh(struct pt_regs *regs)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	u32 irqstat;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	do {
778c2ecf20Sopenharmony_ci		irqstat = readw_relaxed(clps711x_intc->intmr[0]) &
788c2ecf20Sopenharmony_ci			  readw_relaxed(clps711x_intc->intsr[0]);
798c2ecf20Sopenharmony_ci		if (irqstat)
808c2ecf20Sopenharmony_ci			handle_domain_irq(clps711x_intc->domain,
818c2ecf20Sopenharmony_ci					  fls(irqstat) - 1, regs);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci		irqstat = readw_relaxed(clps711x_intc->intmr[1]) &
848c2ecf20Sopenharmony_ci			  readw_relaxed(clps711x_intc->intsr[1]);
858c2ecf20Sopenharmony_ci		if (irqstat)
868c2ecf20Sopenharmony_ci			handle_domain_irq(clps711x_intc->domain,
878c2ecf20Sopenharmony_ci					  fls(irqstat) - 1 + 16, regs);
888c2ecf20Sopenharmony_ci	} while (irqstat);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void clps711x_intc_eoi(struct irq_data *d)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	irq_hw_number_t hwirq = irqd_to_hwirq(d);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hwirq].eoi);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void clps711x_intc_mask(struct irq_data *d)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	irq_hw_number_t hwirq = irqd_to_hwirq(d);
1018c2ecf20Sopenharmony_ci	void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
1028c2ecf20Sopenharmony_ci	u32 tmp;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	tmp = readl_relaxed(intmr);
1058c2ecf20Sopenharmony_ci	tmp &= ~(1 << (hwirq % 16));
1068c2ecf20Sopenharmony_ci	writel_relaxed(tmp, intmr);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic void clps711x_intc_unmask(struct irq_data *d)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	irq_hw_number_t hwirq = irqd_to_hwirq(d);
1128c2ecf20Sopenharmony_ci	void __iomem *intmr = clps711x_intc->intmr[hwirq / 16];
1138c2ecf20Sopenharmony_ci	u32 tmp;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	tmp = readl_relaxed(intmr);
1168c2ecf20Sopenharmony_ci	tmp |= 1 << (hwirq % 16);
1178c2ecf20Sopenharmony_ci	writel_relaxed(tmp, intmr);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic struct irq_chip clps711x_intc_chip = {
1218c2ecf20Sopenharmony_ci	.name		= "clps711x-intc",
1228c2ecf20Sopenharmony_ci	.irq_eoi	= clps711x_intc_eoi,
1238c2ecf20Sopenharmony_ci	.irq_mask	= clps711x_intc_mask,
1248c2ecf20Sopenharmony_ci	.irq_unmask	= clps711x_intc_unmask,
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
1288c2ecf20Sopenharmony_ci					irq_hw_number_t hw)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	irq_flow_handler_t handler = handle_level_irq;
1318c2ecf20Sopenharmony_ci	unsigned int flags = 0;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (!clps711x_irqs[hw].flags)
1348c2ecf20Sopenharmony_ci		return 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) {
1378c2ecf20Sopenharmony_ci		handler = handle_bad_irq;
1388c2ecf20Sopenharmony_ci		flags |= IRQ_NOAUTOEN;
1398c2ecf20Sopenharmony_ci	} else if (clps711x_irqs[hw].eoi) {
1408c2ecf20Sopenharmony_ci		handler = handle_fasteoi_irq;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* Clear down pending interrupt */
1448c2ecf20Sopenharmony_ci	if (clps711x_irqs[hw].eoi)
1458c2ecf20Sopenharmony_ci		writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hw].eoi);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler);
1488c2ecf20Sopenharmony_ci	irq_modify_status(virq, IRQ_NOPROBE, flags);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return 0;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int __init _clps711x_intc_init(struct device_node *np,
1548c2ecf20Sopenharmony_ci				      phys_addr_t base, resource_size_t size)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	int err;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	clps711x_intc = kzalloc(sizeof(*clps711x_intc), GFP_KERNEL);
1598c2ecf20Sopenharmony_ci	if (!clps711x_intc)
1608c2ecf20Sopenharmony_ci		return -ENOMEM;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	clps711x_intc->base = ioremap(base, size);
1638c2ecf20Sopenharmony_ci	if (!clps711x_intc->base) {
1648c2ecf20Sopenharmony_ci		err = -ENOMEM;
1658c2ecf20Sopenharmony_ci		goto out_kfree;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	clps711x_intc->intsr[0] = clps711x_intc->base + CLPS711X_INTSR1;
1698c2ecf20Sopenharmony_ci	clps711x_intc->intmr[0] = clps711x_intc->base + CLPS711X_INTMR1;
1708c2ecf20Sopenharmony_ci	clps711x_intc->intsr[1] = clps711x_intc->base + CLPS711X_INTSR2;
1718c2ecf20Sopenharmony_ci	clps711x_intc->intmr[1] = clps711x_intc->base + CLPS711X_INTMR2;
1728c2ecf20Sopenharmony_ci	clps711x_intc->intsr[2] = clps711x_intc->base + CLPS711X_INTSR3;
1738c2ecf20Sopenharmony_ci	clps711x_intc->intmr[2] = clps711x_intc->base + CLPS711X_INTMR3;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* Mask all interrupts */
1768c2ecf20Sopenharmony_ci	writel_relaxed(0, clps711x_intc->intmr[0]);
1778c2ecf20Sopenharmony_ci	writel_relaxed(0, clps711x_intc->intmr[1]);
1788c2ecf20Sopenharmony_ci	writel_relaxed(0, clps711x_intc->intmr[2]);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	err = irq_alloc_descs(-1, 0, ARRAY_SIZE(clps711x_irqs), numa_node_id());
1818c2ecf20Sopenharmony_ci	if (err < 0)
1828c2ecf20Sopenharmony_ci		goto out_iounmap;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	clps711x_intc->ops.map = clps711x_intc_irq_map;
1858c2ecf20Sopenharmony_ci	clps711x_intc->ops.xlate = irq_domain_xlate_onecell;
1868c2ecf20Sopenharmony_ci	clps711x_intc->domain =
1878c2ecf20Sopenharmony_ci		irq_domain_add_legacy(np, ARRAY_SIZE(clps711x_irqs),
1888c2ecf20Sopenharmony_ci				      0, 0, &clps711x_intc->ops, NULL);
1898c2ecf20Sopenharmony_ci	if (!clps711x_intc->domain) {
1908c2ecf20Sopenharmony_ci		err = -ENOMEM;
1918c2ecf20Sopenharmony_ci		goto out_irqfree;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	irq_set_default_host(clps711x_intc->domain);
1958c2ecf20Sopenharmony_ci	set_handle_irq(clps711x_irqh);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci#ifdef CONFIG_FIQ
1988c2ecf20Sopenharmony_ci	init_FIQ(0);
1998c2ecf20Sopenharmony_ci#endif
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return 0;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciout_irqfree:
2048c2ecf20Sopenharmony_ci	irq_free_descs(0, ARRAY_SIZE(clps711x_irqs));
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ciout_iounmap:
2078c2ecf20Sopenharmony_ci	iounmap(clps711x_intc->base);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ciout_kfree:
2108c2ecf20Sopenharmony_ci	kfree(clps711x_intc);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	return err;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_civoid __init clps711x_intc_init(phys_addr_t base, resource_size_t size)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	BUG_ON(_clps711x_intc_init(NULL, base, size));
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci#ifdef CONFIG_IRQCHIP
2218c2ecf20Sopenharmony_cistatic int __init clps711x_intc_init_dt(struct device_node *np,
2228c2ecf20Sopenharmony_ci					struct device_node *parent)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct resource res;
2258c2ecf20Sopenharmony_ci	int err;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	err = of_address_to_resource(np, 0, &res);
2288c2ecf20Sopenharmony_ci	if (err)
2298c2ecf20Sopenharmony_ci		return err;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	return _clps711x_intc_init(np, res.start, resource_size(&res));
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(clps711x, "cirrus,ep7209-intc", clps711x_intc_init_dt);
2348c2ecf20Sopenharmony_ci#endif
235