162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/kernel.h>
662306a36Sopenharmony_ci#include <linux/init.h>
762306a36Sopenharmony_ci#include <linux/linkage.h>
862306a36Sopenharmony_ci#include <linux/interrupt.h>
962306a36Sopenharmony_ci#include <linux/spinlock.h>
1062306a36Sopenharmony_ci#include <linux/smp.h>
1162306a36Sopenharmony_ci#include <linux/mm.h>
1262306a36Sopenharmony_ci#include <linux/kernel_stat.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <asm/errno.h>
1562306a36Sopenharmony_ci#include <asm/signal.h>
1662306a36Sopenharmony_ci#include <asm/time.h>
1762306a36Sopenharmony_ci#include <asm/io.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <asm/sibyte/sb1250_regs.h>
2062306a36Sopenharmony_ci#include <asm/sibyte/sb1250_int.h>
2162306a36Sopenharmony_ci#include <asm/sibyte/sb1250_uart.h>
2262306a36Sopenharmony_ci#include <asm/sibyte/sb1250_scd.h>
2362306a36Sopenharmony_ci#include <asm/sibyte/sb1250.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * These are the routines that handle all the low level interrupt stuff.
2762306a36Sopenharmony_ci * Actions handled here are: initialization of the interrupt map, requesting of
2862306a36Sopenharmony_ci * interrupt lines by handlers, dispatching if interrupts to handlers, probing
2962306a36Sopenharmony_ci * for interrupt lines
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#ifdef CONFIG_SIBYTE_HAS_LDT
3362306a36Sopenharmony_ciextern unsigned long ldt_eoi_space;
3462306a36Sopenharmony_ci#endif
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* Store the CPU id (not the logical number) */
3762306a36Sopenharmony_ciint sb1250_irq_owner[SB1250_NR_IRQS];
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(sb1250_imr_lock);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_civoid sb1250_mask_irq(int cpu, int irq)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	unsigned long flags;
4462306a36Sopenharmony_ci	u64 cur_ints;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	raw_spin_lock_irqsave(&sb1250_imr_lock, flags);
4762306a36Sopenharmony_ci	cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
4862306a36Sopenharmony_ci					R_IMR_INTERRUPT_MASK));
4962306a36Sopenharmony_ci	cur_ints |= (((u64) 1) << irq);
5062306a36Sopenharmony_ci	____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
5162306a36Sopenharmony_ci					R_IMR_INTERRUPT_MASK));
5262306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&sb1250_imr_lock, flags);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_civoid sb1250_unmask_irq(int cpu, int irq)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	unsigned long flags;
5862306a36Sopenharmony_ci	u64 cur_ints;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&sb1250_imr_lock, flags);
6162306a36Sopenharmony_ci	cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
6262306a36Sopenharmony_ci					R_IMR_INTERRUPT_MASK));
6362306a36Sopenharmony_ci	cur_ints &= ~(((u64) 1) << irq);
6462306a36Sopenharmony_ci	____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
6562306a36Sopenharmony_ci					R_IMR_INTERRUPT_MASK));
6662306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&sb1250_imr_lock, flags);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#ifdef CONFIG_SMP
7062306a36Sopenharmony_cistatic int sb1250_set_affinity(struct irq_data *d, const struct cpumask *mask,
7162306a36Sopenharmony_ci			       bool force)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	int i = 0, old_cpu, cpu, int_on;
7462306a36Sopenharmony_ci	unsigned int irq = d->irq;
7562306a36Sopenharmony_ci	u64 cur_ints;
7662306a36Sopenharmony_ci	unsigned long flags;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	i = cpumask_first_and(mask, cpu_online_mask);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* Convert logical CPU to physical CPU */
8162306a36Sopenharmony_ci	cpu = cpu_logical_map(i);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/* Protect against other affinity changers and IMR manipulation */
8462306a36Sopenharmony_ci	raw_spin_lock_irqsave(&sb1250_imr_lock, flags);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* Swizzle each CPU's IMR (but leave the IP selection alone) */
8762306a36Sopenharmony_ci	old_cpu = sb1250_irq_owner[irq];
8862306a36Sopenharmony_ci	cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(old_cpu) +
8962306a36Sopenharmony_ci					R_IMR_INTERRUPT_MASK));
9062306a36Sopenharmony_ci	int_on = !(cur_ints & (((u64) 1) << irq));
9162306a36Sopenharmony_ci	if (int_on) {
9262306a36Sopenharmony_ci		/* If it was on, mask it */
9362306a36Sopenharmony_ci		cur_ints |= (((u64) 1) << irq);
9462306a36Sopenharmony_ci		____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(old_cpu) +
9562306a36Sopenharmony_ci					R_IMR_INTERRUPT_MASK));
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	sb1250_irq_owner[irq] = cpu;
9862306a36Sopenharmony_ci	if (int_on) {
9962306a36Sopenharmony_ci		/* unmask for the new CPU */
10062306a36Sopenharmony_ci		cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
10162306a36Sopenharmony_ci					R_IMR_INTERRUPT_MASK));
10262306a36Sopenharmony_ci		cur_ints &= ~(((u64) 1) << irq);
10362306a36Sopenharmony_ci		____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
10462306a36Sopenharmony_ci					R_IMR_INTERRUPT_MASK));
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&sb1250_imr_lock, flags);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return 0;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci#endif
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void disable_sb1250_irq(struct irq_data *d)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	unsigned int irq = d->irq;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	sb1250_mask_irq(sb1250_irq_owner[irq], irq);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic void enable_sb1250_irq(struct irq_data *d)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	unsigned int irq = d->irq;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	sb1250_unmask_irq(sb1250_irq_owner[irq], irq);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void ack_sb1250_irq(struct irq_data *d)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	unsigned int irq = d->irq;
13062306a36Sopenharmony_ci#ifdef CONFIG_SIBYTE_HAS_LDT
13162306a36Sopenharmony_ci	u64 pending;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/*
13462306a36Sopenharmony_ci	 * If the interrupt was an HT interrupt, now is the time to
13562306a36Sopenharmony_ci	 * clear it.  NOTE: we assume the HT bridge was set up to
13662306a36Sopenharmony_ci	 * deliver the interrupts to all CPUs (which makes affinity
13762306a36Sopenharmony_ci	 * changing easier for us)
13862306a36Sopenharmony_ci	 */
13962306a36Sopenharmony_ci	pending = __raw_readq(IOADDR(A_IMR_REGISTER(sb1250_irq_owner[irq],
14062306a36Sopenharmony_ci						    R_IMR_LDT_INTERRUPT)));
14162306a36Sopenharmony_ci	pending &= ((u64)1 << (irq));
14262306a36Sopenharmony_ci	if (pending) {
14362306a36Sopenharmony_ci		int i;
14462306a36Sopenharmony_ci		for (i=0; i<NR_CPUS; i++) {
14562306a36Sopenharmony_ci			int cpu;
14662306a36Sopenharmony_ci#ifdef CONFIG_SMP
14762306a36Sopenharmony_ci			cpu = cpu_logical_map(i);
14862306a36Sopenharmony_ci#else
14962306a36Sopenharmony_ci			cpu = i;
15062306a36Sopenharmony_ci#endif
15162306a36Sopenharmony_ci			/*
15262306a36Sopenharmony_ci			 * Clear for all CPUs so an affinity switch
15362306a36Sopenharmony_ci			 * doesn't find an old status
15462306a36Sopenharmony_ci			 */
15562306a36Sopenharmony_ci			__raw_writeq(pending,
15662306a36Sopenharmony_ci				     IOADDR(A_IMR_REGISTER(cpu,
15762306a36Sopenharmony_ci						R_IMR_LDT_INTERRUPT_CLR)));
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		/*
16162306a36Sopenharmony_ci		 * Generate EOI.  For Pass 1 parts, EOI is a nop.  For
16262306a36Sopenharmony_ci		 * Pass 2, the LDT world may be edge-triggered, but
16362306a36Sopenharmony_ci		 * this EOI shouldn't hurt.  If they are
16462306a36Sopenharmony_ci		 * level-sensitive, the EOI is required.
16562306a36Sopenharmony_ci		 */
16662306a36Sopenharmony_ci		*(uint32_t *)(ldt_eoi_space+(irq<<16)+(7<<2)) = 0;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci#endif
16962306a36Sopenharmony_ci	sb1250_mask_irq(sb1250_irq_owner[irq], irq);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic struct irq_chip sb1250_irq_type = {
17362306a36Sopenharmony_ci	.name = "SB1250-IMR",
17462306a36Sopenharmony_ci	.irq_mask_ack = ack_sb1250_irq,
17562306a36Sopenharmony_ci	.irq_unmask = enable_sb1250_irq,
17662306a36Sopenharmony_ci	.irq_mask = disable_sb1250_irq,
17762306a36Sopenharmony_ci#ifdef CONFIG_SMP
17862306a36Sopenharmony_ci	.irq_set_affinity = sb1250_set_affinity
17962306a36Sopenharmony_ci#endif
18062306a36Sopenharmony_ci};
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_civoid __init init_sb1250_irqs(void)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	int i;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	for (i = 0; i < SB1250_NR_IRQS; i++) {
18762306a36Sopenharmony_ci		irq_set_chip_and_handler(i, &sb1250_irq_type,
18862306a36Sopenharmony_ci					 handle_level_irq);
18962306a36Sopenharmony_ci		sb1250_irq_owner[i] = 0;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/*
19562306a36Sopenharmony_ci *  arch_init_irq is called early in the boot sequence from init/main.c via
19662306a36Sopenharmony_ci *  init_IRQ.  It is responsible for setting up the interrupt mapper and
19762306a36Sopenharmony_ci *  installing the handler that will be responsible for dispatching interrupts
19862306a36Sopenharmony_ci *  to the "right" place.
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_ci/*
20162306a36Sopenharmony_ci * For now, map all interrupts to IP[2].  We could save
20262306a36Sopenharmony_ci * some cycles by parceling out system interrupts to different
20362306a36Sopenharmony_ci * IP lines, but keep it simple for bringup.  We'll also direct
20462306a36Sopenharmony_ci * all interrupts to a single CPU; we should probably route
20562306a36Sopenharmony_ci * PCI and LDT to one cpu and everything else to the other
20662306a36Sopenharmony_ci * to balance the load a bit.
20762306a36Sopenharmony_ci *
20862306a36Sopenharmony_ci * On the second cpu, everything is set to IP5, which is
20962306a36Sopenharmony_ci * ignored, EXCEPT the mailbox interrupt.  That one is
21062306a36Sopenharmony_ci * set to IP[2] so it is handled.  This is needed so we
21162306a36Sopenharmony_ci * can do cross-cpu function calls, as required by SMP
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci#define IMR_IP2_VAL	K_INT_MAP_I0
21562306a36Sopenharmony_ci#define IMR_IP3_VAL	K_INT_MAP_I1
21662306a36Sopenharmony_ci#define IMR_IP4_VAL	K_INT_MAP_I2
21762306a36Sopenharmony_ci#define IMR_IP5_VAL	K_INT_MAP_I3
21862306a36Sopenharmony_ci#define IMR_IP6_VAL	K_INT_MAP_I4
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_civoid __init arch_init_irq(void)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	unsigned int i;
22462306a36Sopenharmony_ci	u64 tmp;
22562306a36Sopenharmony_ci	unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
22662306a36Sopenharmony_ci		STATUSF_IP1 | STATUSF_IP0;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* Default everything to IP2 */
22962306a36Sopenharmony_ci	for (i = 0; i < SB1250_NR_IRQS; i++) {	/* was I0 */
23062306a36Sopenharmony_ci		__raw_writeq(IMR_IP2_VAL,
23162306a36Sopenharmony_ci			     IOADDR(A_IMR_REGISTER(0,
23262306a36Sopenharmony_ci						   R_IMR_INTERRUPT_MAP_BASE) +
23362306a36Sopenharmony_ci				    (i << 3)));
23462306a36Sopenharmony_ci		__raw_writeq(IMR_IP2_VAL,
23562306a36Sopenharmony_ci			     IOADDR(A_IMR_REGISTER(1,
23662306a36Sopenharmony_ci						   R_IMR_INTERRUPT_MAP_BASE) +
23762306a36Sopenharmony_ci				    (i << 3)));
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	init_sb1250_irqs();
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/*
24362306a36Sopenharmony_ci	 * Map the high 16 bits of the mailbox registers to IP[3], for
24462306a36Sopenharmony_ci	 * inter-cpu messages
24562306a36Sopenharmony_ci	 */
24662306a36Sopenharmony_ci	/* Was I1 */
24762306a36Sopenharmony_ci	__raw_writeq(IMR_IP3_VAL,
24862306a36Sopenharmony_ci		     IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
24962306a36Sopenharmony_ci			    (K_INT_MBOX_0 << 3)));
25062306a36Sopenharmony_ci	__raw_writeq(IMR_IP3_VAL,
25162306a36Sopenharmony_ci		     IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MAP_BASE) +
25262306a36Sopenharmony_ci			    (K_INT_MBOX_0 << 3)));
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* Clear the mailboxes.	 The firmware may leave them dirty */
25562306a36Sopenharmony_ci	__raw_writeq(0xffffffffffffffffULL,
25662306a36Sopenharmony_ci		     IOADDR(A_IMR_REGISTER(0, R_IMR_MAILBOX_CLR_CPU)));
25762306a36Sopenharmony_ci	__raw_writeq(0xffffffffffffffffULL,
25862306a36Sopenharmony_ci		     IOADDR(A_IMR_REGISTER(1, R_IMR_MAILBOX_CLR_CPU)));
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* Mask everything except the mailbox registers for both cpus */
26162306a36Sopenharmony_ci	tmp = ~((u64) 0) ^ (((u64) 1) << K_INT_MBOX_0);
26262306a36Sopenharmony_ci	__raw_writeq(tmp, IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MASK)));
26362306a36Sopenharmony_ci	__raw_writeq(tmp, IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MASK)));
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* Enable necessary IPs, disable the rest */
26662306a36Sopenharmony_ci	change_c0_status(ST0_IM, imask);
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ciextern void sb1250_mailbox_interrupt(void);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic inline void dispatch_ip2(void)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	unsigned int cpu = smp_processor_id();
27462306a36Sopenharmony_ci	unsigned long long mask;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/*
27762306a36Sopenharmony_ci	 * Default...we've hit an IP[2] interrupt, which means we've got to
27862306a36Sopenharmony_ci	 * check the 1250 interrupt registers to figure out what to do.	 Need
27962306a36Sopenharmony_ci	 * to detect which CPU we're on, now that smp_affinity is supported.
28062306a36Sopenharmony_ci	 */
28162306a36Sopenharmony_ci	mask = __raw_readq(IOADDR(A_IMR_REGISTER(cpu,
28262306a36Sopenharmony_ci				  R_IMR_INTERRUPT_STATUS_BASE)));
28362306a36Sopenharmony_ci	if (mask)
28462306a36Sopenharmony_ci		do_IRQ(fls64(mask) - 1);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ciasmlinkage void plat_irq_dispatch(void)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	unsigned int cpu = smp_processor_id();
29062306a36Sopenharmony_ci	unsigned int pending;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/*
29362306a36Sopenharmony_ci	 * What a pain. We have to be really careful saving the upper 32 bits
29462306a36Sopenharmony_ci	 * of any * register across function calls if we don't want them
29562306a36Sopenharmony_ci	 * trashed--since were running in -o32, the calling routing never saves
29662306a36Sopenharmony_ci	 * the full 64 bits of a register across a function call.  Being the
29762306a36Sopenharmony_ci	 * interrupt handler, we're guaranteed that interrupts are disabled
29862306a36Sopenharmony_ci	 * during this code so we don't have to worry about random interrupts
29962306a36Sopenharmony_ci	 * blasting the high 32 bits.
30062306a36Sopenharmony_ci	 */
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	pending = read_c0_cause() & read_c0_status() & ST0_IM;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (pending & CAUSEF_IP7) /* CPU performance counter interrupt */
30562306a36Sopenharmony_ci		do_IRQ(MIPS_CPU_IRQ_BASE + 7);
30662306a36Sopenharmony_ci	else if (pending & CAUSEF_IP4)
30762306a36Sopenharmony_ci		do_IRQ(K_INT_TIMER_0 + cpu);	/* sb1250_timer_interrupt() */
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci#ifdef CONFIG_SMP
31062306a36Sopenharmony_ci	else if (pending & CAUSEF_IP3)
31162306a36Sopenharmony_ci		sb1250_mailbox_interrupt();
31262306a36Sopenharmony_ci#endif
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	else if (pending & CAUSEF_IP2)
31562306a36Sopenharmony_ci		dispatch_ip2();
31662306a36Sopenharmony_ci	else
31762306a36Sopenharmony_ci		spurious_interrupt();
31862306a36Sopenharmony_ci}
319