123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- /**
- *
- * @file int.c
- * @author Guillermo Marcus
- * @date 2009-04-05
- * @brief Contains the interrupt handler.
- *
- */
- #include <linux/version.h>
- #include <linux/string.h>
- #include <linux/types.h>
- #include <linux/list.h>
- #include <linux/interrupt.h>
- #include <linux/pci.h>
- #include <linux/cdev.h>
- #include <linux/wait.h>
- #include <linux/sched.h>
- #include <stdbool.h>
- #include "base.h"
- /**
- *
- * Acknowledges the receival of an interrupt to the card.
- *
- * @returns true if the card was acknowledget
- * @returns false if the interrupt was not for one of our cards
- *
- * @see check_acknowlegde_channel
- *
- */
- static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata)
- {
- int channel = 0;
- atomic_inc(&(privdata->irq_outstanding[channel]));
- wake_up_interruptible(&(privdata->irq_queues[channel]));
- return true;
- }
- /**
- *
- * Handles IRQs. At the moment, this acknowledges the card that this IRQ
- * was received and then increases the driver's IRQ counter.
- *
- * @see pcidriver_irq_acknowledge
- *
- */
- static irqreturn_t pcidriver_irq_handler(int irq, void *dev_id)
- {
- pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)dev_id;
- if (!pcidriver_irq_acknowledge(privdata))
- return IRQ_NONE;
- privdata->irq_count++;
- return IRQ_HANDLED;
- }
- /**
- *
- * If IRQ-handling is enabled, this function will be called from pcidriver_probe
- * to initialize the IRQ handling (maps the BARs)
- *
- */
- int pcidriver_probe_irq(pcidriver_privdata_t *privdata)
- {
- unsigned char int_pin, int_line;
- unsigned long bar_addr, bar_len, bar_flags;
- int i;
- int err;
- for (i = 0; i < 6; i++)
- privdata->bars_kmapped[i] = NULL;
- for (i = 0; i < 6; i++) {
- bar_addr = pci_resource_start(privdata->pdev, i);
- bar_len = pci_resource_len(privdata->pdev, i);
- bar_flags = pci_resource_flags(privdata->pdev, i);
- /* check if it is a valid BAR, skip if not */
- if ((bar_addr == 0) || (bar_len == 0))
- continue;
- /* Skip IO regions (map only mem regions) */
- if (bar_flags & IORESOURCE_IO)
- continue;
- /* Check if the region is available */
- if ((err = pci_request_region(privdata->pdev, i, NULL)) != 0) {
- mod_info( "Failed to request BAR memory region.\n" );
- return err;
- }
- /* Map it into kernel space. */
- /* For x86 this is just a dereference of the pointer, but that is
- * not portable. So we need to do the portable way. Thanks Joern!
- */
- /* respect the cacheable-bility of the region */
- if (bar_flags & IORESOURCE_PREFETCH)
- privdata->bars_kmapped[i] = ioremap(bar_addr, bar_len);
- else
- privdata->bars_kmapped[i] = ioremap_nocache(bar_addr, bar_len);
- /* check for error */
- if (privdata->bars_kmapped[i] == NULL) {
- mod_info( "Failed to remap BAR%d into kernel space.\n", i );
- return -EIO;
- }
- }
- /* Initialize the interrupt handler for this device */
- /* Initialize the wait queues */
- for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++) {
- init_waitqueue_head(&(privdata->irq_queues[i]));
- atomic_set(&(privdata->irq_outstanding[i]), 0);
- }
- /* Initialize the irq config */
- if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &int_pin)) != 0) {
- /* continue without interrupts */
- int_pin = 0;
- mod_info("Error getting the interrupt pin. Disabling interrupts for this device\n");
- }
- /* Disable interrupts and activate them if everything can be set up properly */
- privdata->irq_enabled = 0;
- if (int_pin == 0)
- return 0;
- if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_LINE, &int_line)) != 0) {
- mod_info("Error getting the interrupt line. Disabling interrupts for this device\n");
- return 0;
- }
- /* Enable interrupts using MSI mode */
- if (!pci_enable_msi(privdata->pdev))
- privdata->msi_mode = 1;
- /* register interrupt handler */
- if ((err = request_irq(privdata->pdev->irq, pcidriver_irq_handler, IRQF_SHARED, MODNAME, privdata)) != 0) {
- mod_info("Error registering the interrupt handler. Disabling interrupts for this device\n");
- return 0;
- }
- privdata->irq_enabled = 1;
- mod_info("Registered Interrupt Handler at pin %i, line %i, IRQ %i\n", int_pin, int_line, privdata->pdev->irq );
- return 0;
- }
- /**
- *
- * Frees/cleans up the data structures, called from pcidriver_remove()
- *
- */
- void pcidriver_remove_irq(pcidriver_privdata_t *privdata)
- {
- /* Release the IRQ handler */
- if (privdata->irq_enabled != 0)
- free_irq(privdata->pdev->irq, privdata);
- if (privdata->msi_mode) {
- pci_disable_msi(privdata->pdev);
- privdata->msi_mode = 0;
- }
- pcidriver_irq_unmap_bars(privdata);
- }
- /**
- *
- * Unmaps the BARs and releases them
- *
- */
- void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata)
- {
- int i;
- for (i = 0; i < 6; i++) {
- if (privdata->bars_kmapped[i] == NULL)
- continue;
- iounmap((void*)privdata->bars_kmapped[i]);
- pci_release_region(privdata->pdev, i);
- }
- }
|