18041dfbdSBaruch Siach /* 28041dfbdSBaruch Siach * Conexant Digicolor SoCs IRQ chip driver 38041dfbdSBaruch Siach * 48041dfbdSBaruch Siach * Author: Baruch Siach <baruch@tkos.co.il> 58041dfbdSBaruch Siach * 68041dfbdSBaruch Siach * Copyright (C) 2014 Paradox Innovation Ltd. 78041dfbdSBaruch Siach * 88041dfbdSBaruch Siach * This file is licensed under the terms of the GNU General Public 98041dfbdSBaruch Siach * License version 2. This program is licensed "as is" without any 108041dfbdSBaruch Siach * warranty of any kind, whether express or implied. 118041dfbdSBaruch Siach */ 128041dfbdSBaruch Siach 138041dfbdSBaruch Siach #include <linux/io.h> 148041dfbdSBaruch Siach #include <linux/irq.h> 1541a83e06SJoel Porquet #include <linux/irqchip.h> 168041dfbdSBaruch Siach #include <linux/of.h> 178041dfbdSBaruch Siach #include <linux/of_address.h> 188041dfbdSBaruch Siach #include <linux/of_irq.h> 198041dfbdSBaruch Siach #include <linux/mfd/syscon.h> 208041dfbdSBaruch Siach #include <linux/regmap.h> 218041dfbdSBaruch Siach 228041dfbdSBaruch Siach #include <asm/exception.h> 238041dfbdSBaruch Siach 248041dfbdSBaruch Siach #define UC_IRQ_CONTROL 0x04 258041dfbdSBaruch Siach 268041dfbdSBaruch Siach #define IC_FLAG_CLEAR_LO 0x00 278041dfbdSBaruch Siach #define IC_FLAG_CLEAR_XLO 0x04 288041dfbdSBaruch Siach #define IC_INT0ENABLE_LO 0x10 298041dfbdSBaruch Siach #define IC_INT0ENABLE_XLO 0x14 308041dfbdSBaruch Siach #define IC_INT0STATUS_LO 0x18 318041dfbdSBaruch Siach #define IC_INT0STATUS_XLO 0x1c 328041dfbdSBaruch Siach 338041dfbdSBaruch Siach static struct irq_domain *digicolor_irq_domain; 348041dfbdSBaruch Siach 358041dfbdSBaruch Siach static void __exception_irq_entry digicolor_handle_irq(struct pt_regs *regs) 368041dfbdSBaruch Siach { 378041dfbdSBaruch Siach struct irq_domain_chip_generic *dgc = digicolor_irq_domain->gc; 388041dfbdSBaruch Siach struct irq_chip_generic *gc = dgc->gc[0]; 398041dfbdSBaruch Siach u32 status, hwirq; 408041dfbdSBaruch Siach 418041dfbdSBaruch Siach do { 428041dfbdSBaruch Siach status = irq_reg_readl(gc, IC_INT0STATUS_LO); 438041dfbdSBaruch Siach if (status) { 448041dfbdSBaruch Siach hwirq = ffs(status) - 1; 458041dfbdSBaruch Siach } else { 468041dfbdSBaruch Siach status = irq_reg_readl(gc, IC_INT0STATUS_XLO); 478041dfbdSBaruch Siach if (status) 488041dfbdSBaruch Siach hwirq = ffs(status) - 1 + 32; 498041dfbdSBaruch Siach else 508041dfbdSBaruch Siach return; 518041dfbdSBaruch Siach } 528041dfbdSBaruch Siach 538041dfbdSBaruch Siach handle_domain_irq(digicolor_irq_domain, hwirq, regs); 548041dfbdSBaruch Siach } while (1); 558041dfbdSBaruch Siach } 568041dfbdSBaruch Siach 5700e6743bSBaruch Siach static void __init digicolor_set_gc(void __iomem *reg_base, unsigned irq_base, 588041dfbdSBaruch Siach unsigned en_reg, unsigned ack_reg) 598041dfbdSBaruch Siach { 608041dfbdSBaruch Siach struct irq_chip_generic *gc; 618041dfbdSBaruch Siach 628041dfbdSBaruch Siach gc = irq_get_domain_generic_chip(digicolor_irq_domain, irq_base); 638041dfbdSBaruch Siach gc->reg_base = reg_base; 648041dfbdSBaruch Siach gc->chip_types[0].regs.ack = ack_reg; 658041dfbdSBaruch Siach gc->chip_types[0].regs.mask = en_reg; 668041dfbdSBaruch Siach gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; 678041dfbdSBaruch Siach gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 688041dfbdSBaruch Siach gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 698041dfbdSBaruch Siach } 708041dfbdSBaruch Siach 718041dfbdSBaruch Siach static int __init digicolor_of_init(struct device_node *node, 728041dfbdSBaruch Siach struct device_node *parent) 738041dfbdSBaruch Siach { 74acc80c39SJulia Lawall void __iomem *reg_base; 758041dfbdSBaruch Siach unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; 768041dfbdSBaruch Siach struct regmap *ucregs; 778041dfbdSBaruch Siach int ret; 788041dfbdSBaruch Siach 798041dfbdSBaruch Siach reg_base = of_iomap(node, 0); 808041dfbdSBaruch Siach if (!reg_base) { 81*e81f54c6SRob Herring pr_err("%pOF: unable to map IC registers\n", node); 828041dfbdSBaruch Siach return -ENXIO; 838041dfbdSBaruch Siach } 848041dfbdSBaruch Siach 858041dfbdSBaruch Siach /* disable all interrupts */ 868041dfbdSBaruch Siach writel(0, reg_base + IC_INT0ENABLE_LO); 878041dfbdSBaruch Siach writel(0, reg_base + IC_INT0ENABLE_XLO); 888041dfbdSBaruch Siach 898041dfbdSBaruch Siach ucregs = syscon_regmap_lookup_by_phandle(node, "syscon"); 908041dfbdSBaruch Siach if (IS_ERR(ucregs)) { 91*e81f54c6SRob Herring pr_err("%pOF: unable to map UC registers\n", node); 928041dfbdSBaruch Siach return PTR_ERR(ucregs); 938041dfbdSBaruch Siach } 948041dfbdSBaruch Siach /* channel 1, regular IRQs */ 958041dfbdSBaruch Siach regmap_write(ucregs, UC_IRQ_CONTROL, 1); 968041dfbdSBaruch Siach 978041dfbdSBaruch Siach digicolor_irq_domain = 988041dfbdSBaruch Siach irq_domain_add_linear(node, 64, &irq_generic_chip_ops, NULL); 998041dfbdSBaruch Siach if (!digicolor_irq_domain) { 100*e81f54c6SRob Herring pr_err("%pOF: unable to create IRQ domain\n", node); 1018041dfbdSBaruch Siach return -ENOMEM; 1028041dfbdSBaruch Siach } 1038041dfbdSBaruch Siach 1048041dfbdSBaruch Siach ret = irq_alloc_domain_generic_chips(digicolor_irq_domain, 32, 1, 1058041dfbdSBaruch Siach "digicolor_irq", handle_level_irq, 1068041dfbdSBaruch Siach clr, 0, 0); 1078041dfbdSBaruch Siach if (ret) { 108*e81f54c6SRob Herring pr_err("%pOF: unable to allocate IRQ gc\n", node); 1098041dfbdSBaruch Siach return ret; 1108041dfbdSBaruch Siach } 1118041dfbdSBaruch Siach 1128041dfbdSBaruch Siach digicolor_set_gc(reg_base, 0, IC_INT0ENABLE_LO, IC_FLAG_CLEAR_LO); 1138041dfbdSBaruch Siach digicolor_set_gc(reg_base, 32, IC_INT0ENABLE_XLO, IC_FLAG_CLEAR_XLO); 1148041dfbdSBaruch Siach 1158041dfbdSBaruch Siach set_handle_irq(digicolor_handle_irq); 1168041dfbdSBaruch Siach 1178041dfbdSBaruch Siach return 0; 1188041dfbdSBaruch Siach } 1198041dfbdSBaruch Siach IRQCHIP_DECLARE(conexant_digicolor_ic, "cnxt,cx92755-ic", digicolor_of_init); 120