diff options
Diffstat (limited to 'drivers/pcmcia/pcmcia_resource.c')
-rw-r--r-- | drivers/pcmcia/pcmcia_resource.c | 214 |
1 files changed, 124 insertions, 90 deletions
diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index ba82cb3b1944..ff9c0bcb7e3a 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include <linux/netdevice.h> | 23 | #include <linux/netdevice.h> |
24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
25 | 25 | ||
26 | #include <asm/irq.h> | ||
27 | |||
26 | #include <pcmcia/cs_types.h> | 28 | #include <pcmcia/cs_types.h> |
27 | #include <pcmcia/ss.h> | 29 | #include <pcmcia/ss.h> |
28 | #include <pcmcia/cs.h> | 30 | #include <pcmcia/cs.h> |
@@ -38,12 +40,6 @@ static int io_speed; | |||
38 | module_param(io_speed, int, 0444); | 40 | module_param(io_speed, int, 0444); |
39 | 41 | ||
40 | 42 | ||
41 | #ifdef CONFIG_PCMCIA_PROBE | ||
42 | #include <asm/irq.h> | ||
43 | /* mask of IRQs already reserved by other cards, we should avoid using them */ | ||
44 | static u8 pcmcia_used_irq[NR_IRQS]; | ||
45 | #endif | ||
46 | |||
47 | static int pcmcia_adjust_io_region(struct resource *res, unsigned long start, | 43 | static int pcmcia_adjust_io_region(struct resource *res, unsigned long start, |
48 | unsigned long end, struct pcmcia_socket *s) | 44 | unsigned long end, struct pcmcia_socket *s) |
49 | { | 45 | { |
@@ -440,15 +436,11 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req) | |||
440 | } | 436 | } |
441 | if (--s->irq.Config == 0) { | 437 | if (--s->irq.Config == 0) { |
442 | c->state &= ~CONFIG_IRQ_REQ; | 438 | c->state &= ~CONFIG_IRQ_REQ; |
443 | s->irq.AssignedIRQ = 0; | ||
444 | } | 439 | } |
445 | 440 | ||
446 | if (req->Handler) | 441 | if (req->Handler) |
447 | free_irq(req->AssignedIRQ, p_dev->priv); | 442 | free_irq(req->AssignedIRQ, p_dev->priv); |
448 | 443 | ||
449 | #ifdef CONFIG_PCMCIA_PROBE | ||
450 | pcmcia_used_irq[req->AssignedIRQ]--; | ||
451 | #endif | ||
452 | ret = 0; | 444 | ret = 0; |
453 | 445 | ||
454 | out: | 446 | out: |
@@ -699,26 +691,14 @@ EXPORT_SYMBOL(pcmcia_request_io); | |||
699 | /** pcmcia_request_irq | 691 | /** pcmcia_request_irq |
700 | * | 692 | * |
701 | * Request_irq() reserves an irq for this client. | 693 | * Request_irq() reserves an irq for this client. |
702 | * | ||
703 | * Also, since Linux only reserves irq's when they are actually | ||
704 | * hooked, we don't guarantee that an irq will still be available | ||
705 | * when the configuration is locked. Now that I think about it, | ||
706 | * there might be a way to fix this using a dummy handler. | ||
707 | */ | 694 | */ |
708 | 695 | ||
709 | #ifdef CONFIG_PCMCIA_PROBE | ||
710 | static irqreturn_t test_action(int cpl, void *dev_id) | ||
711 | { | ||
712 | return IRQ_NONE; | ||
713 | } | ||
714 | #endif | ||
715 | |||
716 | int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) | 696 | int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) |
717 | { | 697 | { |
718 | struct pcmcia_socket *s = p_dev->socket; | 698 | struct pcmcia_socket *s = p_dev->socket; |
719 | config_t *c; | 699 | config_t *c; |
720 | int ret = -EINVAL, irq = 0; | 700 | int ret = -EINVAL, irq = p_dev->irq_v; |
721 | int type; | 701 | int type = IRQF_SHARED; |
722 | 702 | ||
723 | mutex_lock(&s->ops_mutex); | 703 | mutex_lock(&s->ops_mutex); |
724 | 704 | ||
@@ -736,63 +716,20 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) | |||
736 | goto out; | 716 | goto out; |
737 | } | 717 | } |
738 | 718 | ||
739 | /* Decide what type of interrupt we are registering */ | ||
740 | type = 0; | ||
741 | if (s->functions > 1) /* All of this ought to be handled higher up */ | ||
742 | type = IRQF_SHARED; | ||
743 | else if (req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) | ||
744 | type = IRQF_SHARED; | ||
745 | else | ||
746 | printk(KERN_WARNING "pcmcia: Driver needs updating to support IRQ sharing.\n"); | ||
747 | |||
748 | /* If the interrupt is already assigned, it must be the same */ | ||
749 | if (s->irq.AssignedIRQ != 0) | ||
750 | irq = s->irq.AssignedIRQ; | ||
751 | |||
752 | #ifdef CONFIG_PCMCIA_PROBE | ||
753 | if (!irq) { | 719 | if (!irq) { |
754 | int try; | 720 | dev_dbg(&s->dev, "no IRQ available\n"); |
755 | u32 mask = s->irq_mask; | 721 | goto out; |
756 | void *data = p_dev; /* something unique to this device */ | ||
757 | |||
758 | for (try = 0; try < 64; try++) { | ||
759 | irq = try % 32; | ||
760 | |||
761 | /* marked as available by driver, and not blocked by userspace? */ | ||
762 | if (!((mask >> irq) & 1)) | ||
763 | continue; | ||
764 | |||
765 | /* avoid an IRQ which is already used by a PCMCIA card */ | ||
766 | if ((try < 32) && pcmcia_used_irq[irq]) | ||
767 | continue; | ||
768 | |||
769 | /* register the correct driver, if possible, of check whether | ||
770 | * registering a dummy handle works, i.e. if the IRQ isn't | ||
771 | * marked as used by the kernel resource management core */ | ||
772 | ret = request_irq(irq, | ||
773 | (req->Handler) ? req->Handler : test_action, | ||
774 | type, | ||
775 | p_dev->devname, | ||
776 | (req->Handler) ? p_dev->priv : data); | ||
777 | if (!ret) { | ||
778 | if (!req->Handler) | ||
779 | free_irq(irq, data); | ||
780 | break; | ||
781 | } | ||
782 | } | ||
783 | } | 722 | } |
784 | #endif | 723 | |
785 | /* only assign PCI irq if no IRQ already assigned */ | 724 | if (!(req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)) { |
786 | if (ret && !s->irq.AssignedIRQ) { | 725 | req->Attributes |= IRQ_TYPE_DYNAMIC_SHARING; |
787 | if (!s->pci_irq) { | 726 | dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: " |
788 | dev_printk(KERN_INFO, &s->dev, "no IRQ found\n"); | 727 | "request for exclusive IRQ could not be fulfilled.\n"); |
789 | goto out; | 728 | dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver " |
790 | } | 729 | "needs updating to supported shared IRQ lines.\n"); |
791 | type = IRQF_SHARED; | ||
792 | irq = s->pci_irq; | ||
793 | } | 730 | } |
794 | 731 | ||
795 | if (ret && req->Handler) { | 732 | if (req->Handler) { |
796 | ret = request_irq(irq, req->Handler, type, | 733 | ret = request_irq(irq, req->Handler, type, |
797 | p_dev->devname, p_dev->priv); | 734 | p_dev->devname, p_dev->priv); |
798 | if (ret) { | 735 | if (ret) { |
@@ -802,25 +739,13 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) | |||
802 | } | 739 | } |
803 | } | 740 | } |
804 | 741 | ||
805 | /* Make sure the fact the request type was overridden is passed back */ | ||
806 | if (type == IRQF_SHARED && !(req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)) { | ||
807 | req->Attributes |= IRQ_TYPE_DYNAMIC_SHARING; | ||
808 | dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: " | ||
809 | "request for exclusive IRQ could not be fulfilled.\n"); | ||
810 | dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver " | ||
811 | "needs updating to supported shared IRQ lines.\n"); | ||
812 | } | ||
813 | c->irq.Attributes = req->Attributes; | 742 | c->irq.Attributes = req->Attributes; |
814 | s->irq.AssignedIRQ = req->AssignedIRQ = irq; | 743 | req->AssignedIRQ = irq; |
815 | s->irq.Config++; | 744 | s->irq.Config++; |
816 | 745 | ||
817 | c->state |= CONFIG_IRQ_REQ; | 746 | c->state |= CONFIG_IRQ_REQ; |
818 | p_dev->_irq = 1; | 747 | p_dev->_irq = 1; |
819 | 748 | ||
820 | #ifdef CONFIG_PCMCIA_PROBE | ||
821 | pcmcia_used_irq[irq]++; | ||
822 | #endif | ||
823 | |||
824 | ret = 0; | 749 | ret = 0; |
825 | out: | 750 | out: |
826 | mutex_unlock(&s->ops_mutex); | 751 | mutex_unlock(&s->ops_mutex); |
@@ -829,6 +754,115 @@ out: | |||
829 | EXPORT_SYMBOL(pcmcia_request_irq); | 754 | EXPORT_SYMBOL(pcmcia_request_irq); |
830 | 755 | ||
831 | 756 | ||
757 | #ifdef CONFIG_PCMCIA_PROBE | ||
758 | |||
759 | /* mask of IRQs already reserved by other cards, we should avoid using them */ | ||
760 | static u8 pcmcia_used_irq[NR_IRQS]; | ||
761 | |||
762 | static irqreturn_t test_action(int cpl, void *dev_id) | ||
763 | { | ||
764 | return IRQ_NONE; | ||
765 | } | ||
766 | |||
767 | /** | ||
768 | * pcmcia_setup_isa_irq() - determine whether an ISA IRQ can be used | ||
769 | * @p_dev - the associated PCMCIA device | ||
770 | * | ||
771 | * locking note: must be called with ops_mutex locked. | ||
772 | */ | ||
773 | static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type) | ||
774 | { | ||
775 | struct pcmcia_socket *s = p_dev->socket; | ||
776 | unsigned int try, irq; | ||
777 | u32 mask = s->irq_mask; | ||
778 | int ret = -ENODEV; | ||
779 | |||
780 | for (try = 0; try < 64; try++) { | ||
781 | irq = try % 32; | ||
782 | |||
783 | /* marked as available by driver, not blocked by userspace? */ | ||
784 | if (!((mask >> irq) & 1)) | ||
785 | continue; | ||
786 | |||
787 | /* avoid an IRQ which is already used by another PCMCIA card */ | ||
788 | if ((try < 32) && pcmcia_used_irq[irq]) | ||
789 | continue; | ||
790 | |||
791 | /* register the correct driver, if possible, to check whether | ||
792 | * registering a dummy handle works, i.e. if the IRQ isn't | ||
793 | * marked as used by the kernel resource management core */ | ||
794 | ret = request_irq(irq, test_action, type, p_dev->devname, | ||
795 | p_dev); | ||
796 | if (!ret) { | ||
797 | free_irq(irq, p_dev); | ||
798 | p_dev->irq_v = s->irq.AssignedIRQ = irq; | ||
799 | pcmcia_used_irq[irq]++; | ||
800 | break; | ||
801 | } | ||
802 | } | ||
803 | |||
804 | return ret; | ||
805 | } | ||
806 | |||
807 | void pcmcia_cleanup_irq(struct pcmcia_socket *s) | ||
808 | { | ||
809 | pcmcia_used_irq[s->irq.AssignedIRQ]--; | ||
810 | s->irq.AssignedIRQ = 0; | ||
811 | } | ||
812 | |||
813 | #else /* CONFIG_PCMCIA_PROBE */ | ||
814 | |||
815 | static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type) | ||
816 | { | ||
817 | return -EINVAL; | ||
818 | } | ||
819 | |||
820 | void pcmcia_cleanup_irq(struct pcmcia_socket *s) | ||
821 | { | ||
822 | s->irq.AssignedIRQ = 0; | ||
823 | return; | ||
824 | } | ||
825 | |||
826 | #endif /* CONFIG_PCMCIA_PROBE */ | ||
827 | |||
828 | |||
829 | /** | ||
830 | * pcmcia_setup_irq() - determine IRQ to be used for device | ||
831 | * @p_dev - the associated PCMCIA device | ||
832 | * | ||
833 | * locking note: must be called with ops_mutex locked. | ||
834 | */ | ||
835 | int pcmcia_setup_irq(struct pcmcia_device *p_dev) | ||
836 | { | ||
837 | struct pcmcia_socket *s = p_dev->socket; | ||
838 | |||
839 | if (p_dev->irq_v) | ||
840 | return 0; | ||
841 | |||
842 | /* already assigned? */ | ||
843 | if (s->irq.AssignedIRQ) { | ||
844 | p_dev->irq_v = s->irq.AssignedIRQ; | ||
845 | return 0; | ||
846 | } | ||
847 | |||
848 | /* prefer an exclusive ISA irq */ | ||
849 | if (!pcmcia_setup_isa_irq(p_dev, 0)) | ||
850 | return 0; | ||
851 | |||
852 | /* but accept a shared ISA irq */ | ||
853 | if (!pcmcia_setup_isa_irq(p_dev, IRQF_SHARED)) | ||
854 | return 0; | ||
855 | |||
856 | /* but use the PCI irq otherwise */ | ||
857 | if (s->pci_irq) { | ||
858 | p_dev->irq_v = s->irq.AssignedIRQ = s->pci_irq; | ||
859 | return 0; | ||
860 | } | ||
861 | |||
862 | return -EINVAL; | ||
863 | } | ||
864 | |||
865 | |||
832 | /** pcmcia_request_window | 866 | /** pcmcia_request_window |
833 | * | 867 | * |
834 | * Request_window() establishes a mapping between card memory space | 868 | * Request_window() establishes a mapping between card memory space |