irq-crossbar.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * drivers/irqchip/irq-crossbar.c
  3. *
  4. * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
  5. * Author: Sricharan R <r.sricharan@ti.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. *
  11. */
  12. #include <linux/err.h>
  13. #include <linux/io.h>
  14. #include <linux/of_address.h>
  15. #include <linux/of_irq.h>
  16. #include <linux/slab.h>
  17. #include <linux/irqchip/arm-gic.h>
  18. #define IRQ_FREE -1
  19. #define GIC_IRQ_START 32
  20. /*
  21. * @int_max: maximum number of supported interrupts
  22. * @irq_map: array of interrupts to crossbar number mapping
  23. * @crossbar_base: crossbar base address
  24. * @register_offsets: offsets for each irq number
  25. */
  26. struct crossbar_device {
  27. uint int_max;
  28. uint *irq_map;
  29. void __iomem *crossbar_base;
  30. int *register_offsets;
  31. void (*write) (int, int);
  32. };
  33. static struct crossbar_device *cb;
  34. static inline void crossbar_writel(int irq_no, int cb_no)
  35. {
  36. writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
  37. }
  38. static inline void crossbar_writew(int irq_no, int cb_no)
  39. {
  40. writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
  41. }
  42. static inline void crossbar_writeb(int irq_no, int cb_no)
  43. {
  44. writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
  45. }
  46. static inline int allocate_free_irq(int cb_no)
  47. {
  48. int i;
  49. for (i = 0; i < cb->int_max; i++) {
  50. if (cb->irq_map[i] == IRQ_FREE) {
  51. cb->irq_map[i] = cb_no;
  52. return i;
  53. }
  54. }
  55. return -ENODEV;
  56. }
  57. static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
  58. irq_hw_number_t hw)
  59. {
  60. cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]);
  61. return 0;
  62. }
  63. static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq)
  64. {
  65. irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
  66. if (hw > GIC_IRQ_START)
  67. cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE;
  68. }
  69. static int crossbar_domain_xlate(struct irq_domain *d,
  70. struct device_node *controller,
  71. const u32 *intspec, unsigned int intsize,
  72. unsigned long *out_hwirq,
  73. unsigned int *out_type)
  74. {
  75. unsigned long ret;
  76. ret = allocate_free_irq(intspec[1]);
  77. if (IS_ERR_VALUE(ret))
  78. return ret;
  79. *out_hwirq = ret + GIC_IRQ_START;
  80. return 0;
  81. }
  82. const struct irq_domain_ops routable_irq_domain_ops = {
  83. .map = crossbar_domain_map,
  84. .unmap = crossbar_domain_unmap,
  85. .xlate = crossbar_domain_xlate
  86. };
  87. static int __init crossbar_of_init(struct device_node *node)
  88. {
  89. int i, size, max, reserved = 0, entry;
  90. const __be32 *irqsr;
  91. cb = kzalloc(sizeof(*cb), GFP_KERNEL);
  92. if (!cb)
  93. return -ENOMEM;
  94. cb->crossbar_base = of_iomap(node, 0);
  95. if (!cb->crossbar_base)
  96. goto err1;
  97. of_property_read_u32(node, "ti,max-irqs", &max);
  98. cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL);
  99. if (!cb->irq_map)
  100. goto err2;
  101. cb->int_max = max;
  102. for (i = 0; i < max; i++)
  103. cb->irq_map[i] = IRQ_FREE;
  104. /* Get and mark reserved irqs */
  105. irqsr = of_get_property(node, "ti,irqs-reserved", &size);
  106. if (irqsr) {
  107. size /= sizeof(__be32);
  108. for (i = 0; i < size; i++) {
  109. of_property_read_u32_index(node,
  110. "ti,irqs-reserved",
  111. i, &entry);
  112. if (entry > max) {
  113. pr_err("Invalid reserved entry\n");
  114. goto err3;
  115. }
  116. cb->irq_map[entry] = 0;
  117. }
  118. }
  119. cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL);
  120. if (!cb->register_offsets)
  121. goto err3;
  122. of_property_read_u32(node, "ti,reg-size", &size);
  123. switch (size) {
  124. case 1:
  125. cb->write = crossbar_writeb;
  126. break;
  127. case 2:
  128. cb->write = crossbar_writew;
  129. break;
  130. case 4:
  131. cb->write = crossbar_writel;
  132. break;
  133. default:
  134. pr_err("Invalid reg-size property\n");
  135. goto err4;
  136. break;
  137. }
  138. /*
  139. * Register offsets are not linear because of the
  140. * reserved irqs. so find and store the offsets once.
  141. */
  142. for (i = 0; i < max; i++) {
  143. if (!cb->irq_map[i])
  144. continue;
  145. cb->register_offsets[i] = reserved;
  146. reserved += size;
  147. }
  148. register_routable_domain_ops(&routable_irq_domain_ops);
  149. return 0;
  150. err4:
  151. kfree(cb->register_offsets);
  152. err3:
  153. kfree(cb->irq_map);
  154. err2:
  155. iounmap(cb->crossbar_base);
  156. err1:
  157. kfree(cb);
  158. return -ENOMEM;
  159. }
  160. static const struct of_device_id crossbar_match[] __initconst = {
  161. { .compatible = "ti,irq-crossbar" },
  162. {}
  163. };
  164. int __init irqcrossbar_init(void)
  165. {
  166. struct device_node *np;
  167. np = of_find_matching_node(NULL, crossbar_match);
  168. if (!np)
  169. return -ENODEV;
  170. crossbar_of_init(np);
  171. return 0;
  172. }