Message ID | 20200724141837.4542-10-lokeshvutla@ti.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | irqchip: ti, sci-intr/inta: Update the dt bindings to accept different interrupt parents | expand |
On Fri, 24 Jul 2020 15:18:37 +0100, Lokesh Vutla <lokeshvutla@ti.com> wrote: > > Driver assumes that Interrupt parent to Interrupt Aggregator is always > Interrupt router. This is not true always and GIC can be a parent to > Interrupt Aggregator. Update the driver to detect the parent and request > the parent irqs accordingly. > > Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> > --- > drivers/irqchip/irq-ti-sci-inta.c | 90 ++++++++++++++++++++++++++----- > 1 file changed, 77 insertions(+), 13 deletions(-) > > diff --git a/drivers/irqchip/irq-ti-sci-inta.c b/drivers/irqchip/irq-ti-sci-inta.c > index 7e3ebf6ed2cd..00c17ade118a 100644 > --- a/drivers/irqchip/irq-ti-sci-inta.c > +++ b/drivers/irqchip/irq-ti-sci-inta.c > @@ -8,6 +8,7 @@ > > #include <linux/err.h> > #include <linux/io.h> > +#include <linux/irq.h> > #include <linux/irqchip.h> > #include <linux/irqdomain.h> > #include <linux/interrupt.h> > @@ -128,6 +129,37 @@ static void ti_sci_inta_irq_handler(struct irq_desc *desc) > chained_irq_exit(irq_desc_get_chip(desc), desc); > } > > +/** > + * ti_sci_inta_xlate_irq() - Translate hwirq to parent's hwirq. > + * @inta: IRQ domain corresponding to Interrupt Aggregator > + * @irq: Hardware irq corresponding to the above irq domain > + * > + * Return parent irq number if translation is available else -ENOENT. > + */ > +static int ti_sci_inta_xlate_irq(struct ti_sci_inta_irq_domain *inta, > + u16 vint_id) > +{ > + struct device_node *np = dev_of_node(&inta->pdev->dev); > + u32 base, parent_base, size; > + const __be32 *range; > + int len; > + > + range = of_get_property(np, "ti,interrupt-ranges", &len); > + if (!range) > + return vint_id; > + > + for (len /= sizeof(*range); len >= 3; len -= 3) { > + base = be32_to_cpu(*range++); > + parent_base = be32_to_cpu(*range++); > + size = be32_to_cpu(*range++); > + > + if (base <= vint_id && vint_id < base + size) > + return vint_id - base + parent_base; > + } > + > + return -ENOENT; > +} > + > /** > * ti_sci_inta_alloc_parent_irq() - Allocate parent irq to Interrupt aggregator > * @domain: IRQ domain corresponding to Interrupt Aggregator > @@ -139,30 +171,55 @@ static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_dom > struct ti_sci_inta_irq_domain *inta = domain->host_data; > struct ti_sci_inta_vint_desc *vint_desc; > struct irq_fwspec parent_fwspec; > + struct device_node *parent_node; > unsigned int parent_virq; > - u16 vint_id; > + u16 vint_id, p_hwirq; > + int ret; > > vint_id = ti_sci_get_free_resource(inta->vint); > if (vint_id == TI_SCI_RESOURCE_NULL) > return ERR_PTR(-EINVAL); > > + p_hwirq = ti_sci_inta_xlate_irq(inta, vint_id); > + if (p_hwirq < 0) { > + ret = p_hwirq; > + goto free_vint; > + } > + > vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL); > - if (!vint_desc) > - return ERR_PTR(-ENOMEM); > + if (!vint_desc) { > + ret = -ENOMEM; > + goto free_vint; > + } > > vint_desc->domain = domain; > vint_desc->vint_id = vint_id; > INIT_LIST_HEAD(&vint_desc->list); > > - parent_fwspec.fwnode = of_node_to_fwnode(of_irq_find_parent(dev_of_node(&inta->pdev->dev))); > - parent_fwspec.param_count = 2; > - parent_fwspec.param[0] = inta->pdev->id; > - parent_fwspec.param[1] = vint_desc->vint_id; > + parent_node = of_irq_find_parent(dev_of_node(&inta->pdev->dev)); > + parent_fwspec.fwnode = of_node_to_fwnode(parent_node); > + if (of_property_read_u32(parent_node, "#interrupt-cells", > + &parent_fwspec.param_count)) { > + ret = -EINVAL; > + goto free_vint_desc; > + } > + > + if (of_device_is_compatible(parent_node, "arm,gic-v3")) { > + /* Parent is GIC */ > + parent_fwspec.param[0] = 0; > + parent_fwspec.param[1] = p_hwirq - 32; > + parent_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; Same comment as on patch #6. > + } else { > + /* Parent is Interrupt Router */ > + parent_fwspec.param[0] = p_hwirq; > + } > > parent_virq = irq_create_fwspec_mapping(&parent_fwspec); > if (parent_virq == 0) { > - kfree(vint_desc); > - return ERR_PTR(-EINVAL); > + dev_err(&inta->pdev->dev, "Parent IRQ allocation failed\n"); > + ret = -EINVAL; > + goto free_vint_desc; > + > } > vint_desc->parent_virq = parent_virq; > > @@ -171,6 +228,11 @@ static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_dom > ti_sci_inta_irq_handler, vint_desc); > > return vint_desc; > +free_vint_desc: > + kfree(vint_desc); > +free_vint: > + ti_sci_release_resource(inta->vint, vint_id); > + return ERR_PTR(ret); > } > > /** > @@ -555,15 +617,15 @@ static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) > return -EINVAL; > } > > - inta->vint = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, > - "ti,sci-rm-range-vint"); > + inta->vint = devm_ti_sci_get_resource(inta->sci, dev, pdev->id, > + TI_SCI_RESASG_SUBTYPE_IA_VINT); Same comment as on patch #6 as well, although the pdev->id hijacking already existed. I believe this should be fixed, unless you can explain why this can't possibly be implemented in a different way. > if (IS_ERR(inta->vint)) { > dev_err(dev, "VINT resource allocation failed\n"); > return PTR_ERR(inta->vint); > } > > - inta->global_event = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, > - "ti,sci-rm-range-global-event"); > + inta->global_event = devm_ti_sci_get_resource(inta->sci, dev, pdev->id, > + TI_SCI_RESASG_SUBTYPE_GLOBAL_EVENT_SEVT); > if (IS_ERR(inta->global_event)) { > dev_err(dev, "Global event resource allocation failed\n"); > return PTR_ERR(inta->global_event); > @@ -594,6 +656,8 @@ static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) > INIT_LIST_HEAD(&inta->vint_list); > mutex_init(&inta->vint_mutex); > > + dev_info(dev, "Interrupt Aggregator domain %d created\n", pdev->id); > + > return 0; > } > > -- > 2.27.0 > > Thanks, M.
diff --git a/drivers/irqchip/irq-ti-sci-inta.c b/drivers/irqchip/irq-ti-sci-inta.c index 7e3ebf6ed2cd..00c17ade118a 100644 --- a/drivers/irqchip/irq-ti-sci-inta.c +++ b/drivers/irqchip/irq-ti-sci-inta.c @@ -8,6 +8,7 @@ #include <linux/err.h> #include <linux/io.h> +#include <linux/irq.h> #include <linux/irqchip.h> #include <linux/irqdomain.h> #include <linux/interrupt.h> @@ -128,6 +129,37 @@ static void ti_sci_inta_irq_handler(struct irq_desc *desc) chained_irq_exit(irq_desc_get_chip(desc), desc); } +/** + * ti_sci_inta_xlate_irq() - Translate hwirq to parent's hwirq. + * @inta: IRQ domain corresponding to Interrupt Aggregator + * @irq: Hardware irq corresponding to the above irq domain + * + * Return parent irq number if translation is available else -ENOENT. + */ +static int ti_sci_inta_xlate_irq(struct ti_sci_inta_irq_domain *inta, + u16 vint_id) +{ + struct device_node *np = dev_of_node(&inta->pdev->dev); + u32 base, parent_base, size; + const __be32 *range; + int len; + + range = of_get_property(np, "ti,interrupt-ranges", &len); + if (!range) + return vint_id; + + for (len /= sizeof(*range); len >= 3; len -= 3) { + base = be32_to_cpu(*range++); + parent_base = be32_to_cpu(*range++); + size = be32_to_cpu(*range++); + + if (base <= vint_id && vint_id < base + size) + return vint_id - base + parent_base; + } + + return -ENOENT; +} + /** * ti_sci_inta_alloc_parent_irq() - Allocate parent irq to Interrupt aggregator * @domain: IRQ domain corresponding to Interrupt Aggregator @@ -139,30 +171,55 @@ static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_dom struct ti_sci_inta_irq_domain *inta = domain->host_data; struct ti_sci_inta_vint_desc *vint_desc; struct irq_fwspec parent_fwspec; + struct device_node *parent_node; unsigned int parent_virq; - u16 vint_id; + u16 vint_id, p_hwirq; + int ret; vint_id = ti_sci_get_free_resource(inta->vint); if (vint_id == TI_SCI_RESOURCE_NULL) return ERR_PTR(-EINVAL); + p_hwirq = ti_sci_inta_xlate_irq(inta, vint_id); + if (p_hwirq < 0) { + ret = p_hwirq; + goto free_vint; + } + vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL); - if (!vint_desc) - return ERR_PTR(-ENOMEM); + if (!vint_desc) { + ret = -ENOMEM; + goto free_vint; + } vint_desc->domain = domain; vint_desc->vint_id = vint_id; INIT_LIST_HEAD(&vint_desc->list); - parent_fwspec.fwnode = of_node_to_fwnode(of_irq_find_parent(dev_of_node(&inta->pdev->dev))); - parent_fwspec.param_count = 2; - parent_fwspec.param[0] = inta->pdev->id; - parent_fwspec.param[1] = vint_desc->vint_id; + parent_node = of_irq_find_parent(dev_of_node(&inta->pdev->dev)); + parent_fwspec.fwnode = of_node_to_fwnode(parent_node); + if (of_property_read_u32(parent_node, "#interrupt-cells", + &parent_fwspec.param_count)) { + ret = -EINVAL; + goto free_vint_desc; + } + + if (of_device_is_compatible(parent_node, "arm,gic-v3")) { + /* Parent is GIC */ + parent_fwspec.param[0] = 0; + parent_fwspec.param[1] = p_hwirq - 32; + parent_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; + } else { + /* Parent is Interrupt Router */ + parent_fwspec.param[0] = p_hwirq; + } parent_virq = irq_create_fwspec_mapping(&parent_fwspec); if (parent_virq == 0) { - kfree(vint_desc); - return ERR_PTR(-EINVAL); + dev_err(&inta->pdev->dev, "Parent IRQ allocation failed\n"); + ret = -EINVAL; + goto free_vint_desc; + } vint_desc->parent_virq = parent_virq; @@ -171,6 +228,11 @@ static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_dom ti_sci_inta_irq_handler, vint_desc); return vint_desc; +free_vint_desc: + kfree(vint_desc); +free_vint: + ti_sci_release_resource(inta->vint, vint_id); + return ERR_PTR(ret); } /** @@ -555,15 +617,15 @@ static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) return -EINVAL; } - inta->vint = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, - "ti,sci-rm-range-vint"); + inta->vint = devm_ti_sci_get_resource(inta->sci, dev, pdev->id, + TI_SCI_RESASG_SUBTYPE_IA_VINT); if (IS_ERR(inta->vint)) { dev_err(dev, "VINT resource allocation failed\n"); return PTR_ERR(inta->vint); } - inta->global_event = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, - "ti,sci-rm-range-global-event"); + inta->global_event = devm_ti_sci_get_resource(inta->sci, dev, pdev->id, + TI_SCI_RESASG_SUBTYPE_GLOBAL_EVENT_SEVT); if (IS_ERR(inta->global_event)) { dev_err(dev, "Global event resource allocation failed\n"); return PTR_ERR(inta->global_event); @@ -594,6 +656,8 @@ static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) INIT_LIST_HEAD(&inta->vint_list); mutex_init(&inta->vint_mutex); + dev_info(dev, "Interrupt Aggregator domain %d created\n", pdev->id); + return 0; }
Driver assumes that Interrupt parent to Interrupt Aggregator is always Interrupt router. This is not true always and GIC can be a parent to Interrupt Aggregator. Update the driver to detect the parent and request the parent irqs accordingly. Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> --- drivers/irqchip/irq-ti-sci-inta.c | 90 ++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 13 deletions(-)