1 /* 2 * Conexant Digicolor SoCs IRQ chip driver 3 * 4 * Author: Baruch Siach <baruch@tkos.co.il> 5 * 6 * Copyright (C) 2014 Paradox Innovation Ltd. 7 * 8 * This file is licensed under the terms of the GNU General Public 9 * License version 2. This program is licensed "as is" without any 10 * warranty of any kind, whether express or implied. 11 */ 12 13 #include <linux/io.h> 14 #include <linux/irq.h> 15 #include <linux/of.h> 16 #include <linux/of_address.h> 17 #include <linux/of_irq.h> 18 #include <linux/mfd/syscon.h> 19 #include <linux/regmap.h> 20 21 #include <asm/exception.h> 22 23 #include "irqchip.h" 24 25 #define UC_IRQ_CONTROL 0x04 26 27 #define IC_FLAG_CLEAR_LO 0x00 28 #define IC_FLAG_CLEAR_XLO 0x04 29 #define IC_INT0ENABLE_LO 0x10 30 #define IC_INT0ENABLE_XLO 0x14 31 #define IC_INT0STATUS_LO 0x18 32 #define IC_INT0STATUS_XLO 0x1c 33 34 static struct irq_domain *digicolor_irq_domain; 35 36 static void __exception_irq_entry digicolor_handle_irq(struct pt_regs *regs) 37 { 38 struct irq_domain_chip_generic *dgc = digicolor_irq_domain->gc; 39 struct irq_chip_generic *gc = dgc->gc[0]; 40 u32 status, hwirq; 41 42 do { 43 status = irq_reg_readl(gc, IC_INT0STATUS_LO); 44 if (status) { 45 hwirq = ffs(status) - 1; 46 } else { 47 status = irq_reg_readl(gc, IC_INT0STATUS_XLO); 48 if (status) 49 hwirq = ffs(status) - 1 + 32; 50 else 51 return; 52 } 53 54 handle_domain_irq(digicolor_irq_domain, hwirq, regs); 55 } while (1); 56 } 57 58 static void __init digicolor_set_gc(void __iomem *reg_base, unsigned irq_base, 59 unsigned en_reg, unsigned ack_reg) 60 { 61 struct irq_chip_generic *gc; 62 63 gc = irq_get_domain_generic_chip(digicolor_irq_domain, irq_base); 64 gc->reg_base = reg_base; 65 gc->chip_types[0].regs.ack = ack_reg; 66 gc->chip_types[0].regs.mask = en_reg; 67 gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; 68 gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 69 gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 70 } 71 72 static int __init digicolor_of_init(struct device_node *node, 73 struct device_node *parent) 74 { 75 static void __iomem *reg_base; 76 unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; 77 struct regmap *ucregs; 78 int ret; 79 80 reg_base = of_iomap(node, 0); 81 if (!reg_base) { 82 pr_err("%s: unable to map IC registers\n", node->full_name); 83 return -ENXIO; 84 } 85 86 /* disable all interrupts */ 87 writel(0, reg_base + IC_INT0ENABLE_LO); 88 writel(0, reg_base + IC_INT0ENABLE_XLO); 89 90 ucregs = syscon_regmap_lookup_by_phandle(node, "syscon"); 91 if (IS_ERR(ucregs)) { 92 pr_err("%s: unable to map UC registers\n", node->full_name); 93 return PTR_ERR(ucregs); 94 } 95 /* channel 1, regular IRQs */ 96 regmap_write(ucregs, UC_IRQ_CONTROL, 1); 97 98 digicolor_irq_domain = 99 irq_domain_add_linear(node, 64, &irq_generic_chip_ops, NULL); 100 if (!digicolor_irq_domain) { 101 pr_err("%s: unable to create IRQ domain\n", node->full_name); 102 return -ENOMEM; 103 } 104 105 ret = irq_alloc_domain_generic_chips(digicolor_irq_domain, 32, 1, 106 "digicolor_irq", handle_level_irq, 107 clr, 0, 0); 108 if (ret) { 109 pr_err("%s: unable to allocate IRQ gc\n", node->full_name); 110 return ret; 111 } 112 113 digicolor_set_gc(reg_base, 0, IC_INT0ENABLE_LO, IC_FLAG_CLEAR_LO); 114 digicolor_set_gc(reg_base, 32, IC_INT0ENABLE_XLO, IC_FLAG_CLEAR_XLO); 115 116 set_handle_irq(digicolor_handle_irq); 117 118 return 0; 119 } 120 IRQCHIP_DECLARE(conexant_digicolor_ic, "cnxt,cx92755-ic", digicolor_of_init); 121