diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/hpilo.c | 138 | ||||
-rw-r--r-- | drivers/misc/hpilo.h | 8 |
2 files changed, 101 insertions, 45 deletions
diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 35ed12379cd0..9a370d5acf87 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/fs.h> | 14 | #include <linux/fs.h> |
15 | #include <linux/pci.h> | 15 | #include <linux/pci.h> |
16 | #include <linux/interrupt.h> | ||
16 | #include <linux/ioport.h> | 17 | #include <linux/ioport.h> |
17 | #include <linux/device.h> | 18 | #include <linux/device.h> |
18 | #include <linux/file.h> | 19 | #include <linux/file.h> |
@@ -21,6 +22,7 @@ | |||
21 | #include <linux/delay.h> | 22 | #include <linux/delay.h> |
22 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
23 | #include <linux/io.h> | 24 | #include <linux/io.h> |
25 | #include <linux/wait.h> | ||
24 | #include "hpilo.h" | 26 | #include "hpilo.h" |
25 | 27 | ||
26 | static struct class *ilo_class; | 28 | static struct class *ilo_class; |
@@ -61,9 +63,10 @@ static inline int desc_mem_sz(int nr_entry) | |||
61 | static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) | 63 | static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) |
62 | { | 64 | { |
63 | struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); | 65 | struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); |
66 | unsigned long flags; | ||
64 | int ret = 0; | 67 | int ret = 0; |
65 | 68 | ||
66 | spin_lock(&hw->fifo_lock); | 69 | spin_lock_irqsave(&hw->fifo_lock, flags); |
67 | if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask] | 70 | if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask] |
68 | & ENTRY_MASK_O)) { | 71 | & ENTRY_MASK_O)) { |
69 | fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |= | 72 | fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |= |
@@ -71,7 +74,7 @@ static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) | |||
71 | fifo_q->tail += 1; | 74 | fifo_q->tail += 1; |
72 | ret = 1; | 75 | ret = 1; |
73 | } | 76 | } |
74 | spin_unlock(&hw->fifo_lock); | 77 | spin_unlock_irqrestore(&hw->fifo_lock, flags); |
75 | 78 | ||
76 | return ret; | 79 | return ret; |
77 | } | 80 | } |
@@ -79,10 +82,11 @@ static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) | |||
79 | static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) | 82 | static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) |
80 | { | 83 | { |
81 | struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); | 84 | struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); |
85 | unsigned long flags; | ||
82 | int ret = 0; | 86 | int ret = 0; |
83 | u64 c; | 87 | u64 c; |
84 | 88 | ||
85 | spin_lock(&hw->fifo_lock); | 89 | spin_lock_irqsave(&hw->fifo_lock, flags); |
86 | c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; | 90 | c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; |
87 | if (c & ENTRY_MASK_C) { | 91 | if (c & ENTRY_MASK_C) { |
88 | if (entry) | 92 | if (entry) |
@@ -93,7 +97,7 @@ static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) | |||
93 | fifo_q->head += 1; | 97 | fifo_q->head += 1; |
94 | ret = 1; | 98 | ret = 1; |
95 | } | 99 | } |
96 | spin_unlock(&hw->fifo_lock); | 100 | spin_unlock_irqrestore(&hw->fifo_lock, flags); |
97 | 101 | ||
98 | return ret; | 102 | return ret; |
99 | } | 103 | } |
@@ -374,7 +378,18 @@ static inline void clear_device(struct ilo_hwinfo *hw) | |||
374 | clear_pending_db(hw, -1); | 378 | clear_pending_db(hw, -1); |
375 | } | 379 | } |
376 | 380 | ||
377 | static void ilo_locked_reset(struct ilo_hwinfo *hw) | 381 | static inline void ilo_enable_interrupts(struct ilo_hwinfo *hw) |
382 | { | ||
383 | iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) | 1, &hw->mmio_vaddr[DB_IRQ]); | ||
384 | } | ||
385 | |||
386 | static inline void ilo_disable_interrupts(struct ilo_hwinfo *hw) | ||
387 | { | ||
388 | iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) & ~1, | ||
389 | &hw->mmio_vaddr[DB_IRQ]); | ||
390 | } | ||
391 | |||
392 | static void ilo_set_reset(struct ilo_hwinfo *hw) | ||
378 | { | 393 | { |
379 | int slot; | 394 | int slot; |
380 | 395 | ||
@@ -387,19 +402,6 @@ static void ilo_locked_reset(struct ilo_hwinfo *hw) | |||
387 | continue; | 402 | continue; |
388 | set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb); | 403 | set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb); |
389 | } | 404 | } |
390 | |||
391 | clear_device(hw); | ||
392 | } | ||
393 | |||
394 | static void ilo_reset(struct ilo_hwinfo *hw) | ||
395 | { | ||
396 | spin_lock(&hw->alloc_lock); | ||
397 | |||
398 | /* reset might have been handled after lock was taken */ | ||
399 | if (is_device_reset(hw)) | ||
400 | ilo_locked_reset(hw); | ||
401 | |||
402 | spin_unlock(&hw->alloc_lock); | ||
403 | } | 405 | } |
404 | 406 | ||
405 | static ssize_t ilo_read(struct file *fp, char __user *buf, | 407 | static ssize_t ilo_read(struct file *fp, char __user *buf, |
@@ -411,12 +413,11 @@ static ssize_t ilo_read(struct file *fp, char __user *buf, | |||
411 | struct ilo_hwinfo *hw = data->ilo_hw; | 413 | struct ilo_hwinfo *hw = data->ilo_hw; |
412 | void *pkt; | 414 | void *pkt; |
413 | 415 | ||
414 | if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { | 416 | if (is_channel_reset(driver_ccb)) { |
415 | /* | 417 | /* |
416 | * If the device has been reset, applications | 418 | * If the device has been reset, applications |
417 | * need to close and reopen all ccbs. | 419 | * need to close and reopen all ccbs. |
418 | */ | 420 | */ |
419 | ilo_reset(hw); | ||
420 | return -ENODEV; | 421 | return -ENODEV; |
421 | } | 422 | } |
422 | 423 | ||
@@ -462,14 +463,8 @@ static ssize_t ilo_write(struct file *fp, const char __user *buf, | |||
462 | struct ilo_hwinfo *hw = data->ilo_hw; | 463 | struct ilo_hwinfo *hw = data->ilo_hw; |
463 | void *pkt; | 464 | void *pkt; |
464 | 465 | ||
465 | if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { | 466 | if (is_channel_reset(driver_ccb)) |
466 | /* | ||
467 | * If the device has been reset, applications | ||
468 | * need to close and reopen all ccbs. | ||
469 | */ | ||
470 | ilo_reset(hw); | ||
471 | return -ENODEV; | 467 | return -ENODEV; |
472 | } | ||
473 | 468 | ||
474 | /* get a packet to send the user command */ | 469 | /* get a packet to send the user command */ |
475 | if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt)) | 470 | if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt)) |
@@ -496,27 +491,28 @@ static int ilo_close(struct inode *ip, struct file *fp) | |||
496 | int slot; | 491 | int slot; |
497 | struct ccb_data *data; | 492 | struct ccb_data *data; |
498 | struct ilo_hwinfo *hw; | 493 | struct ilo_hwinfo *hw; |
494 | unsigned long flags; | ||
499 | 495 | ||
500 | slot = iminor(ip) % MAX_CCB; | 496 | slot = iminor(ip) % MAX_CCB; |
501 | hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); | 497 | hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); |
502 | 498 | ||
503 | spin_lock(&hw->alloc_lock); | 499 | spin_lock(&hw->open_lock); |
504 | |||
505 | if (is_device_reset(hw)) | ||
506 | ilo_locked_reset(hw); | ||
507 | 500 | ||
508 | if (hw->ccb_alloc[slot]->ccb_cnt == 1) { | 501 | if (hw->ccb_alloc[slot]->ccb_cnt == 1) { |
509 | 502 | ||
510 | data = fp->private_data; | 503 | data = fp->private_data; |
511 | 504 | ||
505 | spin_lock_irqsave(&hw->alloc_lock, flags); | ||
506 | hw->ccb_alloc[slot] = NULL; | ||
507 | spin_unlock_irqrestore(&hw->alloc_lock, flags); | ||
508 | |||
512 | ilo_ccb_close(hw->ilo_dev, data); | 509 | ilo_ccb_close(hw->ilo_dev, data); |
513 | 510 | ||
514 | kfree(data); | 511 | kfree(data); |
515 | hw->ccb_alloc[slot] = NULL; | ||
516 | } else | 512 | } else |
517 | hw->ccb_alloc[slot]->ccb_cnt--; | 513 | hw->ccb_alloc[slot]->ccb_cnt--; |
518 | 514 | ||
519 | spin_unlock(&hw->alloc_lock); | 515 | spin_unlock(&hw->open_lock); |
520 | 516 | ||
521 | return 0; | 517 | return 0; |
522 | } | 518 | } |
@@ -526,6 +522,7 @@ static int ilo_open(struct inode *ip, struct file *fp) | |||
526 | int slot, error; | 522 | int slot, error; |
527 | struct ccb_data *data; | 523 | struct ccb_data *data; |
528 | struct ilo_hwinfo *hw; | 524 | struct ilo_hwinfo *hw; |
525 | unsigned long flags; | ||
529 | 526 | ||
530 | slot = iminor(ip) % MAX_CCB; | 527 | slot = iminor(ip) % MAX_CCB; |
531 | hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); | 528 | hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); |
@@ -535,10 +532,7 @@ static int ilo_open(struct inode *ip, struct file *fp) | |||
535 | if (!data) | 532 | if (!data) |
536 | return -ENOMEM; | 533 | return -ENOMEM; |
537 | 534 | ||
538 | spin_lock(&hw->alloc_lock); | 535 | spin_lock(&hw->open_lock); |
539 | |||
540 | if (is_device_reset(hw)) | ||
541 | ilo_locked_reset(hw); | ||
542 | 536 | ||
543 | /* each fd private_data holds sw/hw view of ccb */ | 537 | /* each fd private_data holds sw/hw view of ccb */ |
544 | if (hw->ccb_alloc[slot] == NULL) { | 538 | if (hw->ccb_alloc[slot] == NULL) { |
@@ -549,22 +543,31 @@ static int ilo_open(struct inode *ip, struct file *fp) | |||
549 | goto out; | 543 | goto out; |
550 | } | 544 | } |
551 | 545 | ||
546 | data->ccb_cnt = 1; | ||
547 | data->ccb_excl = fp->f_flags & O_EXCL; | ||
548 | data->ilo_hw = hw; | ||
549 | init_waitqueue_head(&data->ccb_waitq); | ||
550 | |||
552 | /* write the ccb to hw */ | 551 | /* write the ccb to hw */ |
552 | spin_lock_irqsave(&hw->alloc_lock, flags); | ||
553 | ilo_ccb_open(hw, data, slot); | 553 | ilo_ccb_open(hw, data, slot); |
554 | hw->ccb_alloc[slot] = data; | ||
555 | spin_unlock_irqrestore(&hw->alloc_lock, flags); | ||
554 | 556 | ||
555 | /* make sure the channel is functional */ | 557 | /* make sure the channel is functional */ |
556 | error = ilo_ccb_verify(hw, data); | 558 | error = ilo_ccb_verify(hw, data); |
557 | if (error) { | 559 | if (error) { |
560 | |||
561 | spin_lock_irqsave(&hw->alloc_lock, flags); | ||
562 | hw->ccb_alloc[slot] = NULL; | ||
563 | spin_unlock_irqrestore(&hw->alloc_lock, flags); | ||
564 | |||
558 | ilo_ccb_close(hw->ilo_dev, data); | 565 | ilo_ccb_close(hw->ilo_dev, data); |
566 | |||
559 | kfree(data); | 567 | kfree(data); |
560 | goto out; | 568 | goto out; |
561 | } | 569 | } |
562 | 570 | ||
563 | data->ccb_cnt = 1; | ||
564 | data->ccb_excl = fp->f_flags & O_EXCL; | ||
565 | data->ilo_hw = hw; | ||
566 | hw->ccb_alloc[slot] = data; | ||
567 | |||
568 | } else { | 571 | } else { |
569 | kfree(data); | 572 | kfree(data); |
570 | if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { | 573 | if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { |
@@ -580,7 +583,7 @@ static int ilo_open(struct inode *ip, struct file *fp) | |||
580 | } | 583 | } |
581 | } | 584 | } |
582 | out: | 585 | out: |
583 | spin_unlock(&hw->alloc_lock); | 586 | spin_unlock(&hw->open_lock); |
584 | 587 | ||
585 | if (!error) | 588 | if (!error) |
586 | fp->private_data = hw->ccb_alloc[slot]; | 589 | fp->private_data = hw->ccb_alloc[slot]; |
@@ -596,6 +599,41 @@ static const struct file_operations ilo_fops = { | |||
596 | .release = ilo_close, | 599 | .release = ilo_close, |
597 | }; | 600 | }; |
598 | 601 | ||
602 | static irqreturn_t ilo_isr(int irq, void *data) | ||
603 | { | ||
604 | struct ilo_hwinfo *hw = data; | ||
605 | int pending, i; | ||
606 | |||
607 | spin_lock(&hw->alloc_lock); | ||
608 | |||
609 | /* check for ccbs which have data */ | ||
610 | pending = get_device_outbound(hw); | ||
611 | if (!pending) { | ||
612 | spin_unlock(&hw->alloc_lock); | ||
613 | return IRQ_NONE; | ||
614 | } | ||
615 | |||
616 | if (is_db_reset(pending)) { | ||
617 | /* wake up all ccbs if the device was reset */ | ||
618 | pending = -1; | ||
619 | ilo_set_reset(hw); | ||
620 | } | ||
621 | |||
622 | for (i = 0; i < MAX_CCB; i++) { | ||
623 | if (!hw->ccb_alloc[i]) | ||
624 | continue; | ||
625 | if (pending & (1 << i)) | ||
626 | wake_up_interruptible(&hw->ccb_alloc[i]->ccb_waitq); | ||
627 | } | ||
628 | |||
629 | /* clear the device of the channels that have been handled */ | ||
630 | clear_pending_db(hw, pending); | ||
631 | |||
632 | spin_unlock(&hw->alloc_lock); | ||
633 | |||
634 | return IRQ_HANDLED; | ||
635 | } | ||
636 | |||
599 | static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) | 637 | static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) |
600 | { | 638 | { |
601 | pci_iounmap(pdev, hw->db_vaddr); | 639 | pci_iounmap(pdev, hw->db_vaddr); |
@@ -649,6 +687,8 @@ static void ilo_remove(struct pci_dev *pdev) | |||
649 | device_destroy(ilo_class, MKDEV(ilo_major, i)); | 687 | device_destroy(ilo_class, MKDEV(ilo_major, i)); |
650 | 688 | ||
651 | cdev_del(&ilo_hw->cdev); | 689 | cdev_del(&ilo_hw->cdev); |
690 | ilo_disable_interrupts(ilo_hw); | ||
691 | free_irq(pdev->irq, ilo_hw); | ||
652 | ilo_unmap_device(pdev, ilo_hw); | 692 | ilo_unmap_device(pdev, ilo_hw); |
653 | pci_release_regions(pdev); | 693 | pci_release_regions(pdev); |
654 | pci_disable_device(pdev); | 694 | pci_disable_device(pdev); |
@@ -684,6 +724,7 @@ static int __devinit ilo_probe(struct pci_dev *pdev, | |||
684 | ilo_hw->ilo_dev = pdev; | 724 | ilo_hw->ilo_dev = pdev; |
685 | spin_lock_init(&ilo_hw->alloc_lock); | 725 | spin_lock_init(&ilo_hw->alloc_lock); |
686 | spin_lock_init(&ilo_hw->fifo_lock); | 726 | spin_lock_init(&ilo_hw->fifo_lock); |
727 | spin_lock_init(&ilo_hw->open_lock); | ||
687 | 728 | ||
688 | error = pci_enable_device(pdev); | 729 | error = pci_enable_device(pdev); |
689 | if (error) | 730 | if (error) |
@@ -702,13 +743,19 @@ static int __devinit ilo_probe(struct pci_dev *pdev, | |||
702 | pci_set_drvdata(pdev, ilo_hw); | 743 | pci_set_drvdata(pdev, ilo_hw); |
703 | clear_device(ilo_hw); | 744 | clear_device(ilo_hw); |
704 | 745 | ||
746 | error = request_irq(pdev->irq, ilo_isr, IRQF_SHARED, "hpilo", ilo_hw); | ||
747 | if (error) | ||
748 | goto unmap; | ||
749 | |||
750 | ilo_enable_interrupts(ilo_hw); | ||
751 | |||
705 | cdev_init(&ilo_hw->cdev, &ilo_fops); | 752 | cdev_init(&ilo_hw->cdev, &ilo_fops); |
706 | ilo_hw->cdev.owner = THIS_MODULE; | 753 | ilo_hw->cdev.owner = THIS_MODULE; |
707 | start = devnum * MAX_CCB; | 754 | start = devnum * MAX_CCB; |
708 | error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB); | 755 | error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB); |
709 | if (error) { | 756 | if (error) { |
710 | dev_err(&pdev->dev, "Could not add cdev\n"); | 757 | dev_err(&pdev->dev, "Could not add cdev\n"); |
711 | goto unmap; | 758 | goto remove_isr; |
712 | } | 759 | } |
713 | 760 | ||
714 | for (minor = 0 ; minor < MAX_CCB; minor++) { | 761 | for (minor = 0 ; minor < MAX_CCB; minor++) { |
@@ -721,6 +768,9 @@ static int __devinit ilo_probe(struct pci_dev *pdev, | |||
721 | } | 768 | } |
722 | 769 | ||
723 | return 0; | 770 | return 0; |
771 | remove_isr: | ||
772 | ilo_disable_interrupts(ilo_hw); | ||
773 | free_irq(pdev->irq, ilo_hw); | ||
724 | unmap: | 774 | unmap: |
725 | ilo_unmap_device(pdev, ilo_hw); | 775 | ilo_unmap_device(pdev, ilo_hw); |
726 | free_regions: | 776 | free_regions: |
diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h index 03a14c82aad9..38576050776a 100644 --- a/drivers/misc/hpilo.h +++ b/drivers/misc/hpilo.h | |||
@@ -46,11 +46,14 @@ struct ilo_hwinfo { | |||
46 | 46 | ||
47 | spinlock_t alloc_lock; | 47 | spinlock_t alloc_lock; |
48 | spinlock_t fifo_lock; | 48 | spinlock_t fifo_lock; |
49 | spinlock_t open_lock; | ||
49 | 50 | ||
50 | struct cdev cdev; | 51 | struct cdev cdev; |
51 | }; | 52 | }; |
52 | 53 | ||
53 | /* offset from mmio_vaddr */ | 54 | /* offset from mmio_vaddr for enabling doorbell interrupts */ |
55 | #define DB_IRQ 0xB2 | ||
56 | /* offset from mmio_vaddr for outbound communications */ | ||
54 | #define DB_OUT 0xD4 | 57 | #define DB_OUT 0xD4 |
55 | /* DB_OUT reset bit */ | 58 | /* DB_OUT reset bit */ |
56 | #define DB_RESET 26 | 59 | #define DB_RESET 26 |
@@ -131,6 +134,9 @@ struct ccb_data { | |||
131 | /* pointer to hardware device info */ | 134 | /* pointer to hardware device info */ |
132 | struct ilo_hwinfo *ilo_hw; | 135 | struct ilo_hwinfo *ilo_hw; |
133 | 136 | ||
137 | /* queue for this ccb to wait for recv data */ | ||
138 | wait_queue_head_t ccb_waitq; | ||
139 | |||
134 | /* usage count, to allow for shared ccb's */ | 140 | /* usage count, to allow for shared ccb's */ |
135 | int ccb_cnt; | 141 | int ccb_cnt; |
136 | 142 | ||