diff options
Diffstat (limited to 'kernel/irq')
-rw-r--r-- | kernel/irq/Kconfig | 5 | ||||
-rw-r--r-- | kernel/irq/chip.c | 3 | ||||
-rw-r--r-- | kernel/irq/irqdomain.c | 415 |
3 files changed, 407 insertions, 16 deletions
diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 225086b2652e..4f2eb2b1f23b 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig | |||
@@ -55,6 +55,11 @@ config GENERIC_IRQ_CHIP | |||
55 | config IRQ_DOMAIN | 55 | config IRQ_DOMAIN |
56 | bool | 56 | bool |
57 | 57 | ||
58 | # Support for hierarchical irq domains | ||
59 | config IRQ_DOMAIN_HIERARCHY | ||
60 | bool | ||
61 | select IRQ_DOMAIN | ||
62 | |||
58 | config HANDLE_DOMAIN_IRQ | 63 | config HANDLE_DOMAIN_IRQ |
59 | bool | 64 | bool |
60 | 65 | ||
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index e5202f00cabc..72a93086216b 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/interrupt.h> | 16 | #include <linux/interrupt.h> |
17 | #include <linux/kernel_stat.h> | 17 | #include <linux/kernel_stat.h> |
18 | #include <linux/irqdomain.h> | ||
18 | 19 | ||
19 | #include <trace/events/irq.h> | 20 | #include <trace/events/irq.h> |
20 | 21 | ||
@@ -178,6 +179,7 @@ int irq_startup(struct irq_desc *desc, bool resend) | |||
178 | irq_state_clr_disabled(desc); | 179 | irq_state_clr_disabled(desc); |
179 | desc->depth = 0; | 180 | desc->depth = 0; |
180 | 181 | ||
182 | irq_domain_activate_irq(&desc->irq_data); | ||
181 | if (desc->irq_data.chip->irq_startup) { | 183 | if (desc->irq_data.chip->irq_startup) { |
182 | ret = desc->irq_data.chip->irq_startup(&desc->irq_data); | 184 | ret = desc->irq_data.chip->irq_startup(&desc->irq_data); |
183 | irq_state_clr_masked(desc); | 185 | irq_state_clr_masked(desc); |
@@ -199,6 +201,7 @@ void irq_shutdown(struct irq_desc *desc) | |||
199 | desc->irq_data.chip->irq_disable(&desc->irq_data); | 201 | desc->irq_data.chip->irq_disable(&desc->irq_data); |
200 | else | 202 | else |
201 | desc->irq_data.chip->irq_mask(&desc->irq_data); | 203 | desc->irq_data.chip->irq_mask(&desc->irq_data); |
204 | irq_domain_deactivate_irq(&desc->irq_data); | ||
202 | irq_state_set_masked(desc); | 205 | irq_state_set_masked(desc); |
203 | } | 206 | } |
204 | 207 | ||
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 6534ff6ce02e..43f3be6fac70 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c | |||
@@ -23,6 +23,10 @@ static DEFINE_MUTEX(irq_domain_mutex); | |||
23 | static DEFINE_MUTEX(revmap_trees_mutex); | 23 | static DEFINE_MUTEX(revmap_trees_mutex); |
24 | static struct irq_domain *irq_default_domain; | 24 | static struct irq_domain *irq_default_domain; |
25 | 25 | ||
26 | static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs, | ||
27 | irq_hw_number_t hwirq, int node); | ||
28 | static void irq_domain_check_hierarchy(struct irq_domain *domain); | ||
29 | |||
26 | /** | 30 | /** |
27 | * __irq_domain_add() - Allocate a new irq_domain data structure | 31 | * __irq_domain_add() - Allocate a new irq_domain data structure |
28 | * @of_node: optional device-tree node of the interrupt controller | 32 | * @of_node: optional device-tree node of the interrupt controller |
@@ -30,7 +34,7 @@ static struct irq_domain *irq_default_domain; | |||
30 | * @hwirq_max: Maximum number of interrupts supported by controller | 34 | * @hwirq_max: Maximum number of interrupts supported by controller |
31 | * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no | 35 | * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no |
32 | * direct mapping | 36 | * direct mapping |
33 | * @ops: map/unmap domain callbacks | 37 | * @ops: domain callbacks |
34 | * @host_data: Controller private data pointer | 38 | * @host_data: Controller private data pointer |
35 | * | 39 | * |
36 | * Allocates and initialize and irq_domain structure. | 40 | * Allocates and initialize and irq_domain structure. |
@@ -56,6 +60,7 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node, int size, | |||
56 | domain->hwirq_max = hwirq_max; | 60 | domain->hwirq_max = hwirq_max; |
57 | domain->revmap_size = size; | 61 | domain->revmap_size = size; |
58 | domain->revmap_direct_max_irq = direct_max; | 62 | domain->revmap_direct_max_irq = direct_max; |
63 | irq_domain_check_hierarchy(domain); | ||
59 | 64 | ||
60 | mutex_lock(&irq_domain_mutex); | 65 | mutex_lock(&irq_domain_mutex); |
61 | list_add(&domain->link, &irq_domain_list); | 66 | list_add(&domain->link, &irq_domain_list); |
@@ -109,7 +114,7 @@ EXPORT_SYMBOL_GPL(irq_domain_remove); | |||
109 | * @first_irq: first number of irq block assigned to the domain, | 114 | * @first_irq: first number of irq block assigned to the domain, |
110 | * pass zero to assign irqs on-the-fly. If first_irq is non-zero, then | 115 | * pass zero to assign irqs on-the-fly. If first_irq is non-zero, then |
111 | * pre-map all of the irqs in the domain to virqs starting at first_irq. | 116 | * pre-map all of the irqs in the domain to virqs starting at first_irq. |
112 | * @ops: map/unmap domain callbacks | 117 | * @ops: domain callbacks |
113 | * @host_data: Controller private data pointer | 118 | * @host_data: Controller private data pointer |
114 | * | 119 | * |
115 | * Allocates an irq_domain, and optionally if first_irq is positive then also | 120 | * Allocates an irq_domain, and optionally if first_irq is positive then also |
@@ -174,10 +179,8 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, | |||
174 | 179 | ||
175 | domain = __irq_domain_add(of_node, first_hwirq + size, | 180 | domain = __irq_domain_add(of_node, first_hwirq + size, |
176 | first_hwirq + size, 0, ops, host_data); | 181 | first_hwirq + size, 0, ops, host_data); |
177 | if (!domain) | 182 | if (domain) |
178 | return NULL; | 183 | irq_domain_associate_many(domain, first_irq, first_hwirq, size); |
179 | |||
180 | irq_domain_associate_many(domain, first_irq, first_hwirq, size); | ||
181 | 184 | ||
182 | return domain; | 185 | return domain; |
183 | } | 186 | } |
@@ -388,7 +391,6 @@ EXPORT_SYMBOL_GPL(irq_create_direct_mapping); | |||
388 | unsigned int irq_create_mapping(struct irq_domain *domain, | 391 | unsigned int irq_create_mapping(struct irq_domain *domain, |
389 | irq_hw_number_t hwirq) | 392 | irq_hw_number_t hwirq) |
390 | { | 393 | { |
391 | unsigned int hint; | ||
392 | int virq; | 394 | int virq; |
393 | 395 | ||
394 | pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq); | 396 | pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq); |
@@ -410,12 +412,8 @@ unsigned int irq_create_mapping(struct irq_domain *domain, | |||
410 | } | 412 | } |
411 | 413 | ||
412 | /* Allocate a virtual interrupt number */ | 414 | /* Allocate a virtual interrupt number */ |
413 | hint = hwirq % nr_irqs; | 415 | virq = irq_domain_alloc_descs(-1, 1, hwirq, |
414 | if (hint == 0) | 416 | of_node_to_nid(domain->of_node)); |
415 | hint++; | ||
416 | virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node)); | ||
417 | if (virq <= 0) | ||
418 | virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node)); | ||
419 | if (virq <= 0) { | 417 | if (virq <= 0) { |
420 | pr_debug("-> virq allocation failed\n"); | 418 | pr_debug("-> virq allocation failed\n"); |
421 | return 0; | 419 | return 0; |
@@ -471,7 +469,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) | |||
471 | struct irq_domain *domain; | 469 | struct irq_domain *domain; |
472 | irq_hw_number_t hwirq; | 470 | irq_hw_number_t hwirq; |
473 | unsigned int type = IRQ_TYPE_NONE; | 471 | unsigned int type = IRQ_TYPE_NONE; |
474 | unsigned int virq; | 472 | int virq; |
475 | 473 | ||
476 | domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain; | 474 | domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain; |
477 | if (!domain) { | 475 | if (!domain) { |
@@ -480,6 +478,11 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) | |||
480 | return 0; | 478 | return 0; |
481 | } | 479 | } |
482 | 480 | ||
481 | if (irq_domain_is_hierarchy(domain)) { | ||
482 | virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data); | ||
483 | return virq <= 0 ? 0 : virq; | ||
484 | } | ||
485 | |||
483 | /* If domain has no translation, then we assume interrupt line */ | 486 | /* If domain has no translation, then we assume interrupt line */ |
484 | if (domain->ops->xlate == NULL) | 487 | if (domain->ops->xlate == NULL) |
485 | hwirq = irq_data->args[0]; | 488 | hwirq = irq_data->args[0]; |
@@ -540,8 +543,8 @@ unsigned int irq_find_mapping(struct irq_domain *domain, | |||
540 | return 0; | 543 | return 0; |
541 | 544 | ||
542 | if (hwirq < domain->revmap_direct_max_irq) { | 545 | if (hwirq < domain->revmap_direct_max_irq) { |
543 | data = irq_get_irq_data(hwirq); | 546 | data = irq_domain_get_irq_data(domain, hwirq); |
544 | if (data && (data->domain == domain) && (data->hwirq == hwirq)) | 547 | if (data && data->hwirq == hwirq) |
545 | return hwirq; | 548 | return hwirq; |
546 | } | 549 | } |
547 | 550 | ||
@@ -709,3 +712,383 @@ const struct irq_domain_ops irq_domain_simple_ops = { | |||
709 | .xlate = irq_domain_xlate_onetwocell, | 712 | .xlate = irq_domain_xlate_onetwocell, |
710 | }; | 713 | }; |
711 | EXPORT_SYMBOL_GPL(irq_domain_simple_ops); | 714 | EXPORT_SYMBOL_GPL(irq_domain_simple_ops); |
715 | |||
716 | static int irq_domain_alloc_descs(int virq, unsigned int cnt, | ||
717 | irq_hw_number_t hwirq, int node) | ||
718 | { | ||
719 | unsigned int hint; | ||
720 | |||
721 | if (virq >= 0) { | ||
722 | virq = irq_alloc_descs(virq, virq, cnt, node); | ||
723 | } else { | ||
724 | hint = hwirq % nr_irqs; | ||
725 | if (hint == 0) | ||
726 | hint++; | ||
727 | virq = irq_alloc_descs_from(hint, cnt, node); | ||
728 | if (virq <= 0 && hint > 1) | ||
729 | virq = irq_alloc_descs_from(1, cnt, node); | ||
730 | } | ||
731 | |||
732 | return virq; | ||
733 | } | ||
734 | |||
735 | #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY | ||
736 | static void irq_domain_insert_irq(int virq) | ||
737 | { | ||
738 | struct irq_data *data; | ||
739 | |||
740 | for (data = irq_get_irq_data(virq); data; data = data->parent_data) { | ||
741 | struct irq_domain *domain = data->domain; | ||
742 | irq_hw_number_t hwirq = data->hwirq; | ||
743 | |||
744 | if (hwirq < domain->revmap_size) { | ||
745 | domain->linear_revmap[hwirq] = virq; | ||
746 | } else { | ||
747 | mutex_lock(&revmap_trees_mutex); | ||
748 | radix_tree_insert(&domain->revmap_tree, hwirq, data); | ||
749 | mutex_unlock(&revmap_trees_mutex); | ||
750 | } | ||
751 | |||
752 | /* If not already assigned, give the domain the chip's name */ | ||
753 | if (!domain->name && data->chip) | ||
754 | domain->name = data->chip->name; | ||
755 | } | ||
756 | |||
757 | irq_clear_status_flags(virq, IRQ_NOREQUEST); | ||
758 | } | ||
759 | |||
760 | static void irq_domain_remove_irq(int virq) | ||
761 | { | ||
762 | struct irq_data *data; | ||
763 | |||
764 | irq_set_status_flags(virq, IRQ_NOREQUEST); | ||
765 | irq_set_chip_and_handler(virq, NULL, NULL); | ||
766 | synchronize_irq(virq); | ||
767 | smp_mb(); | ||
768 | |||
769 | for (data = irq_get_irq_data(virq); data; data = data->parent_data) { | ||
770 | struct irq_domain *domain = data->domain; | ||
771 | irq_hw_number_t hwirq = data->hwirq; | ||
772 | |||
773 | if (hwirq < domain->revmap_size) { | ||
774 | domain->linear_revmap[hwirq] = 0; | ||
775 | } else { | ||
776 | mutex_lock(&revmap_trees_mutex); | ||
777 | radix_tree_delete(&domain->revmap_tree, hwirq); | ||
778 | mutex_unlock(&revmap_trees_mutex); | ||
779 | } | ||
780 | } | ||
781 | } | ||
782 | |||
783 | static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain, | ||
784 | struct irq_data *child) | ||
785 | { | ||
786 | struct irq_data *irq_data; | ||
787 | |||
788 | irq_data = kzalloc_node(sizeof(*irq_data), GFP_KERNEL, child->node); | ||
789 | if (irq_data) { | ||
790 | child->parent_data = irq_data; | ||
791 | irq_data->irq = child->irq; | ||
792 | irq_data->node = child->node; | ||
793 | irq_data->domain = domain; | ||
794 | } | ||
795 | |||
796 | return irq_data; | ||
797 | } | ||
798 | |||
799 | static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs) | ||
800 | { | ||
801 | struct irq_data *irq_data, *tmp; | ||
802 | int i; | ||
803 | |||
804 | for (i = 0; i < nr_irqs; i++) { | ||
805 | irq_data = irq_get_irq_data(virq + i); | ||
806 | tmp = irq_data->parent_data; | ||
807 | irq_data->parent_data = NULL; | ||
808 | irq_data->domain = NULL; | ||
809 | |||
810 | while (tmp) { | ||
811 | irq_data = tmp; | ||
812 | tmp = tmp->parent_data; | ||
813 | kfree(irq_data); | ||
814 | } | ||
815 | } | ||
816 | } | ||
817 | |||
818 | static int irq_domain_alloc_irq_data(struct irq_domain *domain, | ||
819 | unsigned int virq, unsigned int nr_irqs) | ||
820 | { | ||
821 | struct irq_data *irq_data; | ||
822 | struct irq_domain *parent; | ||
823 | int i; | ||
824 | |||
825 | /* The outermost irq_data is embedded in struct irq_desc */ | ||
826 | for (i = 0; i < nr_irqs; i++) { | ||
827 | irq_data = irq_get_irq_data(virq + i); | ||
828 | irq_data->domain = domain; | ||
829 | |||
830 | for (parent = domain->parent; parent; parent = parent->parent) { | ||
831 | irq_data = irq_domain_insert_irq_data(parent, irq_data); | ||
832 | if (!irq_data) { | ||
833 | irq_domain_free_irq_data(virq, i + 1); | ||
834 | return -ENOMEM; | ||
835 | } | ||
836 | } | ||
837 | } | ||
838 | |||
839 | return 0; | ||
840 | } | ||
841 | |||
842 | /** | ||
843 | * irq_domain_get_irq_data - Get irq_data associated with @virq and @domain | ||
844 | * @domain: domain to match | ||
845 | * @virq: IRQ number to get irq_data | ||
846 | */ | ||
847 | struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, | ||
848 | unsigned int virq) | ||
849 | { | ||
850 | struct irq_data *irq_data; | ||
851 | |||
852 | for (irq_data = irq_get_irq_data(virq); irq_data; | ||
853 | irq_data = irq_data->parent_data) | ||
854 | if (irq_data->domain == domain) | ||
855 | return irq_data; | ||
856 | |||
857 | return NULL; | ||
858 | } | ||
859 | |||
860 | /** | ||
861 | * irq_domain_set_hwirq_and_chip - Set hwirq and irqchip of @virq at @domain | ||
862 | * @domain: Interrupt domain to match | ||
863 | * @virq: IRQ number | ||
864 | * @hwirq: The hwirq number | ||
865 | * @chip: The associated interrupt chip | ||
866 | * @chip_data: The associated chip data | ||
867 | */ | ||
868 | int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, unsigned int virq, | ||
869 | irq_hw_number_t hwirq, struct irq_chip *chip, | ||
870 | void *chip_data) | ||
871 | { | ||
872 | struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq); | ||
873 | |||
874 | if (!irq_data) | ||
875 | return -ENOENT; | ||
876 | |||
877 | irq_data->hwirq = hwirq; | ||
878 | irq_data->chip = chip ? chip : &no_irq_chip; | ||
879 | irq_data->chip_data = chip_data; | ||
880 | |||
881 | return 0; | ||
882 | } | ||
883 | |||
884 | /** | ||
885 | * irq_domain_reset_irq_data - Clear hwirq, chip and chip_data in @irq_data | ||
886 | * @irq_data: The pointer to irq_data | ||
887 | */ | ||
888 | void irq_domain_reset_irq_data(struct irq_data *irq_data) | ||
889 | { | ||
890 | irq_data->hwirq = 0; | ||
891 | irq_data->chip = &no_irq_chip; | ||
892 | irq_data->chip_data = NULL; | ||
893 | } | ||
894 | |||
895 | /** | ||
896 | * irq_domain_free_irqs_common - Clear irq_data and free the parent | ||
897 | * @domain: Interrupt domain to match | ||
898 | * @virq: IRQ number to start with | ||
899 | * @nr_irqs: The number of irqs to free | ||
900 | */ | ||
901 | void irq_domain_free_irqs_common(struct irq_domain *domain, unsigned int virq, | ||
902 | unsigned int nr_irqs) | ||
903 | { | ||
904 | struct irq_data *irq_data; | ||
905 | int i; | ||
906 | |||
907 | for (i = 0; i < nr_irqs; i++) { | ||
908 | irq_data = irq_domain_get_irq_data(domain, virq + i); | ||
909 | if (irq_data) | ||
910 | irq_domain_reset_irq_data(irq_data); | ||
911 | } | ||
912 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); | ||
913 | } | ||
914 | |||
915 | /** | ||
916 | * irq_domain_free_irqs_top - Clear handler and handler data, clear irqdata and free parent | ||
917 | * @domain: Interrupt domain to match | ||
918 | * @virq: IRQ number to start with | ||
919 | * @nr_irqs: The number of irqs to free | ||
920 | */ | ||
921 | void irq_domain_free_irqs_top(struct irq_domain *domain, unsigned int virq, | ||
922 | unsigned int nr_irqs) | ||
923 | { | ||
924 | int i; | ||
925 | |||
926 | for (i = 0; i < nr_irqs; i++) { | ||
927 | irq_set_handler_data(virq + i, NULL); | ||
928 | irq_set_handler(virq + i, NULL); | ||
929 | } | ||
930 | irq_domain_free_irqs_common(domain, virq, nr_irqs); | ||
931 | } | ||
932 | |||
933 | /** | ||
934 | * __irq_domain_alloc_irqs - Allocate IRQs from domain | ||
935 | * @domain: domain to allocate from | ||
936 | * @irq_base: allocate specified IRQ nubmer if irq_base >= 0 | ||
937 | * @nr_irqs: number of IRQs to allocate | ||
938 | * @node: NUMA node id for memory allocation | ||
939 | * @arg: domain specific argument | ||
940 | * @realloc: IRQ descriptors have already been allocated if true | ||
941 | * | ||
942 | * Allocate IRQ numbers and initialized all data structures to support | ||
943 | * hierarchy IRQ domains. | ||
944 | * Parameter @realloc is mainly to support legacy IRQs. | ||
945 | * Returns error code or allocated IRQ number | ||
946 | * | ||
947 | * The whole process to setup an IRQ has been split into two steps. | ||
948 | * The first step, __irq_domain_alloc_irqs(), is to allocate IRQ | ||
949 | * descriptor and required hardware resources. The second step, | ||
950 | * irq_domain_activate_irq(), is to program hardwares with preallocated | ||
951 | * resources. In this way, it's easier to rollback when failing to | ||
952 | * allocate resources. | ||
953 | */ | ||
954 | int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, | ||
955 | unsigned int nr_irqs, int node, void *arg, | ||
956 | bool realloc) | ||
957 | { | ||
958 | int i, ret, virq; | ||
959 | |||
960 | if (domain == NULL) { | ||
961 | domain = irq_default_domain; | ||
962 | if (WARN(!domain, "domain is NULL; cannot allocate IRQ\n")) | ||
963 | return -EINVAL; | ||
964 | } | ||
965 | |||
966 | if (!domain->ops->alloc) { | ||
967 | pr_debug("domain->ops->alloc() is NULL\n"); | ||
968 | return -ENOSYS; | ||
969 | } | ||
970 | |||
971 | if (realloc && irq_base >= 0) { | ||
972 | virq = irq_base; | ||
973 | } else { | ||
974 | virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node); | ||
975 | if (virq < 0) { | ||
976 | pr_debug("cannot allocate IRQ(base %d, count %d)\n", | ||
977 | irq_base, nr_irqs); | ||
978 | return virq; | ||
979 | } | ||
980 | } | ||
981 | |||
982 | if (irq_domain_alloc_irq_data(domain, virq, nr_irqs)) { | ||
983 | pr_debug("cannot allocate memory for IRQ%d\n", virq); | ||
984 | ret = -ENOMEM; | ||
985 | goto out_free_desc; | ||
986 | } | ||
987 | |||
988 | mutex_lock(&irq_domain_mutex); | ||
989 | ret = domain->ops->alloc(domain, virq, nr_irqs, arg); | ||
990 | if (ret < 0) { | ||
991 | mutex_unlock(&irq_domain_mutex); | ||
992 | goto out_free_irq_data; | ||
993 | } | ||
994 | for (i = 0; i < nr_irqs; i++) | ||
995 | irq_domain_insert_irq(virq + i); | ||
996 | mutex_unlock(&irq_domain_mutex); | ||
997 | |||
998 | return virq; | ||
999 | |||
1000 | out_free_irq_data: | ||
1001 | irq_domain_free_irq_data(virq, nr_irqs); | ||
1002 | out_free_desc: | ||
1003 | irq_free_descs(virq, nr_irqs); | ||
1004 | return ret; | ||
1005 | } | ||
1006 | |||
1007 | /** | ||
1008 | * irq_domain_free_irqs - Free IRQ number and associated data structures | ||
1009 | * @virq: base IRQ number | ||
1010 | * @nr_irqs: number of IRQs to free | ||
1011 | */ | ||
1012 | void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs) | ||
1013 | { | ||
1014 | struct irq_data *data = irq_get_irq_data(virq); | ||
1015 | int i; | ||
1016 | |||
1017 | if (WARN(!data || !data->domain || !data->domain->ops->free, | ||
1018 | "NULL pointer, cannot free irq\n")) | ||
1019 | return; | ||
1020 | |||
1021 | mutex_lock(&irq_domain_mutex); | ||
1022 | for (i = 0; i < nr_irqs; i++) | ||
1023 | irq_domain_remove_irq(virq + i); | ||
1024 | data->domain->ops->free(data->domain, virq, nr_irqs); | ||
1025 | mutex_unlock(&irq_domain_mutex); | ||
1026 | |||
1027 | irq_domain_free_irq_data(virq, nr_irqs); | ||
1028 | irq_free_descs(virq, nr_irqs); | ||
1029 | } | ||
1030 | |||
1031 | /** | ||
1032 | * irq_domain_activate_irq - Call domain_ops->activate recursively to activate | ||
1033 | * interrupt | ||
1034 | * @irq_data: outermost irq_data associated with interrupt | ||
1035 | * | ||
1036 | * This is the second step to call domain_ops->activate to program interrupt | ||
1037 | * controllers, so the interrupt could actually get delivered. | ||
1038 | */ | ||
1039 | void irq_domain_activate_irq(struct irq_data *irq_data) | ||
1040 | { | ||
1041 | if (irq_data && irq_data->domain) { | ||
1042 | struct irq_domain *domain = irq_data->domain; | ||
1043 | |||
1044 | if (irq_data->parent_data) | ||
1045 | irq_domain_activate_irq(irq_data->parent_data); | ||
1046 | if (domain->ops->activate) | ||
1047 | domain->ops->activate(domain, irq_data); | ||
1048 | } | ||
1049 | } | ||
1050 | |||
1051 | /** | ||
1052 | * irq_domain_deactivate_irq - Call domain_ops->deactivate recursively to | ||
1053 | * deactivate interrupt | ||
1054 | * @irq_data: outermost irq_data associated with interrupt | ||
1055 | * | ||
1056 | * It calls domain_ops->deactivate to program interrupt controllers to disable | ||
1057 | * interrupt delivery. | ||
1058 | */ | ||
1059 | void irq_domain_deactivate_irq(struct irq_data *irq_data) | ||
1060 | { | ||
1061 | if (irq_data && irq_data->domain) { | ||
1062 | struct irq_domain *domain = irq_data->domain; | ||
1063 | |||
1064 | if (domain->ops->deactivate) | ||
1065 | domain->ops->deactivate(domain, irq_data); | ||
1066 | if (irq_data->parent_data) | ||
1067 | irq_domain_deactivate_irq(irq_data->parent_data); | ||
1068 | } | ||
1069 | } | ||
1070 | |||
1071 | static void irq_domain_check_hierarchy(struct irq_domain *domain) | ||
1072 | { | ||
1073 | /* Hierarchy irq_domains must implement callback alloc() */ | ||
1074 | if (domain->ops->alloc) | ||
1075 | domain->flags |= IRQ_DOMAIN_FLAG_HIERARCHY; | ||
1076 | } | ||
1077 | #else /* CONFIG_IRQ_DOMAIN_HIERARCHY */ | ||
1078 | /** | ||
1079 | * irq_domain_get_irq_data - Get irq_data associated with @virq and @domain | ||
1080 | * @domain: domain to match | ||
1081 | * @virq: IRQ number to get irq_data | ||
1082 | */ | ||
1083 | struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, | ||
1084 | unsigned int virq) | ||
1085 | { | ||
1086 | struct irq_data *irq_data = irq_get_irq_data(virq); | ||
1087 | |||
1088 | return (irq_data && irq_data->domain == domain) ? irq_data : NULL; | ||
1089 | } | ||
1090 | |||
1091 | static void irq_domain_check_hierarchy(struct irq_domain *domain) | ||
1092 | { | ||
1093 | } | ||
1094 | #endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ | ||