Skip to content

Commit

Permalink
hpilo: add interrupt handler
Browse files Browse the repository at this point in the history
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.

Signed-off-by: David Altobelli <david.altobelli@hp.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
David Altobelli authored and Greg Kroah-Hartman committed Sep 15, 2009
1 parent 66d5e51 commit 9f70484
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 45 deletions.
138 changes: 94 additions & 44 deletions drivers/misc/hpilo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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;
Expand Down Expand Up @@ -61,28 +63,30 @@ static inline int desc_mem_sz(int nr_entry)
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] |=
(entry & ENTRY_MASK_NOSTATE) | fifo_q->merge;
fifo_q->tail += 1;
ret = 1;
}
spin_unlock(&hw->fifo_lock);
spin_unlock_irqrestore(&hw->fifo_lock, flags);

return ret;
}

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)
Expand All @@ -93,7 +97,7 @@ static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry)
fifo_q->head += 1;
ret = 1;
}
spin_unlock(&hw->fifo_lock);
spin_unlock_irqrestore(&hw->fifo_lock, flags);

return ret;
}
Expand Down Expand Up @@ -374,7 +378,18 @@ static inline void clear_device(struct ilo_hwinfo *hw)
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;

Expand All @@ -387,19 +402,6 @@ static void ilo_locked_reset(struct ilo_hwinfo *hw)
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,
Expand All @@ -411,12 +413,11 @@ static ssize_t ilo_read(struct file *fp, char __user *buf,
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;
}

Expand Down Expand Up @@ -462,14 +463,8 @@ static ssize_t ilo_write(struct file *fp, const char __user *buf,
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))
Expand All @@ -496,27 +491,28 @@ static int ilo_close(struct inode *ip, struct file *fp)
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;
}
Expand All @@ -526,6 +522,7 @@ static int ilo_open(struct inode *ip, struct file *fp)
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);
Expand All @@ -535,10 +532,7 @@ static int ilo_open(struct inode *ip, struct file *fp)
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) {
Expand All @@ -549,22 +543,31 @@ static int ilo_open(struct inode *ip, struct file *fp)
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) {
Expand All @@ -580,7 +583,7 @@ static int ilo_open(struct inode *ip, struct file *fp)
}
}
out:
spin_unlock(&hw->alloc_lock);
spin_unlock(&hw->open_lock);

if (!error)
fp->private_data = hw->ccb_alloc[slot];
Expand All @@ -596,6 +599,41 @@ static const struct file_operations ilo_fops = {
.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);
Expand Down Expand Up @@ -649,6 +687,8 @@ static void ilo_remove(struct pci_dev *pdev)
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);
Expand Down Expand Up @@ -684,6 +724,7 @@ static int __devinit ilo_probe(struct pci_dev *pdev,
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)
Expand All @@ -702,13 +743,19 @@ static int __devinit ilo_probe(struct pci_dev *pdev,
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++) {
Expand All @@ -721,6 +768,9 @@ static int __devinit ilo_probe(struct pci_dev *pdev,
}

return 0;
remove_isr:
ilo_disable_interrupts(ilo_hw);
free_irq(pdev->irq, ilo_hw);
unmap:
ilo_unmap_device(pdev, ilo_hw);
free_regions:
Expand Down
8 changes: 7 additions & 1 deletion drivers/misc/hpilo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit 9f70484

Please sign in to comment.