| .. | .. |
|---|
| 31 | 31 | struct fwnode_handle fwnode; |
|---|
| 32 | 32 | unsigned int type; |
|---|
| 33 | 33 | char *name; |
|---|
| 34 | | - void *data; |
|---|
| 34 | + phys_addr_t *pa; |
|---|
| 35 | 35 | }; |
|---|
| 36 | 36 | |
|---|
| 37 | 37 | #ifdef CONFIG_GENERIC_IRQ_DEBUGFS |
|---|
| .. | .. |
|---|
| 46 | 46 | EXPORT_SYMBOL_GPL(irqchip_fwnode_ops); |
|---|
| 47 | 47 | |
|---|
| 48 | 48 | /** |
|---|
| 49 | | - * irq_domain_alloc_fwnode - Allocate a fwnode_handle suitable for |
|---|
| 49 | + * __irq_domain_alloc_fwnode - Allocate a fwnode_handle suitable for |
|---|
| 50 | 50 | * identifying an irq domain |
|---|
| 51 | 51 | * @type: Type of irqchip_fwnode. See linux/irqdomain.h |
|---|
| 52 | | - * @name: Optional user provided domain name |
|---|
| 53 | 52 | * @id: Optional user provided id if name != NULL |
|---|
| 54 | | - * @data: Optional user-provided data |
|---|
| 53 | + * @name: Optional user provided domain name |
|---|
| 54 | + * @pa: Optional user-provided physical address |
|---|
| 55 | 55 | * |
|---|
| 56 | 56 | * Allocate a struct irqchip_fwid, and return a poiner to the embedded |
|---|
| 57 | 57 | * fwnode_handle (or NULL on failure). |
|---|
| .. | .. |
|---|
| 62 | 62 | * domain struct. |
|---|
| 63 | 63 | */ |
|---|
| 64 | 64 | struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id, |
|---|
| 65 | | - const char *name, void *data) |
|---|
| 65 | + const char *name, |
|---|
| 66 | + phys_addr_t *pa) |
|---|
| 66 | 67 | { |
|---|
| 67 | 68 | struct irqchip_fwid *fwid; |
|---|
| 68 | 69 | char *n; |
|---|
| .. | .. |
|---|
| 77 | 78 | n = kasprintf(GFP_KERNEL, "%s-%d", name, id); |
|---|
| 78 | 79 | break; |
|---|
| 79 | 80 | default: |
|---|
| 80 | | - n = kasprintf(GFP_KERNEL, "irqchip@%p", data); |
|---|
| 81 | + n = kasprintf(GFP_KERNEL, "irqchip@%pa", pa); |
|---|
| 81 | 82 | break; |
|---|
| 82 | 83 | } |
|---|
| 83 | 84 | |
|---|
| .. | .. |
|---|
| 89 | 90 | |
|---|
| 90 | 91 | fwid->type = type; |
|---|
| 91 | 92 | fwid->name = n; |
|---|
| 92 | | - fwid->data = data; |
|---|
| 93 | | - fwid->fwnode.ops = &irqchip_fwnode_ops; |
|---|
| 93 | + fwid->pa = pa; |
|---|
| 94 | + fwnode_init(&fwid->fwnode, &irqchip_fwnode_ops); |
|---|
| 94 | 95 | return &fwid->fwnode; |
|---|
| 95 | 96 | } |
|---|
| 96 | 97 | EXPORT_SYMBOL_GPL(__irq_domain_alloc_fwnode); |
|---|
| .. | .. |
|---|
| 123 | 124 | * @ops: domain callbacks |
|---|
| 124 | 125 | * @host_data: Controller private data pointer |
|---|
| 125 | 126 | * |
|---|
| 126 | | - * Allocates and initialize and irq_domain structure. |
|---|
| 127 | + * Allocates and initializes an irq_domain structure. |
|---|
| 127 | 128 | * Returns pointer to IRQ domain, or NULL on failure. |
|---|
| 128 | 129 | */ |
|---|
| 129 | 130 | struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size, |
|---|
| .. | .. |
|---|
| 131 | 132 | const struct irq_domain_ops *ops, |
|---|
| 132 | 133 | void *host_data) |
|---|
| 133 | 134 | { |
|---|
| 134 | | - struct device_node *of_node = to_of_node(fwnode); |
|---|
| 135 | 135 | struct irqchip_fwid *fwid; |
|---|
| 136 | 136 | struct irq_domain *domain; |
|---|
| 137 | 137 | |
|---|
| 138 | 138 | static atomic_t unknown_domains; |
|---|
| 139 | 139 | |
|---|
| 140 | 140 | domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size), |
|---|
| 141 | | - GFP_KERNEL, of_node_to_nid(of_node)); |
|---|
| 142 | | - if (WARN_ON(!domain)) |
|---|
| 141 | + GFP_KERNEL, of_node_to_nid(to_of_node(fwnode))); |
|---|
| 142 | + if (!domain) |
|---|
| 143 | 143 | return NULL; |
|---|
| 144 | 144 | |
|---|
| 145 | | - if (fwnode && is_fwnode_irqchip(fwnode)) { |
|---|
| 145 | + if (is_fwnode_irqchip(fwnode)) { |
|---|
| 146 | 146 | fwid = container_of(fwnode, struct irqchip_fwid, fwnode); |
|---|
| 147 | 147 | |
|---|
| 148 | 148 | switch (fwid->type) { |
|---|
| .. | .. |
|---|
| 161 | 161 | domain->name = fwid->name; |
|---|
| 162 | 162 | break; |
|---|
| 163 | 163 | } |
|---|
| 164 | | -#ifdef CONFIG_ACPI |
|---|
| 165 | | - } else if (is_acpi_device_node(fwnode)) { |
|---|
| 166 | | - struct acpi_buffer buf = { |
|---|
| 167 | | - .length = ACPI_ALLOCATE_BUFFER, |
|---|
| 168 | | - }; |
|---|
| 169 | | - acpi_handle handle; |
|---|
| 170 | | - |
|---|
| 171 | | - handle = acpi_device_handle(to_acpi_device_node(fwnode)); |
|---|
| 172 | | - if (acpi_get_name(handle, ACPI_FULL_PATHNAME, &buf) == AE_OK) { |
|---|
| 173 | | - domain->name = buf.pointer; |
|---|
| 174 | | - domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; |
|---|
| 175 | | - } |
|---|
| 176 | | - |
|---|
| 177 | | - domain->fwnode = fwnode; |
|---|
| 178 | | -#endif |
|---|
| 179 | | - } else if (of_node) { |
|---|
| 164 | + } else if (is_of_node(fwnode) || is_acpi_device_node(fwnode) || |
|---|
| 165 | + is_software_node(fwnode)) { |
|---|
| 180 | 166 | char *name; |
|---|
| 181 | 167 | |
|---|
| 182 | 168 | /* |
|---|
| 183 | | - * DT paths contain '/', which debugfs is legitimately |
|---|
| 169 | + * fwnode paths contain '/', which debugfs is legitimately |
|---|
| 184 | 170 | * unhappy about. Replace them with ':', which does |
|---|
| 185 | 171 | * the trick and is not as offensive as '\'... |
|---|
| 186 | 172 | */ |
|---|
| 187 | | - name = kasprintf(GFP_KERNEL, "%pOF", of_node); |
|---|
| 173 | + name = kasprintf(GFP_KERNEL, "%pfw", fwnode); |
|---|
| 188 | 174 | if (!name) { |
|---|
| 189 | 175 | kfree(domain); |
|---|
| 190 | 176 | return NULL; |
|---|
| .. | .. |
|---|
| 209 | 195 | domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; |
|---|
| 210 | 196 | } |
|---|
| 211 | 197 | |
|---|
| 212 | | - of_node_get(of_node); |
|---|
| 198 | + fwnode_handle_get(fwnode); |
|---|
| 199 | + fwnode_dev_initialized(fwnode, true); |
|---|
| 213 | 200 | |
|---|
| 214 | 201 | /* Fill structure */ |
|---|
| 215 | 202 | INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 258 | 245 | |
|---|
| 259 | 246 | pr_debug("Removed domain %s\n", domain->name); |
|---|
| 260 | 247 | |
|---|
| 261 | | - of_node_put(irq_domain_get_of_node(domain)); |
|---|
| 248 | + fwnode_dev_initialized(domain->fwnode, false); |
|---|
| 249 | + fwnode_handle_put(domain->fwnode); |
|---|
| 262 | 250 | if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED) |
|---|
| 263 | 251 | kfree(domain->name); |
|---|
| 264 | 252 | kfree(domain); |
|---|
| .. | .. |
|---|
| 460 | 448 | } |
|---|
| 461 | 449 | EXPORT_SYMBOL_GPL(irq_set_default_host); |
|---|
| 462 | 450 | |
|---|
| 451 | +/** |
|---|
| 452 | + * irq_get_default_host() - Retrieve the "default" irq domain |
|---|
| 453 | + * |
|---|
| 454 | + * Returns: the default domain, if any. |
|---|
| 455 | + * |
|---|
| 456 | + * Modern code should never use this. This should only be used on |
|---|
| 457 | + * systems that cannot implement a firmware->fwnode mapping (which |
|---|
| 458 | + * both DT and ACPI provide). |
|---|
| 459 | + */ |
|---|
| 460 | +struct irq_domain *irq_get_default_host(void) |
|---|
| 461 | +{ |
|---|
| 462 | + return irq_default_domain; |
|---|
| 463 | +} |
|---|
| 464 | + |
|---|
| 463 | 465 | static void irq_domain_clear_mapping(struct irq_domain *domain, |
|---|
| 464 | 466 | irq_hw_number_t hwirq) |
|---|
| 465 | 467 | { |
|---|
| .. | .. |
|---|
| 624 | 626 | EXPORT_SYMBOL_GPL(irq_create_direct_mapping); |
|---|
| 625 | 627 | |
|---|
| 626 | 628 | /** |
|---|
| 627 | | - * irq_create_mapping() - Map a hardware interrupt into linux irq space |
|---|
| 629 | + * irq_create_mapping_affinity() - Map a hardware interrupt into linux irq space |
|---|
| 628 | 630 | * @domain: domain owning this hardware interrupt or NULL for default domain |
|---|
| 629 | 631 | * @hwirq: hardware irq number in that domain space |
|---|
| 632 | + * @affinity: irq affinity |
|---|
| 630 | 633 | * |
|---|
| 631 | 634 | * Only one mapping per hardware interrupt is permitted. Returns a linux |
|---|
| 632 | 635 | * irq number. |
|---|
| 633 | 636 | * If the sense/trigger is to be specified, set_irq_type() should be called |
|---|
| 634 | 637 | * on the number returned from that call. |
|---|
| 635 | 638 | */ |
|---|
| 636 | | -unsigned int irq_create_mapping(struct irq_domain *domain, |
|---|
| 637 | | - irq_hw_number_t hwirq) |
|---|
| 639 | +unsigned int irq_create_mapping_affinity(struct irq_domain *domain, |
|---|
| 640 | + irq_hw_number_t hwirq, |
|---|
| 641 | + const struct irq_affinity_desc *affinity) |
|---|
| 638 | 642 | { |
|---|
| 639 | 643 | struct device_node *of_node; |
|---|
| 640 | 644 | int virq; |
|---|
| .. | .. |
|---|
| 660 | 664 | } |
|---|
| 661 | 665 | |
|---|
| 662 | 666 | /* Allocate a virtual interrupt number */ |
|---|
| 663 | | - virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node), NULL); |
|---|
| 667 | + virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node), |
|---|
| 668 | + affinity); |
|---|
| 664 | 669 | if (virq <= 0) { |
|---|
| 665 | 670 | pr_debug("-> virq allocation failed\n"); |
|---|
| 666 | 671 | return 0; |
|---|
| .. | .. |
|---|
| 676 | 681 | |
|---|
| 677 | 682 | return virq; |
|---|
| 678 | 683 | } |
|---|
| 679 | | -EXPORT_SYMBOL_GPL(irq_create_mapping); |
|---|
| 684 | +EXPORT_SYMBOL_GPL(irq_create_mapping_affinity); |
|---|
| 680 | 685 | |
|---|
| 681 | 686 | /** |
|---|
| 682 | 687 | * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs |
|---|
| .. | .. |
|---|
| 731 | 736 | return 0; |
|---|
| 732 | 737 | } |
|---|
| 733 | 738 | |
|---|
| 734 | | -static void of_phandle_args_to_fwspec(struct of_phandle_args *irq_data, |
|---|
| 739 | +static void of_phandle_args_to_fwspec(struct device_node *np, const u32 *args, |
|---|
| 740 | + unsigned int count, |
|---|
| 735 | 741 | struct irq_fwspec *fwspec) |
|---|
| 736 | 742 | { |
|---|
| 737 | 743 | int i; |
|---|
| 738 | 744 | |
|---|
| 739 | | - fwspec->fwnode = irq_data->np ? &irq_data->np->fwnode : NULL; |
|---|
| 740 | | - fwspec->param_count = irq_data->args_count; |
|---|
| 745 | + fwspec->fwnode = np ? &np->fwnode : NULL; |
|---|
| 746 | + fwspec->param_count = count; |
|---|
| 741 | 747 | |
|---|
| 742 | | - for (i = 0; i < irq_data->args_count; i++) |
|---|
| 743 | | - fwspec->param[i] = irq_data->args[i]; |
|---|
| 748 | + for (i = 0; i < count; i++) |
|---|
| 749 | + fwspec->param[i] = args[i]; |
|---|
| 744 | 750 | } |
|---|
| 745 | 751 | |
|---|
| 746 | 752 | unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec) |
|---|
| .. | .. |
|---|
| 838 | 844 | { |
|---|
| 839 | 845 | struct irq_fwspec fwspec; |
|---|
| 840 | 846 | |
|---|
| 841 | | - of_phandle_args_to_fwspec(irq_data, &fwspec); |
|---|
| 847 | + of_phandle_args_to_fwspec(irq_data->np, irq_data->args, |
|---|
| 848 | + irq_data->args_count, &fwspec); |
|---|
| 849 | + |
|---|
| 842 | 850 | return irq_create_fwspec_mapping(&fwspec); |
|---|
| 843 | 851 | } |
|---|
| 844 | 852 | EXPORT_SYMBOL_GPL(irq_create_of_mapping); |
|---|
| .. | .. |
|---|
| 869 | 877 | EXPORT_SYMBOL_GPL(irq_dispose_mapping); |
|---|
| 870 | 878 | |
|---|
| 871 | 879 | /** |
|---|
| 872 | | - * irq_find_mapping() - Find a linux irq from an hw irq number. |
|---|
| 880 | + * irq_find_mapping() - Find a linux irq from a hw irq number. |
|---|
| 873 | 881 | * @domain: domain owning this hardware interrupt |
|---|
| 874 | 882 | * @hwirq: hardware irq number in that domain space |
|---|
| 875 | 883 | */ |
|---|
| .. | .. |
|---|
| 930 | 938 | const u32 *intspec, unsigned int intsize, |
|---|
| 931 | 939 | irq_hw_number_t *out_hwirq, unsigned int *out_type) |
|---|
| 932 | 940 | { |
|---|
| 933 | | - if (WARN_ON(intsize < 2)) |
|---|
| 934 | | - return -EINVAL; |
|---|
| 935 | | - *out_hwirq = intspec[0]; |
|---|
| 936 | | - *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; |
|---|
| 937 | | - return 0; |
|---|
| 941 | + struct irq_fwspec fwspec; |
|---|
| 942 | + |
|---|
| 943 | + of_phandle_args_to_fwspec(ctrlr, intspec, intsize, &fwspec); |
|---|
| 944 | + return irq_domain_translate_twocell(d, &fwspec, out_hwirq, out_type); |
|---|
| 938 | 945 | } |
|---|
| 939 | 946 | EXPORT_SYMBOL_GPL(irq_domain_xlate_twocell); |
|---|
| 940 | 947 | |
|---|
| .. | .. |
|---|
| 970 | 977 | }; |
|---|
| 971 | 978 | EXPORT_SYMBOL_GPL(irq_domain_simple_ops); |
|---|
| 972 | 979 | |
|---|
| 980 | +/** |
|---|
| 981 | + * irq_domain_translate_onecell() - Generic translate for direct one cell |
|---|
| 982 | + * bindings |
|---|
| 983 | + */ |
|---|
| 984 | +int irq_domain_translate_onecell(struct irq_domain *d, |
|---|
| 985 | + struct irq_fwspec *fwspec, |
|---|
| 986 | + unsigned long *out_hwirq, |
|---|
| 987 | + unsigned int *out_type) |
|---|
| 988 | +{ |
|---|
| 989 | + if (WARN_ON(fwspec->param_count < 1)) |
|---|
| 990 | + return -EINVAL; |
|---|
| 991 | + *out_hwirq = fwspec->param[0]; |
|---|
| 992 | + *out_type = IRQ_TYPE_NONE; |
|---|
| 993 | + return 0; |
|---|
| 994 | +} |
|---|
| 995 | +EXPORT_SYMBOL_GPL(irq_domain_translate_onecell); |
|---|
| 996 | + |
|---|
| 997 | +/** |
|---|
| 998 | + * irq_domain_translate_twocell() - Generic translate for direct two cell |
|---|
| 999 | + * bindings |
|---|
| 1000 | + * |
|---|
| 1001 | + * Device Tree IRQ specifier translation function which works with two cell |
|---|
| 1002 | + * bindings where the cell values map directly to the hwirq number |
|---|
| 1003 | + * and linux irq flags. |
|---|
| 1004 | + */ |
|---|
| 1005 | +int irq_domain_translate_twocell(struct irq_domain *d, |
|---|
| 1006 | + struct irq_fwspec *fwspec, |
|---|
| 1007 | + unsigned long *out_hwirq, |
|---|
| 1008 | + unsigned int *out_type) |
|---|
| 1009 | +{ |
|---|
| 1010 | + if (WARN_ON(fwspec->param_count < 2)) |
|---|
| 1011 | + return -EINVAL; |
|---|
| 1012 | + *out_hwirq = fwspec->param[0]; |
|---|
| 1013 | + *out_type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; |
|---|
| 1014 | + return 0; |
|---|
| 1015 | +} |
|---|
| 1016 | +EXPORT_SYMBOL_GPL(irq_domain_translate_twocell); |
|---|
| 1017 | + |
|---|
| 973 | 1018 | int irq_domain_alloc_descs(int virq, unsigned int cnt, irq_hw_number_t hwirq, |
|---|
| 974 | | - int node, const struct cpumask *affinity) |
|---|
| 1019 | + int node, const struct irq_affinity_desc *affinity) |
|---|
| 975 | 1020 | { |
|---|
| 976 | 1021 | unsigned int hint; |
|---|
| 977 | 1022 | |
|---|
| .. | .. |
|---|
| 992 | 1037 | |
|---|
| 993 | 1038 | return virq; |
|---|
| 994 | 1039 | } |
|---|
| 1040 | + |
|---|
| 1041 | +/** |
|---|
| 1042 | + * irq_domain_reset_irq_data - Clear hwirq, chip and chip_data in @irq_data |
|---|
| 1043 | + * @irq_data: The pointer to irq_data |
|---|
| 1044 | + */ |
|---|
| 1045 | +void irq_domain_reset_irq_data(struct irq_data *irq_data) |
|---|
| 1046 | +{ |
|---|
| 1047 | + irq_data->hwirq = 0; |
|---|
| 1048 | + irq_data->chip = &no_irq_chip; |
|---|
| 1049 | + irq_data->chip_data = NULL; |
|---|
| 1050 | +} |
|---|
| 1051 | +EXPORT_SYMBOL_GPL(irq_domain_reset_irq_data); |
|---|
| 995 | 1052 | |
|---|
| 996 | 1053 | #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY |
|---|
| 997 | 1054 | /** |
|---|
| .. | .. |
|---|
| 1084 | 1141 | return irq_data; |
|---|
| 1085 | 1142 | } |
|---|
| 1086 | 1143 | |
|---|
| 1144 | +static void __irq_domain_free_hierarchy(struct irq_data *irq_data) |
|---|
| 1145 | +{ |
|---|
| 1146 | + struct irq_data *tmp; |
|---|
| 1147 | + |
|---|
| 1148 | + while (irq_data) { |
|---|
| 1149 | + tmp = irq_data; |
|---|
| 1150 | + irq_data = irq_data->parent_data; |
|---|
| 1151 | + kfree(tmp); |
|---|
| 1152 | + } |
|---|
| 1153 | +} |
|---|
| 1154 | + |
|---|
| 1087 | 1155 | static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs) |
|---|
| 1088 | 1156 | { |
|---|
| 1089 | 1157 | struct irq_data *irq_data, *tmp; |
|---|
| .. | .. |
|---|
| 1095 | 1163 | irq_data->parent_data = NULL; |
|---|
| 1096 | 1164 | irq_data->domain = NULL; |
|---|
| 1097 | 1165 | |
|---|
| 1098 | | - while (tmp) { |
|---|
| 1099 | | - irq_data = tmp; |
|---|
| 1100 | | - tmp = tmp->parent_data; |
|---|
| 1101 | | - kfree(irq_data); |
|---|
| 1166 | + __irq_domain_free_hierarchy(tmp); |
|---|
| 1167 | + } |
|---|
| 1168 | +} |
|---|
| 1169 | + |
|---|
| 1170 | +/** |
|---|
| 1171 | + * irq_domain_disconnect_hierarchy - Mark the first unused level of a hierarchy |
|---|
| 1172 | + * @domain: IRQ domain from which the hierarchy is to be disconnected |
|---|
| 1173 | + * @virq: IRQ number where the hierarchy is to be trimmed |
|---|
| 1174 | + * |
|---|
| 1175 | + * Marks the @virq level belonging to @domain as disconnected. |
|---|
| 1176 | + * Returns -EINVAL if @virq doesn't have a valid irq_data pointing |
|---|
| 1177 | + * to @domain. |
|---|
| 1178 | + * |
|---|
| 1179 | + * Its only use is to be able to trim levels of hierarchy that do not |
|---|
| 1180 | + * have any real meaning for this interrupt, and that the driver marks |
|---|
| 1181 | + * as such from its .alloc() callback. |
|---|
| 1182 | + */ |
|---|
| 1183 | +int irq_domain_disconnect_hierarchy(struct irq_domain *domain, |
|---|
| 1184 | + unsigned int virq) |
|---|
| 1185 | +{ |
|---|
| 1186 | + struct irq_data *irqd; |
|---|
| 1187 | + |
|---|
| 1188 | + irqd = irq_domain_get_irq_data(domain, virq); |
|---|
| 1189 | + if (!irqd) |
|---|
| 1190 | + return -EINVAL; |
|---|
| 1191 | + |
|---|
| 1192 | + irqd->chip = ERR_PTR(-ENOTCONN); |
|---|
| 1193 | + return 0; |
|---|
| 1194 | +} |
|---|
| 1195 | +EXPORT_SYMBOL_GPL(irq_domain_disconnect_hierarchy); |
|---|
| 1196 | + |
|---|
| 1197 | +static int irq_domain_trim_hierarchy(unsigned int virq) |
|---|
| 1198 | +{ |
|---|
| 1199 | + struct irq_data *tail, *irqd, *irq_data; |
|---|
| 1200 | + |
|---|
| 1201 | + irq_data = irq_get_irq_data(virq); |
|---|
| 1202 | + tail = NULL; |
|---|
| 1203 | + |
|---|
| 1204 | + /* The first entry must have a valid irqchip */ |
|---|
| 1205 | + if (!irq_data->chip || IS_ERR(irq_data->chip)) |
|---|
| 1206 | + return -EINVAL; |
|---|
| 1207 | + |
|---|
| 1208 | + /* |
|---|
| 1209 | + * Validate that the irq_data chain is sane in the presence of |
|---|
| 1210 | + * a hierarchy trimming marker. |
|---|
| 1211 | + */ |
|---|
| 1212 | + for (irqd = irq_data->parent_data; irqd; irq_data = irqd, irqd = irqd->parent_data) { |
|---|
| 1213 | + /* Can't have a valid irqchip after a trim marker */ |
|---|
| 1214 | + if (irqd->chip && tail) |
|---|
| 1215 | + return -EINVAL; |
|---|
| 1216 | + |
|---|
| 1217 | + /* Can't have an empty irqchip before a trim marker */ |
|---|
| 1218 | + if (!irqd->chip && !tail) |
|---|
| 1219 | + return -EINVAL; |
|---|
| 1220 | + |
|---|
| 1221 | + if (IS_ERR(irqd->chip)) { |
|---|
| 1222 | + /* Only -ENOTCONN is a valid trim marker */ |
|---|
| 1223 | + if (PTR_ERR(irqd->chip) != -ENOTCONN) |
|---|
| 1224 | + return -EINVAL; |
|---|
| 1225 | + |
|---|
| 1226 | + tail = irq_data; |
|---|
| 1102 | 1227 | } |
|---|
| 1103 | 1228 | } |
|---|
| 1229 | + |
|---|
| 1230 | + /* No trim marker, nothing to do */ |
|---|
| 1231 | + if (!tail) |
|---|
| 1232 | + return 0; |
|---|
| 1233 | + |
|---|
| 1234 | + pr_info("IRQ%d: trimming hierarchy from %s\n", |
|---|
| 1235 | + virq, tail->parent_data->domain->name); |
|---|
| 1236 | + |
|---|
| 1237 | + /* Sever the inner part of the hierarchy... */ |
|---|
| 1238 | + irqd = tail; |
|---|
| 1239 | + tail = tail->parent_data; |
|---|
| 1240 | + irqd->parent_data = NULL; |
|---|
| 1241 | + __irq_domain_free_hierarchy(tail); |
|---|
| 1242 | + |
|---|
| 1243 | + return 0; |
|---|
| 1104 | 1244 | } |
|---|
| 1105 | 1245 | |
|---|
| 1106 | 1246 | static int irq_domain_alloc_irq_data(struct irq_domain *domain, |
|---|
| .. | .. |
|---|
| 1194 | 1334 | EXPORT_SYMBOL(irq_domain_set_info); |
|---|
| 1195 | 1335 | |
|---|
| 1196 | 1336 | /** |
|---|
| 1197 | | - * irq_domain_reset_irq_data - Clear hwirq, chip and chip_data in @irq_data |
|---|
| 1198 | | - * @irq_data: The pointer to irq_data |
|---|
| 1199 | | - */ |
|---|
| 1200 | | -void irq_domain_reset_irq_data(struct irq_data *irq_data) |
|---|
| 1201 | | -{ |
|---|
| 1202 | | - irq_data->hwirq = 0; |
|---|
| 1203 | | - irq_data->chip = &no_irq_chip; |
|---|
| 1204 | | - irq_data->chip_data = NULL; |
|---|
| 1205 | | -} |
|---|
| 1206 | | -EXPORT_SYMBOL_GPL(irq_domain_reset_irq_data); |
|---|
| 1207 | | - |
|---|
| 1208 | | -/** |
|---|
| 1209 | 1337 | * irq_domain_free_irqs_common - Clear irq_data and free the parent |
|---|
| 1210 | 1338 | * @domain: Interrupt domain to match |
|---|
| 1211 | 1339 | * @virq: IRQ number to start with |
|---|
| .. | .. |
|---|
| 1243 | 1371 | } |
|---|
| 1244 | 1372 | irq_domain_free_irqs_common(domain, virq, nr_irqs); |
|---|
| 1245 | 1373 | } |
|---|
| 1246 | | -EXPORT_SYMBOL_GPL(irq_domain_free_irqs_top); |
|---|
| 1247 | 1374 | |
|---|
| 1248 | 1375 | static void irq_domain_free_irqs_hierarchy(struct irq_domain *domain, |
|---|
| 1249 | 1376 | unsigned int irq_base, |
|---|
| .. | .. |
|---|
| 1275 | 1402 | /** |
|---|
| 1276 | 1403 | * __irq_domain_alloc_irqs - Allocate IRQs from domain |
|---|
| 1277 | 1404 | * @domain: domain to allocate from |
|---|
| 1278 | | - * @irq_base: allocate specified IRQ nubmer if irq_base >= 0 |
|---|
| 1405 | + * @irq_base: allocate specified IRQ number if irq_base >= 0 |
|---|
| 1279 | 1406 | * @nr_irqs: number of IRQs to allocate |
|---|
| 1280 | 1407 | * @node: NUMA node id for memory allocation |
|---|
| 1281 | 1408 | * @arg: domain specific argument |
|---|
| .. | .. |
|---|
| 1296 | 1423 | */ |
|---|
| 1297 | 1424 | int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base, |
|---|
| 1298 | 1425 | unsigned int nr_irqs, int node, void *arg, |
|---|
| 1299 | | - bool realloc, const struct cpumask *affinity) |
|---|
| 1426 | + bool realloc, const struct irq_affinity_desc *affinity) |
|---|
| 1300 | 1427 | { |
|---|
| 1301 | 1428 | int i, ret, virq; |
|---|
| 1302 | 1429 | |
|---|
| .. | .. |
|---|
| 1330 | 1457 | mutex_unlock(&irq_domain_mutex); |
|---|
| 1331 | 1458 | goto out_free_irq_data; |
|---|
| 1332 | 1459 | } |
|---|
| 1460 | + |
|---|
| 1461 | + for (i = 0; i < nr_irqs; i++) { |
|---|
| 1462 | + ret = irq_domain_trim_hierarchy(virq + i); |
|---|
| 1463 | + if (ret) { |
|---|
| 1464 | + mutex_unlock(&irq_domain_mutex); |
|---|
| 1465 | + goto out_free_irq_data; |
|---|
| 1466 | + } |
|---|
| 1467 | + } |
|---|
| 1468 | + |
|---|
| 1333 | 1469 | for (i = 0; i < nr_irqs; i++) |
|---|
| 1334 | 1470 | irq_domain_insert_irq(virq + i); |
|---|
| 1335 | 1471 | mutex_unlock(&irq_domain_mutex); |
|---|
| .. | .. |
|---|
| 1752 | 1888 | static void debugfs_remove_domain_dir(struct irq_domain *d) |
|---|
| 1753 | 1889 | { |
|---|
| 1754 | 1890 | debugfs_remove(d->debugfs_file); |
|---|
| 1891 | + d->debugfs_file = NULL; |
|---|
| 1755 | 1892 | } |
|---|
| 1756 | 1893 | |
|---|
| 1757 | 1894 | void __init irq_domain_debugfs_init(struct dentry *root) |
|---|
| .. | .. |
|---|
| 1759 | 1896 | struct irq_domain *d; |
|---|
| 1760 | 1897 | |
|---|
| 1761 | 1898 | domain_dir = debugfs_create_dir("domains", root); |
|---|
| 1762 | | - if (!domain_dir) |
|---|
| 1763 | | - return; |
|---|
| 1764 | 1899 | |
|---|
| 1765 | 1900 | debugfs_create_file("default", 0444, domain_dir, NULL, |
|---|
| 1766 | 1901 | &irq_domain_debug_fops); |
|---|