public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: David Altobelli <david.altobelli@hp.com>
To: David Altobelli <david.altobelli@hp.com>
Cc: linux-kernel@vger.kernel.org, gregkh@suse.de
Subject: [PATCH 2/3] hpilo: add interrupt handler
Date: Mon, 10 Aug 2009 14:00:05 -0600	[thread overview]
Message-ID: <20090810200005.GB29404@ldl.fc.hp.com> (raw)
In-Reply-To: <20090810195735.GA29372@ldl.fc.hp.com>

Add interrupt handler to hpilo.  This is enablement for poll handler, and
it also simplifies the logic for handling an iLO reset, because now 
only the interrupt handler needs to look for reset, the file system interfaces
only need to return failure when a reset has happened.

Please CC me on any replies.

Signed-off-by: David Altobelli <david.altobelli@hp.com>
---
 hpilo.c |  138 +++++++++++++++++++++++++++++++++++++++++++---------------------
 hpilo.h |    8 +++
 2 files changed, 101 insertions(+), 45 deletions(-)
diff -uprN -X linux-2.6.30.3-orig/Documentation/dontdiff linux-2.6.30.3-orig/drivers/misc/hpilo.c linux-2.6.30.3/drivers/misc/hpilo.c
--- linux-2.6.30.3-orig/drivers/misc/hpilo.c	2009-08-07 09:26:45.000000000 -0500
+++ linux-2.6.30.3/drivers/misc/hpilo.c	2009-08-07 09:26:24.000000000 -0500
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/pci.h>
+#include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/device.h>
 #include <linux/file.h>
@@ -21,6 +22,7 @@
 #include <linux/delay.h>
 #include <linux/uaccess.h>
 #include <linux/io.h>
+#include <linux/wait.h>
 #include "hpilo.h"
 
 static struct class *ilo_class;
@@ -61,9 +63,10 @@ static inline int desc_mem_sz(int nr_ent
 static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry)
 {
 	struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar);
+	unsigned long flags;
 	int ret = 0;
 
-	spin_lock(&hw->fifo_lock);
+	spin_lock_irqsave(&hw->fifo_lock, flags);
 	if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask]
 	      & ENTRY_MASK_O)) {
 		fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |=
@@ -71,7 +74,7 @@ static int fifo_enqueue(struct ilo_hwinf
 		fifo_q->tail += 1;
 		ret = 1;
 	}
-	spin_unlock(&hw->fifo_lock);
+	spin_unlock_irqrestore(&hw->fifo_lock, flags);
 
 	return ret;
 }
@@ -79,10 +82,11 @@ static int fifo_enqueue(struct ilo_hwinf
 static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry)
 {
 	struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar);
+	unsigned long flags;
 	int ret = 0;
 	u64 c;
 
-	spin_lock(&hw->fifo_lock);
+	spin_lock_irqsave(&hw->fifo_lock, flags);
 	c = fifo_q->fifobar[fifo_q->head & fifo_q->imask];
 	if (c & ENTRY_MASK_C) {
 		if (entry)
@@ -93,7 +97,7 @@ static int fifo_dequeue(struct ilo_hwinf
 		fifo_q->head += 1;
 		ret = 1;
 	}
-	spin_unlock(&hw->fifo_lock);
+	spin_unlock_irqrestore(&hw->fifo_lock, flags);
 
 	return ret;
 }
@@ -374,7 +378,18 @@ static inline void clear_device(struct i
 	clear_pending_db(hw, -1);
 }
 
-static void ilo_locked_reset(struct ilo_hwinfo *hw)
+static inline void ilo_enable_interrupts(struct ilo_hwinfo *hw)
+{
+	iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) | 1, &hw->mmio_vaddr[DB_IRQ]);
+}
+
+static inline void ilo_disable_interrupts(struct ilo_hwinfo *hw)
+{
+	iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) & ~1,
+		 &hw->mmio_vaddr[DB_IRQ]);
+}
+
+static void ilo_set_reset(struct ilo_hwinfo *hw)
 {
 	int slot;
 
@@ -387,19 +402,6 @@ static void ilo_locked_reset(struct ilo_
 			continue;
 		set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb);
 	}
-
-	clear_device(hw);
-}
-
-static void ilo_reset(struct ilo_hwinfo *hw)
-{
-	spin_lock(&hw->alloc_lock);
-
-	/* reset might have been handled after lock was taken */
-	if (is_device_reset(hw))
-		ilo_locked_reset(hw);
-
-	spin_unlock(&hw->alloc_lock);
 }
 
 static ssize_t ilo_read(struct file *fp, char __user *buf,
@@ -411,12 +413,11 @@ static ssize_t ilo_read(struct file *fp,
 	struct ilo_hwinfo *hw = data->ilo_hw;
 	void *pkt;
 
-	if (is_device_reset(hw) || is_channel_reset(driver_ccb)) {
+	if (is_channel_reset(driver_ccb)) {
 		/*
 		 * If the device has been reset, applications
 		 * need to close and reopen all ccbs.
 		 */
-		ilo_reset(hw);
 		return -ENODEV;
 	}
 
@@ -462,14 +463,8 @@ static ssize_t ilo_write(struct file *fp
 	struct ilo_hwinfo *hw = data->ilo_hw;
 	void *pkt;
 
-	if (is_device_reset(hw) || is_channel_reset(driver_ccb)) {
-		/*
-		 * If the device has been reset, applications
-		 * need to close and reopen all ccbs.
-		 */
-		ilo_reset(hw);
+	if (is_channel_reset(driver_ccb))
 		return -ENODEV;
-	}
 
 	/* get a packet to send the user command */
 	if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt))
@@ -496,27 +491,28 @@ static int ilo_close(struct inode *ip, s
 	int slot;
 	struct ccb_data *data;
 	struct ilo_hwinfo *hw;
+	unsigned long flags;
 
 	slot = iminor(ip) % MAX_CCB;
 	hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev);
 
-	spin_lock(&hw->alloc_lock);
-
-	if (is_device_reset(hw))
-		ilo_locked_reset(hw);
+	spin_lock(&hw->open_lock);
 
 	if (hw->ccb_alloc[slot]->ccb_cnt == 1) {
 
 		data = fp->private_data;
 
+		spin_lock_irqsave(&hw->alloc_lock, flags);
+		hw->ccb_alloc[slot] = NULL;
+		spin_unlock_irqrestore(&hw->alloc_lock, flags);
+
 		ilo_ccb_close(hw->ilo_dev, data);
 
 		kfree(data);
-		hw->ccb_alloc[slot] = NULL;
 	} else
 		hw->ccb_alloc[slot]->ccb_cnt--;
 
-	spin_unlock(&hw->alloc_lock);
+	spin_unlock(&hw->open_lock);
 
 	return 0;
 }
@@ -526,6 +522,7 @@ static int ilo_open(struct inode *ip, st
 	int slot, error;
 	struct ccb_data *data;
 	struct ilo_hwinfo *hw;
+	unsigned long flags;
 
 	slot = iminor(ip) % MAX_CCB;
 	hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev);
@@ -535,10 +532,7 @@ static int ilo_open(struct inode *ip, st
 	if (!data)
 		return -ENOMEM;
 
-	spin_lock(&hw->alloc_lock);
-
-	if (is_device_reset(hw))
-		ilo_locked_reset(hw);
+	spin_lock(&hw->open_lock);
 
 	/* each fd private_data holds sw/hw view of ccb */
 	if (hw->ccb_alloc[slot] == NULL) {
@@ -549,22 +543,31 @@ static int ilo_open(struct inode *ip, st
 			goto out;
 		}
 
+		data->ccb_cnt = 1;
+		data->ccb_excl = fp->f_flags & O_EXCL;
+		data->ilo_hw = hw;
+		init_waitqueue_head(&data->ccb_waitq);
+
 		/* write the ccb to hw */
+		spin_lock_irqsave(&hw->alloc_lock, flags);
 		ilo_ccb_open(hw, data, slot);
+		hw->ccb_alloc[slot] = data;
+		spin_unlock_irqrestore(&hw->alloc_lock, flags);
 
 		/* make sure the channel is functional */
 		error = ilo_ccb_verify(hw, data);
 		if (error) {
+
+			spin_lock_irqsave(&hw->alloc_lock, flags);
+			hw->ccb_alloc[slot] = NULL;
+			spin_unlock_irqrestore(&hw->alloc_lock, flags);
+
 			ilo_ccb_close(hw->ilo_dev, data);
+
 			kfree(data);
 			goto out;
 		}
 
-		data->ccb_cnt = 1;
-		data->ccb_excl = fp->f_flags & O_EXCL;
-		data->ilo_hw = hw;
-		hw->ccb_alloc[slot] = data;
-
 	} else {
 		kfree(data);
 		if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) {
@@ -580,7 +583,7 @@ static int ilo_open(struct inode *ip, st
 		}
 	}
 out:
-	spin_unlock(&hw->alloc_lock);
+	spin_unlock(&hw->open_lock);
 
 	if (!error)
 		fp->private_data = hw->ccb_alloc[slot];
@@ -596,6 +599,41 @@ static const struct file_operations ilo_
 	.release 	= ilo_close,
 };
 
+static irqreturn_t ilo_isr(int irq, void *data)
+{
+	struct ilo_hwinfo *hw = data;
+	int pending, i;
+
+	spin_lock(&hw->alloc_lock);
+
+	/* check for ccbs which have data */
+	pending = get_device_outbound(hw);
+	if (!pending) {
+		spin_unlock(&hw->alloc_lock);
+		return IRQ_NONE;
+	}
+
+	if (is_db_reset(pending)) {
+		/* wake up all ccbs if the device was reset */
+		pending = -1;
+		ilo_set_reset(hw);
+	}
+
+	for (i = 0; i < MAX_CCB; i++) {
+		if (!hw->ccb_alloc[i])
+			continue;
+		if (pending & (1 << i))
+			wake_up_interruptible(&hw->ccb_alloc[i]->ccb_waitq);
+	}
+
+	/* clear the device of the channels that have been handled */
+	clear_pending_db(hw, pending);
+
+	spin_unlock(&hw->alloc_lock);
+
+	return IRQ_HANDLED;
+}
+
 static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
 {
 	pci_iounmap(pdev, hw->db_vaddr);
@@ -649,6 +687,8 @@ static void ilo_remove(struct pci_dev *p
 		device_destroy(ilo_class, MKDEV(ilo_major, i));
 
 	cdev_del(&ilo_hw->cdev);
+	ilo_disable_interrupts(ilo_hw);
+	free_irq(pdev->irq, ilo_hw);
 	ilo_unmap_device(pdev, ilo_hw);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
@@ -684,6 +724,7 @@ static int __devinit ilo_probe(struct pc
 	ilo_hw->ilo_dev = pdev;
 	spin_lock_init(&ilo_hw->alloc_lock);
 	spin_lock_init(&ilo_hw->fifo_lock);
+	spin_lock_init(&ilo_hw->open_lock);
 
 	error = pci_enable_device(pdev);
 	if (error)
@@ -702,13 +743,19 @@ static int __devinit ilo_probe(struct pc
 	pci_set_drvdata(pdev, ilo_hw);
 	clear_device(ilo_hw);
 
+	error = request_irq(pdev->irq, ilo_isr, IRQF_SHARED, "hpilo", ilo_hw);
+	if (error)
+		goto unmap;
+
+	ilo_enable_interrupts(ilo_hw);
+
 	cdev_init(&ilo_hw->cdev, &ilo_fops);
 	ilo_hw->cdev.owner = THIS_MODULE;
 	start = devnum * MAX_CCB;
 	error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB);
 	if (error) {
 		dev_err(&pdev->dev, "Could not add cdev\n");
-		goto unmap;
+		goto remove_isr;
 	}
 
 	for (minor = 0 ; minor < MAX_CCB; minor++) {
@@ -721,6 +768,9 @@ static int __devinit ilo_probe(struct pc
 	}
 
 	return 0;
+remove_isr:
+	ilo_disable_interrupts(ilo_hw);
+	free_irq(pdev->irq, ilo_hw);
 unmap:
 	ilo_unmap_device(pdev, ilo_hw);
 free_regions:
diff -uprN -X linux-2.6.30.3-orig/Documentation/dontdiff linux-2.6.30.3-orig/drivers/misc/hpilo.h linux-2.6.30.3/drivers/misc/hpilo.h
--- linux-2.6.30.3-orig/drivers/misc/hpilo.h	2009-08-07 09:05:09.000000000 -0500
+++ linux-2.6.30.3/drivers/misc/hpilo.h	2009-07-28 16:27:46.000000000 -0500
@@ -46,11 +46,14 @@ struct ilo_hwinfo {
 
 	spinlock_t alloc_lock;
 	spinlock_t fifo_lock;
+	spinlock_t open_lock;
 
 	struct cdev cdev;
 };
 
-/* offset from mmio_vaddr */
+/* offset from mmio_vaddr for enabling doorbell interrupts */
+#define DB_IRQ		0xB2
+/* offset from mmio_vaddr for outbound communications */
 #define DB_OUT		0xD4
 /* DB_OUT reset bit */
 #define DB_RESET	26
@@ -131,6 +134,9 @@ struct ccb_data {
 	/* pointer to hardware device info */
 	struct ilo_hwinfo *ilo_hw;
 
+	/* queue for this ccb to wait for recv data */
+	wait_queue_head_t ccb_waitq;
+
 	/* usage count, to allow for shared ccb's */
 	int	    ccb_cnt;

  parent reply	other threads:[~2009-08-10 20:00 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-08-10 19:57 [PATCH 0/3] Enable poll handler in hpilo David Altobelli
2009-08-10 19:58 ` [PATCH 1/3] hpilo: staging for interrupt handling David Altobelli
2009-08-10 20:00 ` David Altobelli [this message]
2009-08-18 21:30   ` [PATCH 2/3] hpilo: add interrupt handler Andrew Morton
2009-08-18 22:25     ` Altobelli, David
2009-08-10 20:00 ` [PATCH 3/3] hpilo: add poll f_op David Altobelli

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=20090810200005.GB29404@ldl.fc.hp.com \
    --to=david.altobelli@hp.com \
    --cc=gregkh@suse.de \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox