Adding VIOC device driver. Interrupt handler.

Signed-off-by: Misha Tomushev  <[EMAIL PROTECTED]>

diff -uprN linux-2.6.17/drivers/net/vioc/vioc_irq.c
linux-2.6.17.vioc/drivers/net/vioc/vioc_irq.c
--- linux-2.6.17/drivers/net/vioc/vioc_irq.c    1969-12-31 16:00:00.000000000
-0800
+++ linux-2.6.17.vioc/drivers/net/vioc/vioc_irq.c       2006-10-04
10:37:56.000000000 -0700
@@ -0,0 +1,538 @@
+/*
+ * Fabric7 Systems Virtual IO Controller Driver
+ * Copyright (C) 2003-2005 Fabric7 Systems.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+ * USA
+ *
+ * http://www.fabric7.com/
+ *
+ * Maintainers:
+ *    [EMAIL PROTECTED]
+ *
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <asm/apic.h>
+
+#include "f7/vnic_hw_registers.h"
+#include "f7/vnic_defs.h"
+#include "vioc_vnic.h"
+
+#define VIOC_INTERRUPTS_CNT    19      /* 16 Rx + 1 Tx + 1 BMC + 1 Error */
+#define VIOC_INTERRUPTS_CNT_PIN_IRQ    4       /* 2 Rx + 1 Tx + 1 BMC */
+
+#define VIOC_SLVAR(x) x spinlock_t vioc_driver_lock = SPIN_LOCK_UNLOCKED
+#define VIOC_CLI spin_lock_irq(&vioc_driver_lock)
+#define VIOC_STI spin_unlock_irq(&vioc_driver_lock)
+#define IRQRETURN return IRQ_HANDLED
+#define TX_IRQ_IDX                     16
+#define BMC_IRQ_IDX                    17
+#define ERR_IRQ_IDX                    18
+#define HANDLER_TASKLET                1
+#define HANDLER_DIRECT         2
+#define HANDLER_TASKQ          3
+#define VIOC_RX0_PCI_FUNC   0
+#define VIOC_TX_PCI_FUNC    1
+#define VIOC_BMC_PCI_FUNC   2
+#define VIOC_RX1_PCI_FUNC   3
+#define VIOC_IRQ_NONE       (u16) -1
+#define VIOC_ID_NONE        -1
+#define VIOC_IVEC_NONE      -1
+#define VIOC_INTR_NONE      -1
+
+
+struct vioc_msix_entry {
+       u16 vector;
+       u16 entry;
+};
+
+struct vioc_intreq {
+       char name[VIOC_NAME_LEN];
+       void (*intrFuncp) (void *);
+       void *intrFuncparm;
+        irqreturn_t(*hthandler) (int, void *, struct pt_regs *);
+       unsigned int irq;
+       unsigned int vec;
+       unsigned int intr_base;
+       unsigned int intr_offset;
+       unsigned int timeout_value;
+       unsigned int pkt_counter;
+       unsigned int rxc_mask;
+       struct work_struct taskq;
+       struct tasklet_struct tasklet;
+};
+
+struct viocdev_intreq {
+       int vioc_id;
+       struct pci_dev *pci_dev;
+       void *vioc_virt;
+       unsigned long long vioc_phy;
+       void *ioapic_virt;
+       unsigned long long ioapic_phy;
+       struct vioc_intreq intreq[VIOC_INTERRUPTS_CNT];
+       struct vioc_msix_entry irqs[VIOC_INTERRUPTS_CNT];
+};
+
+/* GLOBAL VIOC Interrupt table/structure */
+struct viocdev_intreq vioc_interrupts[VIOC_MAX_VIOCS];
+
+VIOC_SLVAR();
+
+static irqreturn_t taskq_handler(int i, void *p, struct pt_regs *r)
+{
+       int intr_id = VIOC_IRQ_PARAM_INTR_ID(p);
+       int vioc_id = VIOC_IRQ_PARAM_VIOC_ID(p);
+
+       schedule_work(&vioc_interrupts[vioc_id].intreq[intr_id].taskq);
+       IRQRETURN;
+}
+
+static irqreturn_t tasklet_handler(int i, void *p, struct pt_regs *r)
+{
+       int intr_id = VIOC_IRQ_PARAM_INTR_ID(p);
+       int vioc_id = VIOC_IRQ_PARAM_VIOC_ID(p);
+
+       tasklet_schedule(&vioc_interrupts[vioc_id].intreq[intr_id].tasklet);
+       IRQRETURN;
+}
+
+static irqreturn_t direct_handler(int i, void *p, struct pt_regs *r)
+{
+       int intr_id = VIOC_IRQ_PARAM_INTR_ID(p);
+       int vioc_id = VIOC_IRQ_PARAM_VIOC_ID(p);
+
+       vioc_interrupts[vioc_id].intreq[intr_id].
+           intrFuncp(vioc_interrupts[vioc_id].intreq[intr_id].intrFuncparm);
+       IRQRETURN;
+}
+
+static int vioc_enable_msix(u32 viocdev_idx)
+{
+       struct vioc_device *viocdev = vioc_viocdev(viocdev_idx);
+       int ret;
+
+#if defined(CONFIG_MSIX_MOD)
+       ret = pci_enable_msix(viocdev->pdev,
+                             (struct msix_entry *)
+                             &vioc_interrupts[viocdev_idx].irqs,
+                             VIOC_INTERRUPTS_CNT);
+       if (ret == 0) {
+               dev_err(&viocdev->pdev->dev, "MSI-X OK\n");
+               return VIOC_INTERRUPTS_CNT;
+       } else {
+               dev_err(&viocdev->pdev->dev,
+                      "Enabling MSIX failed (%d) VIOC %d, use PIN-IRQ\n", ret,
+                      viocdev_idx);
+               pci_disable_msix(viocdev->pdev);
+               ret = pci_request_regions(viocdev->pdev, VIOC_DRV_MODULE_NAME);
+               if (ret != 0) {
+                       dev_err(&viocdev->pdev->dev, "vioc%d: Cannot obtain PCI 
resources\n",
+                              viocdev_idx);
+                       return 0;
+               }
+               return VIOC_INTERRUPTS_CNT_PIN_IRQ;
+       }
+#else
+       ret = pci_request_regions(viocdev->pdev, VIOC_DRV_MODULE_NAME);
+       if (ret != 0) {
+               dev_err(&viocdev->pdev->dev, "vioc%d: Cannot obtain PCI 
resources\n",
+                      viocdev_idx);
+               return 0;
+       }
+       return VIOC_INTERRUPTS_CNT_PIN_IRQ;
+#endif
+}
+
+static void vioc_irq_remove(int viocdev_idx, int irq)
+{
+       int intr_id;
+
+       if (viocdev_idx >= VIOC_MAX_VIOCS)
+               return;
+
+       for (intr_id = 0; intr_id < VIOC_INTERRUPTS_CNT; intr_id++) {
+               if (vioc_interrupts[viocdev_idx].intreq[intr_id].irq == irq) {
+                       if (vioc_interrupts[viocdev_idx].intreq[intr_id].irq !=
+                           VIOC_IRQ_NONE) {
+                               free_irq(vioc_interrupts[viocdev_idx].
+                                        intreq[intr_id].irq,
+                                        vioc_interrupts[viocdev_idx].
+                                        intreq[intr_id].intrFuncparm);
+                       }
+                       vioc_interrupts[viocdev_idx].intreq[intr_id].irq =
+                           VIOC_IRQ_NONE;
+                       vioc_interrupts[viocdev_idx].irqs[intr_id].vector =
+                           VIOC_IRQ_NONE;
+               }
+       }
+}
+
+void vioc_free_irqs(u32 viocdev_idx)
+{
+       u32 i;
+
+       for (i = 0; i < VIOC_INTERRUPTS_CNT; i++) {
+               if (vioc_interrupts[viocdev_idx].irqs[i].vector !=
+                   VIOC_IRQ_NONE) {
+                       vioc_irq_remove(viocdev_idx,
+                                       vioc_interrupts[viocdev_idx].irqs[i].
+                                       vector);
+               }
+       }
+}
+
+void vioc_irq_exit(void)
+{
+       int vioc_id;
+
+       for (vioc_id = 0; vioc_id < VIOC_MAX_VIOCS; vioc_id++)
+               vioc_free_irqs(vioc_id);
+}
+
+int vioc_irq_init(void)
+{
+       int intr_id, vioc_id;
+
+       /* Zero out whole vioc_interrupts array */
+       memset(&vioc_interrupts, 0, sizeof(vioc_interrupts));
+
+       for (vioc_id = 0; vioc_id < VIOC_MAX_VIOCS; vioc_id++) {
+               vioc_interrupts[vioc_id].vioc_id = VIOC_ID_NONE;
+               for (intr_id = 0; intr_id < VIOC_INTERRUPTS_CNT; intr_id++) {
+                       vioc_interrupts[vioc_id].intreq[intr_id].irq =
+                           VIOC_IRQ_NONE;
+                       vioc_interrupts[vioc_id].irqs[intr_id].vector =
+                           VIOC_IRQ_NONE;
+                       vioc_interrupts[vioc_id].irqs[intr_id].entry = intr_id;
+               }
+       }
+       return 0;
+}
+
+int get_pci_pin_irq(struct pci_dev *dev_in, int func)
+{
+       struct pci_dev *dev = NULL;
+       unsigned int slot, fn, devfn;
+
+       slot = PCI_SLOT(dev_in->devfn);
+       fn = PCI_FUNC(dev_in->devfn);
+       devfn = dev_in->devfn;
+
+       devfn = PCI_DEVFN(slot, func);
+       /* Find pci_dev structure of the requested function */
+       dev = pci_find_slot(dev_in->bus->number, devfn);
+       if (dev) {
+               return dev->irq;
+       } else {
+               return VIOC_IRQ_NONE;
+       }
+}
+
+static int vioc_irq_install(struct pci_dev *vioc_pci_dev,
+                           void (*routine) (void *),
+                           int vioc_id,
+                           int intr_id,
+                           int irq,
+                           int intr_handler_type,
+                           int intr_param, char *intr_name)
+{
+       int ret;
+       void *intr_func_param = (void *)
+           VIOC_IRQ_PARAM_SET(vioc_id, intr_id, intr_param);
+
+       /*
+        * Find IRQ of requested interrupt: For now, search the
+        * vioc_interrupts[] table that was initialized with proper
+        * IRQs during viocdev_htirq_init() In the final product, the
+        * IRQ will be obtained from the pci_dev structure of the VIOC
+        * device.
+        */
+
+       if (vioc_id >= VIOC_MAX_VIOCS)
+               return -ENODEV;
+
+       if (intr_id >= VIOC_INTERRUPTS_CNT) {
+               dev_err(&vioc_pci_dev->dev,
+                      "%s: INTR ID (%d) out of range for Interrupt IRQ %d, 
name %s\n",
+                      __FUNCTION__, intr_id, irq, intr_name);
+               return -EINVAL;
+       }
+       vioc_interrupts[vioc_id].vioc_id = vioc_id;
+
+       if (vioc_interrupts[vioc_id].intreq[intr_id].irq != VIOC_IRQ_NONE) {
+               free_irq(vioc_interrupts[vioc_id].
+                        intreq[intr_id].irq,
+                        vioc_interrupts[vioc_id].intreq[intr_id].intrFuncparm);
+               vioc_interrupts[vioc_id].intreq[intr_id].irq = VIOC_IRQ_NONE;
+       }
+
+       vioc_set_intr_func_param(vioc_id, intr_id, intr_param);
+       INIT_WORK(&vioc_interrupts[vioc_id].intreq[intr_id].taskq, routine,
+                 intr_func_param);
+
+       vioc_interrupts[vioc_id].intreq[intr_id].irq = irq;
+       vioc_interrupts[vioc_id].intreq[intr_id].name[VIOC_NAME_LEN - 1] = '\0';
+
+       vioc_interrupts[vioc_id].intreq[intr_id].timeout_value = 100;
+       vioc_interrupts[vioc_id].intreq[intr_id].pkt_counter = 1;
+       vioc_interrupts[vioc_id].intreq[intr_id].vec = VIOC_IVEC_NONE;
+       vioc_interrupts[vioc_id].intreq[intr_id].intrFuncp = routine;
+
+       /* Init work_struct used to schedule task from INTR handler code */
+
+       /* Init tasklet_struct used to run "tasklet" from INTR handler code */
+       vioc_interrupts[vioc_id].intreq[intr_id].tasklet.next = NULL;
+       vioc_interrupts[vioc_id].intreq[intr_id].tasklet.state = 0;
+       atomic_set(&vioc_interrupts[vioc_id].intreq[intr_id].tasklet.count, 0);
+       vioc_interrupts[vioc_id].intreq[intr_id].tasklet.func =
+           (void (*)(unsigned long))routine;
+
+       snprintf(&vioc_interrupts[vioc_id].intreq[intr_id].name[0],
+                VIOC_NAME_LEN, "%s_%02x", intr_name, vioc_id);
+       vioc_interrupts[vioc_id].intreq[intr_id].name[VIOC_NAME_LEN - 1] = '\0';
+
+       if (intr_handler_type == HANDLER_TASKLET)
+               vioc_interrupts[vioc_id].intreq[intr_id].hthandler =
+                   tasklet_handler;
+       else if (intr_handler_type == HANDLER_TASKQ)
+               vioc_interrupts[vioc_id].intreq[intr_id].hthandler =
+                   taskq_handler;
+       else if (intr_handler_type == HANDLER_DIRECT)
+               vioc_interrupts[vioc_id].intreq[intr_id].hthandler =
+                   direct_handler;
+       else {
+               dev_err(&vioc_pci_dev->dev,
+                      "%s: Interrupt handler type for name %s unknown\n",
+                      __FUNCTION__, intr_name);
+               return -EINVAL;
+       }
+
+       ret = request_irq(vioc_interrupts[vioc_id].intreq[intr_id].irq,
+                         vioc_interrupts[vioc_id].intreq[intr_id].hthandler,
+                         SA_INTERRUPT,
+                         vioc_interrupts[vioc_id].intreq[intr_id].name,
+                         vioc_interrupts[vioc_id].intreq[intr_id].
+                         intrFuncparm);
+       if (ret) {
+               dev_err(&vioc_pci_dev->dev, "%s: request_irq() -> %d\n", 
__FUNCTION__,
ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+int vioc_set_intr_func_param(int viocdev_idx, int intr_idx, int intr_param)
+{
+       struct vioc_device *viocdev = vioc_viocdev(viocdev_idx);
+
+       void *intr_func_param = (void *)
+           VIOC_IRQ_PARAM_SET(viocdev_idx, intr_idx, intr_param);
+
+       if (viocdev_idx >= VIOC_MAX_VIOCS) {
+               dev_err(&viocdev->pdev->dev, "%s: VIOC ID (%d) is out of 
range\n",
+                      __FUNCTION__, viocdev_idx);
+               return -ENODEV;
+       }
+
+       if (intr_idx >= VIOC_INTERRUPTS_CNT) {
+               dev_err(&viocdev->pdev->dev, "%s: INTR ID (%d) is out of 
range\n",
+                      __FUNCTION__, intr_idx);
+               return -EINVAL;
+       }
+
+       vioc_interrupts[viocdev_idx].intreq[intr_idx].intrFuncparm =
+           intr_func_param;
+       vioc_interrupts[viocdev_idx].intreq[intr_idx].taskq.data =
+           intr_func_param;
+       vioc_interrupts[viocdev_idx].intreq[intr_idx].tasklet.data =
+           (unsigned long)intr_func_param;
+       return 0;
+}
+
+/*
+ * Function returns number of Rx IRQs.
+ * When PIN IRQ is used, 2 Rx IRQs are supported.
+ * With MSI-X 16 Rx IRQs.
+ */
+
+int vioc_request_irqs(u32 viocdev_idx)
+{
+       struct vioc_device *viocdev = vioc_viocdev(viocdev_idx);
+       int ret;
+       int total_num_irqs;
+       int intr_idx;
+       char name_buf[64];
+
+       /* Check for MSI-X, install either 2 or 16 Rx IRQs */
+       total_num_irqs = vioc_enable_msix(viocdev_idx);
+       viocdev->num_irqs = total_num_irqs;
+
+       switch (total_num_irqs) {
+
+       case VIOC_INTERRUPTS_CNT_PIN_IRQ:
+               vioc_interrupts[viocdev_idx].irqs[2].vector =
+                   get_pci_pin_irq(viocdev->pdev, VIOC_TX_PCI_FUNC);
+               vioc_interrupts[viocdev_idx].irqs[3].vector =
+                   get_pci_pin_irq(viocdev->pdev, VIOC_BMC_PCI_FUNC);
+
+               intr_idx = 0;
+               vioc_interrupts[viocdev_idx].irqs[intr_idx].vector =
+                   get_pci_pin_irq(viocdev->pdev, VIOC_RX0_PCI_FUNC);
+               sprintf(name_buf, "rx%02d_intr", intr_idx);
+               ret = vioc_irq_install(viocdev->pdev,
+                                      vioc_rxc_interrupt,
+                                      viocdev_idx,
+                                      intr_idx,
+                                      vioc_interrupts[viocdev_idx].
+                                      irqs[intr_idx].vector, HANDLER_DIRECT,
+                                      intr_idx, name_buf);
+               if (ret) {
+                       dev_err(&viocdev->pdev->dev, "vioc%d: RX IRQ %02d not 
installed\n",
+                              viocdev_idx, intr_idx);
+                       return 0;
+               }
+
+               intr_idx = 1;
+               vioc_interrupts[viocdev_idx].irqs[intr_idx].vector =
+                   get_pci_pin_irq(viocdev->pdev, VIOC_RX1_PCI_FUNC);
+               sprintf(name_buf, "rx%02d_intr", intr_idx);
+               ret = vioc_irq_install(viocdev->pdev,
+                                      vioc_rxc_interrupt,
+                                      viocdev_idx,
+                                      intr_idx,
+                                      vioc_interrupts[viocdev_idx].
+                                      irqs[intr_idx].vector, HANDLER_DIRECT,
+                                      intr_idx, name_buf);
+               if (ret) {
+                       dev_err(&viocdev->pdev->dev, "vioc%d: RX IRQ %02d not 
installed\n",
+                              viocdev_idx, intr_idx);
+                       return 0;
+               }
+
+               intr_idx = TX_IRQ_IDX;
+               vioc_interrupts[viocdev_idx].irqs[intr_idx].vector =
+                   get_pci_pin_irq(viocdev->pdev, VIOC_TX_PCI_FUNC);
+               ret = vioc_irq_install(viocdev->pdev,
+                                      vioc_tx_interrupt,
+                                      viocdev_idx,
+                                      intr_idx,
+                                      vioc_interrupts[viocdev_idx].
+                                      irqs[intr_idx].vector, HANDLER_TASKLET,
+                                      intr_idx, "tx_intr");
+               if (ret) {
+                       dev_err(&viocdev->pdev->dev, "vioc%d: TX IRQ not 
installed\n",
+                              viocdev_idx);
+                       return 0;
+               }
+
+               intr_idx = BMC_IRQ_IDX;
+               vioc_interrupts[viocdev_idx].irqs[intr_idx].vector =
+                   get_pci_pin_irq(viocdev->pdev, VIOC_BMC_PCI_FUNC);
+               ret = vioc_irq_install(viocdev->pdev,
+                                      vioc_bmc_interrupt,
+                                      viocdev_idx,
+                                      intr_idx,
+                                      vioc_interrupts[viocdev_idx].
+                                      irqs[intr_idx].vector, HANDLER_TASKQ,
+                                      intr_idx, "bmc_intr");
+               if (ret) {
+                       dev_err(&viocdev->pdev->dev, "vioc%d: BMC IRQ not 
installed\n",
+                              viocdev_idx);
+                       return 0;
+               }
+
+               return 2;
+
+       case VIOC_INTERRUPTS_CNT:
+
+               for (intr_idx = 0; intr_idx < 16; intr_idx++) {
+                       sprintf(name_buf, "rx%02d_intr", intr_idx);
+                       ret = vioc_irq_install(viocdev->pdev,
+                                              vioc_rxc_interrupt,
+                                              viocdev_idx,
+                                              intr_idx,
+                                              vioc_interrupts[viocdev_idx].
+                                              irqs[intr_idx].vector,
+                                              HANDLER_DIRECT, intr_idx,
+                                              name_buf);
+                       if (ret) {
+                               dev_err(&viocdev->pdev->dev,
+                                      "vioc%d: RX IRQ %02d not installed\n",
+                                      viocdev_idx, intr_idx);
+                               return 0;
+                       }
+               }
+
+               intr_idx = TX_IRQ_IDX;
+               ret = vioc_irq_install(viocdev->pdev,
+                                      vioc_tx_interrupt,
+                                      viocdev_idx,
+                                      intr_idx,
+                                      vioc_interrupts[viocdev_idx].
+                                      irqs[intr_idx].vector, HANDLER_TASKLET,
+                                      intr_idx, "tx_intr");
+               if (ret) {
+                       dev_err(&viocdev->pdev->dev, "vioc%d: TX IRQ not 
installed\n",
+                              viocdev_idx);
+                       return 0;
+               }
+
+               intr_idx = BMC_IRQ_IDX;
+               ret = vioc_irq_install(viocdev->pdev,
+                                      vioc_bmc_interrupt,
+                                      viocdev_idx,
+                                      intr_idx,
+                                      vioc_interrupts[viocdev_idx].
+                                      irqs[intr_idx].vector, HANDLER_TASKQ,
+                                      intr_idx, "bmc_intr");
+               if (ret) {
+                       dev_err(&viocdev->pdev->dev, "vioc%d: BMC IRQ not 
installed\n",
+                              viocdev_idx);
+                       return 0;
+               }
+
+               return 16;
+
+       default:
+
+               return 0;
+       }
+
+       return 0;
+}
+


--
Misha Tomushev
[EMAIL PROTECTED]

-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to