18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 78c2ecf20Sopenharmony_ci#include <linux/irq.h> 88c2ecf20Sopenharmony_ci#include <asm/irq_cpu.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <loongson1.h> 118c2ecf20Sopenharmony_ci#include <irq.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define LS1X_INTC_REG(n, x) \ 148c2ecf20Sopenharmony_ci ((void __iomem *)KSEG1ADDR(LS1X_INTC_BASE + (n * 0x18) + (x))) 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define LS1X_INTC_INTISR(n) LS1X_INTC_REG(n, 0x0) 178c2ecf20Sopenharmony_ci#define LS1X_INTC_INTIEN(n) LS1X_INTC_REG(n, 0x4) 188c2ecf20Sopenharmony_ci#define LS1X_INTC_INTSET(n) LS1X_INTC_REG(n, 0x8) 198c2ecf20Sopenharmony_ci#define LS1X_INTC_INTCLR(n) LS1X_INTC_REG(n, 0xc) 208c2ecf20Sopenharmony_ci#define LS1X_INTC_INTPOL(n) LS1X_INTC_REG(n, 0x10) 218c2ecf20Sopenharmony_ci#define LS1X_INTC_INTEDGE(n) LS1X_INTC_REG(n, 0x14) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void ls1x_irq_ack(struct irq_data *d) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; 268c2ecf20Sopenharmony_ci unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n)) 298c2ecf20Sopenharmony_ci | (1 << bit), LS1X_INTC_INTCLR(n)); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void ls1x_irq_mask(struct irq_data *d) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; 358c2ecf20Sopenharmony_ci unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n)) 388c2ecf20Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTIEN(n)); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void ls1x_irq_mask_ack(struct irq_data *d) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; 448c2ecf20Sopenharmony_ci unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n)) 478c2ecf20Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTIEN(n)); 488c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n)) 498c2ecf20Sopenharmony_ci | (1 << bit), LS1X_INTC_INTCLR(n)); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void ls1x_irq_unmask(struct irq_data *d) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; 558c2ecf20Sopenharmony_ci unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n)) 588c2ecf20Sopenharmony_ci | (1 << bit), LS1X_INTC_INTIEN(n)); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int ls1x_irq_settype(struct irq_data *d, unsigned int type) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f; 648c2ecf20Sopenharmony_ci unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci switch (type) { 678c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 688c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) 698c2ecf20Sopenharmony_ci | (1 << bit), LS1X_INTC_INTPOL(n)); 708c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) 718c2ecf20Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTEDGE(n)); 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 748c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) 758c2ecf20Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTPOL(n)); 768c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) 778c2ecf20Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTEDGE(n)); 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 808c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) 818c2ecf20Sopenharmony_ci | (1 << bit), LS1X_INTC_INTPOL(n)); 828c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) 838c2ecf20Sopenharmony_ci | (1 << bit), LS1X_INTC_INTEDGE(n)); 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 868c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) 878c2ecf20Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTPOL(n)); 888c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) 898c2ecf20Sopenharmony_ci | (1 << bit), LS1X_INTC_INTEDGE(n)); 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 928c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n)) 938c2ecf20Sopenharmony_ci & ~(1 << bit), LS1X_INTC_INTPOL(n)); 948c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n)) 958c2ecf20Sopenharmony_ci | (1 << bit), LS1X_INTC_INTEDGE(n)); 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci case IRQ_TYPE_NONE: 988c2ecf20Sopenharmony_ci break; 998c2ecf20Sopenharmony_ci default: 1008c2ecf20Sopenharmony_ci return -EINVAL; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic struct irq_chip ls1x_irq_chip = { 1078c2ecf20Sopenharmony_ci .name = "LS1X-INTC", 1088c2ecf20Sopenharmony_ci .irq_ack = ls1x_irq_ack, 1098c2ecf20Sopenharmony_ci .irq_mask = ls1x_irq_mask, 1108c2ecf20Sopenharmony_ci .irq_mask_ack = ls1x_irq_mask_ack, 1118c2ecf20Sopenharmony_ci .irq_unmask = ls1x_irq_unmask, 1128c2ecf20Sopenharmony_ci .irq_set_type = ls1x_irq_settype, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void ls1x_irq_dispatch(int n) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci u32 int_status, irq; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* Get pending sources, masked by current enables */ 1208c2ecf20Sopenharmony_ci int_status = __raw_readl(LS1X_INTC_INTISR(n)) & 1218c2ecf20Sopenharmony_ci __raw_readl(LS1X_INTC_INTIEN(n)); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (int_status) { 1248c2ecf20Sopenharmony_ci irq = LS1X_IRQ(n, __ffs(int_status)); 1258c2ecf20Sopenharmony_ci do_IRQ(irq); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ciasmlinkage void plat_irq_dispatch(void) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci unsigned int pending; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci pending = read_c0_cause() & read_c0_status() & ST0_IM; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (pending & CAUSEF_IP7) 1368c2ecf20Sopenharmony_ci do_IRQ(TIMER_IRQ); 1378c2ecf20Sopenharmony_ci else if (pending & CAUSEF_IP2) 1388c2ecf20Sopenharmony_ci ls1x_irq_dispatch(0); /* INT0 */ 1398c2ecf20Sopenharmony_ci else if (pending & CAUSEF_IP3) 1408c2ecf20Sopenharmony_ci ls1x_irq_dispatch(1); /* INT1 */ 1418c2ecf20Sopenharmony_ci else if (pending & CAUSEF_IP4) 1428c2ecf20Sopenharmony_ci ls1x_irq_dispatch(2); /* INT2 */ 1438c2ecf20Sopenharmony_ci else if (pending & CAUSEF_IP5) 1448c2ecf20Sopenharmony_ci ls1x_irq_dispatch(3); /* INT3 */ 1458c2ecf20Sopenharmony_ci else if (pending & CAUSEF_IP6) 1468c2ecf20Sopenharmony_ci ls1x_irq_dispatch(4); /* INT4 */ 1478c2ecf20Sopenharmony_ci else 1488c2ecf20Sopenharmony_ci spurious_interrupt(); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void __init ls1x_irq_init(int base) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci int n; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Disable interrupts and clear pending, 1578c2ecf20Sopenharmony_ci * setup all IRQs as high level triggered 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci for (n = 0; n < INTN; n++) { 1608c2ecf20Sopenharmony_ci __raw_writel(0x0, LS1X_INTC_INTIEN(n)); 1618c2ecf20Sopenharmony_ci __raw_writel(0xffffffff, LS1X_INTC_INTCLR(n)); 1628c2ecf20Sopenharmony_ci __raw_writel(0xffffffff, LS1X_INTC_INTPOL(n)); 1638c2ecf20Sopenharmony_ci /* set DMA0, DMA1 and DMA2 to edge trigger */ 1648c2ecf20Sopenharmony_ci __raw_writel(n ? 0x0 : 0xe000, LS1X_INTC_INTEDGE(n)); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci for (n = base; n < NR_IRQS; n++) { 1698c2ecf20Sopenharmony_ci irq_set_chip_and_handler(n, &ls1x_irq_chip, 1708c2ecf20Sopenharmony_ci handle_level_irq); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (request_irq(INT0_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) 1748c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d (cascade)\n", INT0_IRQ); 1758c2ecf20Sopenharmony_ci if (request_irq(INT1_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) 1768c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d (cascade)\n", INT1_IRQ); 1778c2ecf20Sopenharmony_ci if (request_irq(INT2_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) 1788c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d (cascade)\n", INT2_IRQ); 1798c2ecf20Sopenharmony_ci if (request_irq(INT3_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) 1808c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d (cascade)\n", INT3_IRQ); 1818c2ecf20Sopenharmony_ci#if defined(CONFIG_LOONGSON1_LS1C) 1828c2ecf20Sopenharmony_ci if (request_irq(INT4_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL)) 1838c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d (cascade)\n", INT4_IRQ); 1848c2ecf20Sopenharmony_ci#endif 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_civoid __init arch_init_irq(void) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci mips_cpu_irq_init(); 1908c2ecf20Sopenharmony_ci ls1x_irq_init(LS1X_IRQ_BASE); 1918c2ecf20Sopenharmony_ci} 192