xref: /kernel/linux/linux-5.10/arch/mips/bcm63xx/irq.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/irq.h>
148c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
158c2ecf20Sopenharmony_ci#include <asm/irq_cpu.h>
168c2ecf20Sopenharmony_ci#include <asm/mipsregs.h>
178c2ecf20Sopenharmony_ci#include <bcm63xx_cpu.h>
188c2ecf20Sopenharmony_ci#include <bcm63xx_regs.h>
198c2ecf20Sopenharmony_ci#include <bcm63xx_io.h>
208c2ecf20Sopenharmony_ci#include <bcm63xx_irq.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(ipic_lock);
248c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(epic_lock);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic u32 irq_stat_addr[2];
278c2ecf20Sopenharmony_cistatic u32 irq_mask_addr[2];
288c2ecf20Sopenharmony_cistatic void (*dispatch_internal)(int cpu);
298c2ecf20Sopenharmony_cistatic int is_ext_irq_cascaded;
308c2ecf20Sopenharmony_cistatic unsigned int ext_irq_count;
318c2ecf20Sopenharmony_cistatic unsigned int ext_irq_start, ext_irq_end;
328c2ecf20Sopenharmony_cistatic unsigned int ext_irq_cfg_reg1, ext_irq_cfg_reg2;
338c2ecf20Sopenharmony_cistatic void (*internal_irq_mask)(struct irq_data *d);
348c2ecf20Sopenharmony_cistatic void (*internal_irq_unmask)(struct irq_data *d, const struct cpumask *m);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic inline u32 get_ext_irq_perf_reg(int irq)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	if (irq < 4)
408c2ecf20Sopenharmony_ci		return ext_irq_cfg_reg1;
418c2ecf20Sopenharmony_ci	return ext_irq_cfg_reg2;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic inline void handle_internal(int intbit)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	if (is_ext_irq_cascaded &&
478c2ecf20Sopenharmony_ci	    intbit >= ext_irq_start && intbit <= ext_irq_end)
488c2ecf20Sopenharmony_ci		do_IRQ(intbit - ext_irq_start + IRQ_EXTERNAL_BASE);
498c2ecf20Sopenharmony_ci	else
508c2ecf20Sopenharmony_ci		do_IRQ(intbit + IRQ_INTERNAL_BASE);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic inline int enable_irq_for_cpu(int cpu, struct irq_data *d,
548c2ecf20Sopenharmony_ci				     const struct cpumask *m)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	bool enable = cpu_online(cpu);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
598c2ecf20Sopenharmony_ci	if (m)
608c2ecf20Sopenharmony_ci		enable &= cpumask_test_cpu(cpu, m);
618c2ecf20Sopenharmony_ci	else if (irqd_affinity_was_set(d))
628c2ecf20Sopenharmony_ci		enable &= cpumask_test_cpu(cpu, irq_data_get_affinity_mask(d));
638c2ecf20Sopenharmony_ci#endif
648c2ecf20Sopenharmony_ci	return enable;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/*
688c2ecf20Sopenharmony_ci * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
698c2ecf20Sopenharmony_ci * prioritize any interrupt relatively to another. the static counter
708c2ecf20Sopenharmony_ci * will resume the loop where it ended the last time we left this
718c2ecf20Sopenharmony_ci * function.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define BUILD_IPIC_INTERNAL(width)					\
758c2ecf20Sopenharmony_civoid __dispatch_internal_##width(int cpu)				\
768c2ecf20Sopenharmony_ci{									\
778c2ecf20Sopenharmony_ci	u32 pending[width / 32];					\
788c2ecf20Sopenharmony_ci	unsigned int src, tgt;						\
798c2ecf20Sopenharmony_ci	bool irqs_pending = false;					\
808c2ecf20Sopenharmony_ci	static unsigned int i[2];					\
818c2ecf20Sopenharmony_ci	unsigned int *next = &i[cpu];					\
828c2ecf20Sopenharmony_ci	unsigned long flags;						\
838c2ecf20Sopenharmony_ci									\
848c2ecf20Sopenharmony_ci	/* read registers in reverse order */				\
858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipic_lock, flags);				\
868c2ecf20Sopenharmony_ci	for (src = 0, tgt = (width / 32); src < (width / 32); src++) {	\
878c2ecf20Sopenharmony_ci		u32 val;						\
888c2ecf20Sopenharmony_ci									\
898c2ecf20Sopenharmony_ci		val = bcm_readl(irq_stat_addr[cpu] + src * sizeof(u32)); \
908c2ecf20Sopenharmony_ci		val &= bcm_readl(irq_mask_addr[cpu] + src * sizeof(u32)); \
918c2ecf20Sopenharmony_ci		pending[--tgt] = val;					\
928c2ecf20Sopenharmony_ci									\
938c2ecf20Sopenharmony_ci		if (val)						\
948c2ecf20Sopenharmony_ci			irqs_pending = true;				\
958c2ecf20Sopenharmony_ci	}								\
968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipic_lock, flags);			\
978c2ecf20Sopenharmony_ci									\
988c2ecf20Sopenharmony_ci	if (!irqs_pending)						\
998c2ecf20Sopenharmony_ci		return;							\
1008c2ecf20Sopenharmony_ci									\
1018c2ecf20Sopenharmony_ci	while (1) {							\
1028c2ecf20Sopenharmony_ci		unsigned int to_call = *next;				\
1038c2ecf20Sopenharmony_ci									\
1048c2ecf20Sopenharmony_ci		*next = (*next + 1) & (width - 1);			\
1058c2ecf20Sopenharmony_ci		if (pending[to_call / 32] & (1 << (to_call & 0x1f))) {	\
1068c2ecf20Sopenharmony_ci			handle_internal(to_call);			\
1078c2ecf20Sopenharmony_ci			break;						\
1088c2ecf20Sopenharmony_ci		}							\
1098c2ecf20Sopenharmony_ci	}								\
1108c2ecf20Sopenharmony_ci}									\
1118c2ecf20Sopenharmony_ci									\
1128c2ecf20Sopenharmony_cistatic void __internal_irq_mask_##width(struct irq_data *d)		\
1138c2ecf20Sopenharmony_ci{									\
1148c2ecf20Sopenharmony_ci	u32 val;							\
1158c2ecf20Sopenharmony_ci	unsigned irq = d->irq - IRQ_INTERNAL_BASE;			\
1168c2ecf20Sopenharmony_ci	unsigned reg = (irq / 32) ^ (width/32 - 1);			\
1178c2ecf20Sopenharmony_ci	unsigned bit = irq & 0x1f;					\
1188c2ecf20Sopenharmony_ci	unsigned long flags;						\
1198c2ecf20Sopenharmony_ci	int cpu;							\
1208c2ecf20Sopenharmony_ci									\
1218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipic_lock, flags);				\
1228c2ecf20Sopenharmony_ci	for_each_present_cpu(cpu) {					\
1238c2ecf20Sopenharmony_ci		if (!irq_mask_addr[cpu])				\
1248c2ecf20Sopenharmony_ci			break;						\
1258c2ecf20Sopenharmony_ci									\
1268c2ecf20Sopenharmony_ci		val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\
1278c2ecf20Sopenharmony_ci		val &= ~(1 << bit);					\
1288c2ecf20Sopenharmony_ci		bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\
1298c2ecf20Sopenharmony_ci	}								\
1308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipic_lock, flags);			\
1318c2ecf20Sopenharmony_ci}									\
1328c2ecf20Sopenharmony_ci									\
1338c2ecf20Sopenharmony_cistatic void __internal_irq_unmask_##width(struct irq_data *d,		\
1348c2ecf20Sopenharmony_ci					  const struct cpumask *m)	\
1358c2ecf20Sopenharmony_ci{									\
1368c2ecf20Sopenharmony_ci	u32 val;							\
1378c2ecf20Sopenharmony_ci	unsigned irq = d->irq - IRQ_INTERNAL_BASE;			\
1388c2ecf20Sopenharmony_ci	unsigned reg = (irq / 32) ^ (width/32 - 1);			\
1398c2ecf20Sopenharmony_ci	unsigned bit = irq & 0x1f;					\
1408c2ecf20Sopenharmony_ci	unsigned long flags;						\
1418c2ecf20Sopenharmony_ci	int cpu;							\
1428c2ecf20Sopenharmony_ci									\
1438c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipic_lock, flags);				\
1448c2ecf20Sopenharmony_ci	for_each_present_cpu(cpu) {					\
1458c2ecf20Sopenharmony_ci		if (!irq_mask_addr[cpu])				\
1468c2ecf20Sopenharmony_ci			break;						\
1478c2ecf20Sopenharmony_ci									\
1488c2ecf20Sopenharmony_ci		val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\
1498c2ecf20Sopenharmony_ci		if (enable_irq_for_cpu(cpu, d, m))			\
1508c2ecf20Sopenharmony_ci			val |= (1 << bit);				\
1518c2ecf20Sopenharmony_ci		else							\
1528c2ecf20Sopenharmony_ci			val &= ~(1 << bit);				\
1538c2ecf20Sopenharmony_ci		bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\
1548c2ecf20Sopenharmony_ci	}								\
1558c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipic_lock, flags);			\
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ciBUILD_IPIC_INTERNAL(32);
1598c2ecf20Sopenharmony_ciBUILD_IPIC_INTERNAL(64);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ciasmlinkage void plat_irq_dispatch(void)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	u32 cause;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	do {
1668c2ecf20Sopenharmony_ci		cause = read_c0_cause() & read_c0_status() & ST0_IM;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		if (!cause)
1698c2ecf20Sopenharmony_ci			break;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		if (cause & CAUSEF_IP7)
1728c2ecf20Sopenharmony_ci			do_IRQ(7);
1738c2ecf20Sopenharmony_ci		if (cause & CAUSEF_IP0)
1748c2ecf20Sopenharmony_ci			do_IRQ(0);
1758c2ecf20Sopenharmony_ci		if (cause & CAUSEF_IP1)
1768c2ecf20Sopenharmony_ci			do_IRQ(1);
1778c2ecf20Sopenharmony_ci		if (cause & CAUSEF_IP2)
1788c2ecf20Sopenharmony_ci			dispatch_internal(0);
1798c2ecf20Sopenharmony_ci		if (is_ext_irq_cascaded) {
1808c2ecf20Sopenharmony_ci			if (cause & CAUSEF_IP3)
1818c2ecf20Sopenharmony_ci				dispatch_internal(1);
1828c2ecf20Sopenharmony_ci		} else {
1838c2ecf20Sopenharmony_ci			if (cause & CAUSEF_IP3)
1848c2ecf20Sopenharmony_ci				do_IRQ(IRQ_EXT_0);
1858c2ecf20Sopenharmony_ci			if (cause & CAUSEF_IP4)
1868c2ecf20Sopenharmony_ci				do_IRQ(IRQ_EXT_1);
1878c2ecf20Sopenharmony_ci			if (cause & CAUSEF_IP5)
1888c2ecf20Sopenharmony_ci				do_IRQ(IRQ_EXT_2);
1898c2ecf20Sopenharmony_ci			if (cause & CAUSEF_IP6)
1908c2ecf20Sopenharmony_ci				do_IRQ(IRQ_EXT_3);
1918c2ecf20Sopenharmony_ci		}
1928c2ecf20Sopenharmony_ci	} while (1);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/*
1968c2ecf20Sopenharmony_ci * internal IRQs operations: only mask/unmask on PERF irq mask
1978c2ecf20Sopenharmony_ci * register.
1988c2ecf20Sopenharmony_ci */
1998c2ecf20Sopenharmony_cistatic void bcm63xx_internal_irq_mask(struct irq_data *d)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	internal_irq_mask(d);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic void bcm63xx_internal_irq_unmask(struct irq_data *d)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	internal_irq_unmask(d, NULL);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/*
2108c2ecf20Sopenharmony_ci * external IRQs operations: mask/unmask and clear on PERF external
2118c2ecf20Sopenharmony_ci * irq control register.
2128c2ecf20Sopenharmony_ci */
2138c2ecf20Sopenharmony_cistatic void bcm63xx_external_irq_mask(struct irq_data *d)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
2168c2ecf20Sopenharmony_ci	u32 reg, regaddr;
2178c2ecf20Sopenharmony_ci	unsigned long flags;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	regaddr = get_ext_irq_perf_reg(irq);
2208c2ecf20Sopenharmony_ci	spin_lock_irqsave(&epic_lock, flags);
2218c2ecf20Sopenharmony_ci	reg = bcm_perf_readl(regaddr);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (BCMCPU_IS_6348())
2248c2ecf20Sopenharmony_ci		reg &= ~EXTIRQ_CFG_MASK_6348(irq % 4);
2258c2ecf20Sopenharmony_ci	else
2268c2ecf20Sopenharmony_ci		reg &= ~EXTIRQ_CFG_MASK(irq % 4);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	bcm_perf_writel(reg, regaddr);
2298c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&epic_lock, flags);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (is_ext_irq_cascaded)
2328c2ecf20Sopenharmony_ci		internal_irq_mask(irq_get_irq_data(irq + ext_irq_start));
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic void bcm63xx_external_irq_unmask(struct irq_data *d)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
2388c2ecf20Sopenharmony_ci	u32 reg, regaddr;
2398c2ecf20Sopenharmony_ci	unsigned long flags;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	regaddr = get_ext_irq_perf_reg(irq);
2428c2ecf20Sopenharmony_ci	spin_lock_irqsave(&epic_lock, flags);
2438c2ecf20Sopenharmony_ci	reg = bcm_perf_readl(regaddr);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (BCMCPU_IS_6348())
2468c2ecf20Sopenharmony_ci		reg |= EXTIRQ_CFG_MASK_6348(irq % 4);
2478c2ecf20Sopenharmony_ci	else
2488c2ecf20Sopenharmony_ci		reg |= EXTIRQ_CFG_MASK(irq % 4);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	bcm_perf_writel(reg, regaddr);
2518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&epic_lock, flags);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (is_ext_irq_cascaded)
2548c2ecf20Sopenharmony_ci		internal_irq_unmask(irq_get_irq_data(irq + ext_irq_start),
2558c2ecf20Sopenharmony_ci				    NULL);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void bcm63xx_external_irq_clear(struct irq_data *d)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
2618c2ecf20Sopenharmony_ci	u32 reg, regaddr;
2628c2ecf20Sopenharmony_ci	unsigned long flags;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	regaddr = get_ext_irq_perf_reg(irq);
2658c2ecf20Sopenharmony_ci	spin_lock_irqsave(&epic_lock, flags);
2668c2ecf20Sopenharmony_ci	reg = bcm_perf_readl(regaddr);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (BCMCPU_IS_6348())
2698c2ecf20Sopenharmony_ci		reg |= EXTIRQ_CFG_CLEAR_6348(irq % 4);
2708c2ecf20Sopenharmony_ci	else
2718c2ecf20Sopenharmony_ci		reg |= EXTIRQ_CFG_CLEAR(irq % 4);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	bcm_perf_writel(reg, regaddr);
2748c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&epic_lock, flags);
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic int bcm63xx_external_irq_set_type(struct irq_data *d,
2788c2ecf20Sopenharmony_ci					 unsigned int flow_type)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
2818c2ecf20Sopenharmony_ci	u32 reg, regaddr;
2828c2ecf20Sopenharmony_ci	int levelsense, sense, bothedge;
2838c2ecf20Sopenharmony_ci	unsigned long flags;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	flow_type &= IRQ_TYPE_SENSE_MASK;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (flow_type == IRQ_TYPE_NONE)
2888c2ecf20Sopenharmony_ci		flow_type = IRQ_TYPE_LEVEL_LOW;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	levelsense = sense = bothedge = 0;
2918c2ecf20Sopenharmony_ci	switch (flow_type) {
2928c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_BOTH:
2938c2ecf20Sopenharmony_ci		bothedge = 1;
2948c2ecf20Sopenharmony_ci		break;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
2978c2ecf20Sopenharmony_ci		sense = 1;
2988c2ecf20Sopenharmony_ci		break;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
3018c2ecf20Sopenharmony_ci		break;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
3048c2ecf20Sopenharmony_ci		levelsense = 1;
3058c2ecf20Sopenharmony_ci		sense = 1;
3068c2ecf20Sopenharmony_ci		break;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
3098c2ecf20Sopenharmony_ci		levelsense = 1;
3108c2ecf20Sopenharmony_ci		break;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	default:
3138c2ecf20Sopenharmony_ci		pr_err("bogus flow type combination given !\n");
3148c2ecf20Sopenharmony_ci		return -EINVAL;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	regaddr = get_ext_irq_perf_reg(irq);
3188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&epic_lock, flags);
3198c2ecf20Sopenharmony_ci	reg = bcm_perf_readl(regaddr);
3208c2ecf20Sopenharmony_ci	irq %= 4;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	switch (bcm63xx_get_cpu_id()) {
3238c2ecf20Sopenharmony_ci	case BCM6348_CPU_ID:
3248c2ecf20Sopenharmony_ci		if (levelsense)
3258c2ecf20Sopenharmony_ci			reg |= EXTIRQ_CFG_LEVELSENSE_6348(irq);
3268c2ecf20Sopenharmony_ci		else
3278c2ecf20Sopenharmony_ci			reg &= ~EXTIRQ_CFG_LEVELSENSE_6348(irq);
3288c2ecf20Sopenharmony_ci		if (sense)
3298c2ecf20Sopenharmony_ci			reg |= EXTIRQ_CFG_SENSE_6348(irq);
3308c2ecf20Sopenharmony_ci		else
3318c2ecf20Sopenharmony_ci			reg &= ~EXTIRQ_CFG_SENSE_6348(irq);
3328c2ecf20Sopenharmony_ci		if (bothedge)
3338c2ecf20Sopenharmony_ci			reg |= EXTIRQ_CFG_BOTHEDGE_6348(irq);
3348c2ecf20Sopenharmony_ci		else
3358c2ecf20Sopenharmony_ci			reg &= ~EXTIRQ_CFG_BOTHEDGE_6348(irq);
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	case BCM3368_CPU_ID:
3398c2ecf20Sopenharmony_ci	case BCM6328_CPU_ID:
3408c2ecf20Sopenharmony_ci	case BCM6338_CPU_ID:
3418c2ecf20Sopenharmony_ci	case BCM6345_CPU_ID:
3428c2ecf20Sopenharmony_ci	case BCM6358_CPU_ID:
3438c2ecf20Sopenharmony_ci	case BCM6362_CPU_ID:
3448c2ecf20Sopenharmony_ci	case BCM6368_CPU_ID:
3458c2ecf20Sopenharmony_ci		if (levelsense)
3468c2ecf20Sopenharmony_ci			reg |= EXTIRQ_CFG_LEVELSENSE(irq);
3478c2ecf20Sopenharmony_ci		else
3488c2ecf20Sopenharmony_ci			reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
3498c2ecf20Sopenharmony_ci		if (sense)
3508c2ecf20Sopenharmony_ci			reg |= EXTIRQ_CFG_SENSE(irq);
3518c2ecf20Sopenharmony_ci		else
3528c2ecf20Sopenharmony_ci			reg &= ~EXTIRQ_CFG_SENSE(irq);
3538c2ecf20Sopenharmony_ci		if (bothedge)
3548c2ecf20Sopenharmony_ci			reg |= EXTIRQ_CFG_BOTHEDGE(irq);
3558c2ecf20Sopenharmony_ci		else
3568c2ecf20Sopenharmony_ci			reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
3578c2ecf20Sopenharmony_ci		break;
3588c2ecf20Sopenharmony_ci	default:
3598c2ecf20Sopenharmony_ci		BUG();
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	bcm_perf_writel(reg, regaddr);
3638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&epic_lock, flags);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	irqd_set_trigger_type(d, flow_type);
3668c2ecf20Sopenharmony_ci	if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
3678c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_level_irq);
3688c2ecf20Sopenharmony_ci	else
3698c2ecf20Sopenharmony_ci		irq_set_handler_locked(d, handle_edge_irq);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	return IRQ_SET_MASK_OK_NOCOPY;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
3758c2ecf20Sopenharmony_cistatic int bcm63xx_internal_set_affinity(struct irq_data *data,
3768c2ecf20Sopenharmony_ci					 const struct cpumask *dest,
3778c2ecf20Sopenharmony_ci					 bool force)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	if (!irqd_irq_disabled(data))
3808c2ecf20Sopenharmony_ci		internal_irq_unmask(data, dest);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	return 0;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci#endif
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic struct irq_chip bcm63xx_internal_irq_chip = {
3878c2ecf20Sopenharmony_ci	.name		= "bcm63xx_ipic",
3888c2ecf20Sopenharmony_ci	.irq_mask	= bcm63xx_internal_irq_mask,
3898c2ecf20Sopenharmony_ci	.irq_unmask	= bcm63xx_internal_irq_unmask,
3908c2ecf20Sopenharmony_ci};
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic struct irq_chip bcm63xx_external_irq_chip = {
3938c2ecf20Sopenharmony_ci	.name		= "bcm63xx_epic",
3948c2ecf20Sopenharmony_ci	.irq_ack	= bcm63xx_external_irq_clear,
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	.irq_mask	= bcm63xx_external_irq_mask,
3978c2ecf20Sopenharmony_ci	.irq_unmask	= bcm63xx_external_irq_unmask,
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	.irq_set_type	= bcm63xx_external_irq_set_type,
4008c2ecf20Sopenharmony_ci};
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic void bcm63xx_init_irq(void)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	int irq_bits;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	irq_stat_addr[0] = bcm63xx_regset_address(RSET_PERF);
4078c2ecf20Sopenharmony_ci	irq_mask_addr[0] = bcm63xx_regset_address(RSET_PERF);
4088c2ecf20Sopenharmony_ci	irq_stat_addr[1] = bcm63xx_regset_address(RSET_PERF);
4098c2ecf20Sopenharmony_ci	irq_mask_addr[1] = bcm63xx_regset_address(RSET_PERF);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	switch (bcm63xx_get_cpu_id()) {
4128c2ecf20Sopenharmony_ci	case BCM3368_CPU_ID:
4138c2ecf20Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_3368_REG;
4148c2ecf20Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_3368_REG;
4158c2ecf20Sopenharmony_ci		irq_stat_addr[1] = 0;
4168c2ecf20Sopenharmony_ci		irq_mask_addr[1] = 0;
4178c2ecf20Sopenharmony_ci		irq_bits = 32;
4188c2ecf20Sopenharmony_ci		ext_irq_count = 4;
4198c2ecf20Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_3368;
4208c2ecf20Sopenharmony_ci		break;
4218c2ecf20Sopenharmony_ci	case BCM6328_CPU_ID:
4228c2ecf20Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6328_REG(0);
4238c2ecf20Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6328_REG(0);
4248c2ecf20Sopenharmony_ci		irq_stat_addr[1] += PERF_IRQSTAT_6328_REG(1);
4258c2ecf20Sopenharmony_ci		irq_mask_addr[1] += PERF_IRQMASK_6328_REG(1);
4268c2ecf20Sopenharmony_ci		irq_bits = 64;
4278c2ecf20Sopenharmony_ci		ext_irq_count = 4;
4288c2ecf20Sopenharmony_ci		is_ext_irq_cascaded = 1;
4298c2ecf20Sopenharmony_ci		ext_irq_start = BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE;
4308c2ecf20Sopenharmony_ci		ext_irq_end = BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE;
4318c2ecf20Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6328;
4328c2ecf20Sopenharmony_ci		break;
4338c2ecf20Sopenharmony_ci	case BCM6338_CPU_ID:
4348c2ecf20Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6338_REG;
4358c2ecf20Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6338_REG;
4368c2ecf20Sopenharmony_ci		irq_stat_addr[1] = 0;
4378c2ecf20Sopenharmony_ci		irq_mask_addr[1] = 0;
4388c2ecf20Sopenharmony_ci		irq_bits = 32;
4398c2ecf20Sopenharmony_ci		ext_irq_count = 4;
4408c2ecf20Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6338;
4418c2ecf20Sopenharmony_ci		break;
4428c2ecf20Sopenharmony_ci	case BCM6345_CPU_ID:
4438c2ecf20Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6345_REG;
4448c2ecf20Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6345_REG;
4458c2ecf20Sopenharmony_ci		irq_stat_addr[1] = 0;
4468c2ecf20Sopenharmony_ci		irq_mask_addr[1] = 0;
4478c2ecf20Sopenharmony_ci		irq_bits = 32;
4488c2ecf20Sopenharmony_ci		ext_irq_count = 4;
4498c2ecf20Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6345;
4508c2ecf20Sopenharmony_ci		break;
4518c2ecf20Sopenharmony_ci	case BCM6348_CPU_ID:
4528c2ecf20Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6348_REG;
4538c2ecf20Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6348_REG;
4548c2ecf20Sopenharmony_ci		irq_stat_addr[1] = 0;
4558c2ecf20Sopenharmony_ci		irq_mask_addr[1] = 0;
4568c2ecf20Sopenharmony_ci		irq_bits = 32;
4578c2ecf20Sopenharmony_ci		ext_irq_count = 4;
4588c2ecf20Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6348;
4598c2ecf20Sopenharmony_ci		break;
4608c2ecf20Sopenharmony_ci	case BCM6358_CPU_ID:
4618c2ecf20Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6358_REG(0);
4628c2ecf20Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6358_REG(0);
4638c2ecf20Sopenharmony_ci		irq_stat_addr[1] += PERF_IRQSTAT_6358_REG(1);
4648c2ecf20Sopenharmony_ci		irq_mask_addr[1] += PERF_IRQMASK_6358_REG(1);
4658c2ecf20Sopenharmony_ci		irq_bits = 32;
4668c2ecf20Sopenharmony_ci		ext_irq_count = 4;
4678c2ecf20Sopenharmony_ci		is_ext_irq_cascaded = 1;
4688c2ecf20Sopenharmony_ci		ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE;
4698c2ecf20Sopenharmony_ci		ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE;
4708c2ecf20Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6358;
4718c2ecf20Sopenharmony_ci		break;
4728c2ecf20Sopenharmony_ci	case BCM6362_CPU_ID:
4738c2ecf20Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6362_REG(0);
4748c2ecf20Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6362_REG(0);
4758c2ecf20Sopenharmony_ci		irq_stat_addr[1] += PERF_IRQSTAT_6362_REG(1);
4768c2ecf20Sopenharmony_ci		irq_mask_addr[1] += PERF_IRQMASK_6362_REG(1);
4778c2ecf20Sopenharmony_ci		irq_bits = 64;
4788c2ecf20Sopenharmony_ci		ext_irq_count = 4;
4798c2ecf20Sopenharmony_ci		is_ext_irq_cascaded = 1;
4808c2ecf20Sopenharmony_ci		ext_irq_start = BCM_6362_EXT_IRQ0 - IRQ_INTERNAL_BASE;
4818c2ecf20Sopenharmony_ci		ext_irq_end = BCM_6362_EXT_IRQ3 - IRQ_INTERNAL_BASE;
4828c2ecf20Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6362;
4838c2ecf20Sopenharmony_ci		break;
4848c2ecf20Sopenharmony_ci	case BCM6368_CPU_ID:
4858c2ecf20Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6368_REG(0);
4868c2ecf20Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6368_REG(0);
4878c2ecf20Sopenharmony_ci		irq_stat_addr[1] += PERF_IRQSTAT_6368_REG(1);
4888c2ecf20Sopenharmony_ci		irq_mask_addr[1] += PERF_IRQMASK_6368_REG(1);
4898c2ecf20Sopenharmony_ci		irq_bits = 64;
4908c2ecf20Sopenharmony_ci		ext_irq_count = 6;
4918c2ecf20Sopenharmony_ci		is_ext_irq_cascaded = 1;
4928c2ecf20Sopenharmony_ci		ext_irq_start = BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE;
4938c2ecf20Sopenharmony_ci		ext_irq_end = BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE;
4948c2ecf20Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6368;
4958c2ecf20Sopenharmony_ci		ext_irq_cfg_reg2 = PERF_EXTIRQ_CFG_REG2_6368;
4968c2ecf20Sopenharmony_ci		break;
4978c2ecf20Sopenharmony_ci	default:
4988c2ecf20Sopenharmony_ci		BUG();
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (irq_bits == 32) {
5028c2ecf20Sopenharmony_ci		dispatch_internal = __dispatch_internal_32;
5038c2ecf20Sopenharmony_ci		internal_irq_mask = __internal_irq_mask_32;
5048c2ecf20Sopenharmony_ci		internal_irq_unmask = __internal_irq_unmask_32;
5058c2ecf20Sopenharmony_ci	} else {
5068c2ecf20Sopenharmony_ci		dispatch_internal = __dispatch_internal_64;
5078c2ecf20Sopenharmony_ci		internal_irq_mask = __internal_irq_mask_64;
5088c2ecf20Sopenharmony_ci		internal_irq_unmask = __internal_irq_unmask_64;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_civoid __init arch_init_irq(void)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	int i, irq;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	bcm63xx_init_irq();
5178c2ecf20Sopenharmony_ci	mips_cpu_irq_init();
5188c2ecf20Sopenharmony_ci	for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
5198c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
5208c2ecf20Sopenharmony_ci					 handle_level_irq);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + ext_irq_count; ++i)
5238c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
5248c2ecf20Sopenharmony_ci					 handle_edge_irq);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	if (!is_ext_irq_cascaded) {
5278c2ecf20Sopenharmony_ci		for (i = 3; i < 3 + ext_irq_count; ++i) {
5288c2ecf20Sopenharmony_ci			irq = MIPS_CPU_IRQ_BASE + i;
5298c2ecf20Sopenharmony_ci			if (request_irq(irq, no_action, IRQF_NO_THREAD,
5308c2ecf20Sopenharmony_ci					"cascade_extirq", NULL)) {
5318c2ecf20Sopenharmony_ci				pr_err("Failed to request irq %d (cascade_extirq)\n",
5328c2ecf20Sopenharmony_ci				       irq);
5338c2ecf20Sopenharmony_ci			}
5348c2ecf20Sopenharmony_ci		}
5358c2ecf20Sopenharmony_ci	}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	irq = MIPS_CPU_IRQ_BASE + 2;
5388c2ecf20Sopenharmony_ci	if (request_irq(irq, no_action, IRQF_NO_THREAD,	"cascade_ip2", NULL))
5398c2ecf20Sopenharmony_ci		pr_err("Failed to request irq %d (cascade_ip2)\n", irq);
5408c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
5418c2ecf20Sopenharmony_ci	if (is_ext_irq_cascaded) {
5428c2ecf20Sopenharmony_ci		irq = MIPS_CPU_IRQ_BASE + 3;
5438c2ecf20Sopenharmony_ci		if (request_irq(irq, no_action,	IRQF_NO_THREAD, "cascade_ip3",
5448c2ecf20Sopenharmony_ci				NULL))
5458c2ecf20Sopenharmony_ci			pr_err("Failed to request irq %d (cascade_ip3)\n", irq);
5468c2ecf20Sopenharmony_ci		bcm63xx_internal_irq_chip.irq_set_affinity =
5478c2ecf20Sopenharmony_ci			bcm63xx_internal_set_affinity;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci		cpumask_clear(irq_default_affinity);
5508c2ecf20Sopenharmony_ci		cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci#endif
5538c2ecf20Sopenharmony_ci}
554