1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Support for C64x+ Megamodule Interrupt Controller 4 * 5 * Copyright (C) 2010, 2011 Texas Instruments Incorporated 6 * Contributed by: Mark Salter <msalter@redhat.com> 7 */ 8#include <linux/module.h> 9#include <linux/interrupt.h> 10#include <linux/io.h> 11#include <linux/of.h> 12#include <linux/of_irq.h> 13#include <linux/of_address.h> 14#include <linux/slab.h> 15#include <asm/soc.h> 16#include <asm/megamod-pic.h> 17 18#define NR_COMBINERS 4 19#define NR_MUX_OUTPUTS 12 20 21#define IRQ_UNMAPPED 0xffff 22 23/* 24 * Megamodule Interrupt Controller register layout 25 */ 26struct megamod_regs { 27 u32 evtflag[8]; 28 u32 evtset[8]; 29 u32 evtclr[8]; 30 u32 reserved0[8]; 31 u32 evtmask[8]; 32 u32 mevtflag[8]; 33 u32 expmask[8]; 34 u32 mexpflag[8]; 35 u32 intmux_unused; 36 u32 intmux[7]; 37 u32 reserved1[8]; 38 u32 aegmux[2]; 39 u32 reserved2[14]; 40 u32 intxstat; 41 u32 intxclr; 42 u32 intdmask; 43 u32 reserved3[13]; 44 u32 evtasrt; 45}; 46 47struct megamod_pic { 48 struct irq_domain *irqhost; 49 struct megamod_regs __iomem *regs; 50 raw_spinlock_t lock; 51 52 /* hw mux mapping */ 53 unsigned int output_to_irq[NR_MUX_OUTPUTS]; 54}; 55 56static struct megamod_pic *mm_pic; 57 58struct megamod_cascade_data { 59 struct megamod_pic *pic; 60 int index; 61}; 62 63static struct megamod_cascade_data cascade_data[NR_COMBINERS]; 64 65static void mask_megamod(struct irq_data *data) 66{ 67 struct megamod_pic *pic = irq_data_get_irq_chip_data(data); 68 irq_hw_number_t src = irqd_to_hwirq(data); 69 u32 __iomem *evtmask = &pic->regs->evtmask[src / 32]; 70 71 raw_spin_lock(&pic->lock); 72 soc_writel(soc_readl(evtmask) | (1 << (src & 31)), evtmask); 73 raw_spin_unlock(&pic->lock); 74} 75 76static void unmask_megamod(struct irq_data *data) 77{ 78 struct megamod_pic *pic = irq_data_get_irq_chip_data(data); 79 irq_hw_number_t src = irqd_to_hwirq(data); 80 u32 __iomem *evtmask = &pic->regs->evtmask[src / 32]; 81 82 raw_spin_lock(&pic->lock); 83 soc_writel(soc_readl(evtmask) & ~(1 << (src & 31)), evtmask); 84 raw_spin_unlock(&pic->lock); 85} 86 87static struct irq_chip megamod_chip = { 88 .name = "megamod", 89 .irq_mask = mask_megamod, 90 .irq_unmask = unmask_megamod, 91}; 92 93static void megamod_irq_cascade(struct irq_desc *desc) 94{ 95 struct megamod_cascade_data *cascade; 96 struct megamod_pic *pic; 97 unsigned int irq; 98 u32 events; 99 int n, idx; 100 101 cascade = irq_desc_get_handler_data(desc); 102 103 pic = cascade->pic; 104 idx = cascade->index; 105 106 while ((events = soc_readl(&pic->regs->mevtflag[idx])) != 0) { 107 n = __ffs(events); 108 109 irq = irq_linear_revmap(pic->irqhost, idx * 32 + n); 110 111 soc_writel(1 << n, &pic->regs->evtclr[idx]); 112 113 generic_handle_irq(irq); 114 } 115} 116 117static int megamod_map(struct irq_domain *h, unsigned int virq, 118 irq_hw_number_t hw) 119{ 120 struct megamod_pic *pic = h->host_data; 121 int i; 122 123 /* We shouldn't see a hwirq which is muxed to core controller */ 124 for (i = 0; i < NR_MUX_OUTPUTS; i++) 125 if (pic->output_to_irq[i] == hw) 126 return -1; 127 128 irq_set_chip_data(virq, pic); 129 irq_set_chip_and_handler(virq, &megamod_chip, handle_level_irq); 130 131 /* Set default irq type */ 132 irq_set_irq_type(virq, IRQ_TYPE_NONE); 133 134 return 0; 135} 136 137static const struct irq_domain_ops megamod_domain_ops = { 138 .map = megamod_map, 139 .xlate = irq_domain_xlate_onecell, 140}; 141 142static void __init set_megamod_mux(struct megamod_pic *pic, int src, int output) 143{ 144 int index, offset; 145 u32 val; 146 147 if (src < 0 || src >= (NR_COMBINERS * 32)) { 148 pic->output_to_irq[output] = IRQ_UNMAPPED; 149 return; 150 } 151 152 /* four mappings per mux register */ 153 index = output / 4; 154 offset = (output & 3) * 8; 155 156 val = soc_readl(&pic->regs->intmux[index]); 157 val &= ~(0xff << offset); 158 val |= src << offset; 159 soc_writel(val, &pic->regs->intmux[index]); 160} 161 162/* 163 * Parse the MUX mapping, if one exists. 164 * 165 * The MUX map is an array of up to 12 cells; one for each usable core priority 166 * interrupt. The value of a given cell is the megamodule interrupt source 167 * which is to me MUXed to the output corresponding to the cell position 168 * withing the array. The first cell in the array corresponds to priority 169 * 4 and the last (12th) cell corresponds to priority 15. The allowed 170 * values are 4 - ((NR_COMBINERS * 32) - 1). Note that the combined interrupt 171 * sources (0 - 3) are not allowed to be mapped through this property. They 172 * are handled through the "interrupts" property. This allows us to use a 173 * value of zero as a "do not map" placeholder. 174 */ 175static void __init parse_priority_map(struct megamod_pic *pic, 176 int *mapping, int size) 177{ 178 struct device_node *np = irq_domain_get_of_node(pic->irqhost); 179 const __be32 *map; 180 int i, maplen; 181 u32 val; 182 183 map = of_get_property(np, "ti,c64x+megamod-pic-mux", &maplen); 184 if (map) { 185 maplen /= 4; 186 if (maplen > size) 187 maplen = size; 188 189 for (i = 0; i < maplen; i++) { 190 val = be32_to_cpup(map); 191 if (val && val >= 4) 192 mapping[i] = val; 193 ++map; 194 } 195 } 196} 197 198static struct megamod_pic * __init init_megamod_pic(struct device_node *np) 199{ 200 struct megamod_pic *pic; 201 int i, irq; 202 int mapping[NR_MUX_OUTPUTS]; 203 204 pr_info("Initializing C64x+ Megamodule PIC\n"); 205 206 pic = kzalloc(sizeof(struct megamod_pic), GFP_KERNEL); 207 if (!pic) { 208 pr_err("%pOF: Could not alloc PIC structure.\n", np); 209 return NULL; 210 } 211 212 pic->irqhost = irq_domain_add_linear(np, NR_COMBINERS * 32, 213 &megamod_domain_ops, pic); 214 if (!pic->irqhost) { 215 pr_err("%pOF: Could not alloc host.\n", np); 216 goto error_free; 217 } 218 219 pic->irqhost->host_data = pic; 220 221 raw_spin_lock_init(&pic->lock); 222 223 pic->regs = of_iomap(np, 0); 224 if (!pic->regs) { 225 pr_err("%pOF: Could not map registers.\n", np); 226 goto error_free; 227 } 228 229 /* Initialize MUX map */ 230 for (i = 0; i < ARRAY_SIZE(mapping); i++) 231 mapping[i] = IRQ_UNMAPPED; 232 233 parse_priority_map(pic, mapping, ARRAY_SIZE(mapping)); 234 235 /* 236 * We can have up to 12 interrupts cascading to the core controller. 237 * These cascades can be from the combined interrupt sources or for 238 * individual interrupt sources. The "interrupts" property only 239 * deals with the cascaded combined interrupts. The individual 240 * interrupts muxed to the core controller use the core controller 241 * as their interrupt parent. 242 */ 243 for (i = 0; i < NR_COMBINERS; i++) { 244 struct irq_data *irq_data; 245 irq_hw_number_t hwirq; 246 247 irq = irq_of_parse_and_map(np, i); 248 if (irq == NO_IRQ) 249 continue; 250 251 irq_data = irq_get_irq_data(irq); 252 if (!irq_data) { 253 pr_err("%pOF: combiner-%d no irq_data for virq %d!\n", 254 np, i, irq); 255 continue; 256 } 257 258 hwirq = irq_data->hwirq; 259 260 /* 261 * Check that device tree provided something in the range 262 * of the core priority interrupts (4 - 15). 263 */ 264 if (hwirq < 4 || hwirq >= NR_PRIORITY_IRQS) { 265 pr_err("%pOF: combiner-%d core irq %ld out of range!\n", 266 np, i, hwirq); 267 continue; 268 } 269 270 /* record the mapping */ 271 mapping[hwirq - 4] = i; 272 273 pr_debug("%pOF: combiner-%d cascading to hwirq %ld\n", 274 np, i, hwirq); 275 276 cascade_data[i].pic = pic; 277 cascade_data[i].index = i; 278 279 /* mask and clear all events in combiner */ 280 soc_writel(~0, &pic->regs->evtmask[i]); 281 soc_writel(~0, &pic->regs->evtclr[i]); 282 283 irq_set_chained_handler_and_data(irq, megamod_irq_cascade, 284 &cascade_data[i]); 285 } 286 287 /* Finally, set up the MUX registers */ 288 for (i = 0; i < NR_MUX_OUTPUTS; i++) { 289 if (mapping[i] != IRQ_UNMAPPED) { 290 pr_debug("%pOF: setting mux %d to priority %d\n", 291 np, mapping[i], i + 4); 292 set_megamod_mux(pic, mapping[i], i); 293 } 294 } 295 296 return pic; 297 298error_free: 299 kfree(pic); 300 301 return NULL; 302} 303 304/* 305 * Return next active event after ACK'ing it. 306 * Return -1 if no events active. 307 */ 308static int get_exception(void) 309{ 310 int i, bit; 311 u32 mask; 312 313 for (i = 0; i < NR_COMBINERS; i++) { 314 mask = soc_readl(&mm_pic->regs->mexpflag[i]); 315 if (mask) { 316 bit = __ffs(mask); 317 soc_writel(1 << bit, &mm_pic->regs->evtclr[i]); 318 return (i * 32) + bit; 319 } 320 } 321 return -1; 322} 323 324static void assert_event(unsigned int val) 325{ 326 soc_writel(val, &mm_pic->regs->evtasrt); 327} 328 329void __init megamod_pic_init(void) 330{ 331 struct device_node *np; 332 333 np = of_find_compatible_node(NULL, NULL, "ti,c64x+megamod-pic"); 334 if (!np) 335 return; 336 337 mm_pic = init_megamod_pic(np); 338 of_node_put(np); 339 340 soc_ops.get_exception = get_exception; 341 soc_ops.assert_event = assert_event; 342 343 return; 344} 345