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