162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ip30-irq.c: Highlevel interrupt handling for IP30 architecture. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/errno.h> 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <linux/interrupt.h> 862306a36Sopenharmony_ci#include <linux/irq.h> 962306a36Sopenharmony_ci#include <linux/irqdomain.h> 1062306a36Sopenharmony_ci#include <linux/percpu.h> 1162306a36Sopenharmony_ci#include <linux/spinlock.h> 1262306a36Sopenharmony_ci#include <linux/tick.h> 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <asm/irq_cpu.h> 1662306a36Sopenharmony_ci#include <asm/sgi/heart.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "ip30-common.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct heart_irq_data { 2162306a36Sopenharmony_ci u64 *irq_mask; 2262306a36Sopenharmony_ci int cpu; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic DECLARE_BITMAP(heart_irq_map, HEART_NUM_IRQS); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned long, irq_enable_mask); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic inline int heart_alloc_int(void) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci int bit; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciagain: 3462306a36Sopenharmony_ci bit = find_first_zero_bit(heart_irq_map, HEART_NUM_IRQS); 3562306a36Sopenharmony_ci if (bit >= HEART_NUM_IRQS) 3662306a36Sopenharmony_ci return -ENOSPC; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (test_and_set_bit(bit, heart_irq_map)) 3962306a36Sopenharmony_ci goto again; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return bit; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void ip30_error_irq(struct irq_desc *desc) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci u64 pending, mask, cause, error_irqs, err_reg; 4762306a36Sopenharmony_ci int cpu = smp_processor_id(); 4862306a36Sopenharmony_ci int i; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci pending = heart_read(&heart_regs->isr); 5162306a36Sopenharmony_ci mask = heart_read(&heart_regs->imr[cpu]); 5262306a36Sopenharmony_ci cause = heart_read(&heart_regs->cause); 5362306a36Sopenharmony_ci error_irqs = (pending & HEART_L4_INT_MASK & mask); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* Bail if there's nothing to process (how did we get here, then?) */ 5662306a36Sopenharmony_ci if (unlikely(!error_irqs)) 5762306a36Sopenharmony_ci return; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* Prevent any of the error IRQs from firing again. */ 6062306a36Sopenharmony_ci heart_write(mask & ~(pending), &heart_regs->imr[cpu]); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* Ack all error IRQs. */ 6362306a36Sopenharmony_ci heart_write(HEART_L4_INT_MASK, &heart_regs->clear_isr); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * If we also have a cause value, then something happened, so loop 6762306a36Sopenharmony_ci * through the error IRQs and report a "heart attack" for each one 6862306a36Sopenharmony_ci * and print the value of the HEART cause register. This is really 6962306a36Sopenharmony_ci * primitive right now, but it should hopefully work until a more 7062306a36Sopenharmony_ci * robust error handling routine can be put together. 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * Refer to heart.h for the HC_* macros to work out the cause 7362306a36Sopenharmony_ci * that got us here. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci if (cause) { 7662306a36Sopenharmony_ci pr_alert("IP30: CPU%d: HEART ATTACK! ISR = 0x%.16llx, IMR = 0x%.16llx, CAUSE = 0x%.16llx\n", 7762306a36Sopenharmony_ci cpu, pending, mask, cause); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (cause & HC_COR_MEM_ERR) { 8062306a36Sopenharmony_ci err_reg = heart_read(&heart_regs->mem_err_addr); 8162306a36Sopenharmony_ci pr_alert(" HEART_MEMERR_ADDR = 0x%.16llx\n", err_reg); 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* i = 63; i >= 51; i-- */ 8562306a36Sopenharmony_ci for (i = HEART_ERR_MASK_END; i >= HEART_ERR_MASK_START; i--) 8662306a36Sopenharmony_ci if ((pending >> i) & 1) 8762306a36Sopenharmony_ci pr_alert(" HEART Error IRQ #%d\n", i); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* XXX: Seems possible to loop forever here, so panic(). */ 9062306a36Sopenharmony_ci panic("IP30: Fatal Error !\n"); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Unmask the error IRQs. */ 9462306a36Sopenharmony_ci heart_write(mask, &heart_regs->imr[cpu]); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void ip30_normal_irq(struct irq_desc *desc) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci int cpu = smp_processor_id(); 10062306a36Sopenharmony_ci struct irq_domain *domain; 10162306a36Sopenharmony_ci u64 pend, mask; 10262306a36Sopenharmony_ci int ret; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci pend = heart_read(&heart_regs->isr); 10562306a36Sopenharmony_ci mask = (heart_read(&heart_regs->imr[cpu]) & 10662306a36Sopenharmony_ci (HEART_L0_INT_MASK | HEART_L1_INT_MASK | HEART_L2_INT_MASK)); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci pend &= mask; 10962306a36Sopenharmony_ci if (unlikely(!pend)) 11062306a36Sopenharmony_ci return; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#ifdef CONFIG_SMP 11362306a36Sopenharmony_ci if (pend & BIT_ULL(HEART_L2_INT_RESCHED_CPU_0)) { 11462306a36Sopenharmony_ci heart_write(BIT_ULL(HEART_L2_INT_RESCHED_CPU_0), 11562306a36Sopenharmony_ci &heart_regs->clear_isr); 11662306a36Sopenharmony_ci scheduler_ipi(); 11762306a36Sopenharmony_ci } else if (pend & BIT_ULL(HEART_L2_INT_RESCHED_CPU_1)) { 11862306a36Sopenharmony_ci heart_write(BIT_ULL(HEART_L2_INT_RESCHED_CPU_1), 11962306a36Sopenharmony_ci &heart_regs->clear_isr); 12062306a36Sopenharmony_ci scheduler_ipi(); 12162306a36Sopenharmony_ci } else if (pend & BIT_ULL(HEART_L2_INT_CALL_CPU_0)) { 12262306a36Sopenharmony_ci heart_write(BIT_ULL(HEART_L2_INT_CALL_CPU_0), 12362306a36Sopenharmony_ci &heart_regs->clear_isr); 12462306a36Sopenharmony_ci generic_smp_call_function_interrupt(); 12562306a36Sopenharmony_ci } else if (pend & BIT_ULL(HEART_L2_INT_CALL_CPU_1)) { 12662306a36Sopenharmony_ci heart_write(BIT_ULL(HEART_L2_INT_CALL_CPU_1), 12762306a36Sopenharmony_ci &heart_regs->clear_isr); 12862306a36Sopenharmony_ci generic_smp_call_function_interrupt(); 12962306a36Sopenharmony_ci } else 13062306a36Sopenharmony_ci#endif 13162306a36Sopenharmony_ci { 13262306a36Sopenharmony_ci domain = irq_desc_get_handler_data(desc); 13362306a36Sopenharmony_ci ret = generic_handle_domain_irq(domain, __ffs(pend)); 13462306a36Sopenharmony_ci if (ret) 13562306a36Sopenharmony_ci spurious_interrupt(); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void ip30_ack_heart_irq(struct irq_data *d) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci heart_write(BIT_ULL(d->hwirq), &heart_regs->clear_isr); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void ip30_mask_heart_irq(struct irq_data *d) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct heart_irq_data *hd = irq_data_get_irq_chip_data(d); 14762306a36Sopenharmony_ci unsigned long *mask = &per_cpu(irq_enable_mask, hd->cpu); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci clear_bit(d->hwirq, mask); 15062306a36Sopenharmony_ci heart_write(*mask, &heart_regs->imr[hd->cpu]); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void ip30_mask_and_ack_heart_irq(struct irq_data *d) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct heart_irq_data *hd = irq_data_get_irq_chip_data(d); 15662306a36Sopenharmony_ci unsigned long *mask = &per_cpu(irq_enable_mask, hd->cpu); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci clear_bit(d->hwirq, mask); 15962306a36Sopenharmony_ci heart_write(*mask, &heart_regs->imr[hd->cpu]); 16062306a36Sopenharmony_ci heart_write(BIT_ULL(d->hwirq), &heart_regs->clear_isr); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void ip30_unmask_heart_irq(struct irq_data *d) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct heart_irq_data *hd = irq_data_get_irq_chip_data(d); 16662306a36Sopenharmony_ci unsigned long *mask = &per_cpu(irq_enable_mask, hd->cpu); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci set_bit(d->hwirq, mask); 16962306a36Sopenharmony_ci heart_write(*mask, &heart_regs->imr[hd->cpu]); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int ip30_set_heart_irq_affinity(struct irq_data *d, 17362306a36Sopenharmony_ci const struct cpumask *mask, bool force) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct heart_irq_data *hd = irq_data_get_irq_chip_data(d); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (!hd) 17862306a36Sopenharmony_ci return -EINVAL; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (irqd_is_started(d)) 18162306a36Sopenharmony_ci ip30_mask_and_ack_heart_irq(d); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci hd->cpu = cpumask_first_and(mask, cpu_online_mask); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (irqd_is_started(d)) 18662306a36Sopenharmony_ci ip30_unmask_heart_irq(d); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci irq_data_update_effective_affinity(d, cpumask_of(hd->cpu)); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic struct irq_chip heart_irq_chip = { 19462306a36Sopenharmony_ci .name = "HEART", 19562306a36Sopenharmony_ci .irq_ack = ip30_ack_heart_irq, 19662306a36Sopenharmony_ci .irq_mask = ip30_mask_heart_irq, 19762306a36Sopenharmony_ci .irq_mask_ack = ip30_mask_and_ack_heart_irq, 19862306a36Sopenharmony_ci .irq_unmask = ip30_unmask_heart_irq, 19962306a36Sopenharmony_ci .irq_set_affinity = ip30_set_heart_irq_affinity, 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int heart_domain_alloc(struct irq_domain *domain, unsigned int virq, 20362306a36Sopenharmony_ci unsigned int nr_irqs, void *arg) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct irq_alloc_info *info = arg; 20662306a36Sopenharmony_ci struct heart_irq_data *hd; 20762306a36Sopenharmony_ci int hwirq; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (nr_irqs > 1 || !info) 21062306a36Sopenharmony_ci return -EINVAL; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci hd = kzalloc(sizeof(*hd), GFP_KERNEL); 21362306a36Sopenharmony_ci if (!hd) 21462306a36Sopenharmony_ci return -ENOMEM; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci hwirq = heart_alloc_int(); 21762306a36Sopenharmony_ci if (hwirq < 0) { 21862306a36Sopenharmony_ci kfree(hd); 21962306a36Sopenharmony_ci return -EAGAIN; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci irq_domain_set_info(domain, virq, hwirq, &heart_irq_chip, hd, 22262306a36Sopenharmony_ci handle_level_irq, NULL, NULL); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void heart_domain_free(struct irq_domain *domain, 22862306a36Sopenharmony_ci unsigned int virq, unsigned int nr_irqs) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct irq_data *irqd; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (nr_irqs > 1) 23362306a36Sopenharmony_ci return; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci irqd = irq_domain_get_irq_data(domain, virq); 23662306a36Sopenharmony_ci if (irqd) { 23762306a36Sopenharmony_ci clear_bit(irqd->hwirq, heart_irq_map); 23862306a36Sopenharmony_ci kfree(irqd->chip_data); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic const struct irq_domain_ops heart_domain_ops = { 24362306a36Sopenharmony_ci .alloc = heart_domain_alloc, 24462306a36Sopenharmony_ci .free = heart_domain_free, 24562306a36Sopenharmony_ci}; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_civoid __init ip30_install_ipi(void) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci int cpu = smp_processor_id(); 25062306a36Sopenharmony_ci unsigned long *mask = &per_cpu(irq_enable_mask, cpu); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci set_bit(HEART_L2_INT_RESCHED_CPU_0 + cpu, mask); 25362306a36Sopenharmony_ci heart_write(BIT_ULL(HEART_L2_INT_RESCHED_CPU_0 + cpu), 25462306a36Sopenharmony_ci &heart_regs->clear_isr); 25562306a36Sopenharmony_ci set_bit(HEART_L2_INT_CALL_CPU_0 + cpu, mask); 25662306a36Sopenharmony_ci heart_write(BIT_ULL(HEART_L2_INT_CALL_CPU_0 + cpu), 25762306a36Sopenharmony_ci &heart_regs->clear_isr); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci heart_write(*mask, &heart_regs->imr[cpu]); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_civoid __init arch_init_irq(void) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct irq_domain *domain; 26562306a36Sopenharmony_ci struct fwnode_handle *fn; 26662306a36Sopenharmony_ci unsigned long *mask; 26762306a36Sopenharmony_ci int i; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci mips_cpu_irq_init(); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Mask all IRQs. */ 27262306a36Sopenharmony_ci heart_write(HEART_CLR_ALL_MASK, &heart_regs->imr[0]); 27362306a36Sopenharmony_ci heart_write(HEART_CLR_ALL_MASK, &heart_regs->imr[1]); 27462306a36Sopenharmony_ci heart_write(HEART_CLR_ALL_MASK, &heart_regs->imr[2]); 27562306a36Sopenharmony_ci heart_write(HEART_CLR_ALL_MASK, &heart_regs->imr[3]); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Ack everything. */ 27862306a36Sopenharmony_ci heart_write(HEART_ACK_ALL_MASK, &heart_regs->clear_isr); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Enable specific HEART error IRQs for each CPU. */ 28162306a36Sopenharmony_ci mask = &per_cpu(irq_enable_mask, 0); 28262306a36Sopenharmony_ci *mask |= HEART_CPU0_ERR_MASK; 28362306a36Sopenharmony_ci heart_write(*mask, &heart_regs->imr[0]); 28462306a36Sopenharmony_ci mask = &per_cpu(irq_enable_mask, 1); 28562306a36Sopenharmony_ci *mask |= HEART_CPU1_ERR_MASK; 28662306a36Sopenharmony_ci heart_write(*mask, &heart_regs->imr[1]); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* 28962306a36Sopenharmony_ci * Some HEART bits are reserved by hardware or by software convention. 29062306a36Sopenharmony_ci * Mark these as reserved right away so they won't be accidentally 29162306a36Sopenharmony_ci * used later. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci set_bit(HEART_L0_INT_GENERIC, heart_irq_map); 29462306a36Sopenharmony_ci set_bit(HEART_L0_INT_FLOW_CTRL_HWTR_0, heart_irq_map); 29562306a36Sopenharmony_ci set_bit(HEART_L0_INT_FLOW_CTRL_HWTR_1, heart_irq_map); 29662306a36Sopenharmony_ci set_bit(HEART_L2_INT_RESCHED_CPU_0, heart_irq_map); 29762306a36Sopenharmony_ci set_bit(HEART_L2_INT_RESCHED_CPU_1, heart_irq_map); 29862306a36Sopenharmony_ci set_bit(HEART_L2_INT_CALL_CPU_0, heart_irq_map); 29962306a36Sopenharmony_ci set_bit(HEART_L2_INT_CALL_CPU_1, heart_irq_map); 30062306a36Sopenharmony_ci set_bit(HEART_L3_INT_TIMER, heart_irq_map); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Reserve the error interrupts (#51 to #63). */ 30362306a36Sopenharmony_ci for (i = HEART_L4_INT_XWID_ERR_9; i <= HEART_L4_INT_HEART_EXCP; i++) 30462306a36Sopenharmony_ci set_bit(i, heart_irq_map); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci fn = irq_domain_alloc_named_fwnode("HEART"); 30762306a36Sopenharmony_ci WARN_ON(fn == NULL); 30862306a36Sopenharmony_ci if (!fn) 30962306a36Sopenharmony_ci return; 31062306a36Sopenharmony_ci domain = irq_domain_create_linear(fn, HEART_NUM_IRQS, 31162306a36Sopenharmony_ci &heart_domain_ops, NULL); 31262306a36Sopenharmony_ci WARN_ON(domain == NULL); 31362306a36Sopenharmony_ci if (!domain) 31462306a36Sopenharmony_ci return; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci irq_set_default_host(domain); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci irq_set_percpu_devid(IP30_HEART_L0_IRQ); 31962306a36Sopenharmony_ci irq_set_chained_handler_and_data(IP30_HEART_L0_IRQ, ip30_normal_irq, 32062306a36Sopenharmony_ci domain); 32162306a36Sopenharmony_ci irq_set_percpu_devid(IP30_HEART_L1_IRQ); 32262306a36Sopenharmony_ci irq_set_chained_handler_and_data(IP30_HEART_L1_IRQ, ip30_normal_irq, 32362306a36Sopenharmony_ci domain); 32462306a36Sopenharmony_ci irq_set_percpu_devid(IP30_HEART_L2_IRQ); 32562306a36Sopenharmony_ci irq_set_chained_handler_and_data(IP30_HEART_L2_IRQ, ip30_normal_irq, 32662306a36Sopenharmony_ci domain); 32762306a36Sopenharmony_ci irq_set_percpu_devid(IP30_HEART_ERR_IRQ); 32862306a36Sopenharmony_ci irq_set_chained_handler_and_data(IP30_HEART_ERR_IRQ, ip30_error_irq, 32962306a36Sopenharmony_ci domain); 33062306a36Sopenharmony_ci} 331