162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
562306a36Sopenharmony_ci * Copyright (C) 2013 John Crispin <john@phrozen.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/bitops.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/of_address.h>
1262306a36Sopenharmony_ci#include <linux/of_irq.h>
1362306a36Sopenharmony_ci#include <linux/irqdomain.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <asm/irq_cpu.h>
1762306a36Sopenharmony_ci#include <asm/mipsregs.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "common.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define INTC_INT_GLOBAL		BIT(31)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define RALINK_CPU_IRQ_INTC	(MIPS_CPU_IRQ_BASE + 2)
2462306a36Sopenharmony_ci#define RALINK_CPU_IRQ_PCI	(MIPS_CPU_IRQ_BASE + 4)
2562306a36Sopenharmony_ci#define RALINK_CPU_IRQ_FE	(MIPS_CPU_IRQ_BASE + 5)
2662306a36Sopenharmony_ci#define RALINK_CPU_IRQ_WIFI	(MIPS_CPU_IRQ_BASE + 6)
2762306a36Sopenharmony_ci#define RALINK_CPU_IRQ_COUNTER	(MIPS_CPU_IRQ_BASE + 7)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* we have a cascade of 8 irqs */
3062306a36Sopenharmony_ci#define RALINK_INTC_IRQ_BASE	8
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* we have 32 SoC irqs */
3362306a36Sopenharmony_ci#define RALINK_INTC_IRQ_COUNT	32
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define RALINK_INTC_IRQ_PERFC   (RALINK_INTC_IRQ_BASE + 9)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cienum rt_intc_regs_enum {
3862306a36Sopenharmony_ci	INTC_REG_STATUS0 = 0,
3962306a36Sopenharmony_ci	INTC_REG_STATUS1,
4062306a36Sopenharmony_ci	INTC_REG_TYPE,
4162306a36Sopenharmony_ci	INTC_REG_RAW_STATUS,
4262306a36Sopenharmony_ci	INTC_REG_ENABLE,
4362306a36Sopenharmony_ci	INTC_REG_DISABLE,
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic u32 rt_intc_regs[] = {
4762306a36Sopenharmony_ci	[INTC_REG_STATUS0] = 0x00,
4862306a36Sopenharmony_ci	[INTC_REG_STATUS1] = 0x04,
4962306a36Sopenharmony_ci	[INTC_REG_TYPE] = 0x20,
5062306a36Sopenharmony_ci	[INTC_REG_RAW_STATUS] = 0x30,
5162306a36Sopenharmony_ci	[INTC_REG_ENABLE] = 0x34,
5262306a36Sopenharmony_ci	[INTC_REG_DISABLE] = 0x38,
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void __iomem *rt_intc_membase;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic int rt_perfcount_irq;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic inline void rt_intc_w32(u32 val, unsigned reg)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	__raw_writel(val, rt_intc_membase + rt_intc_regs[reg]);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic inline u32 rt_intc_r32(unsigned reg)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	return __raw_readl(rt_intc_membase + rt_intc_regs[reg]);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void ralink_intc_irq_unmask(struct irq_data *d)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	rt_intc_w32(BIT(d->hwirq), INTC_REG_ENABLE);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void ralink_intc_irq_mask(struct irq_data *d)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	rt_intc_w32(BIT(d->hwirq), INTC_REG_DISABLE);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic struct irq_chip ralink_intc_irq_chip = {
8062306a36Sopenharmony_ci	.name		= "INTC",
8162306a36Sopenharmony_ci	.irq_unmask	= ralink_intc_irq_unmask,
8262306a36Sopenharmony_ci	.irq_mask	= ralink_intc_irq_mask,
8362306a36Sopenharmony_ci	.irq_mask_ack	= ralink_intc_irq_mask,
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint get_c0_perfcount_int(void)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	return rt_perfcount_irq;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(get_c0_perfcount_int);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ciunsigned int get_c0_compare_int(void)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	return CP0_LEGACY_COMPARE_IRQ;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void ralink_intc_irq_handler(struct irq_desc *desc)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	u32 pending = rt_intc_r32(INTC_REG_STATUS0);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (pending) {
10262306a36Sopenharmony_ci		struct irq_domain *domain = irq_desc_get_handler_data(desc);
10362306a36Sopenharmony_ci		generic_handle_domain_irq(domain, __ffs(pending));
10462306a36Sopenharmony_ci	} else {
10562306a36Sopenharmony_ci		spurious_interrupt();
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ciasmlinkage void plat_irq_dispatch(void)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	unsigned long pending;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	pending = read_c0_status() & read_c0_cause() & ST0_IM;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (pending & STATUSF_IP7)
11662306a36Sopenharmony_ci		do_IRQ(RALINK_CPU_IRQ_COUNTER);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	else if (pending & STATUSF_IP5)
11962306a36Sopenharmony_ci		do_IRQ(RALINK_CPU_IRQ_FE);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	else if (pending & STATUSF_IP6)
12262306a36Sopenharmony_ci		do_IRQ(RALINK_CPU_IRQ_WIFI);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	else if (pending & STATUSF_IP4)
12562306a36Sopenharmony_ci		do_IRQ(RALINK_CPU_IRQ_PCI);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	else if (pending & STATUSF_IP2)
12862306a36Sopenharmony_ci		do_IRQ(RALINK_CPU_IRQ_INTC);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	else
13162306a36Sopenharmony_ci		spurious_interrupt();
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	irq_set_chip_and_handler(irq, &ralink_intc_irq_chip, handle_level_irq);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic const struct irq_domain_ops irq_domain_ops = {
14262306a36Sopenharmony_ci	.xlate = irq_domain_xlate_onecell,
14362306a36Sopenharmony_ci	.map = intc_map,
14462306a36Sopenharmony_ci};
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int __init intc_of_init(struct device_node *node,
14762306a36Sopenharmony_ci			       struct device_node *parent)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct resource res;
15062306a36Sopenharmony_ci	struct irq_domain *domain;
15162306a36Sopenharmony_ci	int irq;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (!of_property_read_u32_array(node, "ralink,intc-registers",
15462306a36Sopenharmony_ci					rt_intc_regs, 6))
15562306a36Sopenharmony_ci		pr_info("intc: using register map from devicetree\n");
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	irq = irq_of_parse_and_map(node, 0);
15862306a36Sopenharmony_ci	if (!irq)
15962306a36Sopenharmony_ci		panic("Failed to get INTC IRQ");
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (of_address_to_resource(node, 0, &res))
16262306a36Sopenharmony_ci		panic("Failed to get intc memory range");
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (!request_mem_region(res.start, resource_size(&res),
16562306a36Sopenharmony_ci				res.name))
16662306a36Sopenharmony_ci		pr_err("Failed to request intc memory");
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	rt_intc_membase = ioremap(res.start,
16962306a36Sopenharmony_ci					resource_size(&res));
17062306a36Sopenharmony_ci	if (!rt_intc_membase)
17162306a36Sopenharmony_ci		panic("Failed to remap intc memory");
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* disable all interrupts */
17462306a36Sopenharmony_ci	rt_intc_w32(~0, INTC_REG_DISABLE);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* route all INTC interrupts to MIPS HW0 interrupt */
17762306a36Sopenharmony_ci	rt_intc_w32(0, INTC_REG_TYPE);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	domain = irq_domain_add_legacy(node, RALINK_INTC_IRQ_COUNT,
18062306a36Sopenharmony_ci			RALINK_INTC_IRQ_BASE, 0, &irq_domain_ops, NULL);
18162306a36Sopenharmony_ci	if (!domain)
18262306a36Sopenharmony_ci		panic("Failed to add irqdomain");
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	rt_intc_w32(INTC_INT_GLOBAL, INTC_REG_ENABLE);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	irq_set_chained_handler_and_data(irq, ralink_intc_irq_handler, domain);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* tell the kernel which irq is used for performance monitoring */
18962306a36Sopenharmony_ci	rt_perfcount_irq = irq_create_mapping(domain, 9);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return 0;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic struct of_device_id __initdata of_irq_ids[] = {
19562306a36Sopenharmony_ci	{ .compatible = "mti,cpu-interrupt-controller", .data = mips_cpu_irq_of_init },
19662306a36Sopenharmony_ci	{ .compatible = "ralink,rt2880-intc", .data = intc_of_init },
19762306a36Sopenharmony_ci	{},
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_civoid __init arch_init_irq(void)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	of_irq_init(of_irq_ids);
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
205