2024-06-14 17:34:32

by Herve Codina

[permalink] [raw]
Subject: [PATCH 00/23] Introduce irq_domain_instanciate()

Hi,

Previously, I sent a series related the support for the LAN966x PCI
device [1] and in particular several patches related irq domain
modifications and the introduction of the Microchip LAN966x OIC driver.

During the review, it was asked to rework the irq domain modification in
order to avoid more wrappers and a new irq_domain_instanciate() function
was proposed [2].

Also a patch [3] sent by Maitti Vaittinen can benefit of this new
irq_domain_instanciate() function. Even if Maitti's use case is not
handle yet in this series, it should not be a big deal add support for
it on top of this current series.

So, this current series introduces this new irq_domain_instanciate()
function and migrate existing wrappers and functions to this new
function (patches 1 to 20).

It then introduces the first driver that uses directly this new function
with the init()/exit() hooks set: the Microchip LAN966x OIC driver
(patches 21 to 23).

Existing irqchip drivers are not converted yet to use this new API
function in the same way as the LAN966x OIC driver does.
I prefer to have this series accepted first to avoid doing and re-doing
several times the same modifications on existing drivers depending on
changes requested on this current series review.

[1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/lkml/8734pr5yq1.ffs@tglx/
[3] https://lore.kernel.org/lkml/bbd219c95f4fe88752aee5f21232480fe9b949fb.1717486682.git.mazziesaccount@gmail.com/

Best regards,
Hervé

Herve Codina (23):
irqdomain: Introduce irq_domain_free()
irqdomain: Introduce irq_domain_instantiate()
irqdomain: Fixed unbalanced fwnode get and put
irqdomain: Constify parameter in is_fwnode_irqchip()
irqdomain: Use a dedicated function to set the domain name
irqdomain: Convert __irq_domain_create() to use struct irq_domain_info
irqdomain: Handle additional domain flags in irq_domain_instantiate()
irqdomain: Handle domain hierarchy parent in irq_domain_instantiate()
irqdomain: Use irq_domain_instantiate() for hierarchy domain creation
irqdomain: Make __irq_domain_create() return an error code
irqdomain: Handle domain bus token in irq_domain_create()
irqdomain: Introduce init() and exit() hooks
genirq/generic_chip: Introduce
irq_domain_{alloc,remove}_generic_chips()
genirq/generic_chip: Introduce init() and exit() hooks
irqdomain: Add support for generic irq chips creation before
publishing a domain
irqdomain: Add a resource managed version of irq_domain_instantiate()
irqdomain: Convert __irq_domain_add() wrappers to
irq_domain_instantiate()
irqdomain: Convert domain creation functions to
irq_domain_instantiate()
um: virt-pci: Use irq_domain_instantiate()
irqdomain: Remove __irq_domain_add()
dt-bindings: interrupt-controller: Add support for Microchip LAN966x
OIC
irqchip: Add support for LAN966x OIC
MAINTAINERS: Add the Microchip LAN966x OIC driver entry

.../microchip,lan966x-oic.yaml | 55 ++++
MAINTAINERS | 6 +
arch/um/drivers/virt-pci.c | 16 +-
drivers/irqchip/Kconfig | 12 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-lan966x-oic.c | 278 ++++++++++++++++++
include/linux/irq.h | 33 +++
include/linux/irqdomain.h | 116 +++++++-
kernel/irq/devres.c | 41 +++
kernel/irq/generic-chip.c | 111 +++++--
kernel/irq/irqdomain.c | 243 +++++++++------
11 files changed, 782 insertions(+), 130 deletions(-)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/microchip,lan966x-oic.yaml
create mode 100644 drivers/irqchip/irq-lan966x-oic.c

--
2.45.0



2024-06-14 17:34:32

by Herve Codina

[permalink] [raw]
Subject: [PATCH 06/23] irqdomain: Convert __irq_domain_create() to use struct irq_domain_info

The existing __irq_domain_create() use a bunch of parameters to create
an irq domain.

With the introduction of irq_domain_info structure, these parameters are
available in the information structure itself.
Using directly this information structure allows future flexibility to
add other parameters in a simple way without the need to change the
__irq_domain_create() prototype.

Convert __irq_domain_create() to use the information structure.

Suggested-by: Thomas Gleixner <[email protected]>
Signed-off-by: Herve Codina <[email protected]>
---
kernel/irq/irqdomain.c | 48 +++++++++++++++++++++---------------------
1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 0b152061e63a..28a463e25d99 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -179,45 +179,40 @@ static int irq_domain_set_name(struct irq_domain *domain,
return 0;
}

-static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
- unsigned int size,
- irq_hw_number_t hwirq_max,
- int direct_max,
- const struct irq_domain_ops *ops,
- void *host_data)
+static struct irq_domain *__irq_domain_create(const struct irq_domain_info *info)
{
struct irq_domain *domain;
int err;

- if (WARN_ON((size && direct_max) ||
- (!IS_ENABLED(CONFIG_IRQ_DOMAIN_NOMAP) && direct_max) ||
- (direct_max && direct_max != hwirq_max)))
+ if (WARN_ON((info->size && info->direct_max) ||
+ (!IS_ENABLED(CONFIG_IRQ_DOMAIN_NOMAP) && info->direct_max) ||
+ (info->direct_max && info->direct_max != info->hwirq_max)))
return NULL;

- domain = kzalloc_node(struct_size(domain, revmap, size),
- GFP_KERNEL, of_node_to_nid(to_of_node(fwnode)));
+ domain = kzalloc_node(struct_size(domain, revmap, info->size),
+ GFP_KERNEL, of_node_to_nid(to_of_node(info->fwnode)));
if (!domain)
return NULL;

- err = irq_domain_set_name(domain, fwnode);
+ err = irq_domain_set_name(domain, info->fwnode);
if (err) {
kfree(domain);
return NULL;
}

- domain->fwnode = fwnode_handle_get(fwnode);
+ domain->fwnode = fwnode_handle_get(info->fwnode);
fwnode_dev_initialized(domain->fwnode, true);

/* Fill structure */
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
- domain->ops = ops;
- domain->host_data = host_data;
- domain->hwirq_max = hwirq_max;
+ domain->ops = info->ops;
+ domain->host_data = info->host_data;
+ domain->hwirq_max = info->hwirq_max;

- if (direct_max)
+ if (info->direct_max)
domain->flags |= IRQ_DOMAIN_FLAG_NO_MAP;

- domain->revmap_size = size;
+ domain->revmap_size = info->size;

/*
* Hierarchical domains use the domain lock of the root domain
@@ -264,8 +259,7 @@ struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
{
struct irq_domain *domain;

- domain = __irq_domain_create(info->fwnode, info->size, info->hwirq_max,
- info->direct_max, info->ops, info->host_data);
+ domain = __irq_domain_create(info);
if (!domain)
return ERR_PTR(-ENOMEM);

@@ -1204,13 +1198,19 @@ struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
const struct irq_domain_ops *ops,
void *host_data)
{
+ struct irq_domain_info info = {
+ .fwnode = fwnode,
+ .size = size,
+ .hwirq_max = size,
+ .ops = ops,
+ .host_data = host_data,
+ };
struct irq_domain *domain;

- if (size)
- domain = __irq_domain_create(fwnode, size, size, 0, ops, host_data);
- else
- domain = __irq_domain_create(fwnode, 0, ~0, 0, ops, host_data);
+ if (!info.size)
+ info.hwirq_max = ~0U;

+ domain = __irq_domain_create(&info);
if (domain) {
if (parent)
domain->root = parent->root;
--
2.45.0


2024-06-14 17:34:41

by Herve Codina

[permalink] [raw]
Subject: [PATCH 01/23] irqdomain: Introduce irq_domain_free()

In preparation of the introduction of the irq domain instantiation,
introduce irq_domain_free() to avoid code duplication on later
modifications.

This new function is an extraction of the current operations performed
to free the irq domain. No functional changes are introduced.

Suggested-by: Thomas Gleixner <[email protected]>
Signed-off-by: Herve Codina <[email protected]>
---
kernel/irq/irqdomain.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 86f8b91b0d3a..95eda206367f 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -241,6 +241,15 @@ static void __irq_domain_publish(struct irq_domain *domain)
pr_debug("Added domain %s\n", domain->name);
}

+static void irq_domain_free(struct irq_domain *domain)
+{
+ fwnode_dev_initialized(domain->fwnode, false);
+ fwnode_handle_put(domain->fwnode);
+ if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED)
+ kfree(domain->name);
+ kfree(domain);
+}
+
/**
* __irq_domain_add() - Allocate a new irq_domain data structure
* @fwnode: firmware node for the interrupt controller
@@ -296,12 +305,7 @@ void irq_domain_remove(struct irq_domain *domain)
mutex_unlock(&irq_domain_mutex);

pr_debug("Removed domain %s\n", domain->name);
-
- fwnode_dev_initialized(domain->fwnode, false);
- fwnode_handle_put(domain->fwnode);
- if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED)
- kfree(domain->name);
- kfree(domain);
+ irq_domain_free(domain);
}
EXPORT_SYMBOL_GPL(irq_domain_remove);

--
2.45.0


2024-06-14 17:34:48

by Herve Codina

[permalink] [raw]
Subject: [PATCH 08/23] irqdomain: Handle domain hierarchy parent in irq_domain_instantiate()

To use irq_domain_instantiate() from irq_domain_create_hierarchy(),
irq_domain_instantiate() needs to handle the domain hierarchy parent.

Handle this parent.

Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irqdomain.h | 6 ++++++
kernel/irq/irqdomain.c | 7 +++++++
2 files changed, 13 insertions(+)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 4683b66eded9..e52fd5e5494c 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -276,6 +276,12 @@ struct irq_domain_info {
int direct_max;
const struct irq_domain_ops *ops;
void *host_data;
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+ /**
+ * @parent: Pointer to the parent irq domain used in a hierarchy domain
+ */
+ struct irq_domain *parent;
+#endif
};

struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 34acc2ccfee7..7e4a1da63549 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -265,6 +265,13 @@ struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)

domain->flags |= info->domain_flags;

+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+ if (info->parent) {
+ domain->root = info->parent->root;
+ domain->parent = info->parent;
+ }
+#endif
+
__irq_domain_publish(domain);

return domain;
--
2.45.0


2024-06-14 17:35:02

by Herve Codina

[permalink] [raw]
Subject: [PATCH 07/23] irqdomain: Handle additional domain flags in irq_domain_instantiate()

In order to use irq_domain_instantiate() from several places such as
irq_domain_create_hierarchy(), irq_domain_instantiate() needs to handle
additional domain flags.

Handle these additional flags.

Suggested-by: Thomas Gleixner <[email protected]>
Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irqdomain.h | 2 ++
kernel/irq/irqdomain.c | 2 ++
2 files changed, 4 insertions(+)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index a3b43e357009..4683b66eded9 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -260,6 +260,7 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
/**
* struct irq_domain_info - Domain information structure
* @fwnode: firmware node for the interrupt controller
+ * @domain_flags: Additional flags to add to the domain flags
* @size: Size of linear map; 0 for radix mapping only
* @hwirq_max: Maximum number of interrupts supported by controller
* @direct_max: Maximum value of direct maps;
@@ -269,6 +270,7 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
*/
struct irq_domain_info {
struct fwnode_handle *fwnode;
+ unsigned int domain_flags;
unsigned int size;
irq_hw_number_t hwirq_max;
int direct_max;
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 28a463e25d99..34acc2ccfee7 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -263,6 +263,8 @@ struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
if (!domain)
return ERR_PTR(-ENOMEM);

+ domain->flags |= info->domain_flags;
+
__irq_domain_publish(domain);

return domain;
--
2.45.0


2024-06-14 17:35:25

by Herve Codina

[permalink] [raw]
Subject: [PATCH 04/23] irqdomain: Constify parameter in is_fwnode_irqchip()

The fwnode parameter has no reason to be pointer to an un-const struct
fwnode_handle. Indeed, struct fwnode_handle is not modified by the
function.

Be consistent with other function performing the same kind
of operation such as is_of_node(), is_acpi_device_node() or
is_software_node(): constify the fwnode parameter.

Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irqdomain.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index ab8939c8724d..a3b43e357009 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -314,7 +314,7 @@ static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node)

extern const struct fwnode_operations irqchip_fwnode_ops;

-static inline bool is_fwnode_irqchip(struct fwnode_handle *fwnode)
+static inline bool is_fwnode_irqchip(const struct fwnode_handle *fwnode)
{
return fwnode && fwnode->ops == &irqchip_fwnode_ops;
}
--
2.45.0


2024-06-14 17:35:31

by Herve Codina

[permalink] [raw]
Subject: [PATCH 10/23] irqdomain: Make __irq_domain_create() return an error code

__irq_domain_create() can fail for several reasons. When it fails it
returns a NULL pointer and so filters out the exact failure reason.
The only user of __irq_domain_create() is irq_domain_instantiate() which
can return a PTR_ERR value. On __irq_domain_create() failure, it uses an
arbitrary error code.

Rather than using this arbitrary error value, make __irq_domain_create()
return is own error code and use that one.

Signed-off-by: Herve Codina <[email protected]>
---
kernel/irq/irqdomain.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index edfd386be985..5090b1c572c6 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -187,17 +187,17 @@ static struct irq_domain *__irq_domain_create(const struct irq_domain_info *info
if (WARN_ON((info->size && info->direct_max) ||
(!IS_ENABLED(CONFIG_IRQ_DOMAIN_NOMAP) && info->direct_max) ||
(info->direct_max && info->direct_max != info->hwirq_max)))
- return NULL;
+ return ERR_PTR(-EINVAL);

domain = kzalloc_node(struct_size(domain, revmap, info->size),
GFP_KERNEL, of_node_to_nid(to_of_node(info->fwnode)));
if (!domain)
- return NULL;
+ return ERR_PTR(-ENOMEM);

err = irq_domain_set_name(domain, info->fwnode);
if (err) {
kfree(domain);
- return NULL;
+ return ERR_PTR(err);
}

domain->fwnode = fwnode_handle_get(info->fwnode);
@@ -260,8 +260,8 @@ struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
struct irq_domain *domain;

domain = __irq_domain_create(info);
- if (!domain)
- return ERR_PTR(-ENOMEM);
+ if (IS_ERR(domain))
+ return ERR_CAST(domain);

domain->flags |= info->domain_flags;

--
2.45.0


2024-06-14 17:36:06

by Herve Codina

[permalink] [raw]
Subject: [PATCH 11/23] irqdomain: Handle domain bus token in irq_domain_create()

irq_domain_update_bus_token() is the only way to set the domain bus
token. This is sub-optimal as irq_domain_update_bus_token() can be
called only once the domain is created and needs to revert some
operations, change the domain name and redo the operations.

In order to avoid this revert/change/redo sequence, take into account
the domain bus token during the domain creation.

Suggested-by: Thomas Gleixner <[email protected]>
Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irqdomain.h | 2 ++
kernel/irq/irqdomain.c | 30 ++++++++++++++++++++++++------
2 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index e52fd5e5494c..52bed23e5c61 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -265,6 +265,7 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
* @hwirq_max: Maximum number of interrupts supported by controller
* @direct_max: Maximum value of direct maps;
* Use ~0 for no limit; 0 for no direct mapping
+ * @bus_token: Domain bus token
* @ops: Domain operation callbacks
* @host_data: Controller private data pointer
*/
@@ -274,6 +275,7 @@ struct irq_domain_info {
unsigned int size;
irq_hw_number_t hwirq_max;
int direct_max;
+ enum irq_domain_bus_token bus_token;
const struct irq_domain_ops *ops;
void *host_data;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 5090b1c572c6..d05aeb9c0a67 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -129,7 +129,8 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode)
EXPORT_SYMBOL_GPL(irq_domain_free_fwnode);

static int irq_domain_set_name(struct irq_domain *domain,
- const struct fwnode_handle *fwnode)
+ const struct fwnode_handle *fwnode,
+ enum irq_domain_bus_token bus_token)
{
static atomic_t unknown_domains;
struct irqchip_fwid *fwid;
@@ -140,13 +141,23 @@ static int irq_domain_set_name(struct irq_domain *domain,
switch (fwid->type) {
case IRQCHIP_FWNODE_NAMED:
case IRQCHIP_FWNODE_NAMED_ID:
- domain->name = kstrdup(fwid->name, GFP_KERNEL);
+ domain->name = bus_token ?
+ kasprintf(GFP_KERNEL, "%s-%d",
+ fwid->name, bus_token) :
+ kstrdup(fwid->name, GFP_KERNEL);
if (!domain->name)
return -ENOMEM;
domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
break;
default:
domain->name = fwid->name;
+ if (bus_token) {
+ domain->name = kasprintf(GFP_KERNEL, "%s-%d",
+ fwid->name, bus_token);
+ if (!domain->name)
+ return -ENOMEM;
+ domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
+ }
break;
}
} else if (is_of_node(fwnode) || is_acpi_device_node(fwnode) ||
@@ -158,7 +169,9 @@ static int irq_domain_set_name(struct irq_domain *domain,
* unhappy about. Replace them with ':', which does
* the trick and is not as offensive as '\'...
*/
- name = kasprintf(GFP_KERNEL, "%pfw", fwnode);
+ name = bus_token ?
+ kasprintf(GFP_KERNEL, "%pfw-%d", fwnode, bus_token) :
+ kasprintf(GFP_KERNEL, "%pfw", fwnode);
if (!name)
return -ENOMEM;

@@ -169,8 +182,12 @@ static int irq_domain_set_name(struct irq_domain *domain,
if (!domain->name) {
if (fwnode)
pr_err("Invalid fwnode type for irqdomain\n");
- domain->name = kasprintf(GFP_KERNEL, "unknown-%d",
- atomic_inc_return(&unknown_domains));
+ domain->name = bus_token ?
+ kasprintf(GFP_KERNEL, "unknown-%d-%d",
+ atomic_inc_return(&unknown_domains),
+ bus_token) :
+ kasprintf(GFP_KERNEL, "unknown-%d",
+ atomic_inc_return(&unknown_domains));
if (!domain->name)
return -ENOMEM;
domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
@@ -194,7 +211,7 @@ static struct irq_domain *__irq_domain_create(const struct irq_domain_info *info
if (!domain)
return ERR_PTR(-ENOMEM);

- err = irq_domain_set_name(domain, info->fwnode);
+ err = irq_domain_set_name(domain, info->fwnode, info->bus_token);
if (err) {
kfree(domain);
return ERR_PTR(err);
@@ -207,6 +224,7 @@ static struct irq_domain *__irq_domain_create(const struct irq_domain_info *info
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
domain->ops = info->ops;
domain->host_data = info->host_data;
+ domain->bus_token = info->bus_token;
domain->hwirq_max = info->hwirq_max;

if (info->direct_max)
--
2.45.0


2024-06-14 17:36:19

by Herve Codina

[permalink] [raw]
Subject: [PATCH 05/23] irqdomain: Use a dedicated function to set the domain name

The irq domain name computation and setting is directly done in
__irq_domain_create(). This leads to a quite long __irq_domain_create()
function.

In order to simplify __irq_domain_create() and isolate the domain name
computation and setting, move the related operations to a dedicated
function.

Signed-off-by: Herve Codina <[email protected]>
---
kernel/irq/irqdomain.c | 69 +++++++++++++++++++++++-------------------
1 file changed, 38 insertions(+), 31 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 31277488ed42..0b152061e63a 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -128,27 +128,11 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode)
}
EXPORT_SYMBOL_GPL(irq_domain_free_fwnode);

-static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
- unsigned int size,
- irq_hw_number_t hwirq_max,
- int direct_max,
- const struct irq_domain_ops *ops,
- void *host_data)
+static int irq_domain_set_name(struct irq_domain *domain,
+ const struct fwnode_handle *fwnode)
{
- struct irqchip_fwid *fwid;
- struct irq_domain *domain;
-
static atomic_t unknown_domains;
-
- if (WARN_ON((size && direct_max) ||
- (!IS_ENABLED(CONFIG_IRQ_DOMAIN_NOMAP) && direct_max) ||
- (direct_max && (direct_max != hwirq_max))))
- return NULL;
-
- domain = kzalloc_node(struct_size(domain, revmap, size),
- GFP_KERNEL, of_node_to_nid(to_of_node(fwnode)));
- if (!domain)
- return NULL;
+ struct irqchip_fwid *fwid;

if (is_fwnode_irqchip(fwnode)) {
fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
@@ -157,10 +141,8 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
case IRQCHIP_FWNODE_NAMED:
case IRQCHIP_FWNODE_NAMED_ID:
domain->name = kstrdup(fwid->name, GFP_KERNEL);
- if (!domain->name) {
- kfree(domain);
- return NULL;
- }
+ if (!domain->name)
+ return -ENOMEM;
domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
break;
default:
@@ -177,10 +159,8 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
* the trick and is not as offensive as '\'...
*/
name = kasprintf(GFP_KERNEL, "%pfw", fwnode);
- if (!name) {
- kfree(domain);
- return NULL;
- }
+ if (!name)
+ return -ENOMEM;

domain->name = strreplace(name, '/', ':');
domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
@@ -191,13 +171,40 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
pr_err("Invalid fwnode type for irqdomain\n");
domain->name = kasprintf(GFP_KERNEL, "unknown-%d",
atomic_inc_return(&unknown_domains));
- if (!domain->name) {
- kfree(domain);
- return NULL;
- }
+ if (!domain->name)
+ return -ENOMEM;
domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
}

+ return 0;
+}
+
+static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
+ unsigned int size,
+ irq_hw_number_t hwirq_max,
+ int direct_max,
+ const struct irq_domain_ops *ops,
+ void *host_data)
+{
+ struct irq_domain *domain;
+ int err;
+
+ if (WARN_ON((size && direct_max) ||
+ (!IS_ENABLED(CONFIG_IRQ_DOMAIN_NOMAP) && direct_max) ||
+ (direct_max && direct_max != hwirq_max)))
+ return NULL;
+
+ domain = kzalloc_node(struct_size(domain, revmap, size),
+ GFP_KERNEL, of_node_to_nid(to_of_node(fwnode)));
+ if (!domain)
+ return NULL;
+
+ err = irq_domain_set_name(domain, fwnode);
+ if (err) {
+ kfree(domain);
+ return NULL;
+ }
+
domain->fwnode = fwnode_handle_get(fwnode);
fwnode_dev_initialized(domain->fwnode, true);

--
2.45.0


2024-06-14 17:36:27

by Herve Codina

[permalink] [raw]
Subject: [PATCH 12/23] irqdomain: Introduce init() and exit() hooks

The current API does not allow additional initialization before the
domain is published. This can lead to a race condition between consumers
and supplier as a domain can be available for consumers before being
fully ready.

Introduce the init() hook to allow additional initialization before
plublishing the domain. Also introduce the exit() hook to revert
operations done in init() on domain removal.

Suggested-by: Thomas Gleixner <[email protected]>
Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irqdomain.h | 8 ++++++++
kernel/irq/irqdomain.c | 15 +++++++++++++++
2 files changed, 23 insertions(+)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 52bed23e5c61..2c927edc4d43 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -141,6 +141,7 @@ struct irq_domain_chip_generic;
* purposes related to the irq domain.
* @parent: Pointer to parent irq_domain to support hierarchy irq_domains
* @msi_parent_ops: Pointer to MSI parent domain methods for per device domain init
+ * @exit: Function called when the domain is destroyed
*
* Revmap data, used internally by the irq domain code:
* @revmap_size: Size of the linear map table @revmap[]
@@ -169,6 +170,7 @@ struct irq_domain {
#ifdef CONFIG_GENERIC_MSI_IRQ
const struct msi_parent_ops *msi_parent_ops;
#endif
+ void (*exit)(struct irq_domain *d);

/* reverse map data. The linear map gets appended to the irq_domain */
irq_hw_number_t hwirq_max;
@@ -268,6 +270,10 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
* @bus_token: Domain bus token
* @ops: Domain operation callbacks
* @host_data: Controller private data pointer
+ * @init: Function called when the domain is created.
+ * Allow to do some additional domain initialisation.
+ * @exit: Function called when the domain is destroyed.
+ * Allow to do some additional cleanup operation.
*/
struct irq_domain_info {
struct fwnode_handle *fwnode;
@@ -284,6 +290,8 @@ struct irq_domain_info {
*/
struct irq_domain *parent;
#endif
+ int (*init)(struct irq_domain *d);
+ void (*exit)(struct irq_domain *d);
};

struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index d05aeb9c0a67..9efc9f180308 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -276,12 +276,14 @@ static void irq_domain_free(struct irq_domain *domain)
struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
{
struct irq_domain *domain;
+ int err;

domain = __irq_domain_create(info);
if (IS_ERR(domain))
return ERR_CAST(domain);

domain->flags |= info->domain_flags;
+ domain->exit = info->exit;

#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
if (info->parent) {
@@ -290,9 +292,19 @@ struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
}
#endif

+ if (info->init) {
+ err = info->init(domain);
+ if (err)
+ goto err_domain_free;
+ }
+
__irq_domain_publish(domain);

return domain;
+
+err_domain_free:
+ irq_domain_free(domain);
+ return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(irq_domain_instantiate);

@@ -339,6 +351,9 @@ EXPORT_SYMBOL_GPL(__irq_domain_add);
*/
void irq_domain_remove(struct irq_domain *domain)
{
+ if (domain->exit)
+ domain->exit(domain);
+
mutex_lock(&irq_domain_mutex);
debugfs_remove_domain_dir(domain);

--
2.45.0


2024-06-14 17:37:04

by Herve Codina

[permalink] [raw]
Subject: [PATCH 14/23] genirq/generic_chip: Introduce init() and exit() hooks

Most of generic chip drivers need to perform some more additional
initializations on the generic chips allocated before they can be fully
ready.

These additional initializations need to be performed before the IRQ
domain is published to avoid a race condition between IRQ consumers and
suppliers.

Introduce the init() hook to perform these initializations at the right
place just after the generic chip creation. Also introduce the exit()
hook to allow reververting operations done by the init() hook just
before the generic chip is destroyed.

Suggested-by: Thomas Gleixner <[email protected]>
Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irq.h | 8 ++++++++
kernel/irq/generic-chip.c | 24 ++++++++++++++++++++++--
2 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/include/linux/irq.h b/include/linux/irq.h
index 58264b236cbf..712bcee56efc 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -1106,6 +1106,7 @@ enum irq_gc_flags {
* @irq_flags_to_set: IRQ* flags to set on irq setup
* @irq_flags_to_clear: IRQ* flags to clear on irq setup
* @gc_flags: Generic chip specific setup flags
+ * @exit: Function called on each chip when they are destroyed.
* @gc: Array of pointers to generic interrupt chips
*/
struct irq_domain_chip_generic {
@@ -1114,6 +1115,7 @@ struct irq_domain_chip_generic {
unsigned int irq_flags_to_clear;
unsigned int irq_flags_to_set;
enum irq_gc_flags gc_flags;
+ void (*exit)(struct irq_chip_generic *gc);
struct irq_chip_generic *gc[];
};

@@ -1127,6 +1129,10 @@ struct irq_domain_chip_generic {
* @irq_flags_to_clear: IRQ_* bits to clear in the mapping function
* @irq_flags_to_set: IRQ_* bits to set in the mapping function
* @gc_flags: Generic chip specific setup flags
+ * @init: Function called on each chip when they are created.
+ * Allow to do some additional chip initialisation.
+ * @exit: Function called on each chip when they are destroyed.
+ * Allow to do some chip cleanup operation.
*/
struct irq_domain_chip_generic_info {
const char *name;
@@ -1136,6 +1142,8 @@ struct irq_domain_chip_generic_info {
unsigned int irq_flags_to_clear;
unsigned int irq_flags_to_set;
enum irq_gc_flags gc_flags;
+ int (*init)(struct irq_chip_generic *gc);
+ void (*exit)(struct irq_chip_generic *gc);
};

/* Generic chip callback functions */
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c
index d9696f5dcc38..32ffcbb87fa1 100644
--- a/kernel/irq/generic-chip.c
+++ b/kernel/irq/generic-chip.c
@@ -293,6 +293,7 @@ int irq_domain_alloc_generic_chips(struct irq_domain *d,
size_t gc_sz;
size_t sz;
void *tmp;
+ int ret;

if (d->gc)
return -EBUSY;
@@ -314,6 +315,7 @@ int irq_domain_alloc_generic_chips(struct irq_domain *d,
dgc->irq_flags_to_set = info->irq_flags_to_set;
dgc->irq_flags_to_clear = info->irq_flags_to_clear;
dgc->gc_flags = info->gc_flags;
+ dgc->exit = info->exit;
d->gc = dgc;

/* Calc pointer to the first generic chip */
@@ -331,6 +333,12 @@ int irq_domain_alloc_generic_chips(struct irq_domain *d,
gc->reg_writel = &irq_writel_be;
}

+ if (info->init) {
+ ret = info->init(gc);
+ if (ret)
+ goto err;
+ }
+
raw_spin_lock_irqsave(&gc_lock, flags);
list_add_tail(&gc->list, &gc_list);
raw_spin_unlock_irqrestore(&gc_lock, flags);
@@ -338,6 +346,16 @@ int irq_domain_alloc_generic_chips(struct irq_domain *d,
tmp += gc_sz;
}
return 0;
+
+err:
+ while (i--) {
+ if (dgc->exit)
+ dgc->exit(dgc->gc[i]);
+ irq_remove_generic_chip(dgc->gc[i], ~0U, 0, 0);
+ }
+ d->gc = NULL;
+ kfree(dgc);
+ return ret;
}
EXPORT_SYMBOL_GPL(irq_domain_alloc_generic_chips);

@@ -353,9 +371,11 @@ void irq_domain_remove_generic_chips(struct irq_domain *d)
if (!dgc)
return;

- for (i = 0; i < dgc->num_chips; i++)
+ for (i = 0; i < dgc->num_chips; i++) {
+ if (dgc->exit)
+ dgc->exit(dgc->gc[i]);
irq_remove_generic_chip(dgc->gc[i], ~0U, 0, 0);
-
+ }
d->gc = NULL;
kfree(dgc);
}
--
2.45.0


2024-06-14 17:37:14

by Herve Codina

[permalink] [raw]
Subject: [PATCH 02/23] irqdomain: Introduce irq_domain_instantiate()

The existing irq_domain_add_*() functions used to instantiate an IRQ
domain are wrappers built on top of __irq_domain_add() and describes the
domain properties using a bunch of parameters.

Adding more parameters and wrappers to hide new parameters in the
existing code lead to more and more code without any relevant values and
without any flexibility.

Introduce irq_domain_instantiate() where the IRQ domain properties are
given using the irq_domain_info structure instead of the bunch of
parameters to allow flexibility and easy evolution.

This new irq_domain_instantiate() perform the same operation as the one
done by __irq_domain_add(). For compatibility reason with existing code,
keep __irq_domain_add() but convert it to irq_domain_instantiate().

Suggested-by: Thomas Gleixner <[email protected]>
Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irqdomain.h | 21 +++++++++++++++++++++
kernel/irq/irqdomain.c | 39 ++++++++++++++++++++++++++++++++-------
2 files changed, 53 insertions(+), 7 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 21ecf582a0fe..ab8939c8724d 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -257,6 +257,27 @@ static inline struct fwnode_handle *irq_domain_alloc_fwnode(phys_addr_t *pa)
}

void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
+/**
+ * struct irq_domain_info - Domain information structure
+ * @fwnode: firmware node for the interrupt controller
+ * @size: Size of linear map; 0 for radix mapping only
+ * @hwirq_max: Maximum number of interrupts supported by controller
+ * @direct_max: Maximum value of direct maps;
+ * Use ~0 for no limit; 0 for no direct mapping
+ * @ops: Domain operation callbacks
+ * @host_data: Controller private data pointer
+ */
+struct irq_domain_info {
+ struct fwnode_handle *fwnode;
+ unsigned int size;
+ irq_hw_number_t hwirq_max;
+ int direct_max;
+ const struct irq_domain_ops *ops;
+ void *host_data;
+};
+
+struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info);
+
struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int size,
irq_hw_number_t hwirq_max, int direct_max,
const struct irq_domain_ops *ops,
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 95eda206367f..012ada09b419 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -250,6 +250,27 @@ static void irq_domain_free(struct irq_domain *domain)
kfree(domain);
}

+/**
+ * irq_domain_instantiate() - Instantiate a new irq domain data structure
+ * @info: Domain information pointer pointing to the information for this domain
+ *
+ * Return: A pointer to the instantiated irq domain or an ERR_PTR value.
+ */
+struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
+{
+ struct irq_domain *domain;
+
+ domain = __irq_domain_create(info->fwnode, info->size, info->hwirq_max,
+ info->direct_max, info->ops, info->host_data);
+ if (!domain)
+ return ERR_PTR(-ENOMEM);
+
+ __irq_domain_publish(domain);
+
+ return domain;
+}
+EXPORT_SYMBOL_GPL(irq_domain_instantiate);
+
/**
* __irq_domain_add() - Allocate a new irq_domain data structure
* @fwnode: firmware node for the interrupt controller
@@ -268,14 +289,18 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int s
const struct irq_domain_ops *ops,
void *host_data)
{
- struct irq_domain *domain;
-
- domain = __irq_domain_create(fwnode, size, hwirq_max, direct_max,
- ops, host_data);
- if (domain)
- __irq_domain_publish(domain);
+ struct irq_domain_info info = {
+ .fwnode = fwnode,
+ .size = size,
+ .hwirq_max = hwirq_max,
+ .direct_max = direct_max,
+ .ops = ops,
+ .host_data = host_data,
+ };
+ struct irq_domain *d;

- return domain;
+ d = irq_domain_instantiate(&info);
+ return IS_ERR(d) ? NULL : d;
}
EXPORT_SYMBOL_GPL(__irq_domain_add);

--
2.45.0


2024-06-14 17:37:17

by Herve Codina

[permalink] [raw]
Subject: [PATCH 15/23] irqdomain: Add support for generic irq chips creation before publishing a domain

The current API functions create an irq_domain and also publish this
newly created to domain. Once an irq_domain is published, consumers can
request IRQ in order to use them.

Some interrupt controller drivers have to perform some more operations
with the created irq_domain in order to have it ready to be used.
For instance:
- Allocate generic irq chips with irq_alloc_domain_generic_chips()
- Retrieve the generic irq chips with irq_get_domain_generic_chip()
- Initialize retrieved chips: set register base address and offsets,
set several hooks such as irq_mask, irq_unmask, ...

With the newly introduced irq_domain_alloc_generic_chips(), an interrupt
controller driver can use the irq_domain_chip_generic_info structure and
set the init() hook to perform its generic chips initialization.

In order to avoid a window where the domain is published but not yet
ready to be used, handle the generic chip creation (i.e the
irq_domain_alloc_generic_chips() call) before the domain is published.

Suggested-by: Thomas Gleixner <[email protected]>
Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irqdomain.h | 9 +++++++++
kernel/irq/irqdomain.c | 14 +++++++++++++-
2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 2c927edc4d43..5540b22a755c 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -210,6 +210,9 @@ enum {
/* Irq domain is a MSI device domain */
IRQ_DOMAIN_FLAG_MSI_DEVICE = (1 << 9),

+ /* Irq domain must destroy generic chips when removed */
+ IRQ_DOMAIN_FLAG_DESTROY_GC = (1 << 10),
+
/*
* Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
* for implementation specific purposes and ignored by the
@@ -259,6 +262,9 @@ static inline struct fwnode_handle *irq_domain_alloc_fwnode(phys_addr_t *pa)
}

void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
+
+struct irq_domain_chip_generic_info;
+
/**
* struct irq_domain_info - Domain information structure
* @fwnode: firmware node for the interrupt controller
@@ -270,6 +276,8 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
* @bus_token: Domain bus token
* @ops: Domain operation callbacks
* @host_data: Controller private data pointer
+ * @dgc_info: Geneneric chip information structure pointer used to
+ * create generic chips for the domain if not NULL.
* @init: Function called when the domain is created.
* Allow to do some additional domain initialisation.
* @exit: Function called when the domain is destroyed.
@@ -290,6 +298,7 @@ struct irq_domain_info {
*/
struct irq_domain *parent;
#endif
+ struct irq_domain_chip_generic_info *dgc_info;
int (*init)(struct irq_domain *d);
void (*exit)(struct irq_domain *d);
};
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 9efc9f180308..d95ca575a108 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -292,16 +292,25 @@ struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
}
#endif

+ if (info->dgc_info) {
+ err = irq_domain_alloc_generic_chips(domain, info->dgc_info);
+ if (err)
+ goto err_domain_free;
+ }
+
if (info->init) {
err = info->init(domain);
if (err)
- goto err_domain_free;
+ goto err_domain_gc_remove;
}

__irq_domain_publish(domain);

return domain;

+err_domain_gc_remove:
+ if (info->dgc_info)
+ irq_domain_remove_generic_chips(domain);
err_domain_free:
irq_domain_free(domain);
return ERR_PTR(err);
@@ -369,6 +378,9 @@ void irq_domain_remove(struct irq_domain *domain)

mutex_unlock(&irq_domain_mutex);

+ if (domain->flags & IRQ_DOMAIN_FLAG_DESTROY_GC)
+ irq_domain_remove_generic_chips(domain);
+
pr_debug("Removed domain %s\n", domain->name);
irq_domain_free(domain);
}
--
2.45.0


2024-06-14 17:37:25

by Herve Codina

[permalink] [raw]
Subject: [PATCH 09/23] irqdomain: Use irq_domain_instantiate() for hierarchy domain creation

irq_domain_instantiate() handles all needs to be used in
irq_domain_create_hierarchy()

Avoid code duplication and use directly irq_domain_instantiate() for
hierarchy domain creation.

Signed-off-by: Herve Codina <[email protected]>
---
kernel/irq/irqdomain.c | 17 +++++------------
1 file changed, 5 insertions(+), 12 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 7e4a1da63549..edfd386be985 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -1213,23 +1213,16 @@ struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
.hwirq_max = size,
.ops = ops,
.host_data = host_data,
+ .domain_flags = flags,
+ .parent = parent,
};
- struct irq_domain *domain;
+ struct irq_domain *d;

if (!info.size)
info.hwirq_max = ~0U;

- domain = __irq_domain_create(&info);
- if (domain) {
- if (parent)
- domain->root = parent->root;
- domain->parent = parent;
- domain->flags |= flags;
-
- __irq_domain_publish(domain);
- }
-
- return domain;
+ d = irq_domain_instantiate(&info);
+ return IS_ERR(d) ? NULL : d;
}
EXPORT_SYMBOL_GPL(irq_domain_create_hierarchy);

--
2.45.0


2024-06-14 17:38:20

by Herve Codina

[permalink] [raw]
Subject: [PATCH 18/23] irqdomain: Convert domain creation functions to irq_domain_instantiate()

Domain creation functions use __irq_domain_add(). With the introduction
of irq_domain_instantiate(), __irq_domain_add() becomes obsolete.

In order to fully remove __irq_domain_add(), convert domain
creation function to irq_domain_instantiate()

Signed-off-by: Herve Codina <[email protected]>
---
kernel/irq/irqdomain.c | 26 +++++++++++++++++++++-----
1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index d95ca575a108..e1ceb2ba2699 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -442,10 +442,17 @@ struct irq_domain *irq_domain_create_simple(struct fwnode_handle *fwnode,
const struct irq_domain_ops *ops,
void *host_data)
{
+ struct irq_domain_info info = {
+ .fwnode = fwnode,
+ .size = size,
+ .hwirq_max = size,
+ .ops = ops,
+ .host_data = host_data,
+ };
struct irq_domain *domain;

- domain = __irq_domain_add(fwnode, size, size, 0, ops, host_data);
- if (!domain)
+ domain = irq_domain_instantiate(&info);
+ if (IS_ERR(domain))
return NULL;

if (first_irq > 0) {
@@ -498,11 +505,20 @@ struct irq_domain *irq_domain_create_legacy(struct fwnode_handle *fwnode,
const struct irq_domain_ops *ops,
void *host_data)
{
+ struct irq_domain_info info = {
+ .fwnode = fwnode,
+ .size = first_hwirq + size,
+ .hwirq_max = first_hwirq + size,
+ .ops = ops,
+ .host_data = host_data,
+ };
struct irq_domain *domain;

- domain = __irq_domain_add(fwnode, first_hwirq + size, first_hwirq + size, 0, ops, host_data);
- if (domain)
- irq_domain_associate_many(domain, first_irq, first_hwirq, size);
+ domain = irq_domain_instantiate(&info);
+ if (IS_ERR(domain))
+ return NULL;
+
+ irq_domain_associate_many(domain, first_irq, first_hwirq, size);

return domain;
}
--
2.45.0


2024-06-14 17:38:26

by Herve Codina

[permalink] [raw]
Subject: [PATCH 19/23] um: virt-pci: Use irq_domain_instantiate()

um_pci_init() uses __irq_domain_add(). With the introduction of
irq_domain_instantiate(), __irq_domain_add() becomes obsolete.

In order to fully remove __irq_domain_add(), use directly
irq_domain_instantiate().

Signed-off-by: Herve Codina <[email protected]>
---
arch/um/drivers/virt-pci.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/arch/um/drivers/virt-pci.c b/arch/um/drivers/virt-pci.c
index 7cb503469bbd..597d89338693 100644
--- a/arch/um/drivers/virt-pci.c
+++ b/arch/um/drivers/virt-pci.c
@@ -986,6 +986,11 @@ static struct resource virt_platform_resource = {

static int __init um_pci_init(void)
{
+ struct irq_domain_info inner_domain_info = {
+ .size = MAX_MSI_VECTORS,
+ .hwirq_max = MAX_MSI_VECTORS,
+ .ops = &um_pci_inner_domain_ops,
+ };
int err, i;

WARN_ON(logic_iomem_add_region(&virt_cfgspace_resource,
@@ -1015,11 +1020,10 @@ static int __init um_pci_init(void)
goto free;
}

- um_pci_inner_domain = __irq_domain_add(um_pci_fwnode, MAX_MSI_VECTORS,
- MAX_MSI_VECTORS, 0,
- &um_pci_inner_domain_ops, NULL);
- if (!um_pci_inner_domain) {
- err = -ENOMEM;
+ inner_domain_info.fwnode = um_pci_fwnode;
+ um_pci_inner_domain = irq_domain_instantiate(&inner_domain_info);
+ if (IS_ERR(um_pci_inner_domain)) {
+ err = PTR_ERR(um_pci_inner_domain);
goto free;
}

@@ -1056,7 +1060,7 @@ static int __init um_pci_init(void)
goto free;
return 0;
free:
- if (um_pci_inner_domain)
+ if (!IS_ERR_OR_NULL(um_pci_inner_domain))
irq_domain_remove(um_pci_inner_domain);
if (um_pci_fwnode)
irq_domain_free_fwnode(um_pci_fwnode);
--
2.45.0


2024-06-14 17:39:31

by Herve Codina

[permalink] [raw]
Subject: [PATCH 23/23] MAINTAINERS: Add the Microchip LAN966x OIC driver entry

After contributing the driver, add myself as the maintainer for the
Microchip LAN966x OIC driver.

Signed-off-by: Herve Codina <[email protected]>
---
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index d6c90161c7bf..baeb307344cd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14727,6 +14727,12 @@ L: [email protected]
S: Maintained
F: drivers/net/ethernet/microchip/lan966x/*

+MICROCHIP LAN966X OIC DRIVER
+M: Herve Codina <[email protected]>
+S: Maintained
+F: Documentation/devicetree/bindings/interrupt-controller/microchip,lan966x-oic.yaml
+F: drivers/irqchip/irq-lan966x-oic.c
+
MICROCHIP LCDFB DRIVER
M: Nicolas Ferre <[email protected]>
L: [email protected]
--
2.45.0


2024-06-14 17:43:33

by Herve Codina

[permalink] [raw]
Subject: [PATCH 16/23] irqdomain: Add a resource managed version of irq_domain_instantiate()

Add a devres version of irq_domain_instantiate().

Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irqdomain.h | 2 ++
kernel/irq/devres.c | 41 +++++++++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 5540b22a755c..8820317582c4 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -304,6 +304,8 @@ struct irq_domain_info {
};

struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info);
+struct irq_domain *devm_irq_domain_instantiate(struct device *dev,
+ const struct irq_domain_info *info);

struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int size,
irq_hw_number_t hwirq_max, int direct_max,
diff --git a/kernel/irq/devres.c b/kernel/irq/devres.c
index f6e5515ee077..b3e98668f4dd 100644
--- a/kernel/irq/devres.c
+++ b/kernel/irq/devres.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
#include <linux/device.h>
#include <linux/gfp.h>
#include <linux/irq.h>
@@ -282,3 +283,43 @@ int devm_irq_setup_generic_chip(struct device *dev, struct irq_chip_generic *gc,
}
EXPORT_SYMBOL_GPL(devm_irq_setup_generic_chip);
#endif /* CONFIG_GENERIC_IRQ_CHIP */
+
+#ifdef CONFIG_IRQ_DOMAIN
+static void devm_irq_domain_remove(struct device *dev, void *res)
+{
+ struct irq_domain **domain = res;
+
+ irq_domain_remove(*domain);
+}
+
+/**
+ * devm_irq_domain_instantiate() - Instantiate a new irq domain data for a
+ * managed device.
+ * @dev: Device to instantiate the domain for
+ * @info: Domain information pointer pointing to the information for this
+ * domain
+ *
+ * Return: A pointer to the instantiated irq domain or an ERR_PTR value.
+ */
+struct irq_domain *devm_irq_domain_instantiate(struct device *dev,
+ const struct irq_domain_info *info)
+{
+ struct irq_domain *domain;
+ struct irq_domain **dr;
+
+ dr = devres_alloc(devm_irq_domain_remove, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return ERR_PTR(-ENOMEM);
+
+ domain = irq_domain_instantiate(info);
+ if (!IS_ERR(domain)) {
+ *dr = domain;
+ devres_add(dev, dr);
+ } else {
+ devres_free(dr);
+ }
+
+ return domain;
+}
+EXPORT_SYMBOL_GPL(devm_irq_domain_instantiate);
+#endif /* CONFIG_IRQ_DOMAIN */
--
2.45.0


2024-06-14 17:43:44

by Herve Codina

[permalink] [raw]
Subject: [PATCH 17/23] irqdomain: Convert __irq_domain_add() wrappers to irq_domain_instantiate()

__irq_domain_add() wrappers use directly __irq_domain_add(). With the
introduction of irq_domain_instantiate(), __irq_domain_add() becomes
obsolete.

In order to fully remove __irq_domain_add(), convert wrappers to
irq_domain_instantiate()

Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irqdomain.h | 58 +++++++++++++++++++++++++++++++++++----
1 file changed, 53 insertions(+), 5 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 8820317582c4..1cd1cbf57736 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -400,7 +400,17 @@ static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_no
const struct irq_domain_ops *ops,
void *host_data)
{
- return __irq_domain_add(of_node_to_fwnode(of_node), size, size, 0, ops, host_data);
+ struct irq_domain_info info = {
+ .fwnode = of_node_to_fwnode(of_node),
+ .size = size,
+ .hwirq_max = size,
+ .ops = ops,
+ .host_data = host_data,
+ };
+ struct irq_domain *d;
+
+ d = irq_domain_instantiate(&info);
+ return IS_ERR(d) ? NULL : d;
}

#ifdef CONFIG_IRQ_DOMAIN_NOMAP
@@ -409,7 +419,17 @@ static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_nod
const struct irq_domain_ops *ops,
void *host_data)
{
- return __irq_domain_add(of_node_to_fwnode(of_node), 0, max_irq, max_irq, ops, host_data);
+ struct irq_domain_info info = {
+ .fwnode = of_node_to_fwnode(of_node),
+ .hwirq_max = max_irq,
+ .direct_max = max_irq,
+ .ops = ops,
+ .host_data = host_data,
+ };
+ struct irq_domain *d;
+
+ d = irq_domain_instantiate(&info);
+ return IS_ERR(d) ? NULL : d;
}

extern unsigned int irq_create_direct_mapping(struct irq_domain *host);
@@ -419,7 +439,16 @@ static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node
const struct irq_domain_ops *ops,
void *host_data)
{
- return __irq_domain_add(of_node_to_fwnode(of_node), 0, ~0, 0, ops, host_data);
+ struct irq_domain_info info = {
+ .fwnode = of_node_to_fwnode(of_node),
+ .hwirq_max = ~0U,
+ .ops = ops,
+ .host_data = host_data,
+ };
+ struct irq_domain *d;
+
+ d = irq_domain_instantiate(&info);
+ return IS_ERR(d) ? NULL : d;
}

static inline struct irq_domain *irq_domain_create_linear(struct fwnode_handle *fwnode,
@@ -427,14 +456,33 @@ static inline struct irq_domain *irq_domain_create_linear(struct fwnode_handle *
const struct irq_domain_ops *ops,
void *host_data)
{
- return __irq_domain_add(fwnode, size, size, 0, ops, host_data);
+ struct irq_domain_info info = {
+ .fwnode = fwnode,
+ .size = size,
+ .hwirq_max = size,
+ .ops = ops,
+ .host_data = host_data,
+ };
+ struct irq_domain *d;
+
+ d = irq_domain_instantiate(&info);
+ return IS_ERR(d) ? NULL : d;
}

static inline struct irq_domain *irq_domain_create_tree(struct fwnode_handle *fwnode,
const struct irq_domain_ops *ops,
void *host_data)
{
- return __irq_domain_add(fwnode, 0, ~0, 0, ops, host_data);
+ struct irq_domain_info info = {
+ .fwnode = fwnode,
+ .hwirq_max = ~0,
+ .ops = ops,
+ .host_data = host_data,
+ };
+ struct irq_domain *d;
+
+ d = irq_domain_instantiate(&info);
+ return IS_ERR(d) ? NULL : d;
}

extern void irq_domain_remove(struct irq_domain *host);
--
2.45.0


2024-06-14 17:44:26

by Herve Codina

[permalink] [raw]
Subject: [PATCH 20/23] irqdomain: Remove __irq_domain_add()

__irq_domain_add() has been replaced by irq_domain_instanciate() and so,
it is no more used.

Simply remove it.

Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irqdomain.h | 6 +-----
kernel/irq/irqdomain.c | 33 ---------------------------------
2 files changed, 1 insertion(+), 38 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 1cd1cbf57736..401dca796633 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -184,7 +184,7 @@ enum {
/* Irq domain is hierarchical */
IRQ_DOMAIN_FLAG_HIERARCHY = (1 << 0),

- /* Irq domain name was allocated in __irq_domain_add() */
+ /* Irq domain name was allocated internally */
IRQ_DOMAIN_NAME_ALLOCATED = (1 << 1),

/* Irq domain is an IPI domain with virq per cpu */
@@ -307,10 +307,6 @@ struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info);
struct irq_domain *devm_irq_domain_instantiate(struct device *dev,
const struct irq_domain_info *info);

-struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int size,
- irq_hw_number_t hwirq_max, int direct_max,
- const struct irq_domain_ops *ops,
- void *host_data);
struct irq_domain *irq_domain_create_simple(struct fwnode_handle *fwnode,
unsigned int size,
unsigned int first_irq,
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index e1ceb2ba2699..53c38f951348 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -317,39 +317,6 @@ struct irq_domain *irq_domain_instantiate(const struct irq_domain_info *info)
}
EXPORT_SYMBOL_GPL(irq_domain_instantiate);

-/**
- * __irq_domain_add() - Allocate a new irq_domain data structure
- * @fwnode: firmware node for the interrupt controller
- * @size: Size of linear map; 0 for radix mapping only
- * @hwirq_max: Maximum number of interrupts supported by controller
- * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no
- * direct mapping
- * @ops: domain callbacks
- * @host_data: Controller private data pointer
- *
- * Allocates and initializes an irq_domain structure.
- * Returns pointer to IRQ domain, or NULL on failure.
- */
-struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, unsigned int size,
- irq_hw_number_t hwirq_max, int direct_max,
- const struct irq_domain_ops *ops,
- void *host_data)
-{
- struct irq_domain_info info = {
- .fwnode = fwnode,
- .size = size,
- .hwirq_max = hwirq_max,
- .direct_max = direct_max,
- .ops = ops,
- .host_data = host_data,
- };
- struct irq_domain *d;
-
- d = irq_domain_instantiate(&info);
- return IS_ERR(d) ? NULL : d;
-}
-EXPORT_SYMBOL_GPL(__irq_domain_add);
-
/**
* irq_domain_remove() - Remove an irq domain.
* @domain: domain to remove
--
2.45.0


2024-06-14 17:44:38

by Herve Codina

[permalink] [raw]
Subject: [PATCH 21/23] dt-bindings: interrupt-controller: Add support for Microchip LAN966x OIC

The Microchip LAN966x outband interrupt controller (OIC) maps the
internal interrupt sources of the LAN966x device to an external
interrupt.
When the LAN966x device is used as a PCI device, the external interrupt
is routed to the PCI interrupt.

Signed-off-by: Herve Codina <[email protected]>
Reviewed-by: Rob Herring (Arm) <[email protected]>
---
.../microchip,lan966x-oic.yaml | 55 +++++++++++++++++++
1 file changed, 55 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/microchip,lan966x-oic.yaml

diff --git a/Documentation/devicetree/bindings/interrupt-controller/microchip,lan966x-oic.yaml b/Documentation/devicetree/bindings/interrupt-controller/microchip,lan966x-oic.yaml
new file mode 100644
index 000000000000..b2adc7174177
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/microchip,lan966x-oic.yaml
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/microchip,lan966x-oic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip LAN966x outband interrupt controller
+
+maintainers:
+ - Herve Codina <[email protected]>
+
+allOf:
+ - $ref: /schemas/interrupt-controller.yaml#
+
+description: |
+ The Microchip LAN966x outband interrupt controller (OIC) maps the internal
+ interrupt sources of the LAN966x device to an external interrupt.
+ When the LAN966x device is used as a PCI device, the external interrupt is
+ routed to the PCI interrupt.
+
+properties:
+ compatible:
+ const: microchip,lan966x-oic
+
+ '#interrupt-cells':
+ const: 2
+
+ interrupt-controller: true
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - '#interrupt-cells'
+ - interrupt-controller
+ - interrupts
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ interrupt-controller@e00c0120 {
+ compatible = "microchip,lan966x-oic";
+ reg = <0xe00c0120 0x190>;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ interrupts = <0>;
+ interrupt-parent = <&intc>;
+ };
+...
--
2.45.0


2024-06-14 17:45:01

by Herve Codina

[permalink] [raw]
Subject: [PATCH 22/23] irqchip: Add support for LAN966x OIC

The Microchip LAN966x outband interrupt controller (OIC) maps the
internal interrupt sources of the LAN966x device to an external
interrupt.
When the LAN966x device is used as a PCI device, the external interrupt
is routed to the PCI interrupt.

Signed-off-by: Herve Codina <[email protected]>
---
drivers/irqchip/Kconfig | 12 ++
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-lan966x-oic.c | 278 ++++++++++++++++++++++++++++++
3 files changed, 291 insertions(+)
create mode 100644 drivers/irqchip/irq-lan966x-oic.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 14464716bacb..348f34525d23 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -169,6 +169,18 @@ config IXP4XX_IRQ
select IRQ_DOMAIN
select SPARSE_IRQ

+config LAN966X_OIC
+ tristate "Microchip LAN966x OIC Support"
+ select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+ help
+ Enable support for the LAN966x Outbound Interrupt Controller.
+ This controller is present on the Microchip LAN966x PCI device and
+ maps the internal interrupts sources to PCIe interrupt.
+
+ To compile this driver as a module, choose M here: the module
+ will be called irq-lan966x-oic.
+
config MADERA_IRQ
tristate

diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index d9dc3d99aaa8..9f6f88274bec 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o
obj-$(CONFIG_IMX_MU_MSI) += irq-imx-mu-msi.o
obj-$(CONFIG_MADERA_IRQ) += irq-madera.o
+obj-$(CONFIG_LAN966X_OIC) += irq-lan966x-oic.o
obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o
diff --git a/drivers/irqchip/irq-lan966x-oic.c b/drivers/irqchip/irq-lan966x-oic.c
new file mode 100644
index 000000000000..34a10f7615d5
--- /dev/null
+++ b/drivers/irqchip/irq-lan966x-oic.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the Microchip LAN966x outbound interrupt controller
+ *
+ * Copyright (c) 2024 Technology Inc. and its subsidiaries.
+ *
+ * Authors:
+ * Horatiu Vultur <[email protected]>
+ * Clément Léger <[email protected]>
+ * Herve Codina <[email protected]>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqchip.h>
+#include <linux/irq.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct lan966x_oic_chip_regs {
+ int reg_off_ena_set;
+ int reg_off_ena_clr;
+ int reg_off_sticky;
+ int reg_off_ident;
+ int reg_off_map;
+};
+
+struct lan966x_oic_data {
+ void __iomem *regs;
+ int irq;
+};
+
+#define LAN966X_OIC_NR_IRQ 86
+
+/* Interrupt sticky status */
+#define LAN966X_OIC_INTR_STICKY 0x30
+#define LAN966X_OIC_INTR_STICKY1 0x34
+#define LAN966X_OIC_INTR_STICKY2 0x38
+
+/* Interrupt enable */
+#define LAN966X_OIC_INTR_ENA 0x48
+#define LAN966X_OIC_INTR_ENA1 0x4c
+#define LAN966X_OIC_INTR_ENA2 0x50
+
+/* Atomic clear of interrupt enable */
+#define LAN966X_OIC_INTR_ENA_CLR 0x54
+#define LAN966X_OIC_INTR_ENA_CLR1 0x58
+#define LAN966X_OIC_INTR_ENA_CLR2 0x5c
+
+/* Atomic set of interrupt */
+#define LAN966X_OIC_INTR_ENA_SET 0x60
+#define LAN966X_OIC_INTR_ENA_SET1 0x64
+#define LAN966X_OIC_INTR_ENA_SET2 0x68
+
+/* Mapping of source to destination interrupts (_n = 0..8) */
+#define LAN966X_OIC_DST_INTR_MAP(_n) (0x78 + (_n) * 4)
+#define LAN966X_OIC_DST_INTR_MAP1(_n) (0x9c + (_n) * 4)
+#define LAN966X_OIC_DST_INTR_MAP2(_n) (0xc0 + (_n) * 4)
+
+/* Currently active interrupt sources per destination (_n = 0..8) */
+#define LAN966X_OIC_DST_INTR_IDENT(_n) (0xe4 + (_n) * 4)
+#define LAN966X_OIC_DST_INTR_IDENT1(_n) (0x108 + (_n) * 4)
+#define LAN966X_OIC_DST_INTR_IDENT2(_n) (0x12c + (_n) * 4)
+
+static unsigned int lan966x_oic_irq_startup(struct irq_data *data)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+ struct irq_chip_type *ct = irq_data_get_chip_type(data);
+ struct lan966x_oic_chip_regs *chip_regs = gc->private;
+ u32 map;
+
+ irq_gc_lock(gc);
+
+ /* Map the source interrupt to the destination */
+ map = irq_reg_readl(gc, chip_regs->reg_off_map);
+ map |= data->mask;
+ irq_reg_writel(gc, map, chip_regs->reg_off_map);
+
+ irq_gc_unlock(gc);
+
+ ct->chip.irq_ack(data);
+ ct->chip.irq_unmask(data);
+
+ return 0;
+}
+
+static void lan966x_oic_irq_shutdown(struct irq_data *data)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+ struct irq_chip_type *ct = irq_data_get_chip_type(data);
+ struct lan966x_oic_chip_regs *chip_regs = gc->private;
+ u32 map;
+
+ ct->chip.irq_mask(data);
+
+ irq_gc_lock(gc);
+
+ /* Unmap the interrupt */
+ map = irq_reg_readl(gc, chip_regs->reg_off_map);
+ map &= ~data->mask;
+ irq_reg_writel(gc, map, chip_regs->reg_off_map);
+
+ irq_gc_unlock(gc);
+}
+
+static int lan966x_oic_irq_set_type(struct irq_data *data,
+ unsigned int flow_type)
+{
+ if (flow_type != IRQ_TYPE_LEVEL_HIGH) {
+ pr_err("lan966x oic doesn't support flow type %d\n", flow_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void lan966x_oic_irq_handler_domain(struct irq_domain *d, u32 first_irq)
+{
+ struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, first_irq);
+ struct lan966x_oic_chip_regs *chip_regs = gc->private;
+ unsigned long ident;
+ unsigned int hwirq;
+
+ ident = irq_reg_readl(gc, chip_regs->reg_off_ident);
+ if (!ident)
+ return;
+
+ for_each_set_bit(hwirq, &ident, 32)
+ generic_handle_domain_irq(d, hwirq + first_irq);
+}
+
+static void lan966x_oic_irq_handler(struct irq_desc *desc)
+{
+ struct irq_domain *d = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
+ lan966x_oic_irq_handler_domain(d, 0);
+ lan966x_oic_irq_handler_domain(d, 32);
+ lan966x_oic_irq_handler_domain(d, 64);
+ chained_irq_exit(chip, desc);
+}
+
+static struct lan966x_oic_chip_regs lan966x_oic_chip_regs[3] = {
+ {
+ .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET,
+ .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR,
+ .reg_off_sticky = LAN966X_OIC_INTR_STICKY,
+ .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT(0),
+ .reg_off_map = LAN966X_OIC_DST_INTR_MAP(0),
+ }, {
+ .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET1,
+ .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR1,
+ .reg_off_sticky = LAN966X_OIC_INTR_STICKY1,
+ .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT1(0),
+ .reg_off_map = LAN966X_OIC_DST_INTR_MAP1(0),
+ }, {
+ .reg_off_ena_set = LAN966X_OIC_INTR_ENA_SET2,
+ .reg_off_ena_clr = LAN966X_OIC_INTR_ENA_CLR2,
+ .reg_off_sticky = LAN966X_OIC_INTR_STICKY2,
+ .reg_off_ident = LAN966X_OIC_DST_INTR_IDENT2(0),
+ .reg_off_map = LAN966X_OIC_DST_INTR_MAP2(0),
+ }
+};
+
+static int lan966x_oic_chip_init(struct irq_chip_generic *gc)
+{
+ struct lan966x_oic_data *lan966x_oic = gc->domain->host_data;
+ struct lan966x_oic_chip_regs *chip_regs;
+
+ chip_regs = &lan966x_oic_chip_regs[gc->irq_base / 32];
+
+ gc->reg_base = lan966x_oic->regs;
+ gc->chip_types[0].regs.enable = chip_regs->reg_off_ena_set;
+ gc->chip_types[0].regs.disable = chip_regs->reg_off_ena_clr;
+ gc->chip_types[0].regs.ack = chip_regs->reg_off_sticky;
+ gc->chip_types[0].chip.irq_startup = lan966x_oic_irq_startup;
+ gc->chip_types[0].chip.irq_shutdown = lan966x_oic_irq_shutdown;
+ gc->chip_types[0].chip.irq_set_type = lan966x_oic_irq_set_type;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->private = chip_regs;
+
+ /* Disable all interrupts handled by this chip */
+ irq_reg_writel(gc, ~0U, chip_regs->reg_off_ena_clr);
+
+ return 0;
+}
+
+static void lan966x_oic_chip_exit(struct irq_chip_generic *gc)
+{
+ /* Disable and ack all interrupts handled by this chip */
+ irq_reg_writel(gc, ~0U, gc->chip_types[0].regs.disable);
+ irq_reg_writel(gc, ~0U, gc->chip_types[0].regs.ack);
+}
+
+static int lan966x_oic_domain_init(struct irq_domain *d)
+{
+ struct lan966x_oic_data *lan966x_oic = d->host_data;
+
+ irq_set_chained_handler_and_data(lan966x_oic->irq, lan966x_oic_irq_handler, d);
+
+ return 0;
+}
+
+static void lan966x_oic_domain_exit(struct irq_domain *d)
+{
+ struct lan966x_oic_data *lan966x_oic = d->host_data;
+
+ irq_set_chained_handler_and_data(lan966x_oic->irq, NULL, NULL);
+}
+
+static int lan966x_oic_probe(struct platform_device *pdev)
+{
+ struct irq_domain_chip_generic_info dgc_info = {
+ .name = "lan966x-oic",
+ .handler = handle_level_irq,
+ .irqs_per_chip = 32,
+ .num_ct = 1,
+ .init = lan966x_oic_chip_init,
+ .exit = lan966x_oic_chip_exit,
+ };
+ struct irq_domain_info d_info = {
+ .fwnode = of_node_to_fwnode(pdev->dev.of_node),
+ .domain_flags = IRQ_DOMAIN_FLAG_DESTROY_GC,
+ .size = LAN966X_OIC_NR_IRQ,
+ .hwirq_max = LAN966X_OIC_NR_IRQ,
+ .ops = &irq_generic_chip_ops,
+ .dgc_info = &dgc_info,
+ .init = lan966x_oic_domain_init,
+ .exit = lan966x_oic_domain_exit,
+ };
+ struct lan966x_oic_data *lan966x_oic;
+ struct device *dev = &pdev->dev;
+ struct irq_domain *domain;
+
+ lan966x_oic = devm_kmalloc(dev, sizeof(*lan966x_oic), GFP_KERNEL);
+ if (!lan966x_oic)
+ return -ENOMEM;
+
+ lan966x_oic->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(lan966x_oic->regs))
+ return dev_err_probe(dev, PTR_ERR(lan966x_oic->regs),
+ "failed to map resource\n");
+
+ lan966x_oic->irq = platform_get_irq(pdev, 0);
+ if (lan966x_oic->irq < 0)
+ return dev_err_probe(dev, lan966x_oic->irq, "failed to get the IRQ\n");
+
+ d_info.host_data = lan966x_oic;
+ domain = devm_irq_domain_instantiate(dev, &d_info);
+ if (IS_ERR(domain))
+ return dev_err_probe(dev, PTR_ERR(domain),
+ "failed to instantiate the IRQ domain\n");
+ return 0;
+}
+
+static const struct of_device_id lan966x_oic_of_match[] = {
+ { .compatible = "microchip,lan966x-oic" },
+ {} /* sentinel */
+};
+MODULE_DEVICE_TABLE(of, lan966x_oic_of_match);
+
+static struct platform_driver lan966x_oic_driver = {
+ .probe = lan966x_oic_probe,
+ .driver = {
+ .name = "lan966x-oic",
+ .of_match_table = lan966x_oic_of_match,
+ },
+};
+module_platform_driver(lan966x_oic_driver);
+
+MODULE_AUTHOR("Herve Codina <[email protected]>");
+MODULE_DESCRIPTION("Microchip LAN966x OIC driver");
+MODULE_LICENSE("GPL");
--
2.45.0


2024-06-14 18:16:34

by Herve Codina

[permalink] [raw]
Subject: [PATCH 03/23] irqdomain: Fixed unbalanced fwnode get and put

fwnode_handle_get(fwnode) is called when a domain is created with fwnode
passed as a function parameter. fwnode_handle_put(domain->fwnode) is
called when the domain is destroyed but during the creation a path
exists that does not set domain->fwnode.

If this path is taken, the fwnode get will never be put.

To avoid the unbalanced get and put, set domain->fwnode unconditionally.

Fixes: d59f6617eef0 ("genirq: Allow fwnode to carry name information only")
Cc: [email protected]
Signed-off-by: Herve Codina <[email protected]>
---
kernel/irq/irqdomain.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 012ada09b419..31277488ed42 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -156,7 +156,6 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
switch (fwid->type) {
case IRQCHIP_FWNODE_NAMED:
case IRQCHIP_FWNODE_NAMED_ID:
- domain->fwnode = fwnode;
domain->name = kstrdup(fwid->name, GFP_KERNEL);
if (!domain->name) {
kfree(domain);
@@ -165,7 +164,6 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
break;
default:
- domain->fwnode = fwnode;
domain->name = fwid->name;
break;
}
@@ -185,7 +183,6 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
}

domain->name = strreplace(name, '/', ':');
- domain->fwnode = fwnode;
domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
}

@@ -201,8 +198,8 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
}

- fwnode_handle_get(fwnode);
- fwnode_dev_initialized(fwnode, true);
+ domain->fwnode = fwnode_handle_get(fwnode);
+ fwnode_dev_initialized(domain->fwnode, true);

/* Fill structure */
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
--
2.45.0


2024-06-14 18:27:28

by Herve Codina

[permalink] [raw]
Subject: [PATCH 13/23] genirq/generic_chip: Introduce irq_domain_{alloc,remove}_generic_chips()

The existing __irq_alloc_domain_generic_chips() use a bunch of
parameters to describe the generic chips that need to be allocated.

Adding more parameters and wrappers to hide new parameters in the
existing code lead to more and more code without any relevant values and
without any flexibility.

Introduce irq_domain_alloc_generic_chips() where the generic chips
description is done using the irq_domain_chip_generic_info structure
instead of the bunch of parameters to allow flexibility and easy
evolution.

Also introduce irq_domain_remove_generic_chips() to revert the
operations done by irq_domain_alloc_generic_chips().

Suggested-by: Thomas Gleixner <[email protected]>
Signed-off-by: Herve Codina <[email protected]>
---
include/linux/irq.h | 25 +++++++++++
kernel/irq/generic-chip.c | 91 +++++++++++++++++++++++++++++----------
2 files changed, 93 insertions(+), 23 deletions(-)

diff --git a/include/linux/irq.h b/include/linux/irq.h
index a217e1029c1d..58264b236cbf 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -1117,6 +1117,27 @@ struct irq_domain_chip_generic {
struct irq_chip_generic *gc[];
};

+/**
+ * struct irq_domain_chip_generic_info - Generic chip information structure
+ * @name: Name of the generic interrupt chip
+ * @handler: Interrupt handler used by the generic interrupt chip
+ * @irqs_per_chip: Number of interrupts each chip handles (max 32)
+ * @num_ct: Number of irq_chip_type instances associated with each
+ * chip
+ * @irq_flags_to_clear: IRQ_* bits to clear in the mapping function
+ * @irq_flags_to_set: IRQ_* bits to set in the mapping function
+ * @gc_flags: Generic chip specific setup flags
+ */
+struct irq_domain_chip_generic_info {
+ const char *name;
+ irq_flow_handler_t handler;
+ unsigned int irqs_per_chip;
+ unsigned int num_ct;
+ unsigned int irq_flags_to_clear;
+ unsigned int irq_flags_to_set;
+ enum irq_gc_flags gc_flags;
+};
+
/* Generic chip callback functions */
void irq_gc_noop(struct irq_data *d);
void irq_gc_mask_disable_reg(struct irq_data *d);
@@ -1153,6 +1174,10 @@ int devm_irq_setup_generic_chip(struct device *dev, struct irq_chip_generic *gc,

struct irq_chip_generic *irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq);

+int irq_domain_alloc_generic_chips(struct irq_domain *d,
+ const struct irq_domain_chip_generic_info *info);
+void irq_domain_remove_generic_chips(struct irq_domain *d);
+
int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
int num_ct, const char *name,
irq_flow_handler_t handler,
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c
index d39a40bc542b..d9696f5dcc38 100644
--- a/kernel/irq/generic-chip.c
+++ b/kernel/irq/generic-chip.c
@@ -276,21 +276,14 @@ irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags)
}

/**
- * __irq_alloc_domain_generic_chips - Allocate generic chips for an irq domain
- * @d: irq domain for which to allocate chips
- * @irqs_per_chip: Number of interrupts each chip handles (max 32)
- * @num_ct: Number of irq_chip_type instances associated with this
- * @name: Name of the irq chip
- * @handler: Default flow handler associated with these chips
- * @clr: IRQ_* bits to clear in the mapping function
- * @set: IRQ_* bits to set in the mapping function
- * @gcflags: Generic chip specific setup flags
+ * irq_domain_alloc_generic_chips - Allocate generic chips for an irq domain
+ * @d: irq domain for which to allocate chips
+ * @info: Generic chip information
+ *
+ * Return: 0 on success, negative error code on failure
*/
-int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
- int num_ct, const char *name,
- irq_flow_handler_t handler,
- unsigned int clr, unsigned int set,
- enum irq_gc_flags gcflags)
+int irq_domain_alloc_generic_chips(struct irq_domain *d,
+ const struct irq_domain_chip_generic_info *info)
{
struct irq_domain_chip_generic *dgc;
struct irq_chip_generic *gc;
@@ -304,23 +297,23 @@ int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
if (d->gc)
return -EBUSY;

- numchips = DIV_ROUND_UP(d->revmap_size, irqs_per_chip);
+ numchips = DIV_ROUND_UP(d->revmap_size, info->irqs_per_chip);
if (!numchips)
return -EINVAL;

/* Allocate a pointer, generic chip and chiptypes for each chip */
- gc_sz = struct_size(gc, chip_types, num_ct);
+ gc_sz = struct_size(gc, chip_types, info->num_ct);
dgc_sz = struct_size(dgc, gc, numchips);
sz = dgc_sz + numchips * gc_sz;

tmp = dgc = kzalloc(sz, GFP_KERNEL);
if (!dgc)
return -ENOMEM;
- dgc->irqs_per_chip = irqs_per_chip;
+ dgc->irqs_per_chip = info->irqs_per_chip;
dgc->num_chips = numchips;
- dgc->irq_flags_to_set = set;
- dgc->irq_flags_to_clear = clr;
- dgc->gc_flags = gcflags;
+ dgc->irq_flags_to_set = info->irq_flags_to_set;
+ dgc->irq_flags_to_clear = info->irq_flags_to_clear;
+ dgc->gc_flags = info->gc_flags;
d->gc = dgc;

/* Calc pointer to the first generic chip */
@@ -328,11 +321,12 @@ int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
for (i = 0; i < numchips; i++) {
/* Store the pointer to the generic chip */
dgc->gc[i] = gc = tmp;
- irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip,
- NULL, handler);
+ irq_init_generic_chip(gc, info->name, info->num_ct,
+ i * dgc->irqs_per_chip, NULL,
+ info->handler);

gc->domain = d;
- if (gcflags & IRQ_GC_BE_IO) {
+ if (dgc->gc_flags & IRQ_GC_BE_IO) {
gc->reg_readl = &irq_readl_be;
gc->reg_writel = &irq_writel_be;
}
@@ -345,6 +339,57 @@ int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
}
return 0;
}
+EXPORT_SYMBOL_GPL(irq_domain_alloc_generic_chips);
+
+/**
+ * irq_domain_remove_generic_chips - Remove generic chips from an irq domain
+ * @d: irq domain for which generic chips are to be removed
+ */
+void irq_domain_remove_generic_chips(struct irq_domain *d)
+{
+ struct irq_domain_chip_generic *dgc = d->gc;
+ unsigned int i;
+
+ if (!dgc)
+ return;
+
+ for (i = 0; i < dgc->num_chips; i++)
+ irq_remove_generic_chip(dgc->gc[i], ~0U, 0, 0);
+
+ d->gc = NULL;
+ kfree(dgc);
+}
+EXPORT_SYMBOL_GPL(irq_domain_remove_generic_chips);
+
+/**
+ * __irq_alloc_domain_generic_chips - Allocate generic chips for an irq domain
+ * @d: irq domain for which to allocate chips
+ * @irqs_per_chip: Number of interrupts each chip handles (max 32)
+ * @num_ct: Number of irq_chip_type instances associated with this
+ * @name: Name of the irq chip
+ * @handler: Default flow handler associated with these chips
+ * @clr: IRQ_* bits to clear in the mapping function
+ * @set: IRQ_* bits to set in the mapping function
+ * @gcflags: Generic chip specific setup flags
+ */
+int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
+ int num_ct, const char *name,
+ irq_flow_handler_t handler,
+ unsigned int clr, unsigned int set,
+ enum irq_gc_flags gcflags)
+{
+ struct irq_domain_chip_generic_info info = {
+ .irqs_per_chip = irqs_per_chip,
+ .num_ct = num_ct,
+ .name = name,
+ .handler = handler,
+ .irq_flags_to_clear = clr,
+ .irq_flags_to_set = set,
+ .gc_flags = gcflags,
+ };
+
+ return irq_domain_alloc_generic_chips(d, &info);
+}
EXPORT_SYMBOL_GPL(__irq_alloc_domain_generic_chips);

static struct irq_chip_generic *
--
2.45.0