irq-crossbar.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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. #include <linux/irqchip/irq-crossbar.h>
  19. #define IRQ_FREE -1
  20. #define IRQ_RESERVED -2
  21. #define IRQ_SKIP -3
  22. #define GIC_IRQ_START 32
  23. /**
  24. * struct crossbar_device - crossbar device description
  25. * @int_max: maximum number of supported interrupts
  26. * @safe_map: safe default value to initialize the crossbar
  27. * @max_crossbar_sources: Maximum number of crossbar sources
  28. * @irq_map: array of interrupts to crossbar number mapping
  29. * @crossbar_base: crossbar base address
  30. * @register_offsets: offsets for each irq number
  31. * @write: register write function pointer
  32. */
  33. struct crossbar_device {
  34. uint int_max;
  35. uint safe_map;
  36. uint max_crossbar_sources;
  37. uint *irq_map;
  38. void __iomem *crossbar_base;
  39. int *register_offsets;
  40. void (*write)(int, int);
  41. };
  42. static struct crossbar_device *cb;
  43. static inline void crossbar_writel(int irq_no, int cb_no)
  44. {
  45. writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
  46. }
  47. static inline void crossbar_writew(int irq_no, int cb_no)
  48. {
  49. writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
  50. }
  51. static inline void crossbar_writeb(int irq_no, int cb_no)
  52. {
  53. writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
  54. }
  55. static inline int get_prev_map_irq(int cb_no)
  56. {
  57. int i;
  58. for (i = cb->int_max - 1; i >= 0; i--)
  59. if (cb->irq_map[i] == cb_no)
  60. return i;
  61. return -ENODEV;
  62. }
  63. static inline int allocate_free_irq(int cb_no)
  64. {
  65. int i;
  66. for (i = cb->int_max - 1; i >= 0; i--) {
  67. if (cb->irq_map[i] == IRQ_FREE) {
  68. cb->irq_map[i] = cb_no;
  69. return i;
  70. }
  71. }
  72. return -ENODEV;
  73. }
  74. static inline bool needs_crossbar_write(irq_hw_number_t hw)
  75. {
  76. int cb_no;
  77. if (hw > GIC_IRQ_START) {
  78. cb_no = cb->irq_map[hw - GIC_IRQ_START];
  79. if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP)
  80. return true;
  81. }
  82. return false;
  83. }
  84. static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
  85. irq_hw_number_t hw)
  86. {
  87. if (needs_crossbar_write(hw))
  88. cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]);
  89. return 0;
  90. }
  91. /**
  92. * crossbar_domain_unmap - unmap a crossbar<->irq connection
  93. * @d: domain of irq to unmap
  94. * @irq: virq number
  95. *
  96. * We do not maintain a use count of total number of map/unmap
  97. * calls for a particular irq to find out if a irq can be really
  98. * unmapped. This is because unmap is called during irq_dispose_mapping(irq),
  99. * after which irq is anyways unusable. So an explicit map has to be called
  100. * after that.
  101. */
  102. static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq)
  103. {
  104. irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
  105. if (needs_crossbar_write(hw)) {
  106. cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE;
  107. cb->write(hw - GIC_IRQ_START, cb->safe_map);
  108. }
  109. }
  110. static int crossbar_domain_xlate(struct irq_domain *d,
  111. struct device_node *controller,
  112. const u32 *intspec, unsigned int intsize,
  113. unsigned long *out_hwirq,
  114. unsigned int *out_type)
  115. {
  116. int ret;
  117. int req_num = intspec[1];
  118. int direct_map_num;
  119. if (req_num >= cb->max_crossbar_sources) {
  120. direct_map_num = req_num - cb->max_crossbar_sources;
  121. if (direct_map_num < cb->int_max) {
  122. ret = cb->irq_map[direct_map_num];
  123. if (ret == IRQ_RESERVED || ret == IRQ_SKIP) {
  124. /* We use the interrupt num as h/w irq num */
  125. ret = direct_map_num;
  126. goto found;
  127. }
  128. }
  129. pr_err("%s: requested crossbar number %d > max %d\n",
  130. __func__, req_num, cb->max_crossbar_sources);
  131. return -EINVAL;
  132. }
  133. ret = get_prev_map_irq(req_num);
  134. if (ret >= 0)
  135. goto found;
  136. ret = allocate_free_irq(req_num);
  137. if (ret < 0)
  138. return ret;
  139. found:
  140. *out_hwirq = ret + GIC_IRQ_START;
  141. return 0;
  142. }
  143. static const struct irq_domain_ops routable_irq_domain_ops = {
  144. .map = crossbar_domain_map,
  145. .unmap = crossbar_domain_unmap,
  146. .xlate = crossbar_domain_xlate
  147. };
  148. static int __init crossbar_of_init(struct device_node *node)
  149. {
  150. int i, size, max = 0, reserved = 0, entry;
  151. const __be32 *irqsr;
  152. int ret = -ENOMEM;
  153. cb = kzalloc(sizeof(*cb), GFP_KERNEL);
  154. if (!cb)
  155. return ret;
  156. cb->crossbar_base = of_iomap(node, 0);
  157. if (!cb->crossbar_base)
  158. goto err_cb;
  159. of_property_read_u32(node, "ti,max-crossbar-sources",
  160. &cb->max_crossbar_sources);
  161. if (!cb->max_crossbar_sources) {
  162. pr_err("missing 'ti,max-crossbar-sources' property\n");
  163. ret = -EINVAL;
  164. goto err_base;
  165. }
  166. of_property_read_u32(node, "ti,max-irqs", &max);
  167. if (!max) {
  168. pr_err("missing 'ti,max-irqs' property\n");
  169. ret = -EINVAL;
  170. goto err_base;
  171. }
  172. cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL);
  173. if (!cb->irq_map)
  174. goto err_base;
  175. cb->int_max = max;
  176. for (i = 0; i < max; i++)
  177. cb->irq_map[i] = IRQ_FREE;
  178. /* Get and mark reserved irqs */
  179. irqsr = of_get_property(node, "ti,irqs-reserved", &size);
  180. if (irqsr) {
  181. size /= sizeof(__be32);
  182. for (i = 0; i < size; i++) {
  183. of_property_read_u32_index(node,
  184. "ti,irqs-reserved",
  185. i, &entry);
  186. if (entry >= max) {
  187. pr_err("Invalid reserved entry\n");
  188. ret = -EINVAL;
  189. goto err_irq_map;
  190. }
  191. cb->irq_map[entry] = IRQ_RESERVED;
  192. }
  193. }
  194. /* Skip irqs hardwired to bypass the crossbar */
  195. irqsr = of_get_property(node, "ti,irqs-skip", &size);
  196. if (irqsr) {
  197. size /= sizeof(__be32);
  198. for (i = 0; i < size; i++) {
  199. of_property_read_u32_index(node,
  200. "ti,irqs-skip",
  201. i, &entry);
  202. if (entry >= max) {
  203. pr_err("Invalid skip entry\n");
  204. ret = -EINVAL;
  205. goto err_irq_map;
  206. }
  207. cb->irq_map[entry] = IRQ_SKIP;
  208. }
  209. }
  210. cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL);
  211. if (!cb->register_offsets)
  212. goto err_irq_map;
  213. of_property_read_u32(node, "ti,reg-size", &size);
  214. switch (size) {
  215. case 1:
  216. cb->write = crossbar_writeb;
  217. break;
  218. case 2:
  219. cb->write = crossbar_writew;
  220. break;
  221. case 4:
  222. cb->write = crossbar_writel;
  223. break;
  224. default:
  225. pr_err("Invalid reg-size property\n");
  226. ret = -EINVAL;
  227. goto err_reg_offset;
  228. break;
  229. }
  230. /*
  231. * Register offsets are not linear because of the
  232. * reserved irqs. so find and store the offsets once.
  233. */
  234. for (i = 0; i < max; i++) {
  235. if (cb->irq_map[i] == IRQ_RESERVED)
  236. continue;
  237. cb->register_offsets[i] = reserved;
  238. reserved += size;
  239. }
  240. of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map);
  241. /* Initialize the crossbar with safe map to start with */
  242. for (i = 0; i < max; i++) {
  243. if (cb->irq_map[i] == IRQ_RESERVED ||
  244. cb->irq_map[i] == IRQ_SKIP)
  245. continue;
  246. cb->write(i, cb->safe_map);
  247. }
  248. register_routable_domain_ops(&routable_irq_domain_ops);
  249. return 0;
  250. err_reg_offset:
  251. kfree(cb->register_offsets);
  252. err_irq_map:
  253. kfree(cb->irq_map);
  254. err_base:
  255. iounmap(cb->crossbar_base);
  256. err_cb:
  257. kfree(cb);
  258. cb = NULL;
  259. return ret;
  260. }
  261. static const struct of_device_id crossbar_match[] __initconst = {
  262. { .compatible = "ti,irq-crossbar" },
  263. {}
  264. };
  265. int __init irqcrossbar_init(void)
  266. {
  267. struct device_node *np;
  268. np = of_find_matching_node(NULL, crossbar_match);
  269. if (!np)
  270. return -ENODEV;
  271. crossbar_of_init(np);
  272. return 0;
  273. }