diff options
author | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-04-08 14:33:16 -0400 |
---|---|---|
committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-05-10 04:23:11 -0400 |
commit | 6f0f38c45a8f2f511c25893e33011ff32fc811db (patch) | |
tree | d44382f7b5c7db15e39ce7d5dc2b2feb7bd108a8 /drivers/pcmcia | |
parent | 0cb3c49cdd275aa9ef4b1afd090117b1b86a16d4 (diff) |
pcmcia: setup IRQ to be used by PCMCIA drivers at card insert
Setup the IRQ to be used by PCMCIA drivers already during the device
registration stage, making use of a new function pcmcia_setup_irq().
This will allow us to get rid of quite a lot of indirection in the
future.
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r-- | drivers/pcmcia/cs_internal.h | 2 | ||||
-rw-r--r-- | drivers/pcmcia/ds.c | 15 | ||||
-rw-r--r-- | drivers/pcmcia/pcmcia_resource.c | 214 |
3 files changed, 137 insertions, 94 deletions
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index f95864c2191e..74d91c8849ba 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h | |||
@@ -149,6 +149,8 @@ extern struct resource *pcmcia_find_mem_region(u_long base, | |||
149 | int low, | 149 | int low, |
150 | struct pcmcia_socket *s); | 150 | struct pcmcia_socket *s); |
151 | 151 | ||
152 | void pcmcia_cleanup_irq(struct pcmcia_socket *s); | ||
153 | int pcmcia_setup_irq(struct pcmcia_device *p_dev); | ||
152 | 154 | ||
153 | /* cistpl.c */ | 155 | /* cistpl.c */ |
154 | extern struct bin_attribute pccard_cis_attr; | 156 | extern struct bin_attribute pccard_cis_attr; |
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 041eee43fd8d..5fd2948c7ed9 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c | |||
@@ -546,26 +546,32 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu | |||
546 | p_dev->function_config = tmp_dev->function_config; | 546 | p_dev->function_config = tmp_dev->function_config; |
547 | p_dev->io = tmp_dev->io; | 547 | p_dev->io = tmp_dev->io; |
548 | p_dev->irq = tmp_dev->irq; | 548 | p_dev->irq = tmp_dev->irq; |
549 | p_dev->irq_v = tmp_dev->irq_v; | ||
549 | kref_get(&p_dev->function_config->ref); | 550 | kref_get(&p_dev->function_config->ref); |
550 | } | 551 | } |
551 | 552 | ||
552 | /* Add to the list in pcmcia_bus_socket */ | 553 | /* Add to the list in pcmcia_bus_socket */ |
553 | list_add(&p_dev->socket_device_list, &s->devices_list); | 554 | list_add(&p_dev->socket_device_list, &s->devices_list); |
554 | 555 | ||
555 | mutex_unlock(&s->ops_mutex); | 556 | if (pcmcia_setup_irq(p_dev)) |
557 | dev_warn(&p_dev->dev, | ||
558 | "IRQ setup failed -- device might not work\n"); | ||
556 | 559 | ||
557 | if (!p_dev->function_config) { | 560 | if (!p_dev->function_config) { |
558 | dev_dbg(&p_dev->dev, "creating config_t\n"); | 561 | dev_dbg(&p_dev->dev, "creating config_t\n"); |
559 | p_dev->function_config = kzalloc(sizeof(struct config_t), | 562 | p_dev->function_config = kzalloc(sizeof(struct config_t), |
560 | GFP_KERNEL); | 563 | GFP_KERNEL); |
561 | if (!p_dev->function_config) | 564 | if (!p_dev->function_config) { |
565 | mutex_unlock(&s->ops_mutex); | ||
562 | goto err_unreg; | 566 | goto err_unreg; |
567 | } | ||
563 | kref_init(&p_dev->function_config->ref); | 568 | kref_init(&p_dev->function_config->ref); |
564 | } | 569 | } |
570 | mutex_unlock(&s->ops_mutex); | ||
565 | 571 | ||
566 | dev_printk(KERN_NOTICE, &p_dev->dev, | 572 | dev_printk(KERN_NOTICE, &p_dev->dev, |
567 | "pcmcia: registering new device %s\n", | 573 | "pcmcia: registering new device %s (IRQ: %d)\n", |
568 | p_dev->devname); | 574 | p_dev->devname, p_dev->irq_v); |
569 | 575 | ||
570 | pcmcia_device_query(p_dev); | 576 | pcmcia_device_query(p_dev); |
571 | 577 | ||
@@ -1258,6 +1264,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) | |||
1258 | handle_event(skt, event); | 1264 | handle_event(skt, event); |
1259 | mutex_lock(&s->ops_mutex); | 1265 | mutex_lock(&s->ops_mutex); |
1260 | destroy_cis_cache(s); | 1266 | destroy_cis_cache(s); |
1267 | pcmcia_cleanup_irq(s); | ||
1261 | mutex_unlock(&s->ops_mutex); | 1268 | mutex_unlock(&s->ops_mutex); |
1262 | break; | 1269 | break; |
1263 | 1270 | ||
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 |