Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

regmap IRQ support for devices with multiple IRQs

Merge series from Matti Vaittinen <mazziesaccount@gmail.com>:

Devices can provide multiple interrupt lines. One reason for this is that
a device has multiple subfunctions, each providing its own interrupt line.
Another reason is that a device can be designed to be used (also) on a
system where some of the interrupts can be routed to another processor.

A line often further acts as a demultiplex for specific interrupts
and has it's respective set of interrupt (status, mask, ack, ...)
registers.

Regmap supports the handling of these registers and demultiplexing
interrupts, but interrupt domain code ends up assigning the same name for
the per interrupt line domains

This series adds possibility for giving a name suffix for an interrupt

Previous discussion can be found from:
https://lore.kernel.org/all/87plst28yk.ffs@tglx/
https://lore.kernel.org/all/15685ef6-92a5-41df-9148-1a67ceaec47b@gmail.com/

The domain suffix support added in this series will be used by the
ROHM BD96801 ERRB IRQ support code. The BD96801 ERRB support will need
the initial BD96801 driver code, which is not yet in irq/core or regmap
trees. Thus the user for this new support is not included in the series,
but will be sent once the name suffix support gets merged.

+151 -96
+26 -11
drivers/base/regmap/regmap-irq.c
··· 608 608 } 609 609 EXPORT_SYMBOL_GPL(regmap_irq_set_type_config_simple); 610 610 611 + static int regmap_irq_create_domain(struct fwnode_handle *fwnode, int irq_base, 612 + const struct regmap_irq_chip *chip, 613 + struct regmap_irq_chip_data *d) 614 + { 615 + struct irq_domain_info info = { 616 + .fwnode = fwnode, 617 + .size = chip->num_irqs, 618 + .hwirq_max = chip->num_irqs, 619 + .virq_base = irq_base, 620 + .ops = &regmap_domain_ops, 621 + .host_data = d, 622 + .name_suffix = chip->domain_suffix, 623 + }; 624 + 625 + d->domain = irq_domain_instantiate(&info); 626 + if (IS_ERR(d->domain)) { 627 + dev_err(d->map->dev, "Failed to create IRQ domain\n"); 628 + return PTR_ERR(d->domain); 629 + } 630 + 631 + return 0; 632 + } 633 + 634 + 611 635 /** 612 636 * regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling 613 637 * ··· 880 856 } 881 857 } 882 858 883 - if (irq_base) 884 - d->domain = irq_domain_create_legacy(fwnode, chip->num_irqs, 885 - irq_base, 0, 886 - &regmap_domain_ops, d); 887 - else 888 - d->domain = irq_domain_create_linear(fwnode, chip->num_irqs, 889 - &regmap_domain_ops, d); 890 - if (!d->domain) { 891 - dev_err(map->dev, "Failed to create IRQ domain\n"); 892 - ret = -ENOMEM; 859 + ret = regmap_irq_create_domain(fwnode, irq_base, chip, d); 860 + if (ret) 893 861 goto err_alloc; 894 - } 895 862 896 863 ret = request_threaded_irq(irq, NULL, regmap_irq_thread, 897 864 irq_flags | IRQF_ONESHOT,
+8
include/linux/irqdomain.h
··· 291 291 * @hwirq_max: Maximum number of interrupts supported by controller 292 292 * @direct_max: Maximum value of direct maps; 293 293 * Use ~0 for no limit; 0 for no direct mapping 294 + * @hwirq_base: The first hardware interrupt number (legacy domains only) 295 + * @virq_base: The first Linux interrupt number for legacy domains to 296 + * immediately associate the interrupts after domain creation 294 297 * @bus_token: Domain bus token 298 + * @name_suffix: Optional name suffix to avoid collisions when multiple 299 + * domains are added using same fwnode 295 300 * @ops: Domain operation callbacks 296 301 * @host_data: Controller private data pointer 297 302 * @dgc_info: Geneneric chip information structure pointer used to ··· 312 307 unsigned int size; 313 308 irq_hw_number_t hwirq_max; 314 309 int direct_max; 310 + unsigned int hwirq_base; 311 + unsigned int virq_base; 315 312 enum irq_domain_bus_token bus_token; 313 + const char *name_suffix; 316 314 const struct irq_domain_ops *ops; 317 315 void *host_data; 318 316 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+4
include/linux/regmap.h
··· 1521 1521 * struct regmap_irq_chip - Description of a generic regmap irq_chip. 1522 1522 * 1523 1523 * @name: Descriptive name for IRQ controller. 1524 + * @domain_suffix: Name suffix to be appended to end of IRQ domain name. Needed 1525 + * when multiple regmap-IRQ controllers are created from same 1526 + * device. 1524 1527 * 1525 1528 * @main_status: Base main status register address. For chips which have 1526 1529 * interrupts arranged in separate sub-irq blocks with own IRQ ··· 1609 1606 */ 1610 1607 struct regmap_irq_chip { 1611 1608 const char *name; 1609 + const char *domain_suffix; 1612 1610 1613 1611 unsigned int main_status; 1614 1612 unsigned int num_main_status_bits;
+113 -85
kernel/irq/irqdomain.c
··· 128 128 } 129 129 EXPORT_SYMBOL_GPL(irq_domain_free_fwnode); 130 130 131 - static int irq_domain_set_name(struct irq_domain *domain, 132 - const struct fwnode_handle *fwnode, 133 - enum irq_domain_bus_token bus_token) 131 + static int alloc_name(struct irq_domain *domain, char *base, enum irq_domain_bus_token bus_token) 132 + { 133 + domain->name = bus_token ? kasprintf(GFP_KERNEL, "%s-%d", base, bus_token) : 134 + kasprintf(GFP_KERNEL, "%s", base); 135 + if (!domain->name) 136 + return -ENOMEM; 137 + 138 + domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; 139 + return 0; 140 + } 141 + 142 + static int alloc_fwnode_name(struct irq_domain *domain, const struct fwnode_handle *fwnode, 143 + enum irq_domain_bus_token bus_token, const char *suffix) 144 + { 145 + const char *sep = suffix ? "-" : ""; 146 + const char *suf = suffix ? : ""; 147 + char *name; 148 + 149 + name = bus_token ? kasprintf(GFP_KERNEL, "%pfw-%s%s%d", fwnode, suf, sep, bus_token) : 150 + kasprintf(GFP_KERNEL, "%pfw-%s", fwnode, suf); 151 + if (!name) 152 + return -ENOMEM; 153 + 154 + /* 155 + * fwnode paths contain '/', which debugfs is legitimately unhappy 156 + * about. Replace them with ':', which does the trick and is not as 157 + * offensive as '\'... 158 + */ 159 + domain->name = strreplace(name, '/', ':'); 160 + domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; 161 + return 0; 162 + } 163 + 164 + static int alloc_unknown_name(struct irq_domain *domain, enum irq_domain_bus_token bus_token) 134 165 { 135 166 static atomic_t unknown_domains; 136 - struct irqchip_fwid *fwid; 167 + int id = atomic_inc_return(&unknown_domains); 168 + 169 + domain->name = bus_token ? kasprintf(GFP_KERNEL, "unknown-%d-%d", id, bus_token) : 170 + kasprintf(GFP_KERNEL, "unknown-%d", id); 171 + 172 + if (!domain->name) 173 + return -ENOMEM; 174 + domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; 175 + return 0; 176 + } 177 + 178 + static int irq_domain_set_name(struct irq_domain *domain, const struct irq_domain_info *info) 179 + { 180 + enum irq_domain_bus_token bus_token = info->bus_token; 181 + const struct fwnode_handle *fwnode = info->fwnode; 137 182 138 183 if (is_fwnode_irqchip(fwnode)) { 139 - fwid = container_of(fwnode, struct irqchip_fwid, fwnode); 184 + struct irqchip_fwid *fwid = container_of(fwnode, struct irqchip_fwid, fwnode); 185 + 186 + /* 187 + * The name_suffix is only intended to be used to avoid a name 188 + * collision when multiple domains are created for a single 189 + * device and the name is picked using a real device node. 190 + * (Typical use-case is regmap-IRQ controllers for devices 191 + * providing more than one physical IRQ.) There should be no 192 + * need to use name_suffix with irqchip-fwnode. 193 + */ 194 + if (info->name_suffix) 195 + return -EINVAL; 140 196 141 197 switch (fwid->type) { 142 198 case IRQCHIP_FWNODE_NAMED: 143 199 case IRQCHIP_FWNODE_NAMED_ID: 144 - domain->name = bus_token ? 145 - kasprintf(GFP_KERNEL, "%s-%d", 146 - fwid->name, bus_token) : 147 - kstrdup(fwid->name, GFP_KERNEL); 148 - if (!domain->name) 149 - return -ENOMEM; 150 - domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; 151 - break; 200 + return alloc_name(domain, fwid->name, bus_token); 152 201 default: 153 202 domain->name = fwid->name; 154 - if (bus_token) { 155 - domain->name = kasprintf(GFP_KERNEL, "%s-%d", 156 - fwid->name, bus_token); 157 - if (!domain->name) 158 - return -ENOMEM; 159 - domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; 160 - } 161 - break; 203 + if (bus_token) 204 + return alloc_name(domain, fwid->name, bus_token); 162 205 } 163 - } else if (is_of_node(fwnode) || is_acpi_device_node(fwnode) || 164 - is_software_node(fwnode)) { 165 - char *name; 166 206 167 - /* 168 - * fwnode paths contain '/', which debugfs is legitimately 169 - * unhappy about. Replace them with ':', which does 170 - * the trick and is not as offensive as '\'... 171 - */ 172 - name = bus_token ? 173 - kasprintf(GFP_KERNEL, "%pfw-%d", fwnode, bus_token) : 174 - kasprintf(GFP_KERNEL, "%pfw", fwnode); 175 - if (!name) 176 - return -ENOMEM; 177 - 178 - domain->name = strreplace(name, '/', ':'); 179 - domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; 207 + } else if (is_of_node(fwnode) || is_acpi_device_node(fwnode) || is_software_node(fwnode)) { 208 + return alloc_fwnode_name(domain, fwnode, bus_token, info->name_suffix); 180 209 } 181 210 182 - if (!domain->name) { 183 - if (fwnode) 184 - pr_err("Invalid fwnode type for irqdomain\n"); 185 - domain->name = bus_token ? 186 - kasprintf(GFP_KERNEL, "unknown-%d-%d", 187 - atomic_inc_return(&unknown_domains), 188 - bus_token) : 189 - kasprintf(GFP_KERNEL, "unknown-%d", 190 - atomic_inc_return(&unknown_domains)); 191 - if (!domain->name) 192 - return -ENOMEM; 193 - domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED; 194 - } 211 + if (domain->name) 212 + return 0; 195 213 196 - return 0; 214 + if (fwnode) 215 + pr_err("Invalid fwnode type for irqdomain\n"); 216 + return alloc_unknown_name(domain, bus_token); 197 217 } 198 218 199 219 static struct irq_domain *__irq_domain_create(const struct irq_domain_info *info) ··· 231 211 if (!domain) 232 212 return ERR_PTR(-ENOMEM); 233 213 234 - err = irq_domain_set_name(domain, info->fwnode, info->bus_token); 214 + err = irq_domain_set_name(domain, info); 235 215 if (err) { 236 216 kfree(domain); 237 217 return ERR_PTR(err); ··· 287 267 kfree(domain); 288 268 } 289 269 290 - /** 291 - * irq_domain_instantiate() - Instantiate a new irq domain data structure 292 - * @info: Domain information pointer pointing to the information for this domain 293 - * 294 - * Return: A pointer to the instantiated irq domain or an ERR_PTR value. 295 - */ 296 - struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info) 270 + static void irq_domain_instantiate_descs(const struct irq_domain_info *info) 271 + { 272 + if (!IS_ENABLED(CONFIG_SPARSE_IRQ)) 273 + return; 274 + 275 + if (irq_alloc_descs(info->virq_base, info->virq_base, info->size, 276 + of_node_to_nid(to_of_node(info->fwnode))) < 0) { 277 + pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", 278 + info->virq_base); 279 + } 280 + } 281 + 282 + static struct irq_domain *__irq_domain_instantiate(const struct irq_domain_info *info, 283 + bool cond_alloc_descs) 297 284 { 298 285 struct irq_domain *domain; 299 286 int err; ··· 333 306 334 307 __irq_domain_publish(domain); 335 308 309 + if (cond_alloc_descs && info->virq_base > 0) 310 + irq_domain_instantiate_descs(info); 311 + 312 + /* Legacy interrupt domains have a fixed Linux interrupt number */ 313 + if (info->virq_base > 0) { 314 + irq_domain_associate_many(domain, info->virq_base, info->hwirq_base, 315 + info->size - info->hwirq_base); 316 + } 317 + 336 318 return domain; 337 319 338 320 err_domain_gc_remove: ··· 350 314 err_domain_free: 351 315 irq_domain_free(domain); 352 316 return ERR_PTR(err); 317 + } 318 + 319 + /** 320 + * irq_domain_instantiate() - Instantiate a new irq domain data structure 321 + * @info: Domain information pointer pointing to the information for this domain 322 + * 323 + * Return: A pointer to the instantiated irq domain or an ERR_PTR value. 324 + */ 325 + struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info) 326 + { 327 + return __irq_domain_instantiate(info, false); 353 328 } 354 329 EXPORT_SYMBOL_GPL(irq_domain_instantiate); 355 330 ··· 460 413 .fwnode = fwnode, 461 414 .size = size, 462 415 .hwirq_max = size, 416 + .virq_base = first_irq, 463 417 .ops = ops, 464 418 .host_data = host_data, 465 419 }; 466 - struct irq_domain *domain; 420 + struct irq_domain *domain = __irq_domain_instantiate(&info, true); 467 421 468 - domain = irq_domain_instantiate(&info); 469 - if (IS_ERR(domain)) 470 - return NULL; 471 - 472 - if (first_irq > 0) { 473 - if (IS_ENABLED(CONFIG_SPARSE_IRQ)) { 474 - /* attempt to allocated irq_descs */ 475 - int rc = irq_alloc_descs(first_irq, first_irq, size, 476 - of_node_to_nid(to_of_node(fwnode))); 477 - if (rc < 0) 478 - pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", 479 - first_irq); 480 - } 481 - irq_domain_associate_many(domain, first_irq, 0, size); 482 - } 483 - 484 - return domain; 422 + return IS_ERR(domain) ? NULL : domain; 485 423 } 486 424 EXPORT_SYMBOL_GPL(irq_domain_create_simple); 487 425 ··· 508 476 .fwnode = fwnode, 509 477 .size = first_hwirq + size, 510 478 .hwirq_max = first_hwirq + size, 479 + .hwirq_base = first_hwirq, 480 + .virq_base = first_irq, 511 481 .ops = ops, 512 482 .host_data = host_data, 513 483 }; 514 - struct irq_domain *domain; 484 + struct irq_domain *domain = irq_domain_instantiate(&info); 515 485 516 - domain = irq_domain_instantiate(&info); 517 - if (IS_ERR(domain)) 518 - return NULL; 519 - 520 - irq_domain_associate_many(domain, first_irq, first_hwirq, size); 521 - 522 - return domain; 486 + return IS_ERR(domain) ? NULL : domain; 523 487 } 524 488 EXPORT_SYMBOL_GPL(irq_domain_create_legacy); 525 489