mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
4.6 KiB
204 lines
4.6 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* |
|
* Copyright (C) 2009 Gabor Juhos <[email protected]> |
|
* Copyright (C) 2013 John Crispin <[email protected]> |
|
*/ |
|
|
|
#include <linux/io.h> |
|
#include <linux/bitops.h> |
|
#include <linux/of_platform.h> |
|
#include <linux/of_address.h> |
|
#include <linux/of_irq.h> |
|
#include <linux/irqdomain.h> |
|
#include <linux/interrupt.h> |
|
|
|
#include <asm/irq_cpu.h> |
|
#include <asm/mipsregs.h> |
|
|
|
#include "common.h" |
|
|
|
#define INTC_INT_GLOBAL BIT(31) |
|
|
|
#define RALINK_CPU_IRQ_INTC (MIPS_CPU_IRQ_BASE + 2) |
|
#define RALINK_CPU_IRQ_PCI (MIPS_CPU_IRQ_BASE + 4) |
|
#define RALINK_CPU_IRQ_FE (MIPS_CPU_IRQ_BASE + 5) |
|
#define RALINK_CPU_IRQ_WIFI (MIPS_CPU_IRQ_BASE + 6) |
|
#define RALINK_CPU_IRQ_COUNTER (MIPS_CPU_IRQ_BASE + 7) |
|
|
|
/* we have a cascade of 8 irqs */ |
|
#define RALINK_INTC_IRQ_BASE 8 |
|
|
|
/* we have 32 SoC irqs */ |
|
#define RALINK_INTC_IRQ_COUNT 32 |
|
|
|
#define RALINK_INTC_IRQ_PERFC (RALINK_INTC_IRQ_BASE + 9) |
|
|
|
enum rt_intc_regs_enum { |
|
INTC_REG_STATUS0 = 0, |
|
INTC_REG_STATUS1, |
|
INTC_REG_TYPE, |
|
INTC_REG_RAW_STATUS, |
|
INTC_REG_ENABLE, |
|
INTC_REG_DISABLE, |
|
}; |
|
|
|
static u32 rt_intc_regs[] = { |
|
[INTC_REG_STATUS0] = 0x00, |
|
[INTC_REG_STATUS1] = 0x04, |
|
[INTC_REG_TYPE] = 0x20, |
|
[INTC_REG_RAW_STATUS] = 0x30, |
|
[INTC_REG_ENABLE] = 0x34, |
|
[INTC_REG_DISABLE] = 0x38, |
|
}; |
|
|
|
static void __iomem *rt_intc_membase; |
|
|
|
static int rt_perfcount_irq; |
|
|
|
static inline void rt_intc_w32(u32 val, unsigned reg) |
|
{ |
|
__raw_writel(val, rt_intc_membase + rt_intc_regs[reg]); |
|
} |
|
|
|
static inline u32 rt_intc_r32(unsigned reg) |
|
{ |
|
return __raw_readl(rt_intc_membase + rt_intc_regs[reg]); |
|
} |
|
|
|
static void ralink_intc_irq_unmask(struct irq_data *d) |
|
{ |
|
rt_intc_w32(BIT(d->hwirq), INTC_REG_ENABLE); |
|
} |
|
|
|
static void ralink_intc_irq_mask(struct irq_data *d) |
|
{ |
|
rt_intc_w32(BIT(d->hwirq), INTC_REG_DISABLE); |
|
} |
|
|
|
static struct irq_chip ralink_intc_irq_chip = { |
|
.name = "INTC", |
|
.irq_unmask = ralink_intc_irq_unmask, |
|
.irq_mask = ralink_intc_irq_mask, |
|
.irq_mask_ack = ralink_intc_irq_mask, |
|
}; |
|
|
|
int get_c0_perfcount_int(void) |
|
{ |
|
return rt_perfcount_irq; |
|
} |
|
EXPORT_SYMBOL_GPL(get_c0_perfcount_int); |
|
|
|
unsigned int get_c0_compare_int(void) |
|
{ |
|
return CP0_LEGACY_COMPARE_IRQ; |
|
} |
|
|
|
static void ralink_intc_irq_handler(struct irq_desc *desc) |
|
{ |
|
u32 pending = rt_intc_r32(INTC_REG_STATUS0); |
|
|
|
if (pending) { |
|
struct irq_domain *domain = irq_desc_get_handler_data(desc); |
|
generic_handle_irq(irq_find_mapping(domain, __ffs(pending))); |
|
} else { |
|
spurious_interrupt(); |
|
} |
|
} |
|
|
|
asmlinkage void plat_irq_dispatch(void) |
|
{ |
|
unsigned long pending; |
|
|
|
pending = read_c0_status() & read_c0_cause() & ST0_IM; |
|
|
|
if (pending & STATUSF_IP7) |
|
do_IRQ(RALINK_CPU_IRQ_COUNTER); |
|
|
|
else if (pending & STATUSF_IP5) |
|
do_IRQ(RALINK_CPU_IRQ_FE); |
|
|
|
else if (pending & STATUSF_IP6) |
|
do_IRQ(RALINK_CPU_IRQ_WIFI); |
|
|
|
else if (pending & STATUSF_IP4) |
|
do_IRQ(RALINK_CPU_IRQ_PCI); |
|
|
|
else if (pending & STATUSF_IP2) |
|
do_IRQ(RALINK_CPU_IRQ_INTC); |
|
|
|
else |
|
spurious_interrupt(); |
|
} |
|
|
|
static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) |
|
{ |
|
irq_set_chip_and_handler(irq, &ralink_intc_irq_chip, handle_level_irq); |
|
|
|
return 0; |
|
} |
|
|
|
static const struct irq_domain_ops irq_domain_ops = { |
|
.xlate = irq_domain_xlate_onecell, |
|
.map = intc_map, |
|
}; |
|
|
|
static int __init intc_of_init(struct device_node *node, |
|
struct device_node *parent) |
|
{ |
|
struct resource res; |
|
struct irq_domain *domain; |
|
int irq; |
|
|
|
if (!of_property_read_u32_array(node, "ralink,intc-registers", |
|
rt_intc_regs, 6)) |
|
pr_info("intc: using register map from devicetree\n"); |
|
|
|
irq = irq_of_parse_and_map(node, 0); |
|
if (!irq) |
|
panic("Failed to get INTC IRQ"); |
|
|
|
if (of_address_to_resource(node, 0, &res)) |
|
panic("Failed to get intc memory range"); |
|
|
|
if (!request_mem_region(res.start, resource_size(&res), |
|
res.name)) |
|
pr_err("Failed to request intc memory"); |
|
|
|
rt_intc_membase = ioremap(res.start, |
|
resource_size(&res)); |
|
if (!rt_intc_membase) |
|
panic("Failed to remap intc memory"); |
|
|
|
/* disable all interrupts */ |
|
rt_intc_w32(~0, INTC_REG_DISABLE); |
|
|
|
/* route all INTC interrupts to MIPS HW0 interrupt */ |
|
rt_intc_w32(0, INTC_REG_TYPE); |
|
|
|
domain = irq_domain_add_legacy(node, RALINK_INTC_IRQ_COUNT, |
|
RALINK_INTC_IRQ_BASE, 0, &irq_domain_ops, NULL); |
|
if (!domain) |
|
panic("Failed to add irqdomain"); |
|
|
|
rt_intc_w32(INTC_INT_GLOBAL, INTC_REG_ENABLE); |
|
|
|
irq_set_chained_handler_and_data(irq, ralink_intc_irq_handler, domain); |
|
|
|
/* tell the kernel which irq is used for performance monitoring */ |
|
rt_perfcount_irq = irq_create_mapping(domain, 9); |
|
|
|
return 0; |
|
} |
|
|
|
static struct of_device_id __initdata of_irq_ids[] = { |
|
{ .compatible = "mti,cpu-interrupt-controller", .data = mips_cpu_irq_of_init }, |
|
{ .compatible = "ralink,rt2880-intc", .data = intc_of_init }, |
|
{}, |
|
}; |
|
|
|
void __init arch_init_irq(void) |
|
{ |
|
of_irq_init(of_irq_ids); |
|
} |
|
|
|
|