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