18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/init.h> 68c2ecf20Sopenharmony_ci#include <linux/of.h> 78c2ecf20Sopenharmony_ci#include <linux/of_address.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 108c2ecf20Sopenharmony_ci#include <linux/irqchip.h> 118c2ecf20Sopenharmony_ci#include <linux/irq.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <asm/irq.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define INTC_IRQS 64 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define CK_INTC_ICR 0x00 198c2ecf20Sopenharmony_ci#define CK_INTC_PEN31_00 0x14 208c2ecf20Sopenharmony_ci#define CK_INTC_PEN63_32 0x2c 218c2ecf20Sopenharmony_ci#define CK_INTC_NEN31_00 0x10 228c2ecf20Sopenharmony_ci#define CK_INTC_NEN63_32 0x28 238c2ecf20Sopenharmony_ci#define CK_INTC_SOURCE 0x40 248c2ecf20Sopenharmony_ci#define CK_INTC_DUAL_BASE 0x100 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define GX_INTC_PEN31_00 0x00 278c2ecf20Sopenharmony_ci#define GX_INTC_PEN63_32 0x04 288c2ecf20Sopenharmony_ci#define GX_INTC_NEN31_00 0x40 298c2ecf20Sopenharmony_ci#define GX_INTC_NEN63_32 0x44 308c2ecf20Sopenharmony_ci#define GX_INTC_NMASK31_00 0x50 318c2ecf20Sopenharmony_ci#define GX_INTC_NMASK63_32 0x54 328c2ecf20Sopenharmony_ci#define GX_INTC_SOURCE 0x60 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void __iomem *reg_base; 358c2ecf20Sopenharmony_cistatic struct irq_domain *root_domain; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int nr_irq = INTC_IRQS; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * When controller support pulse signal, the PEN_reg will hold on signal 418c2ecf20Sopenharmony_ci * without software trigger. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * So, to support pulse signal we need to clear IFR_reg and the address of 448c2ecf20Sopenharmony_ci * IFR_offset is NEN_offset - 8. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_cistatic void irq_ck_mask_set_bit(struct irq_data *d) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 498c2ecf20Sopenharmony_ci struct irq_chip_type *ct = irq_data_get_chip_type(d); 508c2ecf20Sopenharmony_ci unsigned long ifr = ct->regs.mask - 8; 518c2ecf20Sopenharmony_ci u32 mask = d->mask; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci irq_gc_lock(gc); 548c2ecf20Sopenharmony_ci *ct->mask_cache |= mask; 558c2ecf20Sopenharmony_ci irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); 568c2ecf20Sopenharmony_ci irq_reg_writel(gc, irq_reg_readl(gc, ifr) & ~mask, ifr); 578c2ecf20Sopenharmony_ci irq_gc_unlock(gc); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void __init ck_set_gc(struct device_node *node, void __iomem *reg_base, 618c2ecf20Sopenharmony_ci u32 mask_reg, u32 irq_base) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct irq_chip_generic *gc; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci gc = irq_get_domain_generic_chip(root_domain, irq_base); 668c2ecf20Sopenharmony_ci gc->reg_base = reg_base; 678c2ecf20Sopenharmony_ci gc->chip_types[0].regs.mask = mask_reg; 688c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 698c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (of_find_property(node, "csky,support-pulse-signal", NULL)) 728c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_unmask = irq_ck_mask_set_bit; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic inline u32 build_channel_val(u32 idx, u32 magic) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci u32 res; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * Set the same index for each channel 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci res = idx | (idx << 8) | (idx << 16) | (idx << 24); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* 858c2ecf20Sopenharmony_ci * Set the channel magic number in descending order. 868c2ecf20Sopenharmony_ci * The magic is 0x00010203 for ck-intc 878c2ecf20Sopenharmony_ci * The magic is 0x03020100 for gx6605s-intc 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci return res | magic; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic inline void setup_irq_channel(u32 magic, void __iomem *reg_addr) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci u32 i; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Setup 64 channel slots */ 978c2ecf20Sopenharmony_ci for (i = 0; i < INTC_IRQS; i += 4) 988c2ecf20Sopenharmony_ci writel(build_channel_val(i, magic), reg_addr + i); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int __init 1028c2ecf20Sopenharmony_cick_intc_init_comm(struct device_node *node, struct device_node *parent) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (parent) { 1078c2ecf20Sopenharmony_ci pr_err("C-SKY Intc not a root irq controller\n"); 1088c2ecf20Sopenharmony_ci return -EINVAL; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci reg_base = of_iomap(node, 0); 1128c2ecf20Sopenharmony_ci if (!reg_base) { 1138c2ecf20Sopenharmony_ci pr_err("C-SKY Intc unable to map: %p.\n", node); 1148c2ecf20Sopenharmony_ci return -EINVAL; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci root_domain = irq_domain_add_linear(node, nr_irq, 1188c2ecf20Sopenharmony_ci &irq_generic_chip_ops, NULL); 1198c2ecf20Sopenharmony_ci if (!root_domain) { 1208c2ecf20Sopenharmony_ci pr_err("C-SKY Intc irq_domain_add failed.\n"); 1218c2ecf20Sopenharmony_ci return -ENOMEM; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci ret = irq_alloc_domain_generic_chips(root_domain, 32, 1, 1258c2ecf20Sopenharmony_ci "csky_intc", handle_level_irq, 1268c2ecf20Sopenharmony_ci IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, 0, 0); 1278c2ecf20Sopenharmony_ci if (ret) { 1288c2ecf20Sopenharmony_ci pr_err("C-SKY Intc irq_alloc_gc failed.\n"); 1298c2ecf20Sopenharmony_ci return -ENOMEM; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic inline bool handle_irq_perbit(struct pt_regs *regs, u32 hwirq, 1368c2ecf20Sopenharmony_ci u32 irq_base) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci if (hwirq == 0) 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci handle_domain_irq(root_domain, irq_base + __fls(hwirq), regs); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 1; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* gx6605s 64 irqs interrupt controller */ 1478c2ecf20Sopenharmony_cistatic void gx_irq_handler(struct pt_regs *regs) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci bool ret; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ciretry: 1528c2ecf20Sopenharmony_ci ret = handle_irq_perbit(regs, 1538c2ecf20Sopenharmony_ci readl(reg_base + GX_INTC_PEN63_32), 32); 1548c2ecf20Sopenharmony_ci if (ret) 1558c2ecf20Sopenharmony_ci goto retry; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci ret = handle_irq_perbit(regs, 1588c2ecf20Sopenharmony_ci readl(reg_base + GX_INTC_PEN31_00), 0); 1598c2ecf20Sopenharmony_ci if (ret) 1608c2ecf20Sopenharmony_ci goto retry; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int __init 1648c2ecf20Sopenharmony_cigx_intc_init(struct device_node *node, struct device_node *parent) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci int ret; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ret = ck_intc_init_comm(node, parent); 1698c2ecf20Sopenharmony_ci if (ret) 1708c2ecf20Sopenharmony_ci return ret; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * Initial enable reg to disable all interrupts 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci writel(0x0, reg_base + GX_INTC_NEN31_00); 1768c2ecf20Sopenharmony_ci writel(0x0, reg_base + GX_INTC_NEN63_32); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* 1798c2ecf20Sopenharmony_ci * Initial mask reg with all unmasked, because we only use enable reg 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci writel(0x0, reg_base + GX_INTC_NMASK31_00); 1828c2ecf20Sopenharmony_ci writel(0x0, reg_base + GX_INTC_NMASK63_32); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci setup_irq_channel(0x03020100, reg_base + GX_INTC_SOURCE); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ck_set_gc(node, reg_base, GX_INTC_NEN31_00, 0); 1878c2ecf20Sopenharmony_ci ck_set_gc(node, reg_base, GX_INTC_NEN63_32, 32); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci set_handle_irq(gx_irq_handler); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(csky_gx6605s_intc, "csky,gx6605s-intc", gx_intc_init); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* 1968c2ecf20Sopenharmony_ci * C-SKY simple 64 irqs interrupt controller, dual-together could support 128 1978c2ecf20Sopenharmony_ci * irqs. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic void ck_irq_handler(struct pt_regs *regs) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci bool ret; 2028c2ecf20Sopenharmony_ci void __iomem *reg_pen_lo = reg_base + CK_INTC_PEN31_00; 2038c2ecf20Sopenharmony_ci void __iomem *reg_pen_hi = reg_base + CK_INTC_PEN63_32; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ciretry: 2068c2ecf20Sopenharmony_ci /* handle 0 - 63 irqs */ 2078c2ecf20Sopenharmony_ci ret = handle_irq_perbit(regs, readl(reg_pen_hi), 32); 2088c2ecf20Sopenharmony_ci if (ret) 2098c2ecf20Sopenharmony_ci goto retry; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ret = handle_irq_perbit(regs, readl(reg_pen_lo), 0); 2128c2ecf20Sopenharmony_ci if (ret) 2138c2ecf20Sopenharmony_ci goto retry; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (nr_irq == INTC_IRQS) 2168c2ecf20Sopenharmony_ci return; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* handle 64 - 127 irqs */ 2198c2ecf20Sopenharmony_ci ret = handle_irq_perbit(regs, 2208c2ecf20Sopenharmony_ci readl(reg_pen_hi + CK_INTC_DUAL_BASE), 96); 2218c2ecf20Sopenharmony_ci if (ret) 2228c2ecf20Sopenharmony_ci goto retry; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci ret = handle_irq_perbit(regs, 2258c2ecf20Sopenharmony_ci readl(reg_pen_lo + CK_INTC_DUAL_BASE), 64); 2268c2ecf20Sopenharmony_ci if (ret) 2278c2ecf20Sopenharmony_ci goto retry; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int __init 2318c2ecf20Sopenharmony_cick_intc_init(struct device_node *node, struct device_node *parent) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci int ret; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ret = ck_intc_init_comm(node, parent); 2368c2ecf20Sopenharmony_ci if (ret) 2378c2ecf20Sopenharmony_ci return ret; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Initial enable reg to disable all interrupts */ 2408c2ecf20Sopenharmony_ci writel(0, reg_base + CK_INTC_NEN31_00); 2418c2ecf20Sopenharmony_ci writel(0, reg_base + CK_INTC_NEN63_32); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* Enable irq intc */ 2448c2ecf20Sopenharmony_ci writel(BIT(31), reg_base + CK_INTC_ICR); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci ck_set_gc(node, reg_base, CK_INTC_NEN31_00, 0); 2478c2ecf20Sopenharmony_ci ck_set_gc(node, reg_base, CK_INTC_NEN63_32, 32); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci setup_irq_channel(0x00010203, reg_base + CK_INTC_SOURCE); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci set_handle_irq(ck_irq_handler); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(ck_intc, "csky,apb-intc", ck_intc_init); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int __init 2588c2ecf20Sopenharmony_cick_dual_intc_init(struct device_node *node, struct device_node *parent) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci int ret; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* dual-apb-intc up to 128 irq sources*/ 2638c2ecf20Sopenharmony_ci nr_irq = INTC_IRQS * 2; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = ck_intc_init(node, parent); 2668c2ecf20Sopenharmony_ci if (ret) 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Initial enable reg to disable all interrupts */ 2708c2ecf20Sopenharmony_ci writel(0, reg_base + CK_INTC_NEN31_00 + CK_INTC_DUAL_BASE); 2718c2ecf20Sopenharmony_ci writel(0, reg_base + CK_INTC_NEN63_32 + CK_INTC_DUAL_BASE); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci ck_set_gc(node, reg_base + CK_INTC_DUAL_BASE, CK_INTC_NEN31_00, 64); 2748c2ecf20Sopenharmony_ci ck_set_gc(node, reg_base + CK_INTC_DUAL_BASE, CK_INTC_NEN63_32, 96); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci setup_irq_channel(0x00010203, 2778c2ecf20Sopenharmony_ci reg_base + CK_INTC_SOURCE + CK_INTC_DUAL_BASE); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci return 0; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(ck_dual_intc, "csky,dual-apb-intc", ck_dual_intc_init); 282