socrates_fpga_pic.c (8424B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2008 Ilya Yanok, Emcraft Systems 4 */ 5 6#include <linux/irq.h> 7#include <linux/of_address.h> 8#include <linux/of_irq.h> 9#include <linux/of_platform.h> 10#include <linux/io.h> 11 12/* 13 * The FPGA supports 9 interrupt sources, which can be routed to 3 14 * interrupt request lines of the MPIC. The line to be used can be 15 * specified through the third cell of FDT property "interrupts". 16 */ 17 18#define SOCRATES_FPGA_NUM_IRQS 9 19 20#define FPGA_PIC_IRQCFG (0x0) 21#define FPGA_PIC_IRQMASK(n) (0x4 + 0x4 * (n)) 22 23#define SOCRATES_FPGA_IRQ_MASK ((1 << SOCRATES_FPGA_NUM_IRQS) - 1) 24 25struct socrates_fpga_irq_info { 26 unsigned int irq_line; 27 int type; 28}; 29 30/* 31 * Interrupt routing and type table 32 * 33 * IRQ_TYPE_NONE means the interrupt type is configurable, 34 * otherwise it's fixed to the specified value. 35 */ 36static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = { 37 [0] = {0, IRQ_TYPE_NONE}, 38 [1] = {0, IRQ_TYPE_LEVEL_HIGH}, 39 [2] = {0, IRQ_TYPE_LEVEL_LOW}, 40 [3] = {0, IRQ_TYPE_NONE}, 41 [4] = {0, IRQ_TYPE_NONE}, 42 [5] = {0, IRQ_TYPE_NONE}, 43 [6] = {0, IRQ_TYPE_NONE}, 44 [7] = {0, IRQ_TYPE_NONE}, 45 [8] = {0, IRQ_TYPE_LEVEL_HIGH}, 46}; 47 48static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock); 49 50static void __iomem *socrates_fpga_pic_iobase; 51static struct irq_domain *socrates_fpga_pic_irq_host; 52static unsigned int socrates_fpga_irqs[3]; 53 54static inline uint32_t socrates_fpga_pic_read(int reg) 55{ 56 return in_be32(socrates_fpga_pic_iobase + reg); 57} 58 59static inline void socrates_fpga_pic_write(int reg, uint32_t val) 60{ 61 out_be32(socrates_fpga_pic_iobase + reg, val); 62} 63 64static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq) 65{ 66 uint32_t cause; 67 unsigned long flags; 68 int i; 69 70 /* Check irq line routed to the MPIC */ 71 for (i = 0; i < 3; i++) { 72 if (irq == socrates_fpga_irqs[i]) 73 break; 74 } 75 if (i == 3) 76 return 0; 77 78 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); 79 cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i)); 80 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); 81 for (i = SOCRATES_FPGA_NUM_IRQS - 1; i >= 0; i--) { 82 if (cause >> (i + 16)) 83 break; 84 } 85 return irq_linear_revmap(socrates_fpga_pic_irq_host, 86 (irq_hw_number_t)i); 87} 88 89static void socrates_fpga_pic_cascade(struct irq_desc *desc) 90{ 91 struct irq_chip *chip = irq_desc_get_chip(desc); 92 unsigned int irq = irq_desc_get_irq(desc); 93 unsigned int cascade_irq; 94 95 /* 96 * See if we actually have an interrupt, call generic handling code if 97 * we do. 98 */ 99 cascade_irq = socrates_fpga_pic_get_irq(irq); 100 101 if (cascade_irq) 102 generic_handle_irq(cascade_irq); 103 chip->irq_eoi(&desc->irq_data); 104} 105 106static void socrates_fpga_pic_ack(struct irq_data *d) 107{ 108 unsigned long flags; 109 unsigned int irq_line, hwirq = irqd_to_hwirq(d); 110 uint32_t mask; 111 112 irq_line = fpga_irqs[hwirq].irq_line; 113 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); 114 mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line)) 115 & SOCRATES_FPGA_IRQ_MASK; 116 mask |= (1 << (hwirq + 16)); 117 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask); 118 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); 119} 120 121static void socrates_fpga_pic_mask(struct irq_data *d) 122{ 123 unsigned long flags; 124 unsigned int hwirq = irqd_to_hwirq(d); 125 int irq_line; 126 u32 mask; 127 128 irq_line = fpga_irqs[hwirq].irq_line; 129 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); 130 mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line)) 131 & SOCRATES_FPGA_IRQ_MASK; 132 mask &= ~(1 << hwirq); 133 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask); 134 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); 135} 136 137static void socrates_fpga_pic_mask_ack(struct irq_data *d) 138{ 139 unsigned long flags; 140 unsigned int hwirq = irqd_to_hwirq(d); 141 int irq_line; 142 u32 mask; 143 144 irq_line = fpga_irqs[hwirq].irq_line; 145 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); 146 mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line)) 147 & SOCRATES_FPGA_IRQ_MASK; 148 mask &= ~(1 << hwirq); 149 mask |= (1 << (hwirq + 16)); 150 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask); 151 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); 152} 153 154static void socrates_fpga_pic_unmask(struct irq_data *d) 155{ 156 unsigned long flags; 157 unsigned int hwirq = irqd_to_hwirq(d); 158 int irq_line; 159 u32 mask; 160 161 irq_line = fpga_irqs[hwirq].irq_line; 162 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); 163 mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line)) 164 & SOCRATES_FPGA_IRQ_MASK; 165 mask |= (1 << hwirq); 166 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask); 167 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); 168} 169 170static void socrates_fpga_pic_eoi(struct irq_data *d) 171{ 172 unsigned long flags; 173 unsigned int hwirq = irqd_to_hwirq(d); 174 int irq_line; 175 u32 mask; 176 177 irq_line = fpga_irqs[hwirq].irq_line; 178 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); 179 mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line)) 180 & SOCRATES_FPGA_IRQ_MASK; 181 mask |= (1 << (hwirq + 16)); 182 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask); 183 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); 184} 185 186static int socrates_fpga_pic_set_type(struct irq_data *d, 187 unsigned int flow_type) 188{ 189 unsigned long flags; 190 unsigned int hwirq = irqd_to_hwirq(d); 191 int polarity; 192 u32 mask; 193 194 if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE) 195 return -EINVAL; 196 197 switch (flow_type & IRQ_TYPE_SENSE_MASK) { 198 case IRQ_TYPE_LEVEL_HIGH: 199 polarity = 1; 200 break; 201 case IRQ_TYPE_LEVEL_LOW: 202 polarity = 0; 203 break; 204 default: 205 return -EINVAL; 206 } 207 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); 208 mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG); 209 if (polarity) 210 mask |= (1 << hwirq); 211 else 212 mask &= ~(1 << hwirq); 213 socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask); 214 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); 215 return 0; 216} 217 218static struct irq_chip socrates_fpga_pic_chip = { 219 .name = "FPGA-PIC", 220 .irq_ack = socrates_fpga_pic_ack, 221 .irq_mask = socrates_fpga_pic_mask, 222 .irq_mask_ack = socrates_fpga_pic_mask_ack, 223 .irq_unmask = socrates_fpga_pic_unmask, 224 .irq_eoi = socrates_fpga_pic_eoi, 225 .irq_set_type = socrates_fpga_pic_set_type, 226}; 227 228static int socrates_fpga_pic_host_map(struct irq_domain *h, unsigned int virq, 229 irq_hw_number_t hwirq) 230{ 231 /* All interrupts are LEVEL sensitive */ 232 irq_set_status_flags(virq, IRQ_LEVEL); 233 irq_set_chip_and_handler(virq, &socrates_fpga_pic_chip, 234 handle_fasteoi_irq); 235 236 return 0; 237} 238 239static int socrates_fpga_pic_host_xlate(struct irq_domain *h, 240 struct device_node *ct, const u32 *intspec, unsigned int intsize, 241 irq_hw_number_t *out_hwirq, unsigned int *out_flags) 242{ 243 struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]]; 244 245 *out_hwirq = intspec[0]; 246 if (fpga_irq->type == IRQ_TYPE_NONE) { 247 /* type is configurable */ 248 if (intspec[1] != IRQ_TYPE_LEVEL_LOW && 249 intspec[1] != IRQ_TYPE_LEVEL_HIGH) { 250 pr_warn("FPGA PIC: invalid irq type, setting default active low\n"); 251 *out_flags = IRQ_TYPE_LEVEL_LOW; 252 } else { 253 *out_flags = intspec[1]; 254 } 255 } else { 256 /* type is fixed */ 257 *out_flags = fpga_irq->type; 258 } 259 260 /* Use specified interrupt routing */ 261 if (intspec[2] <= 2) 262 fpga_irq->irq_line = intspec[2]; 263 else 264 pr_warn("FPGA PIC: invalid irq routing\n"); 265 266 return 0; 267} 268 269static const struct irq_domain_ops socrates_fpga_pic_host_ops = { 270 .map = socrates_fpga_pic_host_map, 271 .xlate = socrates_fpga_pic_host_xlate, 272}; 273 274void __init socrates_fpga_pic_init(struct device_node *pic) 275{ 276 unsigned long flags; 277 int i; 278 279 /* Setup an irq_domain structure */ 280 socrates_fpga_pic_irq_host = irq_domain_add_linear(pic, 281 SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops, NULL); 282 if (socrates_fpga_pic_irq_host == NULL) { 283 pr_err("FPGA PIC: Unable to allocate host\n"); 284 return; 285 } 286 287 for (i = 0; i < 3; i++) { 288 socrates_fpga_irqs[i] = irq_of_parse_and_map(pic, i); 289 if (!socrates_fpga_irqs[i]) { 290 pr_warn("FPGA PIC: can't get irq%d\n", i); 291 continue; 292 } 293 irq_set_chained_handler(socrates_fpga_irqs[i], 294 socrates_fpga_pic_cascade); 295 } 296 297 socrates_fpga_pic_iobase = of_iomap(pic, 0); 298 299 raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags); 300 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0), 301 SOCRATES_FPGA_IRQ_MASK << 16); 302 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1), 303 SOCRATES_FPGA_IRQ_MASK << 16); 304 socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2), 305 SOCRATES_FPGA_IRQ_MASK << 16); 306 raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags); 307 308 pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n"); 309}