162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/arch/arm/mach-pxa/irq.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Generic PXA IRQ handling
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Author:	Nicolas Pitre
862306a36Sopenharmony_ci *  Created:	Jun 15, 2001
962306a36Sopenharmony_ci *  Copyright:	MontaVista Software Inc.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/bitops.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/syscore_ops.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <linux/irq.h>
1862306a36Sopenharmony_ci#include <linux/of_address.h>
1962306a36Sopenharmony_ci#include <linux/of_irq.h>
2062306a36Sopenharmony_ci#include <linux/soc/pxa/cpu.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <asm/exception.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "irqs.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "generic.h"
2762306a36Sopenharmony_ci#include "pxa-regs.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define ICIP			(0x000)
3062306a36Sopenharmony_ci#define ICMR			(0x004)
3162306a36Sopenharmony_ci#define ICLR			(0x008)
3262306a36Sopenharmony_ci#define ICFR			(0x00c)
3362306a36Sopenharmony_ci#define ICPR			(0x010)
3462306a36Sopenharmony_ci#define ICCR			(0x014)
3562306a36Sopenharmony_ci#define ICHP			(0x018)
3662306a36Sopenharmony_ci#define IPR(i)			(((i) < 32) ? (0x01c + ((i) << 2)) :		\
3762306a36Sopenharmony_ci				((i) < 64) ? (0x0b0 + (((i) - 32) << 2)) :	\
3862306a36Sopenharmony_ci				      (0x144 + (((i) - 64) << 2)))
3962306a36Sopenharmony_ci#define ICHP_VAL_IRQ		(1 << 31)
4062306a36Sopenharmony_ci#define ICHP_IRQ(i)		(((i) >> 16) & 0x7fff)
4162306a36Sopenharmony_ci#define IPR_VALID		(1 << 31)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define MAX_INTERNAL_IRQS	128
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * This is for peripheral IRQs internal to the PXA chip.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void __iomem *pxa_irq_base;
5062306a36Sopenharmony_cistatic int pxa_internal_irq_nr;
5162306a36Sopenharmony_cistatic bool cpu_has_ipr;
5262306a36Sopenharmony_cistatic struct irq_domain *pxa_irq_domain;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic inline void __iomem *irq_base(int i)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	static unsigned long phys_base_offset[] = {
5762306a36Sopenharmony_ci		0x0,
5862306a36Sopenharmony_ci		0x9c,
5962306a36Sopenharmony_ci		0x130,
6062306a36Sopenharmony_ci	};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return pxa_irq_base + phys_base_offset[i];
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_civoid pxa_mask_irq(struct irq_data *d)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	void __iomem *base = irq_data_get_irq_chip_data(d);
6862306a36Sopenharmony_ci	irq_hw_number_t irq = irqd_to_hwirq(d);
6962306a36Sopenharmony_ci	uint32_t icmr = __raw_readl(base + ICMR);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	icmr &= ~BIT(irq & 0x1f);
7262306a36Sopenharmony_ci	__raw_writel(icmr, base + ICMR);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_civoid pxa_unmask_irq(struct irq_data *d)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	void __iomem *base = irq_data_get_irq_chip_data(d);
7862306a36Sopenharmony_ci	irq_hw_number_t irq = irqd_to_hwirq(d);
7962306a36Sopenharmony_ci	uint32_t icmr = __raw_readl(base + ICMR);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	icmr |= BIT(irq & 0x1f);
8262306a36Sopenharmony_ci	__raw_writel(icmr, base + ICMR);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic struct irq_chip pxa_internal_irq_chip = {
8662306a36Sopenharmony_ci	.name		= "SC",
8762306a36Sopenharmony_ci	.irq_ack	= pxa_mask_irq,
8862306a36Sopenharmony_ci	.irq_mask	= pxa_mask_irq,
8962306a36Sopenharmony_ci	.irq_unmask	= pxa_unmask_irq,
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ciasmlinkage void __exception_irq_entry icip_handle_irq(struct pt_regs *regs)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	uint32_t icip, icmr, mask;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	do {
9762306a36Sopenharmony_ci		icip = __raw_readl(pxa_irq_base + ICIP);
9862306a36Sopenharmony_ci		icmr = __raw_readl(pxa_irq_base + ICMR);
9962306a36Sopenharmony_ci		mask = icip & icmr;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		if (mask == 0)
10262306a36Sopenharmony_ci			break;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		handle_IRQ(PXA_IRQ(fls(mask) - 1), regs);
10562306a36Sopenharmony_ci	} while (1);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ciasmlinkage void __exception_irq_entry ichp_handle_irq(struct pt_regs *regs)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	uint32_t ichp;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	do {
11362306a36Sopenharmony_ci		__asm__ __volatile__("mrc p6, 0, %0, c5, c0, 0\n": "=r"(ichp));
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		if ((ichp & ICHP_VAL_IRQ) == 0)
11662306a36Sopenharmony_ci			break;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		handle_IRQ(PXA_IRQ(ICHP_IRQ(ichp)), regs);
11962306a36Sopenharmony_ci	} while (1);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic int pxa_irq_map(struct irq_domain *h, unsigned int virq,
12362306a36Sopenharmony_ci		       irq_hw_number_t hw)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	void __iomem *base = irq_base(hw / 32);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* initialize interrupt priority */
12862306a36Sopenharmony_ci	if (cpu_has_ipr)
12962306a36Sopenharmony_ci		__raw_writel(hw | IPR_VALID, pxa_irq_base + IPR(hw));
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	irq_set_chip_and_handler(virq, &pxa_internal_irq_chip,
13262306a36Sopenharmony_ci				 handle_level_irq);
13362306a36Sopenharmony_ci	irq_set_chip_data(virq, base);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return 0;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic const struct irq_domain_ops pxa_irq_ops = {
13962306a36Sopenharmony_ci	.map    = pxa_irq_map,
14062306a36Sopenharmony_ci	.xlate  = irq_domain_xlate_onecell,
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic __init void
14462306a36Sopenharmony_cipxa_init_irq_common(struct device_node *node, int irq_nr,
14562306a36Sopenharmony_ci		    int (*fn)(struct irq_data *, unsigned int))
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	int n;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	pxa_internal_irq_nr = irq_nr;
15062306a36Sopenharmony_ci	pxa_irq_domain = irq_domain_add_legacy(node, irq_nr,
15162306a36Sopenharmony_ci					       PXA_IRQ(0), 0,
15262306a36Sopenharmony_ci					       &pxa_irq_ops, NULL);
15362306a36Sopenharmony_ci	if (!pxa_irq_domain)
15462306a36Sopenharmony_ci		panic("Unable to add PXA IRQ domain\n");
15562306a36Sopenharmony_ci	irq_set_default_host(pxa_irq_domain);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	for (n = 0; n < irq_nr; n += 32) {
15862306a36Sopenharmony_ci		void __iomem *base = irq_base(n >> 5);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		__raw_writel(0, base + ICMR);	/* disable all IRQs */
16162306a36Sopenharmony_ci		__raw_writel(0, base + ICLR);	/* all IRQs are IRQ, not FIQ */
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci	/* only unmasked interrupts kick us out of idle */
16462306a36Sopenharmony_ci	__raw_writel(1, irq_base(0) + ICCR);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	pxa_internal_irq_chip.irq_set_wake = fn;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_civoid __init pxa_init_irq(int irq_nr, int (*fn)(struct irq_data *, unsigned int))
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	BUG_ON(irq_nr > MAX_INTERNAL_IRQS);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	pxa_irq_base = io_p2v(0x40d00000);
17462306a36Sopenharmony_ci	cpu_has_ipr = !cpu_is_pxa25x();
17562306a36Sopenharmony_ci	pxa_init_irq_common(NULL, irq_nr, fn);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci#ifdef CONFIG_PM
17962306a36Sopenharmony_cistatic unsigned long saved_icmr[MAX_INTERNAL_IRQS/32];
18062306a36Sopenharmony_cistatic unsigned long saved_ipr[MAX_INTERNAL_IRQS];
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic int pxa_irq_suspend(void)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	int i;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	for (i = 0; i < DIV_ROUND_UP(pxa_internal_irq_nr, 32); i++) {
18762306a36Sopenharmony_ci		void __iomem *base = irq_base(i);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		saved_icmr[i] = __raw_readl(base + ICMR);
19062306a36Sopenharmony_ci		__raw_writel(0, base + ICMR);
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (cpu_has_ipr) {
19462306a36Sopenharmony_ci		for (i = 0; i < pxa_internal_irq_nr; i++)
19562306a36Sopenharmony_ci			saved_ipr[i] = __raw_readl(pxa_irq_base + IPR(i));
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void pxa_irq_resume(void)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int i;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	for (i = 0; i < DIV_ROUND_UP(pxa_internal_irq_nr, 32); i++) {
20662306a36Sopenharmony_ci		void __iomem *base = irq_base(i);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		__raw_writel(saved_icmr[i], base + ICMR);
20962306a36Sopenharmony_ci		__raw_writel(0, base + ICLR);
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (cpu_has_ipr)
21362306a36Sopenharmony_ci		for (i = 0; i < pxa_internal_irq_nr; i++)
21462306a36Sopenharmony_ci			__raw_writel(saved_ipr[i], pxa_irq_base + IPR(i));
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	__raw_writel(1, pxa_irq_base + ICCR);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci#else
21962306a36Sopenharmony_ci#define pxa_irq_suspend		NULL
22062306a36Sopenharmony_ci#define pxa_irq_resume		NULL
22162306a36Sopenharmony_ci#endif
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistruct syscore_ops pxa_irq_syscore_ops = {
22462306a36Sopenharmony_ci	.suspend	= pxa_irq_suspend,
22562306a36Sopenharmony_ci	.resume		= pxa_irq_resume,
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci#ifdef CONFIG_OF
22962306a36Sopenharmony_cistatic const struct of_device_id intc_ids[] __initconst = {
23062306a36Sopenharmony_ci	{ .compatible = "marvell,pxa-intc", },
23162306a36Sopenharmony_ci	{}
23262306a36Sopenharmony_ci};
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_civoid __init pxa_dt_irq_init(int (*fn)(struct irq_data *, unsigned int))
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct device_node *node;
23762306a36Sopenharmony_ci	struct resource res;
23862306a36Sopenharmony_ci	int ret;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	node = of_find_matching_node(NULL, intc_ids);
24162306a36Sopenharmony_ci	if (!node) {
24262306a36Sopenharmony_ci		pr_err("Failed to find interrupt controller in arch-pxa\n");
24362306a36Sopenharmony_ci		return;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	ret = of_property_read_u32(node, "marvell,intc-nr-irqs",
24762306a36Sopenharmony_ci				   &pxa_internal_irq_nr);
24862306a36Sopenharmony_ci	if (ret) {
24962306a36Sopenharmony_ci		pr_err("Not found marvell,intc-nr-irqs property\n");
25062306a36Sopenharmony_ci		return;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	ret = of_address_to_resource(node, 0, &res);
25462306a36Sopenharmony_ci	if (ret < 0) {
25562306a36Sopenharmony_ci		pr_err("No registers defined for node\n");
25662306a36Sopenharmony_ci		return;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci	pxa_irq_base = io_p2v(res.start);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	cpu_has_ipr = of_property_read_bool(node, "marvell,intc-priority");
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ret = irq_alloc_descs(-1, 0, pxa_internal_irq_nr, 0);
26362306a36Sopenharmony_ci	if (ret < 0) {
26462306a36Sopenharmony_ci		pr_err("Failed to allocate IRQ numbers\n");
26562306a36Sopenharmony_ci		return;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	pxa_init_irq_common(node, pxa_internal_irq_nr, fn);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci#endif /* CONFIG_OF */
271