irq-davinci-aintc.c (4835B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2// 3// Copyright (C) 2006, 2019 Texas Instruments. 4// 5// Interrupt handler for DaVinci boards. 6 7#include <linux/kernel.h> 8#include <linux/init.h> 9#include <linux/interrupt.h> 10#include <linux/irq.h> 11#include <linux/irqchip/irq-davinci-aintc.h> 12#include <linux/io.h> 13#include <linux/irqdomain.h> 14 15#include <asm/exception.h> 16 17#define DAVINCI_AINTC_FIQ_REG0 0x00 18#define DAVINCI_AINTC_FIQ_REG1 0x04 19#define DAVINCI_AINTC_IRQ_REG0 0x08 20#define DAVINCI_AINTC_IRQ_REG1 0x0c 21#define DAVINCI_AINTC_IRQ_IRQENTRY 0x14 22#define DAVINCI_AINTC_IRQ_ENT_REG0 0x18 23#define DAVINCI_AINTC_IRQ_ENT_REG1 0x1c 24#define DAVINCI_AINTC_IRQ_INCTL_REG 0x20 25#define DAVINCI_AINTC_IRQ_EABASE_REG 0x24 26#define DAVINCI_AINTC_IRQ_INTPRI0_REG 0x30 27#define DAVINCI_AINTC_IRQ_INTPRI7_REG 0x4c 28 29static void __iomem *davinci_aintc_base; 30static struct irq_domain *davinci_aintc_irq_domain; 31 32static inline void davinci_aintc_writel(unsigned long value, int offset) 33{ 34 writel_relaxed(value, davinci_aintc_base + offset); 35} 36 37static inline unsigned long davinci_aintc_readl(int offset) 38{ 39 return readl_relaxed(davinci_aintc_base + offset); 40} 41 42static __init void 43davinci_aintc_setup_gc(void __iomem *base, 44 unsigned int irq_start, unsigned int num) 45{ 46 struct irq_chip_generic *gc; 47 struct irq_chip_type *ct; 48 49 gc = irq_get_domain_generic_chip(davinci_aintc_irq_domain, irq_start); 50 gc->reg_base = base; 51 gc->irq_base = irq_start; 52 53 ct = gc->chip_types; 54 ct->chip.irq_ack = irq_gc_ack_set_bit; 55 ct->chip.irq_mask = irq_gc_mask_clr_bit; 56 ct->chip.irq_unmask = irq_gc_mask_set_bit; 57 58 ct->regs.ack = DAVINCI_AINTC_IRQ_REG0; 59 ct->regs.mask = DAVINCI_AINTC_IRQ_ENT_REG0; 60 irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, 61 IRQ_NOREQUEST | IRQ_NOPROBE, 0); 62} 63 64static asmlinkage void __exception_irq_entry 65davinci_aintc_handle_irq(struct pt_regs *regs) 66{ 67 int irqnr = davinci_aintc_readl(DAVINCI_AINTC_IRQ_IRQENTRY); 68 69 /* 70 * Use the formula for entry vector index generation from section 71 * 8.3.3 of the manual. 72 */ 73 irqnr >>= 2; 74 irqnr -= 1; 75 76 generic_handle_domain_irq(davinci_aintc_irq_domain, irqnr); 77} 78 79/* ARM Interrupt Controller Initialization */ 80void __init davinci_aintc_init(const struct davinci_aintc_config *config) 81{ 82 unsigned int irq_off, reg_off, prio, shift; 83 void __iomem *req; 84 int ret, irq_base; 85 const u8 *prios; 86 87 req = request_mem_region(config->reg.start, 88 resource_size(&config->reg), 89 "davinci-cp-intc"); 90 if (!req) { 91 pr_err("%s: register range busy\n", __func__); 92 return; 93 } 94 95 davinci_aintc_base = ioremap(config->reg.start, 96 resource_size(&config->reg)); 97 if (!davinci_aintc_base) { 98 pr_err("%s: unable to ioremap register range\n", __func__); 99 return; 100 } 101 102 /* Clear all interrupt requests */ 103 davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG0); 104 davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG1); 105 davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG0); 106 davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG1); 107 108 /* Disable all interrupts */ 109 davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_ENT_REG0); 110 davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_ENT_REG1); 111 112 /* Interrupts disabled immediately, IRQ entry reflects all */ 113 davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_INCTL_REG); 114 115 /* we don't use the hardware vector table, just its entry addresses */ 116 davinci_aintc_writel(0, DAVINCI_AINTC_IRQ_EABASE_REG); 117 118 /* Clear all interrupt requests */ 119 davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG0); 120 davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG1); 121 davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG0); 122 davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG1); 123 124 prios = config->prios; 125 for (reg_off = DAVINCI_AINTC_IRQ_INTPRI0_REG; 126 reg_off <= DAVINCI_AINTC_IRQ_INTPRI7_REG; reg_off += 4) { 127 for (shift = 0, prio = 0; shift < 32; shift += 4, prios++) 128 prio |= (*prios & 0x07) << shift; 129 davinci_aintc_writel(prio, reg_off); 130 } 131 132 irq_base = irq_alloc_descs(-1, 0, config->num_irqs, 0); 133 if (irq_base < 0) { 134 pr_err("%s: unable to allocate interrupt descriptors: %d\n", 135 __func__, irq_base); 136 return; 137 } 138 139 davinci_aintc_irq_domain = irq_domain_add_legacy(NULL, 140 config->num_irqs, irq_base, 0, 141 &irq_domain_simple_ops, NULL); 142 if (!davinci_aintc_irq_domain) { 143 pr_err("%s: unable to create interrupt domain\n", __func__); 144 return; 145 } 146 147 ret = irq_alloc_domain_generic_chips(davinci_aintc_irq_domain, 32, 1, 148 "AINTC", handle_edge_irq, 149 IRQ_NOREQUEST | IRQ_NOPROBE, 0, 0); 150 if (ret) { 151 pr_err("%s: unable to allocate generic irq chips for domain\n", 152 __func__); 153 return; 154 } 155 156 for (irq_off = 0, reg_off = 0; 157 irq_off < config->num_irqs; 158 irq_off += 32, reg_off += 0x04) 159 davinci_aintc_setup_gc(davinci_aintc_base + reg_off, 160 irq_base + irq_off, 32); 161 162 set_handle_irq(davinci_aintc_handle_irq); 163}