1 /* 2 * (C) Copyright 2019 Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <dm.h> 9 #include <fdtdec.h> 10 #include <malloc.h> 11 #include <asm/io.h> 12 #include <asm/u-boot-arm.h> 13 #include <irq-generic.h> 14 #include "irq-internal.h" 15 16 DECLARE_GLOBAL_DATA_PTR; 17 18 static LIST_HEAD(virq_desc_head); 19 static u32 virq_id = PLATFORM_MAX_IRQ; 20 21 static u32 virq_id_alloc(void) 22 { 23 return ++virq_id; 24 } 25 26 struct virq_data { 27 int irq; 28 u32 flag; 29 u32 count; 30 31 void *data; 32 interrupt_handler_t *handle_irq; 33 }; 34 35 /* The structure to maintail the irqchip and child virqs */ 36 struct virq_desc { 37 struct virq_chip *chip; /* irq chip */ 38 struct virq_data *virqs; /* child irq data list */ 39 struct udevice *parent; /* parent device */ 40 int pirq; /* parent irq */ 41 int use_count; /* enable count */ 42 int irq_base; /* child irq base */ 43 int irq_end; /* child irq end */ 44 uint reg_stride; 45 uint unalign_reg_idx; 46 uint unalign_reg_stride; 47 uint *status_buf; 48 49 struct list_head node; 50 }; 51 52 static struct virq_desc *find_virq_desc(int irq) 53 { 54 struct virq_desc *desc; 55 struct list_head *node; 56 57 list_for_each(node, &virq_desc_head) { 58 desc = list_entry(node, struct virq_desc, node); 59 if (irq >= desc->irq_base && irq <= desc->irq_end) 60 return desc; 61 } 62 63 return NULL; 64 } 65 66 static struct virq_desc *find_virq_desc_by_pirq(int parent_irq) 67 { 68 struct virq_desc *desc; 69 struct list_head *node; 70 71 list_for_each(node, &virq_desc_head) { 72 desc = list_entry(node, struct virq_desc, node); 73 if (parent_irq == desc->pirq) 74 return desc; 75 } 76 77 return NULL; 78 } 79 80 int virq_to_irq(struct virq_chip *chip, int virq) 81 { 82 struct virq_desc *desc; 83 struct list_head *node; 84 int irq; 85 86 if (!chip) 87 return -EINVAL; 88 89 list_for_each(node, &virq_desc_head) { 90 desc = list_entry(node, struct virq_desc, node); 91 if (desc->chip == chip) { 92 irq = desc->irq_base + virq; 93 if (irq >= desc->irq_base && irq <= desc->irq_end) 94 return irq; 95 } 96 } 97 98 return -ENONET; 99 } 100 101 int bad_virq(int irq) 102 { 103 return !find_virq_desc(irq); 104 } 105 106 void virqs_show(int pirq) 107 { 108 struct virq_data *vdata; 109 struct virq_desc *desc; 110 struct udevice *dev; 111 int num; 112 int i; 113 114 desc = find_virq_desc_by_pirq(pirq); 115 if (!desc) 116 return; 117 118 vdata = desc->virqs; 119 num = desc->irq_end - desc->irq_base; 120 121 for (i = 0; i < num; i++) { 122 if (!vdata[i].handle_irq) 123 continue; 124 125 dev = (struct udevice *)vdata[i].data; 126 printf(" %3d %d 0x%08lx %-12s |-- %-12s %d\n", 127 vdata[i].irq, 128 vdata[i].flag & IRQ_FLG_ENABLE ? 1 : 0, 129 (ulong)vdata[i].handle_irq, dev->driver->name, dev->name, 130 vdata[i].count); 131 } 132 } 133 134 int virq_install_handler(int irq, interrupt_handler_t *handler, void *data) 135 { 136 struct virq_desc *desc; 137 int virq; 138 139 if (!handler) 140 return -EINVAL; 141 142 desc = find_virq_desc(irq); 143 if (!desc) 144 return -ENOENT; 145 146 virq = irq - desc->irq_base; 147 if (desc->virqs[virq].handle_irq) 148 return -EBUSY; 149 150 desc->virqs[virq].handle_irq = handler; 151 desc->virqs[virq].data = data; 152 153 return 0; 154 } 155 156 void virq_free_handler(int irq) 157 { 158 struct virq_desc *desc; 159 int virq; 160 161 desc = find_virq_desc(irq); 162 if (!desc) 163 return; 164 165 virq = irq - desc->irq_base; 166 desc->virqs[virq].handle_irq = NULL; 167 desc->virqs[virq].data = NULL; 168 } 169 170 static uint reg_base_get(struct virq_desc *desc, uint reg_base, int idx) 171 { 172 int reg_addr; 173 174 if (idx <= desc->unalign_reg_idx) { 175 reg_addr = reg_base + (idx * desc->unalign_reg_stride); 176 } else { 177 reg_addr = reg_base + 178 (desc->unalign_reg_idx * desc->unalign_reg_stride); 179 reg_addr += (idx - desc->unalign_reg_idx) * desc->reg_stride; 180 } 181 182 return reg_addr; 183 } 184 185 void virq_chip_generic_handler(int pirq, void *pdata) 186 { 187 struct virq_chip *chip; 188 struct virq_desc *desc; 189 struct virq_data *vdata; 190 struct udevice *parent; 191 uint status_reg; 192 void *data; 193 int irq; 194 int ret; 195 int i; 196 197 desc = find_virq_desc_by_pirq(pirq); 198 if (!desc) 199 return; 200 201 chip = desc->chip; 202 vdata = desc->virqs; 203 parent = (struct udevice *)pdata; 204 205 if (!chip || !vdata || !parent) 206 return; 207 208 /* Read all status register */ 209 for (i = 0; i < chip->num_regs; i++) { 210 status_reg = reg_base_get(desc, chip->status_base, i); 211 desc->status_buf[i] = chip->read(parent, status_reg); 212 if (desc->status_buf[i] < 0) { 213 printf("%s: Read status register 0x%x failed, ret=%d\n", 214 __func__, status_reg, desc->status_buf[i]); 215 } 216 } 217 218 /* Handle all virq handler */ 219 for (i = 0; i < chip->num_irqs; i++) { 220 if (desc->status_buf[chip->irqs[i].reg_offset] & 221 chip->irqs[i].mask) { 222 irq = vdata[i].irq; 223 data = vdata[i].data; 224 225 if (vdata[i].handle_irq) { 226 vdata[i].count++; 227 vdata[i].handle_irq(irq, data); 228 } 229 } 230 } 231 232 /* Clear all status register */ 233 for (i = 0; i < chip->num_regs; i++) { 234 status_reg = reg_base_get(desc, chip->status_base, i); 235 ret = chip->write(parent, status_reg, ~0U); 236 if (ret) 237 printf("%s: Clear status register 0x%x failed, ret=%d\n", 238 __func__, status_reg, ret); 239 } 240 } 241 242 int virq_add_chip(struct udevice *dev, struct virq_chip *chip, int irq) 243 { 244 struct virq_data *vdata; 245 struct virq_desc *desc; 246 uint *status_buf; 247 uint status_reg; 248 uint mask_reg; 249 int ret; 250 int i; 251 252 if (irq < 0) 253 return -EINVAL; 254 255 desc = (struct virq_desc *)malloc(sizeof(*desc)); 256 if (!desc) 257 return -ENOMEM; 258 259 vdata = (struct virq_data *)calloc(sizeof(*vdata), chip->num_irqs); 260 if (!vdata) { 261 ret = -ENOMEM; 262 goto free1; 263 } 264 265 status_buf = (uint *)calloc(sizeof(*status_buf), chip->num_irqs); 266 if (!status_buf) { 267 ret = -ENOMEM; 268 goto free2; 269 } 270 271 for (i = 0; i < chip->num_irqs; i++) 272 vdata[i].irq = virq_id_alloc(); 273 274 desc->parent = dev; 275 desc->pirq = irq; 276 desc->chip = chip; 277 desc->virqs = vdata; 278 desc->use_count = 0; 279 desc->irq_base = vdata[0].irq; 280 desc->irq_end = vdata[chip->num_irqs - 1].irq; 281 desc->status_buf = status_buf; 282 desc->reg_stride = chip->irq_reg_stride ? : 1; 283 desc->unalign_reg_stride = chip->irq_unalign_reg_stride ? : 1; 284 desc->unalign_reg_idx = chip->irq_unalign_reg_stride ? 285 chip->irq_unalign_reg_idx : 0; 286 list_add_tail(&desc->node, &virq_desc_head); 287 288 /* Mask all register */ 289 for (i = 0; i < chip->num_regs; i++) { 290 mask_reg = reg_base_get(desc, chip->mask_base, i); 291 ret = chip->write(dev, mask_reg, ~0U); 292 if (ret) 293 printf("%s: Set mask register 0x%x failed, ret=%d\n", 294 __func__, mask_reg, ret); 295 } 296 297 /* Clear all status */ 298 for (i = 0; i < chip->num_regs; i++) { 299 status_reg = reg_base_get(desc, chip->status_base, i); 300 ret = chip->write(dev, status_reg, ~0U); 301 if (ret) 302 printf("%s: Clear status register 0x%x failed, ret=%d\n", 303 __func__, status_reg, ret); 304 } 305 306 /* Add parent irq into interrupt framework with generic virq handler */ 307 irq_install_handler(irq, virq_chip_generic_handler, dev); 308 309 return irq_handler_disable(irq); 310 311 free1: 312 free(desc); 313 free2: 314 free(status_buf); 315 316 return ret; 317 } 318 319 static int virq_init(void) 320 { 321 INIT_LIST_HEAD(&virq_desc_head); 322 return 0; 323 } 324 325 static int __virq_enable(int irq, int enable) 326 { 327 struct virq_chip *chip; 328 struct virq_desc *desc; 329 uint mask_reg, mask_val; 330 uint reg_val; 331 int virq; 332 int ret; 333 334 desc = find_virq_desc(irq); 335 if (!desc) { 336 printf("%s: %s Invalid irq %d\n", 337 __func__, enable ? "Enable" : "Disable", irq); 338 return -ENOENT; 339 } 340 341 chip = desc->chip; 342 if (!chip) 343 return -ENOENT; 344 345 virq = irq - desc->irq_base; 346 mask_val = chip->irqs[virq].mask; 347 mask_reg = reg_base_get(desc, chip->mask_base, 348 chip->irqs[virq].reg_offset); 349 reg_val = chip->read(desc->parent, mask_reg); 350 if (enable) 351 reg_val &= ~mask_val; 352 else 353 reg_val |= mask_val; 354 355 ret = chip->write(desc->parent, mask_reg, reg_val); 356 if (ret) { 357 printf("%s: Clear status register 0x%x failed, ret=%d\n", 358 __func__, mask_reg, ret); 359 return ret; 360 } 361 362 if (enable) 363 desc->virqs[virq].flag |= IRQ_FLG_ENABLE; 364 else 365 desc->virqs[virq].flag &= ~IRQ_FLG_ENABLE; 366 367 return 0; 368 } 369 370 static int virq_enable(int irq) 371 { 372 struct virq_desc *desc = find_virq_desc(irq); 373 int ret; 374 375 if (bad_virq(irq)) 376 return -EINVAL; 377 378 ret = __virq_enable(irq, 1); 379 if (!ret) { 380 if (desc->use_count == 0) 381 irq_handler_enable(desc->pirq); 382 desc->use_count++; 383 } 384 385 return ret; 386 } 387 388 static int virq_disable(int irq) 389 { 390 struct virq_desc *desc = find_virq_desc(irq); 391 int ret; 392 393 if (bad_virq(irq)) 394 return -EINVAL; 395 396 ret = __virq_enable(irq, 0); 397 if (!ret) { 398 if (desc->use_count <= 0) 399 return ret; 400 401 if (desc->use_count == 1) 402 irq_handler_disable(desc->pirq); 403 desc->use_count--; 404 } 405 406 return ret; 407 } 408 409 struct irq_chip virq_generic_chip = { 410 .name = "virq-irq-chip", 411 .irq_init = virq_init, 412 .irq_enable = virq_enable, 413 .irq_disable = virq_disable, 414 }; 415 416 struct irq_chip *arch_virq_get_irqchip(void) 417 { 418 return &virq_generic_chip; 419 } 420