Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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