Re: [PATCH v5 06/10] irqchip: ti-sci-intr: Add support for Interrupt Router driver
From: Lokesh Vutla
Date: Wed Feb 20 2019 - 08:18:09 EST
Hi Marc,
On 18/02/19 9:22 PM, Marc Zyngier wrote:
> On Tue, 12 Feb 2019 13:12:33 +0530
> Lokesh Vutla <lokeshvutla@xxxxxx> wrote:
>
>> Texas Instruments' K3 generation SoCs has an IP Interrupt Router
>> that does allows for redirection of input interrupts to host
>> interrupt controller. Interrupt Router inputs are either from a
>> peripheral or from an Interrupt Aggregator which is another
>> interrupt controller.
>>
>> Configuration of the interrupt router registers can only be done by
>> a system co-processor and the driver needs to send a message to this
>> co processor over TISCI protocol.
>>
>> Add support for Interrupt Router driver over TISCI protocol.
>>
>> Signed-off-by: Lokesh Vutla <lokeshvutla@xxxxxx>
>> ---
>> Changes since v4:
>> - Moved the ti_sci irq resource handling to ti-sci-common.c from ti_sci.c.
>> Firmware maintainer rejected the idea of having this in firmware
>> driver as the resource handling is specific to the client.
>> - Obtain the number of peripheral interrupts attached to INTR by
>> parsing DT. Using this information store the max irqs that can be
>> allocated to INTA. This is done for pre allocating the INTA
>> irqs(vints) during inta driver probe.
>> This will not work for cases where there are more that 1 INTAs
>> attached to INTR, but wanted to show this approach as suggested by
>> Marc.
>
> Or not.
>
>>
>> MAINTAINERS | 3 +
>> drivers/irqchip/Kconfig | 11 +
>> drivers/irqchip/Makefile | 1 +
>> drivers/irqchip/irq-ti-sci-common.c | 131 ++++++++++++
>> drivers/irqchip/irq-ti-sci-common.h | 59 ++++++
>> drivers/irqchip/irq-ti-sci-intr.c | 315
>> ++++++++++++++++++++++++++++ 6 files changed, 520 insertions(+)
>> create mode 100644 drivers/irqchip/irq-ti-sci-common.c
>> create mode 100644 drivers/irqchip/irq-ti-sci-common.h
>> create mode 100644 drivers/irqchip/irq-ti-sci-intr.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index c918d9b2ee18..823eeb672cf0 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -15065,6 +15065,9 @@ F:
>> Documentation/devicetree/bindings/clock/ti,sci-clk.txt F:
>> drivers/clk/keystone/sci-clk.c F: drivers/reset/reset-ti-sci.c
>> F:
>> Documentation/devicetree/bindings/interrupt-controller/ti,sci-intr.txt
>> +F: drivers/irqchip/irq-ti-sci-intr.c +F:
>> drivers/irqchip/irq-ti-sci-common.c +F:
>> drivers/irqchip/irq-ti-sci-common.h
>> Texas Instruments ASoC drivers
>> M: Peter Ujfalusi <peter.ujfalusi@xxxxxx>
>> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
>> index 3d1e60779078..a8d9bed0254b 100644
>> --- a/drivers/irqchip/Kconfig
>> +++ b/drivers/irqchip/Kconfig
>> @@ -406,6 +406,17 @@ config IMX_IRQSTEER
>> help
>> Support for the i.MX IRQSTEER interrupt
>> multiplexer/remapper.
>> +config TI_SCI_INTR_IRQCHIP
>> + bool
>> + depends on TI_SCI_PROTOCOL && ARCH_K3
>> + select IRQ_DOMAIN
>> + select IRQ_DOMAIN_HIERARCHY
>> + help
>> + This enables the irqchip driver support for K3 Interrupt
>> router
>> + over TI System Control Interface available on some new
>> TI's SoCs.
>> + If you wish to use interrupt router irq resources managed
>> by the
>> + TI System Controller, say Y here. Otherwise, say N.
>> +
>> endmenu
>>
>> config SIFIVE_PLIC
>> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
>> index c93713d24b86..0499fae148a9 100644
>> --- a/drivers/irqchip/Makefile
>> +++ b/drivers/irqchip/Makefile
>> @@ -94,3 +94,4 @@ obj-$(CONFIG_CSKY_APB_INTC) +=
>> irq-csky-apb-intc.o obj-$(CONFIG_SIFIVE_PLIC) +=
>> irq-sifive-plic.o obj-$(CONFIG_IMX_IRQSTEER) +=
>> irq-imx-irqsteer.o obj-$(CONFIG_MADERA_IRQ) +=
>> irq-madera.o +obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) +=
>> irq-ti-sci-intr.o irq-ti-sci-common.o diff --git
>> a/drivers/irqchip/irq-ti-sci-common.c
>> b/drivers/irqchip/irq-ti-sci-common.c new file mode 100644 index
>> 000000000000..79d9c4e8ea14 --- /dev/null
>> +++ b/drivers/irqchip/irq-ti-sci-common.c
>> @@ -0,0 +1,131 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Common Code for TISCI IRQCHIP drivers
>> + *
>> + * Copyright (C) 2019 Texas Instruments Incorporated -
>> http://www.ti.com/
>> + * Lokesh Vutla <lokeshvutla@xxxxxx>
>> + */
>> +
>> +#include <linux/of.h>
>> +#include <linux/bitmap.h>
>> +#include <linux/device.h>
>> +#include <linux/soc/ti/ti_sci_protocol.h>
>> +#include "irq-ti-sci-common.h"
>> +
>> +/**
>> + * ti_sci_get_free_resource() - Get a free resource from TISCI
>> resource.
>> + * @res: Pointer to the TISCI resource
>> + *
>> + * Return: resource num if all went ok else TI_SCI_RESOURCE_NULL.
>> + */
>> +u16 ti_sci_get_free_resource(struct ti_sci_resource *res)
>> +{
>> + u16 set, free_bit;
>> +
>> + mutex_lock(&res->request_mutex);
>> + for (set = 0; set < res->sets; set++) {
>> + free_bit =
>> find_first_zero_bit(res->desc[set].res_map,
>> + res->desc[set].num);
>> + if (free_bit != res->desc[set].num) {
>> + set_bit(free_bit, res->desc[set].res_map);
>> + mutex_unlock(&res->request_mutex);
>> + return res->desc[set].start + free_bit;
>> + }
>> + }
>> + mutex_unlock(&res->request_mutex);
>> +
>> + return TI_SCI_RESOURCE_NULL;
>> +}
>> +
>> +/**
>> + * ti_sci_release_resource() - Release a resource from TISCI
>> resource.
>> + * @res: Pointer to the TISCI resource
>> + * @id: Resource id to be released.
>> + */
>> +void ti_sci_release_resource(struct ti_sci_resource *res, u16 id)
>> +{
>> + u16 set;
>> +
>> + mutex_lock(&res->request_mutex);
>> + for (set = 0; set < res->sets; set++) {
>> + if (res->desc[set].start <= id &&
>> + (res->desc[set].num + res->desc[set].start) > id)
>> + clear_bit(id - res->desc[set].start,
>> + res->desc[set].res_map);
>> + }
>> + mutex_unlock(&res->request_mutex);
>> +}
>> +
>> +u32 ti_sci_get_num_resources(struct ti_sci_resource *res)
>> +{
>> + u32 count = 0, set;
>> +
>> + for (set = 0; set < res->sets; set++)
>> + count += res->desc[set].num;
>> +
>> + return count;
>> +}
>> +
>> +/**
>> + * devm_ti_sci_get_of_resource() - Get a TISCI resource assigned to
>> a device
>> + * @handle: TISCI handle
>> + * @dev: Device pointer to which the resource is assigned
>> + * @of_prop: property name by which the resource are
>> represented
>> + *
>> + * Return: Pointer to ti_sci_resource if all went well else
>> appropriate
>> + * error pointer.
>> + */
>> +struct ti_sci_resource *
>> +devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle,
>> + struct device *dev, u32 dev_id, char
>> *of_prop) +{
>> + struct ti_sci_resource *res;
>> + u32 resource_subtype;
>> + int i, ret;
>> +
>> + res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL);
>> + if (!res)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + res->sets =
>> of_property_count_elems_of_size(dev_of_node(dev), of_prop,
>> + sizeof(u32));
>> + if (res->sets < 0) {
>> + dev_err(dev, "%s resource type ids not available\n",
>> of_prop);
>> + return ERR_PTR(res->sets);
>> + }
>> +
>> + res->desc = devm_kcalloc(dev, res->sets, sizeof(*res->desc),
>> + GFP_KERNEL);
>> + if (!res->desc)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + for (i = 0; i < res->sets; i++) {
>> + ret = of_property_read_u32_index(dev_of_node(dev),
>> of_prop, i,
>> + &resource_subtype);
>> + if (ret)
>> + return ERR_PTR(-EINVAL);
>> +
>> + ret = handle->ops.rm_core_ops.get_range(handle,
>> dev_id,
>> +
>> resource_subtype,
>> +
>> &res->desc[i].start,
>> +
>> &res->desc[i].num);
>> + if (ret) {
>> + dev_err(dev, "dev = %d subtype %d not
>> allocated for this host\n",
>> + dev_id, resource_subtype);
>> + return ERR_PTR(ret);
>> + }
>> +
>> + dev_dbg(dev, "dev = %d, subtype = %d, start = %d,
>> num = %d\n",
>> + dev_id, resource_subtype, res->desc[i].start,
>> + res->desc[i].num);
>> +
>> + res->desc[i].res_map =
>> + devm_kzalloc(dev,
>> BITS_TO_LONGS(res->desc[i].num) *
>> + sizeof(*res->desc[i].res_map),
>> GFP_KERNEL);
>> + if (!res->desc[i].res_map)
>> + return ERR_PTR(-ENOMEM);
>> + }
>> + mutex_init(&res->request_mutex);
>> +
>> + return res;
>> +}
>> diff --git a/drivers/irqchip/irq-ti-sci-common.h
>> b/drivers/irqchip/irq-ti-sci-common.h new file mode 100644
>> index 000000000000..60c4b28bebab
>> --- /dev/null
>> +++ b/drivers/irqchip/irq-ti-sci-common.h
>> @@ -0,0 +1,59 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Common header file for TISCI IRQCHIP drivers
>> + *
>> + * Copyright (C) 2019 Texas Instruments Incorporated -
>> http://www.ti.com/
>> + * Lokesh Vutla <lokeshvutla@xxxxxx>
>> + */
>> +
>> +#ifndef __TI_SCI_COMMON_IRQCHIP_H
>> +#define __TI_SCI_COMMON_IRQCHIP_H
>> +
>> +#include <linux/mutex.h>
>> +
>> +#define TI_SCI_RESOURCE_NULL 0xffff
>> +#define TI_SCI_DEV_ID_MASK 0xffff
>> +#define TI_SCI_DEV_ID_SHIFT 16
>> +#define TI_SCI_IRQ_ID_MASK 0xffff
>> +#define TI_SCI_IRQ_ID_SHIFT 0
>> +#define TI_SCI_VINT_IRQ BIT(0)
>> +#define HWIRQ_TO_DEVID(hwirq) (((hwirq) >>
>> (TI_SCI_DEV_ID_SHIFT)) & \
>> + (TI_SCI_DEV_ID_MASK))
>> +#define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK))
>> +#define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK)
>> << \
>> + TI_SCI_DEV_ID_SHIFT) | \
>> + ((index) & TI_SCI_IRQ_ID_MASK))
>> +
>> +/**
>> + * struct ti_sci_resource_desc - Description of TI SCI resource
>> instance range.
>> + * @start: Start index of the resource.
>> + * @num: Number of resources.
>> + * @res_map: Bitmap to manage the allocation of these
>> resources.
>> + */
>> +struct ti_sci_resource_desc {
>> + u16 start;
>> + u16 num;
>> + unsigned long *res_map;
>> +};
>> +
>> +/**
>> + * struct ti_sci_resource - Structure representing a resource
>> assigned
>> + * to a device.
>> + * @sets: Number of sets available from this resource
>> type
>> + * @request_mutex: mutex to protect request and release of
>> resources.
>> + * @desc: Array of resource descriptors.
>> + */
>> +struct ti_sci_resource {
>> + u16 sets;
>> + /* Mutex to protect request and release of resources */
>> + struct mutex request_mutex;
>> + struct ti_sci_resource_desc *desc;
>> +};
>> +
>> +u16 ti_sci_get_free_resource(struct ti_sci_resource *res);
>> +void ti_sci_release_resource(struct ti_sci_resource *res, u16 id);
>> +u32 ti_sci_get_num_resources(struct ti_sci_resource *res);
>> +struct ti_sci_resource *
>> +devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle,
>> + struct device *dev, u32 dev_id, char
>> *of_prop); +#endif /*__TI_SCI_COMMON_IRQCHIP_H */
>
> Most the above "common" should be a separate patch, really. Also, why
ok will move this into a separate patch.
> isn't the whole resource management part of the firmware interface? I
> see that "Firmware maintainer rejected the idea", but I don't really
> buy the rational. I don't see anything irq specific to the whole
> devm_ti_sci_get_of_resource and co, to be honest.
I guess the concern is mostly about the way ids are represented from DT. Using
this every tisci client should mandate to have separate DT property for each of
its resource.
>
>> diff --git a/drivers/irqchip/irq-ti-sci-intr.c b/drivers/irqchip/irq-ti-sci-intr.c
>> new file mode 100644
>> index 000000000000..7e224552a735
>> --- /dev/null
>> +++ b/drivers/irqchip/irq-ti-sci-intr.c
>> @@ -0,0 +1,315 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Texas Instruments' K3 Interrupt Router irqchip driver
>> + *
>> + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/
>> + * Lokesh Vutla <lokeshvutla@xxxxxx>
>> + */
[..snip..]
>> + err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &type);
>> + if (err)
>> + return err;
>> +
>> + vint_irq = fwspec->param[3] & TI_SCI_VINT_IRQ;
>> +
>> + err = ti_sci_intr_alloc_gic_irq(domain, virq, hwirq, type, vint_irq);
>> + if (err)
>> + return err;
>> +
>> + err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
>> + &ti_sci_intr_irq_chip,
>> + (void *)vint_irq);
>
> I think I see what you're trying to achieve. I suggest you look a
> uintptr_t instead of playing with unrelated data types whose semantic
> really doesn't apply here.
As you commented on the dt-bindings patch to differentiate vints using the INTA
ids, this will not be needed anymore. Will completely drop this.
>
>> + if (err) {
>> + ti_sci_intr_irq_domain_free(domain, virq, 1);
>> + return err;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static const struct irq_domain_ops ti_sci_intr_irq_domain_ops = {
>> + .alloc = ti_sci_intr_irq_domain_alloc,
>> + .free = ti_sci_intr_irq_domain_free,
>> + .translate = ti_sci_intr_irq_domain_translate,
>> +};
>> +
>> +static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev)
>> +{
>> + struct irq_domain *parent_domain, *domain;
>> + struct device_node *parent_node, *dn;
>> + struct ti_sci_intr_irq_domain *intr;
>> + struct device *dev = &pdev->dev;
>> + struct of_phandle_iterator it;
>> + int ret, count, intsize;
>> +
>> + parent_node = of_irq_find_parent(dev_of_node(dev));
>> + if (!parent_node) {
>> + dev_err(dev, "Failed to get IRQ parent node\n");
>> + return -ENODEV;
>> + }
>> +
>> + parent_domain = irq_find_host(parent_node);
>> + if (!parent_domain) {
>> + dev_err(dev, "Failed to find IRQ parent domain\n");
>> + return -ENODEV;
>> + }
>> +
>> + intr = devm_kzalloc(dev, sizeof(*intr), GFP_KERNEL);
>> + if (!intr)
>> + return -ENOMEM;
>> +
>> + intr->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci");
>> + if (IS_ERR(intr->sci)) {
>> + ret = PTR_ERR(intr->sci);
>> + if (ret != -EPROBE_DEFER)
>> + dev_err(dev, "ti,sci read fail %d\n", ret);
>> + intr->sci = NULL;
>> + return ret;
>> + }
>> +
>> + ret = of_property_read_u32(dev_of_node(dev), "ti,sci-dst-id",
>> + (u32 *)&intr->dst_id);
>> + if (ret) {
>> + dev_err(dev, "missing 'ti,sci-dst-id' property\n");
>> + return -EINVAL;
>> + }
>> +
>> + intr->dst_irq = devm_ti_sci_get_of_resource(intr->sci, dev,
>> + intr->dst_id,
>> + "ti,sci-rm-range-girq");
>> + if (IS_ERR(intr->dst_irq)) {
>> + dev_err(dev, "Destination irq resource allocation failed\n");
>> + return PTR_ERR(intr->dst_irq);
>> + }
>> +
>> + domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev),
>> + &ti_sci_intr_irq_domain_ops, intr);
>> + if (!domain) {
>> + dev_err(dev, "Failed to allocate IRQ domain\n");
>> + return -ENOMEM;
>> + }
>> +
>> + if (of_property_read_u32(dev_of_node(dev), "#interrupt-cells",
>> + &intsize))
>> + return -EINVAL;
>> +
>> + count = 0;
>> + for_each_node_with_property(dn, "interrupts") {
>> + if (of_irq_find_parent(dn) != dev_of_node(dev))
>> + continue;
>> + count += of_property_count_elems_of_size(dn, "interrupts",
>> + sizeof(u32) * intsize);
>> + }
>> +
>> + for_each_node_with_property(dn, "interrupts-extended") {
>> + of_for_each_phandle(&it, ret, dn, "interrupts-extended",
>> + "#interrupt-cells", 0) {
>> + if (it.node == dev_of_node(dev))
>> + count++;
>> + }
>> + }
>
> What is this trying to do? Counting the number of interrupt descriptors
> in the whole system? What does this even mean?
I am just trying for max utilization of GIC interrupts available for this. Since
inta vints are not represented in DT, total gic available - whatever is in DT
gives the max interrupts that can be used for the child INTA.
>
> What I suggested was to allocate the required number of interrupts
> for a given device, based on the requirements of that device. I also
okay, then Ill get the number of interrupts needed for INTA from DT.
> suggested to investigate the x86 two phase allocation mechanism for the
> INTA driver.
Sorry, Ill try to explore this path. Any pointers to the doc/code will be really
helpful :)
Thanks and regards,
Lokesh
>
> I never suggested that you'd parse the DT to guess how many interrupts
> you'd need to allocate...
>
> M.
>
>> +
>> + intr->vint_irqs = ti_sci_get_num_resources(intr->dst_irq) - count;
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id ti_sci_intr_irq_domain_of_match[] = {
>> + { .compatible = "ti,sci-intr", },
>> + { /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, ti_sci_intr_irq_domain_of_match);
>> +
>> +static struct platform_driver ti_sci_intr_irq_domain_driver = {
>> + .probe = ti_sci_intr_irq_domain_probe,
>> + .driver = {
>> + .name = "ti-sci-intr",
>> + .of_match_table = ti_sci_intr_irq_domain_of_match,
>> + },
>> +};
>> +module_platform_driver(ti_sci_intr_irq_domain_driver);
>> +
>> +MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>");
>> +MODULE_DESCRIPTION("K3 Interrupt Router driver over TI SCI protocol");
>> +MODULE_LICENSE("GPL v2");
>
>
>