irq-crossbar.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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 IRQ_RESERVED -2
  20. #define IRQ_SKIP -3
  21. #define GIC_IRQ_START 32
  22. /*
  23. * @int_max: maximum number of supported interrupts
  24. * @safe_map: safe default value to initialize the crossbar
  25. * @irq_map: array of interrupts to crossbar number mapping
  26. * @crossbar_base: crossbar base address
  27. * @register_offsets: offsets for each irq number
  28. */
  29. struct crossbar_device {
  30. uint int_max;
  31. uint safe_map;
  32. uint *irq_map;
  33. void __iomem *crossbar_base;
  34. int *register_offsets;
  35. void (*write)(int, int);
  36. };
  37. static struct crossbar_device *cb;
  38. static inline void crossbar_writel(int irq_no, int cb_no)
  39. {
  40. writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
  41. }
  42. static inline void crossbar_writew(int irq_no, int cb_no)
  43. {
  44. writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
  45. }
  46. static inline void crossbar_writeb(int irq_no, int cb_no)
  47. {
  48. writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
  49. }
  50. static inline int get_prev_map_irq(int cb_no)
  51. {
  52. int i;
  53. for (i = cb->int_max - 1; i >= 0; i--)
  54. if (cb->irq_map[i] == cb_no)
  55. return i;
  56. return -ENODEV;
  57. }
  58. static inline int allocate_free_irq(int cb_no)
  59. {
  60. int i;
  61. for (i = cb->int_max - 1; i >= 0; i--) {
  62. if (cb->irq_map[i] == IRQ_FREE) {
  63. cb->irq_map[i] = cb_no;
  64. return i;
  65. }
  66. }
  67. return -ENODEV;
  68. }
  69. static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
  70. irq_hw_number_t hw)
  71. {
  72. cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]);
  73. return 0;
  74. }
  75. static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq)
  76. {
  77. irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
  78. if (hw > GIC_IRQ_START) {
  79. cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE;
  80. cb->write(hw - GIC_IRQ_START, cb->safe_map);
  81. }
  82. }
  83. static int crossbar_domain_xlate(struct irq_domain *d,
  84. struct device_node *controller,
  85. const u32 *intspec, unsigned int intsize,
  86. unsigned long *out_hwirq,
  87. unsigned int *out_type)
  88. {
  89. unsigned long ret;
  90. ret = get_prev_map_irq(intspec[1]);
  91. if (!IS_ERR_VALUE(ret))
  92. goto found;
  93. ret = allocate_free_irq(intspec[1]);
  94. if (IS_ERR_VALUE(ret))
  95. return ret;
  96. found:
  97. *out_hwirq = ret + GIC_IRQ_START;
  98. return 0;
  99. }
  100. const struct irq_domain_ops routable_irq_domain_ops = {
  101. .map = crossbar_domain_map,
  102. .unmap = crossbar_domain_unmap,
  103. .xlate = crossbar_domain_xlate
  104. };
  105. static int __init crossbar_of_init(struct device_node *node)
  106. {
  107. int i, size, max, reserved = 0, entry;
  108. const __be32 *irqsr;
  109. cb = kzalloc(sizeof(*cb), GFP_KERNEL);
  110. if (!cb)
  111. return -ENOMEM;
  112. cb->crossbar_base = of_iomap(node, 0);
  113. if (!cb->crossbar_base)
  114. goto err1;
  115. of_property_read_u32(node, "ti,max-irqs", &max);
  116. cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL);
  117. if (!cb->irq_map)
  118. goto err2;
  119. cb->int_max = max;
  120. for (i = 0; i < max; i++)
  121. cb->irq_map[i] = IRQ_FREE;
  122. /* Get and mark reserved irqs */
  123. irqsr = of_get_property(node, "ti,irqs-reserved", &size);
  124. if (irqsr) {
  125. size /= sizeof(__be32);
  126. for (i = 0; i < size; i++) {
  127. of_property_read_u32_index(node,
  128. "ti,irqs-reserved",
  129. i, &entry);
  130. if (entry > max) {
  131. pr_err("Invalid reserved entry\n");
  132. goto err3;
  133. }
  134. cb->irq_map[entry] = IRQ_RESERVED;
  135. }
  136. }
  137. /* Skip irqs hardwired to bypass the crossbar */
  138. irqsr = of_get_property(node, "ti,irqs-skip", &size);
  139. if (irqsr) {
  140. size /= sizeof(__be32);
  141. for (i = 0; i < size; i++) {
  142. of_property_read_u32_index(node,
  143. "ti,irqs-skip",
  144. i, &entry);
  145. if (entry > max) {
  146. pr_err("Invalid skip entry\n");
  147. ret = -EINVAL;
  148. goto err3;
  149. }
  150. cb->irq_map[entry] = IRQ_SKIP;
  151. }
  152. }
  153. cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL);
  154. if (!cb->register_offsets)
  155. goto err3;
  156. of_property_read_u32(node, "ti,reg-size", &size);
  157. switch (size) {
  158. case 1:
  159. cb->write = crossbar_writeb;
  160. break;
  161. case 2:
  162. cb->write = crossbar_writew;
  163. break;
  164. case 4:
  165. cb->write = crossbar_writel;
  166. break;
  167. default:
  168. pr_err("Invalid reg-size property\n");
  169. goto err4;
  170. break;
  171. }
  172. /*
  173. * Register offsets are not linear because of the
  174. * reserved irqs. so find and store the offsets once.
  175. */
  176. for (i = 0; i < max; i++) {
  177. if (cb->irq_map[i] == IRQ_RESERVED)
  178. continue;
  179. cb->register_offsets[i] = reserved;
  180. reserved += size;
  181. }
  182. of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map);
  183. /* Initialize the crossbar with safe map to start with */
  184. for (i = 0; i < max; i++) {
  185. if (cb->irq_map[i] == IRQ_RESERVED ||
  186. cb->irq_map[i] == IRQ_SKIP)
  187. continue;
  188. cb->write(i, cb->safe_map);
  189. }
  190. register_routable_domain_ops(&routable_irq_domain_ops);
  191. return 0;
  192. err4:
  193. kfree(cb->register_offsets);
  194. err3:
  195. kfree(cb->irq_map);
  196. err2:
  197. iounmap(cb->crossbar_base);
  198. err1:
  199. kfree(cb);
  200. return -ENOMEM;
  201. }
  202. static const struct of_device_id crossbar_match[] __initconst = {
  203. { .compatible = "ti,irq-crossbar" },
  204. {}
  205. };
  206. int __init irqcrossbar_init(void)
  207. {
  208. struct device_node *np;
  209. np = of_find_matching_node(NULL, crossbar_match);
  210. if (!np)
  211. return -ENODEV;
  212. crossbar_of_init(np);
  213. return 0;
  214. }