162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/interrupt.h> 762306a36Sopenharmony_ci#include <linux/irq.h> 862306a36Sopenharmony_ci#include <asm/irq_cpu.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <loongson1.h> 1162306a36Sopenharmony_ci#include <irq.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define LS1X_INTC_REG(n, x) \ 1462306a36Sopenharmony_ci ((void __iomem *)KSEG1ADDR(LS1X_INTC_BASE + (n * 0x18) + (x))) 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define LS1X_INTC_INTISR(n) LS1X_INTC_REG(n, 0x0) 1762306a36Sopenharmony_ci#define LS1X_INTC_INTIEN(n) LS1X_INTC_REG(n, 0x4) 1862306a36Sopenharmony_ci#define LS1X_INTC_INTSET(n) LS1X_INTC_REG(n, 0x8) 1962306a36Sopenharmony_ci#define LS1X_INTC_INTCLR(n) LS1X_INTC_REG(n, 0xc) 2062306a36Sopenharmony_ci#define LS1X_INTC_INTPOL(n) LS1X_INTC_REG(n, 0x10) 2162306a36Sopenharmony_ci#define LS1X_INTC_INTEDGE(n) LS1X_INTC_REG(n, 0x14) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void ls1x_irq_ack(struct irq_data *d) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; 2662306a36Sopenharmony_ci unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n)) 2962306a36Sopenharmony_ci | (1 << bit), LS1X_INTC_INTCLR(n)); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void ls1x_irq_mask(struct irq_data *d) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; 3562306a36Sopenharmony_ci unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n)) 3862306a36Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTIEN(n)); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void ls1x_irq_mask_ack(struct irq_data *d) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; 4462306a36Sopenharmony_ci unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n)) 4762306a36Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTIEN(n)); 4862306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n)) 4962306a36Sopenharmony_ci | (1 << bit), LS1X_INTC_INTCLR(n)); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void ls1x_irq_unmask(struct irq_data *d) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; 5562306a36Sopenharmony_ci unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n)) 5862306a36Sopenharmony_ci | (1 << bit), LS1X_INTC_INTIEN(n)); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int ls1x_irq_settype(struct irq_data *d, unsigned int type) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; 6462306a36Sopenharmony_ci unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci switch (type) { 6762306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 6862306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) 6962306a36Sopenharmony_ci | (1 << bit), LS1X_INTC_INTPOL(n)); 7062306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) 7162306a36Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTEDGE(n)); 7262306a36Sopenharmony_ci break; 7362306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 7462306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) 7562306a36Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTPOL(n)); 7662306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) 7762306a36Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTEDGE(n)); 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 8062306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) 8162306a36Sopenharmony_ci | (1 << bit), LS1X_INTC_INTPOL(n)); 8262306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) 8362306a36Sopenharmony_ci | (1 << bit), LS1X_INTC_INTEDGE(n)); 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 8662306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) 8762306a36Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTPOL(n)); 8862306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) 8962306a36Sopenharmony_ci | (1 << bit), LS1X_INTC_INTEDGE(n)); 9062306a36Sopenharmony_ci break; 9162306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 9262306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) 9362306a36Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTPOL(n)); 9462306a36Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) 9562306a36Sopenharmony_ci | (1 << bit), LS1X_INTC_INTEDGE(n)); 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci case IRQ_TYPE_NONE: 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci default: 10062306a36Sopenharmony_ci return -EINVAL; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic struct irq_chip ls1x_irq_chip = { 10762306a36Sopenharmony_ci .name = "LS1X-INTC", 10862306a36Sopenharmony_ci .irq_ack = ls1x_irq_ack, 10962306a36Sopenharmony_ci .irq_mask = ls1x_irq_mask, 11062306a36Sopenharmony_ci .irq_mask_ack = ls1x_irq_mask_ack, 11162306a36Sopenharmony_ci .irq_unmask = ls1x_irq_unmask, 11262306a36Sopenharmony_ci .irq_set_type = ls1x_irq_settype, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void ls1x_irq_dispatch(int n) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci u32 int_status, irq; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Get pending sources, masked by current enables */ 12062306a36Sopenharmony_ci int_status = __raw_readl(LS1X_INTC_INTISR(n)) & 12162306a36Sopenharmony_ci __raw_readl(LS1X_INTC_INTIEN(n)); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (int_status) { 12462306a36Sopenharmony_ci irq = LS1X_IRQ(n, __ffs(int_status)); 12562306a36Sopenharmony_ci do_IRQ(irq); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ciasmlinkage void plat_irq_dispatch(void) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci unsigned int pending; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci pending = read_c0_cause() & read_c0_status() & ST0_IM; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (pending & CAUSEF_IP7) 13662306a36Sopenharmony_ci do_IRQ(TIMER_IRQ); 13762306a36Sopenharmony_ci else if (pending & CAUSEF_IP2) 13862306a36Sopenharmony_ci ls1x_irq_dispatch(0); /* INT0 */ 13962306a36Sopenharmony_ci else if (pending & CAUSEF_IP3) 14062306a36Sopenharmony_ci ls1x_irq_dispatch(1); /* INT1 */ 14162306a36Sopenharmony_ci else if (pending & CAUSEF_IP4) 14262306a36Sopenharmony_ci ls1x_irq_dispatch(2); /* INT2 */ 14362306a36Sopenharmony_ci else if (pending & CAUSEF_IP5) 14462306a36Sopenharmony_ci ls1x_irq_dispatch(3); /* INT3 */ 14562306a36Sopenharmony_ci else if (pending & CAUSEF_IP6) 14662306a36Sopenharmony_ci ls1x_irq_dispatch(4); /* INT4 */ 14762306a36Sopenharmony_ci else 14862306a36Sopenharmony_ci spurious_interrupt(); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void __init ls1x_irq_init(int base) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci int n; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Disable interrupts and clear pending, 15762306a36Sopenharmony_ci * setup all IRQs as high level triggered 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci for (n = 0; n < INTN; n++) { 16062306a36Sopenharmony_ci __raw_writel(0x0, LS1X_INTC_INTIEN(n)); 16162306a36Sopenharmony_ci __raw_writel(0xffffffff, LS1X_INTC_INTCLR(n)); 16262306a36Sopenharmony_ci __raw_writel(0xffffffff, LS1X_INTC_INTPOL(n)); 16362306a36Sopenharmony_ci /* set DMA0, DMA1 and DMA2 to edge trigger */ 16462306a36Sopenharmony_ci __raw_writel(n ? 0x0 : 0xe000, LS1X_INTC_INTEDGE(n)); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci for (n = base; n < NR_IRQS; n++) { 16962306a36Sopenharmony_ci irq_set_chip_and_handler(n, &ls1x_irq_chip, 17062306a36Sopenharmony_ci handle_level_irq); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (request_irq(INT0_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) 17462306a36Sopenharmony_ci pr_err("Failed to request irq %d (cascade)\n", INT0_IRQ); 17562306a36Sopenharmony_ci if (request_irq(INT1_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) 17662306a36Sopenharmony_ci pr_err("Failed to request irq %d (cascade)\n", INT1_IRQ); 17762306a36Sopenharmony_ci if (request_irq(INT2_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) 17862306a36Sopenharmony_ci pr_err("Failed to request irq %d (cascade)\n", INT2_IRQ); 17962306a36Sopenharmony_ci if (request_irq(INT3_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) 18062306a36Sopenharmony_ci pr_err("Failed to request irq %d (cascade)\n", INT3_IRQ); 18162306a36Sopenharmony_ci#if defined(CONFIG_LOONGSON1_LS1C) 18262306a36Sopenharmony_ci if (request_irq(INT4_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) 18362306a36Sopenharmony_ci pr_err("Failed to request irq %d (cascade)\n", INT4_IRQ); 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_civoid __init arch_init_irq(void) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci mips_cpu_irq_init(); 19062306a36Sopenharmony_ci ls1x_irq_init(LS1X_IRQ_BASE); 19162306a36Sopenharmony_ci} 192