int.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /**
  2. *
  3. * @file int.c
  4. * @author Guillermo Marcus
  5. * @date 2009-04-05
  6. * @brief Contains the interrupt handler.
  7. *
  8. */
  9. #include <linux/version.h>
  10. #include <linux/string.h>
  11. #include <linux/types.h>
  12. #include <linux/list.h>
  13. #include <linux/interrupt.h>
  14. #include <linux/pci.h>
  15. #include <linux/cdev.h>
  16. #include <linux/wait.h>
  17. #include <linux/sched.h>
  18. #include <stdbool.h>
  19. #include "base.h"
  20. /**
  21. *
  22. * Acknowledges the receival of an interrupt to the card.
  23. *
  24. * @returns true if the card was acknowledget
  25. * @returns false if the interrupt was not for one of our cards
  26. *
  27. * @see check_acknowlegde_channel
  28. *
  29. */
  30. static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata)
  31. {
  32. int channel = 0;
  33. atomic_inc(&(privdata->irq_outstanding[channel]));
  34. wake_up_interruptible(&(privdata->irq_queues[channel]));
  35. return true;
  36. }
  37. /**
  38. *
  39. * Handles IRQs. At the moment, this acknowledges the card that this IRQ
  40. * was received and then increases the driver's IRQ counter.
  41. *
  42. * @see pcidriver_irq_acknowledge
  43. *
  44. */
  45. static irqreturn_t pcidriver_irq_handler(int irq, void *dev_id)
  46. {
  47. pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)dev_id;
  48. if (!pcidriver_irq_acknowledge(privdata))
  49. return IRQ_NONE;
  50. privdata->irq_count++;
  51. return IRQ_HANDLED;
  52. }
  53. /**
  54. *
  55. * If IRQ-handling is enabled, this function will be called from pcidriver_probe
  56. * to initialize the IRQ handling (maps the BARs)
  57. *
  58. */
  59. int pcidriver_probe_irq(pcidriver_privdata_t *privdata)
  60. {
  61. unsigned char int_pin, int_line;
  62. unsigned long bar_addr, bar_len, bar_flags;
  63. int i;
  64. int err;
  65. for (i = 0; i < 6; i++)
  66. privdata->bars_kmapped[i] = NULL;
  67. for (i = 0; i < 6; i++) {
  68. bar_addr = pci_resource_start(privdata->pdev, i);
  69. bar_len = pci_resource_len(privdata->pdev, i);
  70. bar_flags = pci_resource_flags(privdata->pdev, i);
  71. /* check if it is a valid BAR, skip if not */
  72. if ((bar_addr == 0) || (bar_len == 0))
  73. continue;
  74. /* Skip IO regions (map only mem regions) */
  75. if (bar_flags & IORESOURCE_IO)
  76. continue;
  77. /* Check if the region is available */
  78. if ((err = pci_request_region(privdata->pdev, i, NULL)) != 0) {
  79. mod_info( "Failed to request BAR memory region.\n" );
  80. return err;
  81. }
  82. /* Map it into kernel space. */
  83. /* For x86 this is just a dereference of the pointer, but that is
  84. * not portable. So we need to do the portable way. Thanks Joern!
  85. */
  86. /* respect the cacheable-bility of the region */
  87. if (bar_flags & IORESOURCE_PREFETCH)
  88. privdata->bars_kmapped[i] = ioremap(bar_addr, bar_len);
  89. else
  90. privdata->bars_kmapped[i] = ioremap_nocache(bar_addr, bar_len);
  91. /* check for error */
  92. if (privdata->bars_kmapped[i] == NULL) {
  93. mod_info( "Failed to remap BAR%d into kernel space.\n", i );
  94. return -EIO;
  95. }
  96. }
  97. /* Initialize the interrupt handler for this device */
  98. /* Initialize the wait queues */
  99. for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++) {
  100. init_waitqueue_head(&(privdata->irq_queues[i]));
  101. atomic_set(&(privdata->irq_outstanding[i]), 0);
  102. }
  103. /* Initialize the irq config */
  104. if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &int_pin)) != 0) {
  105. /* continue without interrupts */
  106. int_pin = 0;
  107. mod_info("Error getting the interrupt pin. Disabling interrupts for this device\n");
  108. }
  109. /* Disable interrupts and activate them if everything can be set up properly */
  110. privdata->irq_enabled = 0;
  111. if (int_pin == 0)
  112. return 0;
  113. if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_LINE, &int_line)) != 0) {
  114. mod_info("Error getting the interrupt line. Disabling interrupts for this device\n");
  115. return 0;
  116. }
  117. /* Enable interrupts using MSI mode */
  118. if (!pci_enable_msi(privdata->pdev))
  119. privdata->msi_mode = 1;
  120. /* register interrupt handler */
  121. if ((err = request_irq(privdata->pdev->irq, pcidriver_irq_handler, IRQF_SHARED, MODNAME, privdata)) != 0) {
  122. mod_info("Error registering the interrupt handler. Disabling interrupts for this device\n");
  123. return 0;
  124. }
  125. privdata->irq_enabled = 1;
  126. mod_info("Registered Interrupt Handler at pin %i, line %i, IRQ %i\n", int_pin, int_line, privdata->pdev->irq );
  127. return 0;
  128. }
  129. /**
  130. *
  131. * Frees/cleans up the data structures, called from pcidriver_remove()
  132. *
  133. */
  134. void pcidriver_remove_irq(pcidriver_privdata_t *privdata)
  135. {
  136. /* Release the IRQ handler */
  137. if (privdata->irq_enabled != 0)
  138. free_irq(privdata->pdev->irq, privdata);
  139. if (privdata->msi_mode) {
  140. pci_disable_msi(privdata->pdev);
  141. privdata->msi_mode = 0;
  142. }
  143. pcidriver_irq_unmap_bars(privdata);
  144. }
  145. /**
  146. *
  147. * Unmaps the BARs and releases them
  148. *
  149. */
  150. void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata)
  151. {
  152. int i;
  153. for (i = 0; i < 6; i++) {
  154. if (privdata->bars_kmapped[i] == NULL)
  155. continue;
  156. iounmap((void*)privdata->bars_kmapped[i]);
  157. pci_release_region(privdata->pdev, i);
  158. }
  159. }