irq-pic32-evic.c (8262B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Cristian Birsan <cristian.birsan@microchip.com> 4 * Joshua Henderson <joshua.henderson@microchip.com> 5 * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. 6 */ 7#include <linux/kernel.h> 8#include <linux/module.h> 9#include <linux/interrupt.h> 10#include <linux/irqdomain.h> 11#include <linux/of_address.h> 12#include <linux/slab.h> 13#include <linux/io.h> 14#include <linux/irqchip.h> 15#include <linux/irq.h> 16 17#include <asm/irq.h> 18#include <asm/traps.h> 19#include <asm/mach-pic32/pic32.h> 20 21#define REG_INTCON 0x0000 22#define REG_INTSTAT 0x0020 23#define REG_IFS_OFFSET 0x0040 24#define REG_IEC_OFFSET 0x00C0 25#define REG_IPC_OFFSET 0x0140 26#define REG_OFF_OFFSET 0x0540 27 28#define MAJPRI_MASK 0x07 29#define SUBPRI_MASK 0x03 30#define PRIORITY_MASK 0x1F 31 32#define PIC32_INT_PRI(pri, subpri) \ 33 ((((pri) & MAJPRI_MASK) << 2) | ((subpri) & SUBPRI_MASK)) 34 35struct evic_chip_data { 36 u32 irq_types[NR_IRQS]; 37 u32 ext_irqs[8]; 38}; 39 40static struct irq_domain *evic_irq_domain; 41static void __iomem *evic_base; 42 43asmlinkage void __weak plat_irq_dispatch(void) 44{ 45 unsigned int hwirq; 46 47 hwirq = readl(evic_base + REG_INTSTAT) & 0xFF; 48 do_domain_IRQ(evic_irq_domain, hwirq); 49} 50 51static struct evic_chip_data *irqd_to_priv(struct irq_data *data) 52{ 53 return (struct evic_chip_data *)data->domain->host_data; 54} 55 56static int pic32_set_ext_polarity(int bit, u32 type) 57{ 58 /* 59 * External interrupts can be either edge rising or edge falling, 60 * but not both. 61 */ 62 switch (type) { 63 case IRQ_TYPE_EDGE_RISING: 64 writel(BIT(bit), evic_base + PIC32_SET(REG_INTCON)); 65 break; 66 case IRQ_TYPE_EDGE_FALLING: 67 writel(BIT(bit), evic_base + PIC32_CLR(REG_INTCON)); 68 break; 69 default: 70 return -EINVAL; 71 } 72 73 return 0; 74} 75 76static int pic32_set_type_edge(struct irq_data *data, 77 unsigned int flow_type) 78{ 79 struct evic_chip_data *priv = irqd_to_priv(data); 80 int ret; 81 int i; 82 83 if (!(flow_type & IRQ_TYPE_EDGE_BOTH)) 84 return -EBADR; 85 86 /* set polarity for external interrupts only */ 87 for (i = 0; i < ARRAY_SIZE(priv->ext_irqs); i++) { 88 if (priv->ext_irqs[i] == data->hwirq) { 89 ret = pic32_set_ext_polarity(i, flow_type); 90 if (ret) 91 return ret; 92 } 93 } 94 95 irqd_set_trigger_type(data, flow_type); 96 97 return IRQ_SET_MASK_OK; 98} 99 100static void pic32_bind_evic_interrupt(int irq, int set) 101{ 102 writel(set, evic_base + REG_OFF_OFFSET + irq * 4); 103} 104 105static void pic32_set_irq_priority(int irq, int priority) 106{ 107 u32 reg, shift; 108 109 reg = irq / 4; 110 shift = (irq % 4) * 8; 111 112 writel(PRIORITY_MASK << shift, 113 evic_base + PIC32_CLR(REG_IPC_OFFSET + reg * 0x10)); 114 writel(priority << shift, 115 evic_base + PIC32_SET(REG_IPC_OFFSET + reg * 0x10)); 116} 117 118#define IRQ_REG_MASK(_hwirq, _reg, _mask) \ 119 do { \ 120 _reg = _hwirq / 32; \ 121 _mask = 1 << (_hwirq % 32); \ 122 } while (0) 123 124static int pic32_irq_domain_map(struct irq_domain *d, unsigned int virq, 125 irq_hw_number_t hw) 126{ 127 struct evic_chip_data *priv = d->host_data; 128 struct irq_data *data; 129 int ret; 130 u32 iecclr, ifsclr; 131 u32 reg, mask; 132 133 ret = irq_map_generic_chip(d, virq, hw); 134 if (ret) 135 return ret; 136 137 /* 138 * Piggyback on xlate function to move to an alternate chip as necessary 139 * at time of mapping instead of allowing the flow handler/chip to be 140 * changed later. This requires all interrupts to be configured through 141 * DT. 142 */ 143 if (priv->irq_types[hw] & IRQ_TYPE_SENSE_MASK) { 144 data = irq_domain_get_irq_data(d, virq); 145 irqd_set_trigger_type(data, priv->irq_types[hw]); 146 irq_setup_alt_chip(data, priv->irq_types[hw]); 147 } 148 149 IRQ_REG_MASK(hw, reg, mask); 150 151 iecclr = PIC32_CLR(REG_IEC_OFFSET + reg * 0x10); 152 ifsclr = PIC32_CLR(REG_IFS_OFFSET + reg * 0x10); 153 154 /* mask and clear flag */ 155 writel(mask, evic_base + iecclr); 156 writel(mask, evic_base + ifsclr); 157 158 /* default priority is required */ 159 pic32_set_irq_priority(hw, PIC32_INT_PRI(2, 0)); 160 161 return ret; 162} 163 164int pic32_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, 165 const u32 *intspec, unsigned int intsize, 166 irq_hw_number_t *out_hwirq, unsigned int *out_type) 167{ 168 struct evic_chip_data *priv = d->host_data; 169 170 if (WARN_ON(intsize < 2)) 171 return -EINVAL; 172 173 if (WARN_ON(intspec[0] >= NR_IRQS)) 174 return -EINVAL; 175 176 *out_hwirq = intspec[0]; 177 *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; 178 179 priv->irq_types[intspec[0]] = intspec[1] & IRQ_TYPE_SENSE_MASK; 180 181 return 0; 182} 183 184static const struct irq_domain_ops pic32_irq_domain_ops = { 185 .map = pic32_irq_domain_map, 186 .xlate = pic32_irq_domain_xlate, 187}; 188 189static void __init pic32_ext_irq_of_init(struct irq_domain *domain) 190{ 191 struct device_node *node = irq_domain_get_of_node(domain); 192 struct evic_chip_data *priv = domain->host_data; 193 struct property *prop; 194 const __le32 *p; 195 u32 hwirq; 196 int i = 0; 197 const char *pname = "microchip,external-irqs"; 198 199 of_property_for_each_u32(node, pname, prop, p, hwirq) { 200 if (i >= ARRAY_SIZE(priv->ext_irqs)) { 201 pr_warn("More than %d external irq, skip rest\n", 202 ARRAY_SIZE(priv->ext_irqs)); 203 break; 204 } 205 206 priv->ext_irqs[i] = hwirq; 207 i++; 208 } 209} 210 211static int __init pic32_of_init(struct device_node *node, 212 struct device_node *parent) 213{ 214 struct irq_chip_generic *gc; 215 struct evic_chip_data *priv; 216 unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; 217 int nchips, ret; 218 int i; 219 220 nchips = DIV_ROUND_UP(NR_IRQS, 32); 221 222 evic_base = of_iomap(node, 0); 223 if (!evic_base) 224 return -ENOMEM; 225 226 priv = kcalloc(nchips, sizeof(*priv), GFP_KERNEL); 227 if (!priv) { 228 ret = -ENOMEM; 229 goto err_iounmap; 230 } 231 232 evic_irq_domain = irq_domain_add_linear(node, nchips * 32, 233 &pic32_irq_domain_ops, 234 priv); 235 if (!evic_irq_domain) { 236 ret = -ENOMEM; 237 goto err_free_priv; 238 } 239 240 /* 241 * The PIC32 EVIC has a linear list of irqs and the type of each 242 * irq is determined by the hardware peripheral the EVIC is arbitrating. 243 * These irq types are defined in the datasheet as "persistent" and 244 * "non-persistent" which are mapped here to level and edge 245 * respectively. To manage the different flow handler requirements of 246 * each irq type, different chip_types are used. 247 */ 248 ret = irq_alloc_domain_generic_chips(evic_irq_domain, 32, 2, 249 "evic-level", handle_level_irq, 250 clr, 0, 0); 251 if (ret) 252 goto err_domain_remove; 253 254 board_bind_eic_interrupt = &pic32_bind_evic_interrupt; 255 256 for (i = 0; i < nchips; i++) { 257 u32 ifsclr = PIC32_CLR(REG_IFS_OFFSET + (i * 0x10)); 258 u32 iec = REG_IEC_OFFSET + (i * 0x10); 259 260 gc = irq_get_domain_generic_chip(evic_irq_domain, i * 32); 261 262 gc->reg_base = evic_base; 263 gc->unused = 0; 264 265 /* 266 * Level/persistent interrupts have a special requirement that 267 * the condition generating the interrupt be cleared before the 268 * interrupt flag (ifs) can be cleared. chip.irq_eoi is used to 269 * complete the interrupt with an ack. 270 */ 271 gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK; 272 gc->chip_types[0].handler = handle_fasteoi_irq; 273 gc->chip_types[0].regs.ack = ifsclr; 274 gc->chip_types[0].regs.mask = iec; 275 gc->chip_types[0].chip.name = "evic-level"; 276 gc->chip_types[0].chip.irq_eoi = irq_gc_ack_set_bit; 277 gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 278 gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 279 gc->chip_types[0].chip.flags = IRQCHIP_SKIP_SET_WAKE; 280 281 /* Edge interrupts */ 282 gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH; 283 gc->chip_types[1].handler = handle_edge_irq; 284 gc->chip_types[1].regs.ack = ifsclr; 285 gc->chip_types[1].regs.mask = iec; 286 gc->chip_types[1].chip.name = "evic-edge"; 287 gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit; 288 gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit; 289 gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit; 290 gc->chip_types[1].chip.irq_set_type = pic32_set_type_edge; 291 gc->chip_types[1].chip.flags = IRQCHIP_SKIP_SET_WAKE; 292 293 gc->private = &priv[i]; 294 } 295 296 irq_set_default_host(evic_irq_domain); 297 298 /* 299 * External interrupts have software configurable edge polarity. These 300 * interrupts are defined in DT allowing polarity to be configured only 301 * for these interrupts when requested. 302 */ 303 pic32_ext_irq_of_init(evic_irq_domain); 304 305 return 0; 306 307err_domain_remove: 308 irq_domain_remove(evic_irq_domain); 309 310err_free_priv: 311 kfree(priv); 312 313err_iounmap: 314 iounmap(evic_base); 315 316 return ret; 317} 318 319IRQCHIP_DECLARE(pic32_evic, "microchip,pic32mzda-evic", pic32_of_init);