int.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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. /* Enable interrupts using MSI mode */
  139. if (!pci_enable_msi(privdata->pdev))
  140. privdata->msi_mode = 1;
  141. /* register interrupt handler */
  142. if ((err = request_irq(privdata->pdev->irq, pcidriver_irq_handler, MODNAME, privdata)) != 0) {
  143. mod_info("Error registering the interrupt handler. Disabling interrupts for this device\n");
  144. return 0;
  145. }
  146. privdata->irq_enabled = 1;
  147. mod_info("Registered Interrupt Handler at pin %i, line %i, IRQ %i\n", int_pin, int_line, privdata->pdev->irq );
  148. return 0;
  149. }
  150. /**
  151. *
  152. * Frees/cleans up the data structures, called from pcidriver_remove()
  153. *
  154. */
  155. void pcidriver_remove_irq(pcidriver_privdata_t *privdata)
  156. {
  157. /* Release the IRQ handler */
  158. if (privdata->irq_enabled != 0)
  159. free_irq(privdata->pdev->irq, privdata);
  160. if (privdata->msi_mode) {
  161. pci_disable_msi(privdata->pdev);
  162. privdata->msi_mode = 0;
  163. }
  164. pcidriver_irq_unmap_bars(privdata);
  165. }
  166. /**
  167. *
  168. * Unmaps the BARs and releases them
  169. *
  170. */
  171. void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata)
  172. {
  173. int i;
  174. for (i = 0; i < 6; i++) {
  175. if (privdata->bars_kmapped[i] == NULL)
  176. continue;
  177. iounmap((void*)privdata->bars_kmapped[i]);
  178. pci_release_region(privdata->pdev, i);
  179. }
  180. }
  181. /**
  182. *
  183. * Acknowledges the receival of an interrupt to the card.
  184. *
  185. * @returns true if the card was acknowledget
  186. * @returns false if the interrupt was not for one of our cards
  187. *
  188. * @see check_acknowlegde_channel
  189. *
  190. */
  191. static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata)
  192. {
  193. int channel = 0;
  194. // volatile unsigned int *bar;
  195. // bar = privdata->bars_kmapped[0];
  196. // mod_info_dbg("interrupt registers. ISR: %x, IER: %x\n", bar[ABB_INT_STAT], bar[ABB_INT_ENABLE]);
  197. atomic_inc(&(privdata->irq_outstanding[channel]));
  198. wake_up_interruptible(&(privdata->irq_queues[channel]));
  199. return true;
  200. }
  201. /**
  202. *
  203. * Handles IRQs. At the moment, this acknowledges the card that this IRQ
  204. * was received and then increases the driver's IRQ counter.
  205. *
  206. * @see pcidriver_irq_acknowledge
  207. *
  208. */
  209. IRQ_HANDLER_FUNC(pcidriver_irq_handler)
  210. {
  211. pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)dev_id;
  212. if (!pcidriver_irq_acknowledge(privdata))
  213. return IRQ_NONE;
  214. privdata->irq_count++;
  215. return IRQ_HANDLED;
  216. }