162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
762306a36Sopenharmony_ci * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/irq.h>
1462306a36Sopenharmony_ci#include <linux/spinlock.h>
1562306a36Sopenharmony_ci#include <asm/irq_cpu.h>
1662306a36Sopenharmony_ci#include <asm/mipsregs.h>
1762306a36Sopenharmony_ci#include <bcm63xx_cpu.h>
1862306a36Sopenharmony_ci#include <bcm63xx_regs.h>
1962306a36Sopenharmony_ci#include <bcm63xx_io.h>
2062306a36Sopenharmony_ci#include <bcm63xx_irq.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ipic_lock);
2462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(epic_lock);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic u32 irq_stat_addr[2];
2762306a36Sopenharmony_cistatic u32 irq_mask_addr[2];
2862306a36Sopenharmony_cistatic void (*dispatch_internal)(int cpu);
2962306a36Sopenharmony_cistatic int is_ext_irq_cascaded;
3062306a36Sopenharmony_cistatic unsigned int ext_irq_count;
3162306a36Sopenharmony_cistatic unsigned int ext_irq_start, ext_irq_end;
3262306a36Sopenharmony_cistatic unsigned int ext_irq_cfg_reg1, ext_irq_cfg_reg2;
3362306a36Sopenharmony_cistatic void (*internal_irq_mask)(struct irq_data *d);
3462306a36Sopenharmony_cistatic void (*internal_irq_unmask)(struct irq_data *d, const struct cpumask *m);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic inline u32 get_ext_irq_perf_reg(int irq)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	if (irq < 4)
4062306a36Sopenharmony_ci		return ext_irq_cfg_reg1;
4162306a36Sopenharmony_ci	return ext_irq_cfg_reg2;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic inline void handle_internal(int intbit)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	if (is_ext_irq_cascaded &&
4762306a36Sopenharmony_ci	    intbit >= ext_irq_start && intbit <= ext_irq_end)
4862306a36Sopenharmony_ci		do_IRQ(intbit - ext_irq_start + IRQ_EXTERNAL_BASE);
4962306a36Sopenharmony_ci	else
5062306a36Sopenharmony_ci		do_IRQ(intbit + IRQ_INTERNAL_BASE);
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic inline int enable_irq_for_cpu(int cpu, struct irq_data *d,
5462306a36Sopenharmony_ci				     const struct cpumask *m)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	bool enable = cpu_online(cpu);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#ifdef CONFIG_SMP
5962306a36Sopenharmony_ci	if (m)
6062306a36Sopenharmony_ci		enable &= cpumask_test_cpu(cpu, m);
6162306a36Sopenharmony_ci	else if (irqd_affinity_was_set(d))
6262306a36Sopenharmony_ci		enable &= cpumask_test_cpu(cpu, irq_data_get_affinity_mask(d));
6362306a36Sopenharmony_ci#endif
6462306a36Sopenharmony_ci	return enable;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/*
6862306a36Sopenharmony_ci * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
6962306a36Sopenharmony_ci * prioritize any interrupt relatively to another. the static counter
7062306a36Sopenharmony_ci * will resume the loop where it ended the last time we left this
7162306a36Sopenharmony_ci * function.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define BUILD_IPIC_INTERNAL(width)					\
7562306a36Sopenharmony_civoid __dispatch_internal_##width(int cpu)				\
7662306a36Sopenharmony_ci{									\
7762306a36Sopenharmony_ci	u32 pending[width / 32];					\
7862306a36Sopenharmony_ci	unsigned int src, tgt;						\
7962306a36Sopenharmony_ci	bool irqs_pending = false;					\
8062306a36Sopenharmony_ci	static unsigned int i[2];					\
8162306a36Sopenharmony_ci	unsigned int *next = &i[cpu];					\
8262306a36Sopenharmony_ci	unsigned long flags;						\
8362306a36Sopenharmony_ci									\
8462306a36Sopenharmony_ci	/* read registers in reverse order */				\
8562306a36Sopenharmony_ci	spin_lock_irqsave(&ipic_lock, flags);				\
8662306a36Sopenharmony_ci	for (src = 0, tgt = (width / 32); src < (width / 32); src++) {	\
8762306a36Sopenharmony_ci		u32 val;						\
8862306a36Sopenharmony_ci									\
8962306a36Sopenharmony_ci		val = bcm_readl(irq_stat_addr[cpu] + src * sizeof(u32)); \
9062306a36Sopenharmony_ci		val &= bcm_readl(irq_mask_addr[cpu] + src * sizeof(u32)); \
9162306a36Sopenharmony_ci		pending[--tgt] = val;					\
9262306a36Sopenharmony_ci									\
9362306a36Sopenharmony_ci		if (val)						\
9462306a36Sopenharmony_ci			irqs_pending = true;				\
9562306a36Sopenharmony_ci	}								\
9662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipic_lock, flags);			\
9762306a36Sopenharmony_ci									\
9862306a36Sopenharmony_ci	if (!irqs_pending)						\
9962306a36Sopenharmony_ci		return;							\
10062306a36Sopenharmony_ci									\
10162306a36Sopenharmony_ci	while (1) {							\
10262306a36Sopenharmony_ci		unsigned int to_call = *next;				\
10362306a36Sopenharmony_ci									\
10462306a36Sopenharmony_ci		*next = (*next + 1) & (width - 1);			\
10562306a36Sopenharmony_ci		if (pending[to_call / 32] & (1 << (to_call & 0x1f))) {	\
10662306a36Sopenharmony_ci			handle_internal(to_call);			\
10762306a36Sopenharmony_ci			break;						\
10862306a36Sopenharmony_ci		}							\
10962306a36Sopenharmony_ci	}								\
11062306a36Sopenharmony_ci}									\
11162306a36Sopenharmony_ci									\
11262306a36Sopenharmony_cistatic void __internal_irq_mask_##width(struct irq_data *d)		\
11362306a36Sopenharmony_ci{									\
11462306a36Sopenharmony_ci	u32 val;							\
11562306a36Sopenharmony_ci	unsigned irq = d->irq - IRQ_INTERNAL_BASE;			\
11662306a36Sopenharmony_ci	unsigned reg = (irq / 32) ^ (width/32 - 1);			\
11762306a36Sopenharmony_ci	unsigned bit = irq & 0x1f;					\
11862306a36Sopenharmony_ci	unsigned long flags;						\
11962306a36Sopenharmony_ci	int cpu;							\
12062306a36Sopenharmony_ci									\
12162306a36Sopenharmony_ci	spin_lock_irqsave(&ipic_lock, flags);				\
12262306a36Sopenharmony_ci	for_each_present_cpu(cpu) {					\
12362306a36Sopenharmony_ci		if (!irq_mask_addr[cpu])				\
12462306a36Sopenharmony_ci			break;						\
12562306a36Sopenharmony_ci									\
12662306a36Sopenharmony_ci		val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\
12762306a36Sopenharmony_ci		val &= ~(1 << bit);					\
12862306a36Sopenharmony_ci		bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\
12962306a36Sopenharmony_ci	}								\
13062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipic_lock, flags);			\
13162306a36Sopenharmony_ci}									\
13262306a36Sopenharmony_ci									\
13362306a36Sopenharmony_cistatic void __internal_irq_unmask_##width(struct irq_data *d,		\
13462306a36Sopenharmony_ci					  const struct cpumask *m)	\
13562306a36Sopenharmony_ci{									\
13662306a36Sopenharmony_ci	u32 val;							\
13762306a36Sopenharmony_ci	unsigned irq = d->irq - IRQ_INTERNAL_BASE;			\
13862306a36Sopenharmony_ci	unsigned reg = (irq / 32) ^ (width/32 - 1);			\
13962306a36Sopenharmony_ci	unsigned bit = irq & 0x1f;					\
14062306a36Sopenharmony_ci	unsigned long flags;						\
14162306a36Sopenharmony_ci	int cpu;							\
14262306a36Sopenharmony_ci									\
14362306a36Sopenharmony_ci	spin_lock_irqsave(&ipic_lock, flags);				\
14462306a36Sopenharmony_ci	for_each_present_cpu(cpu) {					\
14562306a36Sopenharmony_ci		if (!irq_mask_addr[cpu])				\
14662306a36Sopenharmony_ci			break;						\
14762306a36Sopenharmony_ci									\
14862306a36Sopenharmony_ci		val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\
14962306a36Sopenharmony_ci		if (enable_irq_for_cpu(cpu, d, m))			\
15062306a36Sopenharmony_ci			val |= (1 << bit);				\
15162306a36Sopenharmony_ci		else							\
15262306a36Sopenharmony_ci			val &= ~(1 << bit);				\
15362306a36Sopenharmony_ci		bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\
15462306a36Sopenharmony_ci	}								\
15562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ipic_lock, flags);			\
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciBUILD_IPIC_INTERNAL(32);
15962306a36Sopenharmony_ciBUILD_IPIC_INTERNAL(64);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ciasmlinkage void plat_irq_dispatch(void)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	u32 cause;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	do {
16662306a36Sopenharmony_ci		cause = read_c0_cause() & read_c0_status() & ST0_IM;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		if (!cause)
16962306a36Sopenharmony_ci			break;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		if (cause & CAUSEF_IP7)
17262306a36Sopenharmony_ci			do_IRQ(7);
17362306a36Sopenharmony_ci		if (cause & CAUSEF_IP0)
17462306a36Sopenharmony_ci			do_IRQ(0);
17562306a36Sopenharmony_ci		if (cause & CAUSEF_IP1)
17662306a36Sopenharmony_ci			do_IRQ(1);
17762306a36Sopenharmony_ci		if (cause & CAUSEF_IP2)
17862306a36Sopenharmony_ci			dispatch_internal(0);
17962306a36Sopenharmony_ci		if (is_ext_irq_cascaded) {
18062306a36Sopenharmony_ci			if (cause & CAUSEF_IP3)
18162306a36Sopenharmony_ci				dispatch_internal(1);
18262306a36Sopenharmony_ci		} else {
18362306a36Sopenharmony_ci			if (cause & CAUSEF_IP3)
18462306a36Sopenharmony_ci				do_IRQ(IRQ_EXT_0);
18562306a36Sopenharmony_ci			if (cause & CAUSEF_IP4)
18662306a36Sopenharmony_ci				do_IRQ(IRQ_EXT_1);
18762306a36Sopenharmony_ci			if (cause & CAUSEF_IP5)
18862306a36Sopenharmony_ci				do_IRQ(IRQ_EXT_2);
18962306a36Sopenharmony_ci			if (cause & CAUSEF_IP6)
19062306a36Sopenharmony_ci				do_IRQ(IRQ_EXT_3);
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci	} while (1);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/*
19662306a36Sopenharmony_ci * internal IRQs operations: only mask/unmask on PERF irq mask
19762306a36Sopenharmony_ci * register.
19862306a36Sopenharmony_ci */
19962306a36Sopenharmony_cistatic void bcm63xx_internal_irq_mask(struct irq_data *d)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	internal_irq_mask(d);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void bcm63xx_internal_irq_unmask(struct irq_data *d)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	internal_irq_unmask(d, NULL);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/*
21062306a36Sopenharmony_ci * external IRQs operations: mask/unmask and clear on PERF external
21162306a36Sopenharmony_ci * irq control register.
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_cistatic void bcm63xx_external_irq_mask(struct irq_data *d)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
21662306a36Sopenharmony_ci	u32 reg, regaddr;
21762306a36Sopenharmony_ci	unsigned long flags;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	regaddr = get_ext_irq_perf_reg(irq);
22062306a36Sopenharmony_ci	spin_lock_irqsave(&epic_lock, flags);
22162306a36Sopenharmony_ci	reg = bcm_perf_readl(regaddr);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (BCMCPU_IS_6348())
22462306a36Sopenharmony_ci		reg &= ~EXTIRQ_CFG_MASK_6348(irq % 4);
22562306a36Sopenharmony_ci	else
22662306a36Sopenharmony_ci		reg &= ~EXTIRQ_CFG_MASK(irq % 4);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	bcm_perf_writel(reg, regaddr);
22962306a36Sopenharmony_ci	spin_unlock_irqrestore(&epic_lock, flags);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (is_ext_irq_cascaded)
23262306a36Sopenharmony_ci		internal_irq_mask(irq_get_irq_data(irq + ext_irq_start));
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic void bcm63xx_external_irq_unmask(struct irq_data *d)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
23862306a36Sopenharmony_ci	u32 reg, regaddr;
23962306a36Sopenharmony_ci	unsigned long flags;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	regaddr = get_ext_irq_perf_reg(irq);
24262306a36Sopenharmony_ci	spin_lock_irqsave(&epic_lock, flags);
24362306a36Sopenharmony_ci	reg = bcm_perf_readl(regaddr);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (BCMCPU_IS_6348())
24662306a36Sopenharmony_ci		reg |= EXTIRQ_CFG_MASK_6348(irq % 4);
24762306a36Sopenharmony_ci	else
24862306a36Sopenharmony_ci		reg |= EXTIRQ_CFG_MASK(irq % 4);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	bcm_perf_writel(reg, regaddr);
25162306a36Sopenharmony_ci	spin_unlock_irqrestore(&epic_lock, flags);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (is_ext_irq_cascaded)
25462306a36Sopenharmony_ci		internal_irq_unmask(irq_get_irq_data(irq + ext_irq_start),
25562306a36Sopenharmony_ci				    NULL);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic void bcm63xx_external_irq_clear(struct irq_data *d)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
26162306a36Sopenharmony_ci	u32 reg, regaddr;
26262306a36Sopenharmony_ci	unsigned long flags;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	regaddr = get_ext_irq_perf_reg(irq);
26562306a36Sopenharmony_ci	spin_lock_irqsave(&epic_lock, flags);
26662306a36Sopenharmony_ci	reg = bcm_perf_readl(regaddr);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (BCMCPU_IS_6348())
26962306a36Sopenharmony_ci		reg |= EXTIRQ_CFG_CLEAR_6348(irq % 4);
27062306a36Sopenharmony_ci	else
27162306a36Sopenharmony_ci		reg |= EXTIRQ_CFG_CLEAR(irq % 4);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	bcm_perf_writel(reg, regaddr);
27462306a36Sopenharmony_ci	spin_unlock_irqrestore(&epic_lock, flags);
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic int bcm63xx_external_irq_set_type(struct irq_data *d,
27862306a36Sopenharmony_ci					 unsigned int flow_type)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
28162306a36Sopenharmony_ci	u32 reg, regaddr;
28262306a36Sopenharmony_ci	int levelsense, sense, bothedge;
28362306a36Sopenharmony_ci	unsigned long flags;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	flow_type &= IRQ_TYPE_SENSE_MASK;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (flow_type == IRQ_TYPE_NONE)
28862306a36Sopenharmony_ci		flow_type = IRQ_TYPE_LEVEL_LOW;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	levelsense = sense = bothedge = 0;
29162306a36Sopenharmony_ci	switch (flow_type) {
29262306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_BOTH:
29362306a36Sopenharmony_ci		bothedge = 1;
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
29762306a36Sopenharmony_ci		sense = 1;
29862306a36Sopenharmony_ci		break;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_FALLING:
30162306a36Sopenharmony_ci		break;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
30462306a36Sopenharmony_ci		levelsense = 1;
30562306a36Sopenharmony_ci		sense = 1;
30662306a36Sopenharmony_ci		break;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
30962306a36Sopenharmony_ci		levelsense = 1;
31062306a36Sopenharmony_ci		break;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	default:
31362306a36Sopenharmony_ci		pr_err("bogus flow type combination given !\n");
31462306a36Sopenharmony_ci		return -EINVAL;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	regaddr = get_ext_irq_perf_reg(irq);
31862306a36Sopenharmony_ci	spin_lock_irqsave(&epic_lock, flags);
31962306a36Sopenharmony_ci	reg = bcm_perf_readl(regaddr);
32062306a36Sopenharmony_ci	irq %= 4;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	switch (bcm63xx_get_cpu_id()) {
32362306a36Sopenharmony_ci	case BCM6348_CPU_ID:
32462306a36Sopenharmony_ci		if (levelsense)
32562306a36Sopenharmony_ci			reg |= EXTIRQ_CFG_LEVELSENSE_6348(irq);
32662306a36Sopenharmony_ci		else
32762306a36Sopenharmony_ci			reg &= ~EXTIRQ_CFG_LEVELSENSE_6348(irq);
32862306a36Sopenharmony_ci		if (sense)
32962306a36Sopenharmony_ci			reg |= EXTIRQ_CFG_SENSE_6348(irq);
33062306a36Sopenharmony_ci		else
33162306a36Sopenharmony_ci			reg &= ~EXTIRQ_CFG_SENSE_6348(irq);
33262306a36Sopenharmony_ci		if (bothedge)
33362306a36Sopenharmony_ci			reg |= EXTIRQ_CFG_BOTHEDGE_6348(irq);
33462306a36Sopenharmony_ci		else
33562306a36Sopenharmony_ci			reg &= ~EXTIRQ_CFG_BOTHEDGE_6348(irq);
33662306a36Sopenharmony_ci		break;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	case BCM3368_CPU_ID:
33962306a36Sopenharmony_ci	case BCM6328_CPU_ID:
34062306a36Sopenharmony_ci	case BCM6338_CPU_ID:
34162306a36Sopenharmony_ci	case BCM6345_CPU_ID:
34262306a36Sopenharmony_ci	case BCM6358_CPU_ID:
34362306a36Sopenharmony_ci	case BCM6362_CPU_ID:
34462306a36Sopenharmony_ci	case BCM6368_CPU_ID:
34562306a36Sopenharmony_ci		if (levelsense)
34662306a36Sopenharmony_ci			reg |= EXTIRQ_CFG_LEVELSENSE(irq);
34762306a36Sopenharmony_ci		else
34862306a36Sopenharmony_ci			reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
34962306a36Sopenharmony_ci		if (sense)
35062306a36Sopenharmony_ci			reg |= EXTIRQ_CFG_SENSE(irq);
35162306a36Sopenharmony_ci		else
35262306a36Sopenharmony_ci			reg &= ~EXTIRQ_CFG_SENSE(irq);
35362306a36Sopenharmony_ci		if (bothedge)
35462306a36Sopenharmony_ci			reg |= EXTIRQ_CFG_BOTHEDGE(irq);
35562306a36Sopenharmony_ci		else
35662306a36Sopenharmony_ci			reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
35762306a36Sopenharmony_ci		break;
35862306a36Sopenharmony_ci	default:
35962306a36Sopenharmony_ci		BUG();
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	bcm_perf_writel(reg, regaddr);
36362306a36Sopenharmony_ci	spin_unlock_irqrestore(&epic_lock, flags);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	irqd_set_trigger_type(d, flow_type);
36662306a36Sopenharmony_ci	if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
36762306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_level_irq);
36862306a36Sopenharmony_ci	else
36962306a36Sopenharmony_ci		irq_set_handler_locked(d, handle_edge_irq);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	return IRQ_SET_MASK_OK_NOCOPY;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci#ifdef CONFIG_SMP
37562306a36Sopenharmony_cistatic int bcm63xx_internal_set_affinity(struct irq_data *data,
37662306a36Sopenharmony_ci					 const struct cpumask *dest,
37762306a36Sopenharmony_ci					 bool force)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	if (!irqd_irq_disabled(data))
38062306a36Sopenharmony_ci		internal_irq_unmask(data, dest);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci#endif
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic struct irq_chip bcm63xx_internal_irq_chip = {
38762306a36Sopenharmony_ci	.name		= "bcm63xx_ipic",
38862306a36Sopenharmony_ci	.irq_mask	= bcm63xx_internal_irq_mask,
38962306a36Sopenharmony_ci	.irq_unmask	= bcm63xx_internal_irq_unmask,
39062306a36Sopenharmony_ci};
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic struct irq_chip bcm63xx_external_irq_chip = {
39362306a36Sopenharmony_ci	.name		= "bcm63xx_epic",
39462306a36Sopenharmony_ci	.irq_ack	= bcm63xx_external_irq_clear,
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	.irq_mask	= bcm63xx_external_irq_mask,
39762306a36Sopenharmony_ci	.irq_unmask	= bcm63xx_external_irq_unmask,
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	.irq_set_type	= bcm63xx_external_irq_set_type,
40062306a36Sopenharmony_ci};
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic void bcm63xx_init_irq(void)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	int irq_bits;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	irq_stat_addr[0] = bcm63xx_regset_address(RSET_PERF);
40762306a36Sopenharmony_ci	irq_mask_addr[0] = bcm63xx_regset_address(RSET_PERF);
40862306a36Sopenharmony_ci	irq_stat_addr[1] = bcm63xx_regset_address(RSET_PERF);
40962306a36Sopenharmony_ci	irq_mask_addr[1] = bcm63xx_regset_address(RSET_PERF);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	switch (bcm63xx_get_cpu_id()) {
41262306a36Sopenharmony_ci	case BCM3368_CPU_ID:
41362306a36Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_3368_REG;
41462306a36Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_3368_REG;
41562306a36Sopenharmony_ci		irq_stat_addr[1] = 0;
41662306a36Sopenharmony_ci		irq_mask_addr[1] = 0;
41762306a36Sopenharmony_ci		irq_bits = 32;
41862306a36Sopenharmony_ci		ext_irq_count = 4;
41962306a36Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_3368;
42062306a36Sopenharmony_ci		break;
42162306a36Sopenharmony_ci	case BCM6328_CPU_ID:
42262306a36Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6328_REG(0);
42362306a36Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6328_REG(0);
42462306a36Sopenharmony_ci		irq_stat_addr[1] += PERF_IRQSTAT_6328_REG(1);
42562306a36Sopenharmony_ci		irq_mask_addr[1] += PERF_IRQMASK_6328_REG(1);
42662306a36Sopenharmony_ci		irq_bits = 64;
42762306a36Sopenharmony_ci		ext_irq_count = 4;
42862306a36Sopenharmony_ci		is_ext_irq_cascaded = 1;
42962306a36Sopenharmony_ci		ext_irq_start = BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE;
43062306a36Sopenharmony_ci		ext_irq_end = BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE;
43162306a36Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6328;
43262306a36Sopenharmony_ci		break;
43362306a36Sopenharmony_ci	case BCM6338_CPU_ID:
43462306a36Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6338_REG;
43562306a36Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6338_REG;
43662306a36Sopenharmony_ci		irq_stat_addr[1] = 0;
43762306a36Sopenharmony_ci		irq_mask_addr[1] = 0;
43862306a36Sopenharmony_ci		irq_bits = 32;
43962306a36Sopenharmony_ci		ext_irq_count = 4;
44062306a36Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6338;
44162306a36Sopenharmony_ci		break;
44262306a36Sopenharmony_ci	case BCM6345_CPU_ID:
44362306a36Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6345_REG;
44462306a36Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6345_REG;
44562306a36Sopenharmony_ci		irq_stat_addr[1] = 0;
44662306a36Sopenharmony_ci		irq_mask_addr[1] = 0;
44762306a36Sopenharmony_ci		irq_bits = 32;
44862306a36Sopenharmony_ci		ext_irq_count = 4;
44962306a36Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6345;
45062306a36Sopenharmony_ci		break;
45162306a36Sopenharmony_ci	case BCM6348_CPU_ID:
45262306a36Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6348_REG;
45362306a36Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6348_REG;
45462306a36Sopenharmony_ci		irq_stat_addr[1] = 0;
45562306a36Sopenharmony_ci		irq_mask_addr[1] = 0;
45662306a36Sopenharmony_ci		irq_bits = 32;
45762306a36Sopenharmony_ci		ext_irq_count = 4;
45862306a36Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6348;
45962306a36Sopenharmony_ci		break;
46062306a36Sopenharmony_ci	case BCM6358_CPU_ID:
46162306a36Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6358_REG(0);
46262306a36Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6358_REG(0);
46362306a36Sopenharmony_ci		irq_stat_addr[1] += PERF_IRQSTAT_6358_REG(1);
46462306a36Sopenharmony_ci		irq_mask_addr[1] += PERF_IRQMASK_6358_REG(1);
46562306a36Sopenharmony_ci		irq_bits = 32;
46662306a36Sopenharmony_ci		ext_irq_count = 4;
46762306a36Sopenharmony_ci		is_ext_irq_cascaded = 1;
46862306a36Sopenharmony_ci		ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE;
46962306a36Sopenharmony_ci		ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE;
47062306a36Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6358;
47162306a36Sopenharmony_ci		break;
47262306a36Sopenharmony_ci	case BCM6362_CPU_ID:
47362306a36Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6362_REG(0);
47462306a36Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6362_REG(0);
47562306a36Sopenharmony_ci		irq_stat_addr[1] += PERF_IRQSTAT_6362_REG(1);
47662306a36Sopenharmony_ci		irq_mask_addr[1] += PERF_IRQMASK_6362_REG(1);
47762306a36Sopenharmony_ci		irq_bits = 64;
47862306a36Sopenharmony_ci		ext_irq_count = 4;
47962306a36Sopenharmony_ci		is_ext_irq_cascaded = 1;
48062306a36Sopenharmony_ci		ext_irq_start = BCM_6362_EXT_IRQ0 - IRQ_INTERNAL_BASE;
48162306a36Sopenharmony_ci		ext_irq_end = BCM_6362_EXT_IRQ3 - IRQ_INTERNAL_BASE;
48262306a36Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6362;
48362306a36Sopenharmony_ci		break;
48462306a36Sopenharmony_ci	case BCM6368_CPU_ID:
48562306a36Sopenharmony_ci		irq_stat_addr[0] += PERF_IRQSTAT_6368_REG(0);
48662306a36Sopenharmony_ci		irq_mask_addr[0] += PERF_IRQMASK_6368_REG(0);
48762306a36Sopenharmony_ci		irq_stat_addr[1] += PERF_IRQSTAT_6368_REG(1);
48862306a36Sopenharmony_ci		irq_mask_addr[1] += PERF_IRQMASK_6368_REG(1);
48962306a36Sopenharmony_ci		irq_bits = 64;
49062306a36Sopenharmony_ci		ext_irq_count = 6;
49162306a36Sopenharmony_ci		is_ext_irq_cascaded = 1;
49262306a36Sopenharmony_ci		ext_irq_start = BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE;
49362306a36Sopenharmony_ci		ext_irq_end = BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE;
49462306a36Sopenharmony_ci		ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6368;
49562306a36Sopenharmony_ci		ext_irq_cfg_reg2 = PERF_EXTIRQ_CFG_REG2_6368;
49662306a36Sopenharmony_ci		break;
49762306a36Sopenharmony_ci	default:
49862306a36Sopenharmony_ci		BUG();
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (irq_bits == 32) {
50262306a36Sopenharmony_ci		dispatch_internal = __dispatch_internal_32;
50362306a36Sopenharmony_ci		internal_irq_mask = __internal_irq_mask_32;
50462306a36Sopenharmony_ci		internal_irq_unmask = __internal_irq_unmask_32;
50562306a36Sopenharmony_ci	} else {
50662306a36Sopenharmony_ci		dispatch_internal = __dispatch_internal_64;
50762306a36Sopenharmony_ci		internal_irq_mask = __internal_irq_mask_64;
50862306a36Sopenharmony_ci		internal_irq_unmask = __internal_irq_unmask_64;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_civoid __init arch_init_irq(void)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	int i, irq;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	bcm63xx_init_irq();
51762306a36Sopenharmony_ci	mips_cpu_irq_init();
51862306a36Sopenharmony_ci	for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
51962306a36Sopenharmony_ci		irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
52062306a36Sopenharmony_ci					 handle_level_irq);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + ext_irq_count; ++i)
52362306a36Sopenharmony_ci		irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
52462306a36Sopenharmony_ci					 handle_edge_irq);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	if (!is_ext_irq_cascaded) {
52762306a36Sopenharmony_ci		for (i = 3; i < 3 + ext_irq_count; ++i) {
52862306a36Sopenharmony_ci			irq = MIPS_CPU_IRQ_BASE + i;
52962306a36Sopenharmony_ci			if (request_irq(irq, no_action, IRQF_NO_THREAD,
53062306a36Sopenharmony_ci					"cascade_extirq", NULL)) {
53162306a36Sopenharmony_ci				pr_err("Failed to request irq %d (cascade_extirq)\n",
53262306a36Sopenharmony_ci				       irq);
53362306a36Sopenharmony_ci			}
53462306a36Sopenharmony_ci		}
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	irq = MIPS_CPU_IRQ_BASE + 2;
53862306a36Sopenharmony_ci	if (request_irq(irq, no_action, IRQF_NO_THREAD,	"cascade_ip2", NULL))
53962306a36Sopenharmony_ci		pr_err("Failed to request irq %d (cascade_ip2)\n", irq);
54062306a36Sopenharmony_ci#ifdef CONFIG_SMP
54162306a36Sopenharmony_ci	if (is_ext_irq_cascaded) {
54262306a36Sopenharmony_ci		irq = MIPS_CPU_IRQ_BASE + 3;
54362306a36Sopenharmony_ci		if (request_irq(irq, no_action,	IRQF_NO_THREAD, "cascade_ip3",
54462306a36Sopenharmony_ci				NULL))
54562306a36Sopenharmony_ci			pr_err("Failed to request irq %d (cascade_ip3)\n", irq);
54662306a36Sopenharmony_ci		bcm63xx_internal_irq_chip.irq_set_affinity =
54762306a36Sopenharmony_ci			bcm63xx_internal_set_affinity;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		cpumask_clear(irq_default_affinity);
55062306a36Sopenharmony_ci		cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci#endif
55362306a36Sopenharmony_ci}
554