18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CLPS711X IRQ driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/io.h> 98c2ecf20Sopenharmony_ci#include <linux/irq.h> 108c2ecf20Sopenharmony_ci#include <linux/irqchip.h> 118c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 128c2ecf20Sopenharmony_ci#include <linux/of_address.h> 138c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/exception.h> 178c2ecf20Sopenharmony_ci#include <asm/mach/irq.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define CLPS711X_INTSR1 (0x0240) 208c2ecf20Sopenharmony_ci#define CLPS711X_INTMR1 (0x0280) 218c2ecf20Sopenharmony_ci#define CLPS711X_BLEOI (0x0600) 228c2ecf20Sopenharmony_ci#define CLPS711X_MCEOI (0x0640) 238c2ecf20Sopenharmony_ci#define CLPS711X_TEOI (0x0680) 248c2ecf20Sopenharmony_ci#define CLPS711X_TC1EOI (0x06c0) 258c2ecf20Sopenharmony_ci#define CLPS711X_TC2EOI (0x0700) 268c2ecf20Sopenharmony_ci#define CLPS711X_RTCEOI (0x0740) 278c2ecf20Sopenharmony_ci#define CLPS711X_UMSEOI (0x0780) 288c2ecf20Sopenharmony_ci#define CLPS711X_COEOI (0x07c0) 298c2ecf20Sopenharmony_ci#define CLPS711X_INTSR2 (0x1240) 308c2ecf20Sopenharmony_ci#define CLPS711X_INTMR2 (0x1280) 318c2ecf20Sopenharmony_ci#define CLPS711X_SRXEOF (0x1600) 328c2ecf20Sopenharmony_ci#define CLPS711X_KBDEOI (0x1700) 338c2ecf20Sopenharmony_ci#define CLPS711X_INTSR3 (0x2240) 348c2ecf20Sopenharmony_ci#define CLPS711X_INTMR3 (0x2280) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic const struct { 378c2ecf20Sopenharmony_ci#define CLPS711X_FLAG_EN (1 << 0) 388c2ecf20Sopenharmony_ci#define CLPS711X_FLAG_FIQ (1 << 1) 398c2ecf20Sopenharmony_ci unsigned int flags; 408c2ecf20Sopenharmony_ci phys_addr_t eoi; 418c2ecf20Sopenharmony_ci} clps711x_irqs[] = { 428c2ecf20Sopenharmony_ci [1] = { CLPS711X_FLAG_FIQ, CLPS711X_BLEOI, }, 438c2ecf20Sopenharmony_ci [3] = { CLPS711X_FLAG_FIQ, CLPS711X_MCEOI, }, 448c2ecf20Sopenharmony_ci [4] = { CLPS711X_FLAG_EN, CLPS711X_COEOI, }, 458c2ecf20Sopenharmony_ci [5] = { CLPS711X_FLAG_EN, }, 468c2ecf20Sopenharmony_ci [6] = { CLPS711X_FLAG_EN, }, 478c2ecf20Sopenharmony_ci [7] = { CLPS711X_FLAG_EN, }, 488c2ecf20Sopenharmony_ci [8] = { CLPS711X_FLAG_EN, CLPS711X_TC1EOI, }, 498c2ecf20Sopenharmony_ci [9] = { CLPS711X_FLAG_EN, CLPS711X_TC2EOI, }, 508c2ecf20Sopenharmony_ci [10] = { CLPS711X_FLAG_EN, CLPS711X_RTCEOI, }, 518c2ecf20Sopenharmony_ci [11] = { CLPS711X_FLAG_EN, CLPS711X_TEOI, }, 528c2ecf20Sopenharmony_ci [12] = { CLPS711X_FLAG_EN, }, 538c2ecf20Sopenharmony_ci [13] = { CLPS711X_FLAG_EN, }, 548c2ecf20Sopenharmony_ci [14] = { CLPS711X_FLAG_EN, CLPS711X_UMSEOI, }, 558c2ecf20Sopenharmony_ci [15] = { CLPS711X_FLAG_EN, CLPS711X_SRXEOF, }, 568c2ecf20Sopenharmony_ci [16] = { CLPS711X_FLAG_EN, CLPS711X_KBDEOI, }, 578c2ecf20Sopenharmony_ci [17] = { CLPS711X_FLAG_EN, }, 588c2ecf20Sopenharmony_ci [18] = { CLPS711X_FLAG_EN, }, 598c2ecf20Sopenharmony_ci [28] = { CLPS711X_FLAG_EN, }, 608c2ecf20Sopenharmony_ci [29] = { CLPS711X_FLAG_EN, }, 618c2ecf20Sopenharmony_ci [32] = { CLPS711X_FLAG_FIQ, }, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic struct { 658c2ecf20Sopenharmony_ci void __iomem *base; 668c2ecf20Sopenharmony_ci void __iomem *intmr[3]; 678c2ecf20Sopenharmony_ci void __iomem *intsr[3]; 688c2ecf20Sopenharmony_ci struct irq_domain *domain; 698c2ecf20Sopenharmony_ci struct irq_domain_ops ops; 708c2ecf20Sopenharmony_ci} *clps711x_intc; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic asmlinkage void __exception_irq_entry clps711x_irqh(struct pt_regs *regs) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci u32 irqstat; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci do { 778c2ecf20Sopenharmony_ci irqstat = readw_relaxed(clps711x_intc->intmr[0]) & 788c2ecf20Sopenharmony_ci readw_relaxed(clps711x_intc->intsr[0]); 798c2ecf20Sopenharmony_ci if (irqstat) 808c2ecf20Sopenharmony_ci handle_domain_irq(clps711x_intc->domain, 818c2ecf20Sopenharmony_ci fls(irqstat) - 1, regs); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci irqstat = readw_relaxed(clps711x_intc->intmr[1]) & 848c2ecf20Sopenharmony_ci readw_relaxed(clps711x_intc->intsr[1]); 858c2ecf20Sopenharmony_ci if (irqstat) 868c2ecf20Sopenharmony_ci handle_domain_irq(clps711x_intc->domain, 878c2ecf20Sopenharmony_ci fls(irqstat) - 1 + 16, regs); 888c2ecf20Sopenharmony_ci } while (irqstat); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void clps711x_intc_eoi(struct irq_data *d) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci irq_hw_number_t hwirq = irqd_to_hwirq(d); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hwirq].eoi); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void clps711x_intc_mask(struct irq_data *d) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci irq_hw_number_t hwirq = irqd_to_hwirq(d); 1018c2ecf20Sopenharmony_ci void __iomem *intmr = clps711x_intc->intmr[hwirq / 16]; 1028c2ecf20Sopenharmony_ci u32 tmp; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci tmp = readl_relaxed(intmr); 1058c2ecf20Sopenharmony_ci tmp &= ~(1 << (hwirq % 16)); 1068c2ecf20Sopenharmony_ci writel_relaxed(tmp, intmr); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void clps711x_intc_unmask(struct irq_data *d) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci irq_hw_number_t hwirq = irqd_to_hwirq(d); 1128c2ecf20Sopenharmony_ci void __iomem *intmr = clps711x_intc->intmr[hwirq / 16]; 1138c2ecf20Sopenharmony_ci u32 tmp; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci tmp = readl_relaxed(intmr); 1168c2ecf20Sopenharmony_ci tmp |= 1 << (hwirq % 16); 1178c2ecf20Sopenharmony_ci writel_relaxed(tmp, intmr); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct irq_chip clps711x_intc_chip = { 1218c2ecf20Sopenharmony_ci .name = "clps711x-intc", 1228c2ecf20Sopenharmony_ci .irq_eoi = clps711x_intc_eoi, 1238c2ecf20Sopenharmony_ci .irq_mask = clps711x_intc_mask, 1248c2ecf20Sopenharmony_ci .irq_unmask = clps711x_intc_unmask, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq, 1288c2ecf20Sopenharmony_ci irq_hw_number_t hw) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci irq_flow_handler_t handler = handle_level_irq; 1318c2ecf20Sopenharmony_ci unsigned int flags = 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (!clps711x_irqs[hw].flags) 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) { 1378c2ecf20Sopenharmony_ci handler = handle_bad_irq; 1388c2ecf20Sopenharmony_ci flags |= IRQ_NOAUTOEN; 1398c2ecf20Sopenharmony_ci } else if (clps711x_irqs[hw].eoi) { 1408c2ecf20Sopenharmony_ci handler = handle_fasteoi_irq; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Clear down pending interrupt */ 1448c2ecf20Sopenharmony_ci if (clps711x_irqs[hw].eoi) 1458c2ecf20Sopenharmony_ci writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hw].eoi); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler); 1488c2ecf20Sopenharmony_ci irq_modify_status(virq, IRQ_NOPROBE, flags); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int __init _clps711x_intc_init(struct device_node *np, 1548c2ecf20Sopenharmony_ci phys_addr_t base, resource_size_t size) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci int err; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci clps711x_intc = kzalloc(sizeof(*clps711x_intc), GFP_KERNEL); 1598c2ecf20Sopenharmony_ci if (!clps711x_intc) 1608c2ecf20Sopenharmony_ci return -ENOMEM; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci clps711x_intc->base = ioremap(base, size); 1638c2ecf20Sopenharmony_ci if (!clps711x_intc->base) { 1648c2ecf20Sopenharmony_ci err = -ENOMEM; 1658c2ecf20Sopenharmony_ci goto out_kfree; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci clps711x_intc->intsr[0] = clps711x_intc->base + CLPS711X_INTSR1; 1698c2ecf20Sopenharmony_ci clps711x_intc->intmr[0] = clps711x_intc->base + CLPS711X_INTMR1; 1708c2ecf20Sopenharmony_ci clps711x_intc->intsr[1] = clps711x_intc->base + CLPS711X_INTSR2; 1718c2ecf20Sopenharmony_ci clps711x_intc->intmr[1] = clps711x_intc->base + CLPS711X_INTMR2; 1728c2ecf20Sopenharmony_ci clps711x_intc->intsr[2] = clps711x_intc->base + CLPS711X_INTSR3; 1738c2ecf20Sopenharmony_ci clps711x_intc->intmr[2] = clps711x_intc->base + CLPS711X_INTMR3; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Mask all interrupts */ 1768c2ecf20Sopenharmony_ci writel_relaxed(0, clps711x_intc->intmr[0]); 1778c2ecf20Sopenharmony_ci writel_relaxed(0, clps711x_intc->intmr[1]); 1788c2ecf20Sopenharmony_ci writel_relaxed(0, clps711x_intc->intmr[2]); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci err = irq_alloc_descs(-1, 0, ARRAY_SIZE(clps711x_irqs), numa_node_id()); 1818c2ecf20Sopenharmony_ci if (err < 0) 1828c2ecf20Sopenharmony_ci goto out_iounmap; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci clps711x_intc->ops.map = clps711x_intc_irq_map; 1858c2ecf20Sopenharmony_ci clps711x_intc->ops.xlate = irq_domain_xlate_onecell; 1868c2ecf20Sopenharmony_ci clps711x_intc->domain = 1878c2ecf20Sopenharmony_ci irq_domain_add_legacy(np, ARRAY_SIZE(clps711x_irqs), 1888c2ecf20Sopenharmony_ci 0, 0, &clps711x_intc->ops, NULL); 1898c2ecf20Sopenharmony_ci if (!clps711x_intc->domain) { 1908c2ecf20Sopenharmony_ci err = -ENOMEM; 1918c2ecf20Sopenharmony_ci goto out_irqfree; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci irq_set_default_host(clps711x_intc->domain); 1958c2ecf20Sopenharmony_ci set_handle_irq(clps711x_irqh); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci#ifdef CONFIG_FIQ 1988c2ecf20Sopenharmony_ci init_FIQ(0); 1998c2ecf20Sopenharmony_ci#endif 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ciout_irqfree: 2048c2ecf20Sopenharmony_ci irq_free_descs(0, ARRAY_SIZE(clps711x_irqs)); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciout_iounmap: 2078c2ecf20Sopenharmony_ci iounmap(clps711x_intc->base); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciout_kfree: 2108c2ecf20Sopenharmony_ci kfree(clps711x_intc); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return err; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_civoid __init clps711x_intc_init(phys_addr_t base, resource_size_t size) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci BUG_ON(_clps711x_intc_init(NULL, base, size)); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci#ifdef CONFIG_IRQCHIP 2218c2ecf20Sopenharmony_cistatic int __init clps711x_intc_init_dt(struct device_node *np, 2228c2ecf20Sopenharmony_ci struct device_node *parent) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct resource res; 2258c2ecf20Sopenharmony_ci int err; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci err = of_address_to_resource(np, 0, &res); 2288c2ecf20Sopenharmony_ci if (err) 2298c2ecf20Sopenharmony_ci return err; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return _clps711x_intc_init(np, res.start, resource_size(&res)); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(clps711x, "cirrus,ep7209-intc", clps711x_intc_init_dt); 2348c2ecf20Sopenharmony_ci#endif 235