From: Misha Tomushev <misha@fabric7.com>
To: Jeff Garzik <jeff@garzik.org>
Cc: KERNEL Linux <linux-kernel@vger.kernel.org>,
NETDEV Linux <linux-kernel@vger.kernel.org>
Subject: [PATCH 7/10] VIOC: New Network Device Driver
Date: Thu, 5 Oct 2006 11:07:59 -0700 [thread overview]
Message-ID: <200610051107.59678.misha@fabric7.com> (raw)
Adding VIOC device driver. Interrupt handler.
Signed-off-by: Misha Tomushev <misha@fabric7.com>
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:
+ * driver-support@fabric7.com
+ *
+ *
+ */
+#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
misha@fabric7.com
next reply other threads:[~2006-10-05 18:24 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-10-05 18:07 Misha Tomushev [this message]
-- strict thread matches above, loose matches on Subject: below --
2006-10-05 18:55 [PATCH 7/10] VIOC: New Network Device Driver Misha Tomushev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=200610051107.59678.misha@fabric7.com \
--to=misha@fabric7.com \
--cc=jeff@garzik.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.