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