|
@@ -27,6 +27,57 @@ static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
|
|
|
irq_hw_number_t hwirq, int node);
|
|
|
static void irq_domain_check_hierarchy(struct irq_domain *domain);
|
|
|
|
|
|
+struct irqchip_fwid {
|
|
|
+ struct fwnode_handle fwnode;
|
|
|
+ char *name;
|
|
|
+ void *data;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * irq_domain_alloc_fwnode - Allocate a fwnode_handle suitable for
|
|
|
+ * identifying an irq domain
|
|
|
+ * @data: optional user-provided data
|
|
|
+ *
|
|
|
+ * Allocate a struct device_node, and return a poiner to the embedded
|
|
|
+ * fwnode_handle (or NULL on failure).
|
|
|
+ */
|
|
|
+struct fwnode_handle *irq_domain_alloc_fwnode(void *data)
|
|
|
+{
|
|
|
+ struct irqchip_fwid *fwid;
|
|
|
+ char *name;
|
|
|
+
|
|
|
+ fwid = kzalloc(sizeof(*fwid), GFP_KERNEL);
|
|
|
+ name = kasprintf(GFP_KERNEL, "irqchip@%p", data);
|
|
|
+
|
|
|
+ if (!fwid || !name) {
|
|
|
+ kfree(fwid);
|
|
|
+ kfree(name);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ fwid->name = name;
|
|
|
+ fwid->data = data;
|
|
|
+ fwid->fwnode.type = FWNODE_IRQCHIP;
|
|
|
+ return &fwid->fwnode;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * irq_domain_free_fwnode - Free a non-OF-backed fwnode_handle
|
|
|
+ *
|
|
|
+ * Free a fwnode_handle allocated with irq_domain_alloc_fwnode.
|
|
|
+ */
|
|
|
+void irq_domain_free_fwnode(struct fwnode_handle *fwnode)
|
|
|
+{
|
|
|
+ struct irqchip_fwid *fwid;
|
|
|
+
|
|
|
+ if (WARN_ON(fwnode->type != FWNODE_IRQCHIP))
|
|
|
+ return;
|
|
|
+
|
|
|
+ fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
|
|
|
+ kfree(fwid->name);
|
|
|
+ kfree(fwid);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* __irq_domain_add() - Allocate a new irq_domain data structure
|
|
|
* @of_node: optional device-tree node of the interrupt controller
|
|
@@ -40,23 +91,28 @@ static void irq_domain_check_hierarchy(struct irq_domain *domain);
|
|
|
* Allocates and initialize and irq_domain structure.
|
|
|
* Returns pointer to IRQ domain, or NULL on failure.
|
|
|
*/
|
|
|
-struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
|
|
|
+struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
|
|
|
irq_hw_number_t hwirq_max, int direct_max,
|
|
|
const struct irq_domain_ops *ops,
|
|
|
void *host_data)
|
|
|
{
|
|
|
struct irq_domain *domain;
|
|
|
+ struct device_node *of_node;
|
|
|
+
|
|
|
+ of_node = to_of_node(fwnode);
|
|
|
|
|
|
domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
|
|
|
GFP_KERNEL, of_node_to_nid(of_node));
|
|
|
if (WARN_ON(!domain))
|
|
|
return NULL;
|
|
|
|
|
|
+ of_node_get(of_node);
|
|
|
+
|
|
|
/* Fill structure */
|
|
|
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
|
|
|
domain->ops = ops;
|
|
|
domain->host_data = host_data;
|
|
|
- domain->of_node = of_node_get(of_node);
|
|
|
+ domain->fwnode = fwnode;
|
|
|
domain->hwirq_max = hwirq_max;
|
|
|
domain->revmap_size = size;
|
|
|
domain->revmap_direct_max_irq = direct_max;
|
|
@@ -102,7 +158,7 @@ void irq_domain_remove(struct irq_domain *domain)
|
|
|
|
|
|
pr_debug("Removed domain %s\n", domain->name);
|
|
|
|
|
|
- of_node_put(domain->of_node);
|
|
|
+ of_node_put(irq_domain_get_of_node(domain));
|
|
|
kfree(domain);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(irq_domain_remove);
|
|
@@ -133,7 +189,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
|
|
|
{
|
|
|
struct irq_domain *domain;
|
|
|
|
|
|
- domain = __irq_domain_add(of_node, size, size, 0, ops, host_data);
|
|
|
+ domain = __irq_domain_add(of_node_to_fwnode(of_node), size, size, 0, ops, host_data);
|
|
|
if (!domain)
|
|
|
return NULL;
|
|
|
|
|
@@ -177,7 +233,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
|
|
|
{
|
|
|
struct irq_domain *domain;
|
|
|
|
|
|
- domain = __irq_domain_add(of_node, first_hwirq + size,
|
|
|
+ domain = __irq_domain_add(of_node_to_fwnode(of_node), first_hwirq + size,
|
|
|
first_hwirq + size, 0, ops, host_data);
|
|
|
if (domain)
|
|
|
irq_domain_associate_many(domain, first_irq, first_hwirq, size);
|
|
@@ -187,12 +243,12 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
|
|
|
EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
|
|
|
|
|
|
/**
|
|
|
- * irq_find_matching_host() - Locates a domain for a given device node
|
|
|
- * @node: device-tree node of the interrupt controller
|
|
|
+ * irq_find_matching_fwnode() - Locates a domain for a given fwnode
|
|
|
+ * @fwnode: FW descriptor of the interrupt controller
|
|
|
* @bus_token: domain-specific data
|
|
|
*/
|
|
|
-struct irq_domain *irq_find_matching_host(struct device_node *node,
|
|
|
- enum irq_domain_bus_token bus_token)
|
|
|
+struct irq_domain *irq_find_matching_fwnode(struct fwnode_handle *fwnode,
|
|
|
+ enum irq_domain_bus_token bus_token)
|
|
|
{
|
|
|
struct irq_domain *h, *found = NULL;
|
|
|
int rc;
|
|
@@ -209,9 +265,9 @@ struct irq_domain *irq_find_matching_host(struct device_node *node,
|
|
|
mutex_lock(&irq_domain_mutex);
|
|
|
list_for_each_entry(h, &irq_domain_list, link) {
|
|
|
if (h->ops->match)
|
|
|
- rc = h->ops->match(h, node, bus_token);
|
|
|
+ rc = h->ops->match(h, to_of_node(fwnode), bus_token);
|
|
|
else
|
|
|
- rc = ((h->of_node != NULL) && (h->of_node == node) &&
|
|
|
+ rc = ((fwnode != NULL) && (h->fwnode == fwnode) &&
|
|
|
((bus_token == DOMAIN_BUS_ANY) ||
|
|
|
(h->bus_token == bus_token)));
|
|
|
|
|
@@ -223,7 +279,7 @@ struct irq_domain *irq_find_matching_host(struct device_node *node,
|
|
|
mutex_unlock(&irq_domain_mutex);
|
|
|
return found;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(irq_find_matching_host);
|
|
|
+EXPORT_SYMBOL_GPL(irq_find_matching_fwnode);
|
|
|
|
|
|
/**
|
|
|
* irq_set_default_host() - Set a "default" irq domain
|
|
@@ -336,10 +392,12 @@ EXPORT_SYMBOL_GPL(irq_domain_associate);
|
|
|
void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
|
|
|
irq_hw_number_t hwirq_base, int count)
|
|
|
{
|
|
|
+ struct device_node *of_node;
|
|
|
int i;
|
|
|
|
|
|
+ of_node = irq_domain_get_of_node(domain);
|
|
|
pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
|
|
|
- of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
|
|
|
+ of_node_full_name(of_node), irq_base, (int)hwirq_base, count);
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
irq_domain_associate(domain, irq_base + i, hwirq_base + i);
|
|
@@ -359,12 +417,14 @@ EXPORT_SYMBOL_GPL(irq_domain_associate_many);
|
|
|
*/
|
|
|
unsigned int irq_create_direct_mapping(struct irq_domain *domain)
|
|
|
{
|
|
|
+ struct device_node *of_node;
|
|
|
unsigned int virq;
|
|
|
|
|
|
if (domain == NULL)
|
|
|
domain = irq_default_domain;
|
|
|
|
|
|
- virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
|
|
|
+ of_node = irq_domain_get_of_node(domain);
|
|
|
+ virq = irq_alloc_desc_from(1, of_node_to_nid(of_node));
|
|
|
if (!virq) {
|
|
|
pr_debug("create_direct virq allocation failed\n");
|
|
|
return 0;
|
|
@@ -399,6 +459,7 @@ EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
|
|
|
unsigned int irq_create_mapping(struct irq_domain *domain,
|
|
|
irq_hw_number_t hwirq)
|
|
|
{
|
|
|
+ struct device_node *of_node;
|
|
|
int virq;
|
|
|
|
|
|
pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
|
|
@@ -412,6 +473,8 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
|
|
|
}
|
|
|
pr_debug("-> using domain @%p\n", domain);
|
|
|
|
|
|
+ of_node = irq_domain_get_of_node(domain);
|
|
|
+
|
|
|
/* Check if mapping already exists */
|
|
|
virq = irq_find_mapping(domain, hwirq);
|
|
|
if (virq) {
|
|
@@ -420,8 +483,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
|
|
|
}
|
|
|
|
|
|
/* Allocate a virtual interrupt number */
|
|
|
- virq = irq_domain_alloc_descs(-1, 1, hwirq,
|
|
|
- of_node_to_nid(domain->of_node));
|
|
|
+ virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node));
|
|
|
if (virq <= 0) {
|
|
|
pr_debug("-> virq allocation failed\n");
|
|
|
return 0;
|
|
@@ -433,7 +495,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
|
|
|
}
|
|
|
|
|
|
pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
|
|
|
- hwirq, of_node_full_name(domain->of_node), virq);
|
|
|
+ hwirq, of_node_full_name(of_node), virq);
|
|
|
|
|
|
return virq;
|
|
|
}
|
|
@@ -460,10 +522,12 @@ EXPORT_SYMBOL_GPL(irq_create_mapping);
|
|
|
int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
|
|
|
irq_hw_number_t hwirq_base, int count)
|
|
|
{
|
|
|
+ struct device_node *of_node;
|
|
|
int ret;
|
|
|
|
|
|
+ of_node = irq_domain_get_of_node(domain);
|
|
|
ret = irq_alloc_descs(irq_base, irq_base, count,
|
|
|
- of_node_to_nid(domain->of_node));
|
|
|
+ of_node_to_nid(of_node));
|
|
|
if (unlikely(ret < 0))
|
|
|
return ret;
|
|
|
|
|
@@ -472,28 +536,56 @@ int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
|
|
|
|
|
|
-unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
|
|
|
+static int irq_domain_translate(struct irq_domain *d,
|
|
|
+ struct irq_fwspec *fwspec,
|
|
|
+ irq_hw_number_t *hwirq, unsigned int *type)
|
|
|
+{
|
|
|
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
|
|
+ if (d->ops->translate)
|
|
|
+ return d->ops->translate(d, fwspec, hwirq, type);
|
|
|
+#endif
|
|
|
+ if (d->ops->xlate)
|
|
|
+ return d->ops->xlate(d, to_of_node(fwspec->fwnode),
|
|
|
+ fwspec->param, fwspec->param_count,
|
|
|
+ hwirq, type);
|
|
|
+
|
|
|
+ /* If domain has no translation, then we assume interrupt line */
|
|
|
+ *hwirq = fwspec->param[0];
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void of_phandle_args_to_fwspec(struct of_phandle_args *irq_data,
|
|
|
+ struct irq_fwspec *fwspec)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ fwspec->fwnode = irq_data->np ? &irq_data->np->fwnode : NULL;
|
|
|
+ fwspec->param_count = irq_data->args_count;
|
|
|
+
|
|
|
+ for (i = 0; i < irq_data->args_count; i++)
|
|
|
+ fwspec->param[i] = irq_data->args[i];
|
|
|
+}
|
|
|
+
|
|
|
+unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
|
|
|
{
|
|
|
struct irq_domain *domain;
|
|
|
irq_hw_number_t hwirq;
|
|
|
unsigned int type = IRQ_TYPE_NONE;
|
|
|
int virq;
|
|
|
|
|
|
- domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
|
|
|
+ if (fwspec->fwnode)
|
|
|
+ domain = irq_find_matching_fwnode(fwspec->fwnode, DOMAIN_BUS_ANY);
|
|
|
+ else
|
|
|
+ domain = irq_default_domain;
|
|
|
+
|
|
|
if (!domain) {
|
|
|
pr_warn("no irq domain found for %s !\n",
|
|
|
- of_node_full_name(irq_data->np));
|
|
|
+ of_node_full_name(to_of_node(fwspec->fwnode)));
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- /* If domain has no translation, then we assume interrupt line */
|
|
|
- if (domain->ops->xlate == NULL)
|
|
|
- hwirq = irq_data->args[0];
|
|
|
- else {
|
|
|
- if (domain->ops->xlate(domain, irq_data->np, irq_data->args,
|
|
|
- irq_data->args_count, &hwirq, &type))
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ if (irq_domain_translate(domain, fwspec, &hwirq, &type))
|
|
|
+ return 0;
|
|
|
|
|
|
if (irq_domain_is_hierarchy(domain)) {
|
|
|
/*
|
|
@@ -504,7 +596,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
|
|
|
if (virq)
|
|
|
return virq;
|
|
|
|
|
|
- virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data);
|
|
|
+ virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);
|
|
|
if (virq <= 0)
|
|
|
return 0;
|
|
|
} else {
|
|
@@ -520,6 +612,15 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
|
|
|
irq_set_irq_type(virq, type);
|
|
|
return virq;
|
|
|
}
|
|
|
+EXPORT_SYMBOL_GPL(irq_create_fwspec_mapping);
|
|
|
+
|
|
|
+unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
|
|
|
+{
|
|
|
+ struct irq_fwspec fwspec;
|
|
|
+
|
|
|
+ of_phandle_args_to_fwspec(irq_data, &fwspec);
|
|
|
+ return irq_create_fwspec_mapping(&fwspec);
|
|
|
+}
|
|
|
EXPORT_SYMBOL_GPL(irq_create_of_mapping);
|
|
|
|
|
|
/**
|
|
@@ -590,14 +691,16 @@ static int virq_debug_show(struct seq_file *m, void *private)
|
|
|
"name", "mapped", "linear-max", "direct-max", "devtree-node");
|
|
|
mutex_lock(&irq_domain_mutex);
|
|
|
list_for_each_entry(domain, &irq_domain_list, link) {
|
|
|
+ struct device_node *of_node;
|
|
|
int count = 0;
|
|
|
+ of_node = irq_domain_get_of_node(domain);
|
|
|
radix_tree_for_each_slot(slot, &domain->revmap_tree, &iter, 0)
|
|
|
count++;
|
|
|
seq_printf(m, "%c%-16s %6u %10u %10u %s\n",
|
|
|
domain == irq_default_domain ? '*' : ' ', domain->name,
|
|
|
domain->revmap_size + count, domain->revmap_size,
|
|
|
domain->revmap_direct_max_irq,
|
|
|
- domain->of_node ? of_node_full_name(domain->of_node) : "");
|
|
|
+ of_node ? of_node_full_name(of_node) : "");
|
|
|
}
|
|
|
mutex_unlock(&irq_domain_mutex);
|
|
|
|
|
@@ -751,11 +854,11 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt,
|
|
|
|
|
|
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
|
|
/**
|
|
|
- * irq_domain_add_hierarchy - Add a irqdomain into the hierarchy
|
|
|
+ * irq_domain_create_hierarchy - Add a irqdomain into the hierarchy
|
|
|
* @parent: Parent irq domain to associate with the new domain
|
|
|
* @flags: Irq domain flags associated to the domain
|
|
|
* @size: Size of the domain. See below
|
|
|
- * @node: Optional device-tree node of the interrupt controller
|
|
|
+ * @fwnode: Optional fwnode of the interrupt controller
|
|
|
* @ops: Pointer to the interrupt domain callbacks
|
|
|
* @host_data: Controller private data pointer
|
|
|
*
|
|
@@ -765,19 +868,19 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt,
|
|
|
* domain flags are set.
|
|
|
* Returns pointer to IRQ domain, or NULL on failure.
|
|
|
*/
|
|
|
-struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
|
|
|
+struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
|
|
|
unsigned int flags,
|
|
|
unsigned int size,
|
|
|
- struct device_node *node,
|
|
|
+ struct fwnode_handle *fwnode,
|
|
|
const struct irq_domain_ops *ops,
|
|
|
void *host_data)
|
|
|
{
|
|
|
struct irq_domain *domain;
|
|
|
|
|
|
if (size)
|
|
|
- domain = irq_domain_add_linear(node, size, ops, host_data);
|
|
|
+ domain = irq_domain_create_linear(fwnode, size, ops, host_data);
|
|
|
else
|
|
|
- domain = irq_domain_add_tree(node, ops, host_data);
|
|
|
+ domain = irq_domain_create_tree(fwnode, ops, host_data);
|
|
|
if (domain) {
|
|
|
domain->parent = parent;
|
|
|
domain->flags |= flags;
|