Skip to content

Commit c69bb91

Browse files
committed
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.
2 parents 7c626ce + dde286e commit c69bb91

4 files changed

Lines changed: 151 additions & 96 deletions

File tree

drivers/base/regmap/regmap-irq.c

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,30 @@ int regmap_irq_set_type_config_simple(unsigned int **buf, unsigned int type,
608608
}
609609
EXPORT_SYMBOL_GPL(regmap_irq_set_type_config_simple);
610610

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+
611635
/**
612636
* regmap_add_irq_chip_fwnode() - Use standard regmap IRQ controller handling
613637
*
@@ -856,18 +880,9 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,
856880
}
857881
}
858882

859-
if (irq_base)
860-
d->domain = irq_domain_create_legacy(fwnode, chip->num_irqs,
861-
irq_base, 0,
862-
&regmap_domain_ops, d);
863-
else
864-
d->domain = irq_domain_create_linear(fwnode, chip->num_irqs,
865-
&regmap_domain_ops, d);
866-
if (!d->domain) {
867-
dev_err(map->dev, "Failed to create IRQ domain\n");
868-
ret = -ENOMEM;
883+
ret = regmap_irq_create_domain(fwnode, irq_base, chip, d);
884+
if (ret)
869885
goto err_alloc;
870-
}
871886

872887
ret = request_threaded_irq(irq, NULL, regmap_irq_thread,
873888
irq_flags | IRQF_ONESHOT,

include/linux/irqdomain.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,12 @@ struct irq_domain_chip_generic_info;
291291
* @hwirq_max: Maximum number of interrupts supported by controller
292292
* @direct_max: Maximum value of direct maps;
293293
* 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
294297
* @bus_token: Domain bus token
298+
* @name_suffix: Optional name suffix to avoid collisions when multiple
299+
* domains are added using same fwnode
295300
* @ops: Domain operation callbacks
296301
* @host_data: Controller private data pointer
297302
* @dgc_info: Geneneric chip information structure pointer used to
@@ -307,7 +312,10 @@ struct irq_domain_info {
307312
unsigned int size;
308313
irq_hw_number_t hwirq_max;
309314
int direct_max;
315+
unsigned int hwirq_base;
316+
unsigned int virq_base;
310317
enum irq_domain_bus_token bus_token;
318+
const char *name_suffix;
311319
const struct irq_domain_ops *ops;
312320
void *host_data;
313321
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY

include/linux/regmap.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,9 @@ struct regmap_irq_chip_data;
15211521
* struct regmap_irq_chip - Description of a generic regmap irq_chip.
15221522
*
15231523
* @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.
15241527
*
15251528
* @main_status: Base main status register address. For chips which have
15261529
* interrupts arranged in separate sub-irq blocks with own IRQ
@@ -1606,6 +1609,7 @@ struct regmap_irq_chip_data;
16061609
*/
16071610
struct regmap_irq_chip {
16081611
const char *name;
1612+
const char *domain_suffix;
16091613

16101614
unsigned int main_status;
16111615
unsigned int num_main_status_bits;

kernel/irq/irqdomain.c

Lines changed: 113 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -128,72 +128,92 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode)
128128
}
129129
EXPORT_SYMBOL_GPL(irq_domain_free_fwnode);
130130

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)
134165
{
135166
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;
137182

138183
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;
140196

141197
switch (fwid->type) {
142198
case IRQCHIP_FWNODE_NAMED:
143199
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);
152201
default:
153202
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);
162205
}
163-
} else if (is_of_node(fwnode) || is_acpi_device_node(fwnode) ||
164-
is_software_node(fwnode)) {
165-
char *name;
166-
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;
177206

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);
180209
}
181210

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;
195213

196-
return 0;
214+
if (fwnode)
215+
pr_err("Invalid fwnode type for irqdomain\n");
216+
return alloc_unknown_name(domain, bus_token);
197217
}
198218

199219
static struct irq_domain *__irq_domain_create(const struct irq_domain_info *info)
@@ -211,7 +231,7 @@ static struct irq_domain *__irq_domain_create(const struct irq_domain_info *info
211231
if (!domain)
212232
return ERR_PTR(-ENOMEM);
213233

214-
err = irq_domain_set_name(domain, info->fwnode, info->bus_token);
234+
err = irq_domain_set_name(domain, info);
215235
if (err) {
216236
kfree(domain);
217237
return ERR_PTR(err);
@@ -267,13 +287,20 @@ static void irq_domain_free(struct irq_domain *domain)
267287
kfree(domain);
268288
}
269289

270-
/**
271-
* irq_domain_instantiate() - Instantiate a new irq domain data structure
272-
* @info: Domain information pointer pointing to the information for this domain
273-
*
274-
* Return: A pointer to the instantiated irq domain or an ERR_PTR value.
275-
*/
276-
struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
290+
static void irq_domain_instantiate_descs(const struct irq_domain_info *info)
291+
{
292+
if (!IS_ENABLED(CONFIG_SPARSE_IRQ))
293+
return;
294+
295+
if (irq_alloc_descs(info->virq_base, info->virq_base, info->size,
296+
of_node_to_nid(to_of_node(info->fwnode))) < 0) {
297+
pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
298+
info->virq_base);
299+
}
300+
}
301+
302+
static struct irq_domain *__irq_domain_instantiate(const struct irq_domain_info *info,
303+
bool cond_alloc_descs)
277304
{
278305
struct irq_domain *domain;
279306
int err;
@@ -306,6 +333,15 @@ struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
306333

307334
__irq_domain_publish(domain);
308335

336+
if (cond_alloc_descs && info->virq_base > 0)
337+
irq_domain_instantiate_descs(info);
338+
339+
/* Legacy interrupt domains have a fixed Linux interrupt number */
340+
if (info->virq_base > 0) {
341+
irq_domain_associate_many(domain, info->virq_base, info->hwirq_base,
342+
info->size - info->hwirq_base);
343+
}
344+
309345
return domain;
310346

311347
err_domain_gc_remove:
@@ -315,6 +351,17 @@ struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
315351
irq_domain_free(domain);
316352
return ERR_PTR(err);
317353
}
354+
355+
/**
356+
* irq_domain_instantiate() - Instantiate a new irq domain data structure
357+
* @info: Domain information pointer pointing to the information for this domain
358+
*
359+
* Return: A pointer to the instantiated irq domain or an ERR_PTR value.
360+
*/
361+
struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
362+
{
363+
return __irq_domain_instantiate(info, false);
364+
}
318365
EXPORT_SYMBOL_GPL(irq_domain_instantiate);
319366

320367
/**
@@ -413,28 +460,13 @@ struct irq_domain *irq_domain_create_simple(struct fwnode_handle *fwnode,
413460
.fwnode = fwnode,
414461
.size = size,
415462
.hwirq_max = size,
463+
.virq_base = first_irq,
416464
.ops = ops,
417465
.host_data = host_data,
418466
};
419-
struct irq_domain *domain;
420-
421-
domain = irq_domain_instantiate(&info);
422-
if (IS_ERR(domain))
423-
return NULL;
467+
struct irq_domain *domain = __irq_domain_instantiate(&info, true);
424468

425-
if (first_irq > 0) {
426-
if (IS_ENABLED(CONFIG_SPARSE_IRQ)) {
427-
/* attempt to allocated irq_descs */
428-
int rc = irq_alloc_descs(first_irq, first_irq, size,
429-
of_node_to_nid(to_of_node(fwnode)));
430-
if (rc < 0)
431-
pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
432-
first_irq);
433-
}
434-
irq_domain_associate_many(domain, first_irq, 0, size);
435-
}
436-
437-
return domain;
469+
return IS_ERR(domain) ? NULL : domain;
438470
}
439471
EXPORT_SYMBOL_GPL(irq_domain_create_simple);
440472

@@ -476,18 +508,14 @@ struct irq_domain *irq_domain_create_legacy(struct fwnode_handle *fwnode,
476508
.fwnode = fwnode,
477509
.size = first_hwirq + size,
478510
.hwirq_max = first_hwirq + size,
511+
.hwirq_base = first_hwirq,
512+
.virq_base = first_irq,
479513
.ops = ops,
480514
.host_data = host_data,
481515
};
482-
struct irq_domain *domain;
516+
struct irq_domain *domain = irq_domain_instantiate(&info);
483517

484-
domain = irq_domain_instantiate(&info);
485-
if (IS_ERR(domain))
486-
return NULL;
487-
488-
irq_domain_associate_many(domain, first_irq, first_hwirq, size);
489-
490-
return domain;
518+
return IS_ERR(domain) ? NULL : domain;
491519
}
492520
EXPORT_SYMBOL_GPL(irq_domain_create_legacy);
493521

0 commit comments

Comments
 (0)