int.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /**
  2. *
  3. * @file int.c
  4. * @author Guillermo Marcus
  5. * @date 2009-04-05
  6. * @brief Contains the interrupt handler.
  7. *
  8. */
  9. /*
  10. * Change History:
  11. *
  12. * $Log: not supported by cvs2svn $
  13. * Revision 1.7 2008-01-11 10:18:28 marcus
  14. * Modified interrupt mechanism. Added atomic functions and queues, to address race conditions. Removed unused interrupt code.
  15. *
  16. * Revision 1.6 2007-11-04 20:58:22 marcus
  17. * Added interrupt generator acknowledge.
  18. * Fixed wrong operator.
  19. *
  20. * Revision 1.5 2007-10-31 15:42:21 marcus
  21. * Added IG ack for testing, may be removed later.
  22. *
  23. * Revision 1.4 2007-07-17 13:15:56 marcus
  24. * Removed Tasklets.
  25. * Using newest map for the ABB interrupts.
  26. *
  27. * Revision 1.3 2007-07-05 15:30:30 marcus
  28. * Added support for both register maps of the ABB.
  29. *
  30. * Revision 1.2 2007-05-29 07:50:18 marcus
  31. * Split code into 2 files. May get merged in the future again....
  32. *
  33. * Revision 1.1 2007/03/01 16:57:43 marcus
  34. * Divided driver file to ease the interrupt hooks for the user of the driver.
  35. * Modified Makefile accordingly.
  36. *
  37. */
  38. #include <linux/version.h>
  39. #include <linux/string.h>
  40. #include <linux/types.h>
  41. #include <linux/list.h>
  42. #include <linux/interrupt.h>
  43. #include <linux/pci.h>
  44. #include <linux/cdev.h>
  45. #include <linux/wait.h>
  46. #include <linux/sched.h>
  47. #include <stdbool.h>
  48. #include "config.h"
  49. #include "compat.h"
  50. #include "pciDriver.h"
  51. #include "common.h"
  52. #include "int.h"
  53. /*
  54. * The ID between IRQ_SOURCE in irq_outstanding and the actual source is arbitrary.
  55. * Therefore, be careful when communicating with multiple implementations.
  56. */
  57. /* IRQ_SOURCES */
  58. #define ABB_IRQ_CH0 0
  59. #define ABB_IRQ_CH1 1
  60. #define ABB_IRQ_IG 2
  61. /* See ABB user’s guide, register definitions (3.1) */
  62. #define ABB_INT_ENABLE (0x0010 >> 2)
  63. #define ABB_INT_STAT (0x0008 >> 2)
  64. #define ABB_INT_CH1_TIMEOUT (1 << 4)
  65. #define ABB_INT_CH0_TIMEOUT (1 << 5)
  66. #define ABB_INT_IG (1 << 2)
  67. #define ABB_INT_CH0 (1 << 1) /* downstream */
  68. #define ABB_INT_CH1 (1) /* upstream */
  69. #define ABB_CH0_CTRL (108 >> 2)
  70. #define ABB_CH1_CTRL (72 >> 2)
  71. #define ABB_CH_RESET (0x0201000A)
  72. #define ABB_IG_CTRL (0x0080 >> 2)
  73. #define ABB_IG_ACK (0x00F0)
  74. /**
  75. *
  76. * If IRQ-handling is enabled, this function will be called from pcidriver_probe
  77. * to initialize the IRQ handling (maps the BARs)
  78. *
  79. */
  80. int pcidriver_probe_irq(pcidriver_privdata_t *privdata)
  81. {
  82. unsigned char int_pin, int_line;
  83. unsigned long bar_addr, bar_len, bar_flags;
  84. int i;
  85. int err;
  86. for (i = 0; i < 6; i++)
  87. privdata->bars_kmapped[i] = NULL;
  88. for (i = 0; i < 6; i++) {
  89. bar_addr = pci_resource_start(privdata->pdev, i);
  90. bar_len = pci_resource_len(privdata->pdev, i);
  91. bar_flags = pci_resource_flags(privdata->pdev, i);
  92. /* check if it is a valid BAR, skip if not */
  93. if ((bar_addr == 0) || (bar_len == 0))
  94. continue;
  95. /* Skip IO regions (map only mem regions) */
  96. if (bar_flags & IORESOURCE_IO)
  97. continue;
  98. /* Check if the region is available */
  99. if ((err = pci_request_region(privdata->pdev, i, NULL)) != 0) {
  100. mod_info( "Failed to request BAR memory region.\n" );
  101. return err;
  102. }
  103. /* Map it into kernel space. */
  104. /* For x86 this is just a dereference of the pointer, but that is
  105. * not portable. So we need to do the portable way. Thanks Joern!
  106. */
  107. /* respect the cacheable-bility of the region */
  108. if (bar_flags & IORESOURCE_PREFETCH)
  109. privdata->bars_kmapped[i] = ioremap(bar_addr, bar_len);
  110. else
  111. privdata->bars_kmapped[i] = ioremap_nocache(bar_addr, bar_len);
  112. /* check for error */
  113. if (privdata->bars_kmapped[i] == NULL) {
  114. mod_info( "Failed to remap BAR%d into kernel space.\n", i );
  115. return -EIO;
  116. }
  117. }
  118. /* Initialize the interrupt handler for this device */
  119. /* Initialize the wait queues */
  120. for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++) {
  121. init_waitqueue_head(&(privdata->irq_queues[i]));
  122. atomic_set(&(privdata->irq_outstanding[i]), 0);
  123. }
  124. /* Initialize the irq config */
  125. if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &int_pin)) != 0) {
  126. /* continue without interrupts */
  127. int_pin = 0;
  128. mod_info("Error getting the interrupt pin. Disabling interrupts for this device\n");
  129. }
  130. /* Disable interrupts and activate them if everything can be set up properly */
  131. privdata->irq_enabled = 0;
  132. if (int_pin == 0)
  133. return 0;
  134. if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_LINE, &int_line)) != 0) {
  135. mod_info("Error getting the interrupt line. Disabling interrupts for this device\n");
  136. return 0;
  137. }
  138. /* register interrupt handler */
  139. if ((err = request_irq(privdata->pdev->irq, pcidriver_irq_handler, MODNAME, privdata)) != 0) {
  140. mod_info("Error registering the interrupt handler. Disabling interrupts for this device\n");
  141. return 0;
  142. }
  143. privdata->irq_enabled = 1;
  144. mod_info("Registered Interrupt Handler at pin %i, line %i, IRQ %i\n", int_pin, int_line, privdata->pdev->irq );
  145. return 0;
  146. }
  147. /**
  148. *
  149. * Frees/cleans up the data structures, called from pcidriver_remove()
  150. *
  151. */
  152. void pcidriver_remove_irq(pcidriver_privdata_t *privdata)
  153. {
  154. /* Release the IRQ handler */
  155. if (privdata->irq_enabled != 0)
  156. free_irq(privdata->pdev->irq, privdata);
  157. pcidriver_irq_unmap_bars(privdata);
  158. }
  159. /**
  160. *
  161. * Unmaps the BARs and releases them
  162. *
  163. */
  164. void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata)
  165. {
  166. int i;
  167. for (i = 0; i < 6; i++) {
  168. if (privdata->bars_kmapped[i] == NULL)
  169. continue;
  170. iounmap((void*)privdata->bars_kmapped[i]);
  171. pci_release_region(privdata->pdev, i);
  172. }
  173. }
  174. /**
  175. *
  176. * Acknowledge the interrupt by ACKing the interrupt generator.
  177. *
  178. * @returns true if the channel was acknowledged and the interrupt handler is done
  179. *
  180. */
  181. static bool check_acknowlegde_channel(pcidriver_privdata_t *privdata, int interrupt,
  182. int channel, volatile unsigned int *bar)
  183. {
  184. if (!(bar[ABB_INT_STAT] & interrupt))
  185. return false;
  186. bar[ABB_INT_ENABLE] &= !interrupt;
  187. if (interrupt == ABB_INT_IG)
  188. bar[ABB_IG_CTRL] = ABB_IG_ACK;
  189. /* Wake up the waiting loop in ioctl.c:ioctl_wait_interrupt() */
  190. atomic_inc(&(privdata->irq_outstanding[channel]));
  191. wake_up_interruptible(&(privdata->irq_queues[channel]));
  192. return true;
  193. }
  194. /**
  195. *
  196. * Acknowledges the receival of an interrupt to the card.
  197. *
  198. * @returns true if the card was acknowledget
  199. * @returns false if the interrupt was not for one of our cards
  200. *
  201. * @see check_acknowlegde_channel
  202. *
  203. */
  204. static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata)
  205. {
  206. volatile unsigned int *bar;
  207. /* TODO: add subvendor / subsystem ids */
  208. /* FIXME: guillermo: which ones? all? */
  209. /* Test if we have to handle this interrupt */
  210. if ((privdata->pdev->vendor != PCIEABB_VENDOR_ID) ||
  211. (privdata->pdev->device != PCIEABB_DEVICE_ID))
  212. return false;
  213. /* Acknowledge the device */
  214. /* this is for ABB / wenxue DMA engine */
  215. bar = privdata->bars_kmapped[0];
  216. mod_info_dbg("interrupt registers. ISR: %x, IER: %x\n", bar[ABB_INT_STAT], bar[ABB_INT_ENABLE]);
  217. if (check_acknowlegde_channel(privdata, ABB_INT_CH0, ABB_IRQ_CH0, bar))
  218. return true;
  219. if (check_acknowlegde_channel(privdata, ABB_INT_CH1, ABB_IRQ_CH1, bar))
  220. return true;
  221. if (check_acknowlegde_channel(privdata, ABB_INT_IG, ABB_IRQ_IG, bar))
  222. return true;
  223. if (check_acknowlegde_channel(privdata, ABB_INT_CH0_TIMEOUT, ABB_IRQ_CH0, bar))
  224. return true;
  225. if (check_acknowlegde_channel(privdata, ABB_INT_CH1_TIMEOUT, ABB_IRQ_CH1, bar))
  226. return true;
  227. mod_info_dbg("err: interrupt registers. ISR: %x, IER: %x\n", bar[ ABB_INT_STAT ], bar[ ABB_INT_ENABLE ] );
  228. return false;
  229. }
  230. /**
  231. *
  232. * Handles IRQs. At the moment, this acknowledges the card that this IRQ
  233. * was received and then increases the driver's IRQ counter.
  234. *
  235. * @see pcidriver_irq_acknowledge
  236. *
  237. */
  238. IRQ_HANDLER_FUNC(pcidriver_irq_handler)
  239. {
  240. pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)dev_id;
  241. if (!pcidriver_irq_acknowledge(privdata))
  242. return IRQ_NONE;
  243. privdata->irq_count++;
  244. return IRQ_HANDLED;
  245. }