[PATCH RFC v2 1/2] irqchip: irq-ti-sci-inta: Allocates VINTs at probe

Vignesh Raghavendra vigneshr at ti.com
Mon Mar 27 08:04:26 PDT 2023


Simplify driver by allocating all VINTs at probe instead of
allocating on IRQ request. This will allow dedicating few VINTs as
direct IRQs without aggregation in future.

Signed-off-by: Vignesh Raghavendra <vigneshr at ti.com>
---
 drivers/irqchip/irq-ti-sci-inta.c | 144 +++++-----
 1 file changed, 67 insertions(+), 77 deletions(-)

diff --git a/drivers/irqchip/irq-ti-sci-inta.c b/drivers/irqchip/irq-ti-sci-inta.c
index a6ecc53d055c..a3a22edbe0f0 100644
--- a/drivers/irqchip/irq-ti-sci-inta.c
+++ b/drivers/irqchip/irq-ti-sci-inta.c
@@ -96,6 +96,7 @@ struct ti_sci_inta_vint_desc {
  *			device identifier in place of the source device
  *			identifier to let sysfw know where it has to program the
  *			Global Event number.
+ * @num_vints:		Number of VINT lines allocated to this IA.
  */
 struct ti_sci_inta_irq_domain {
 	const struct ti_sci_handle *sci;
@@ -110,6 +111,7 @@ struct ti_sci_inta_irq_domain {
 
 	int unmapped_cnt;
 	u16 *unmapped_dev_ids;
+	int num_vints;
 };
 
 #define to_vint_desc(e, i) container_of(e, struct ti_sci_inta_vint_desc, \
@@ -197,12 +199,12 @@ static int ti_sci_inta_xlate_irq(struct ti_sci_inta_irq_domain *inta,
 }
 
 /**
- * ti_sci_inta_alloc_parent_irq() - Allocate parent irq to Interrupt aggregator
+ * ti_sci_inta_alloc_parent_irqs() - Allocate parent irqs to Interrupt aggregator
  * @domain:	IRQ domain corresponding to Interrupt Aggregator
  *
  * Return 0 if all went well else corresponding error value.
  */
-static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_domain *domain)
+static int ti_sci_inta_alloc_parent_irqs(struct irq_domain *domain)
 {
 	struct ti_sci_inta_irq_domain *inta = domain->host_data;
 	struct ti_sci_inta_vint_desc *vint_desc;
@@ -211,61 +213,69 @@ static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_dom
 	unsigned int parent_virq;
 	int p_hwirq, ret;
 	u16 vint_id;
+	int i;
 
-	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;
-	}
+	parent_node = of_irq_find_parent(dev_of_node(&inta->pdev->dev));
+	parent_fwspec.fwnode = of_node_to_fwnode(parent_node);
 
-	vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL);
-	if (!vint_desc) {
-		ret = -ENOMEM;
-		goto free_vint;
-	}
+	for (i = 0; i < inta->num_vints; i++) {
+		vint_id = ti_sci_get_free_resource(inta->vint);
+		if (vint_id == TI_SCI_RESOURCE_NULL)
+			break;
 
-	vint_desc->domain = domain;
-	vint_desc->vint_id = vint_id;
-	INIT_LIST_HEAD(&vint_desc->list);
+		p_hwirq = ti_sci_inta_xlate_irq(inta, vint_id);
+		if (p_hwirq < 0) {
+			ret = p_hwirq;
+			goto free_vint;
+		}
 
-	parent_node = of_irq_find_parent(dev_of_node(&inta->pdev->dev));
-	parent_fwspec.fwnode = of_node_to_fwnode(parent_node);
+		vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL);
+		if (!vint_desc) {
+			ret = -ENOMEM;
+			goto free_vint;
+		}
 
-	if (of_device_is_compatible(parent_node, "arm,gic-v3")) {
-		/* Parent is GIC */
-		parent_fwspec.param_count = 3;
-		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_count = 1;
-		parent_fwspec.param[0] = p_hwirq;
-	}
+		INIT_LIST_HEAD(&vint_desc->list);
+		vint_desc->domain = domain;
+		vint_desc->vint_id = vint_id;
+
+		if (of_device_is_compatible(parent_node, "arm,gic-v3")) {
+			/* Parent is GIC */
+			parent_fwspec.param_count = 3;
+			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_count = 1;
+			parent_fwspec.param[0] = p_hwirq;
+		}
 
-	parent_virq = irq_create_fwspec_mapping(&parent_fwspec);
-	if (parent_virq == 0) {
-		dev_err(&inta->pdev->dev, "Parent IRQ allocation failed\n");
-		ret = -EINVAL;
-		goto free_vint_desc;
+		parent_virq = irq_create_fwspec_mapping(&parent_fwspec);
+		if (parent_virq == 0) {
+			dev_dbg(&inta->pdev->dev, "Parent IRQ allocation failed\n");
+			/*
+			 * Certain versions of firmwares don't have
+			 * enough INTR lines allocated to match number
+			 * of VINTs, hence don't error out to maintain
+			 * backward compatibility.
+			 */
+			ret = 0;
+			goto free_vint_desc;
+		}
+		vint_desc->parent_virq = parent_virq;
 
+		list_add_tail(&vint_desc->list, &inta->vint_list);
+		irq_set_chained_handler_and_data(vint_desc->parent_virq,
+						 ti_sci_inta_irq_handler, vint_desc);
 	}
-	vint_desc->parent_virq = parent_virq;
 
-	list_add_tail(&vint_desc->list, &inta->vint_list);
-	irq_set_chained_handler_and_data(vint_desc->parent_virq,
-					 ti_sci_inta_irq_handler, vint_desc);
-
-	return vint_desc;
+	return 0;
 free_vint_desc:
 	kfree(vint_desc);
 free_vint:
 	ti_sci_release_resource(inta->vint, vint_id);
-	return ERR_PTR(ret);
+	return ret;
 }
 
 /**
@@ -335,22 +345,14 @@ static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_irq(struct irq_domain *d
 					       MAX_EVENTS_PER_VINT);
 		if (free_bit != MAX_EVENTS_PER_VINT) {
 			set_bit(free_bit, vint_desc->event_map);
-			goto alloc_event;
+			break;
 		}
 	}
-
-	/* No free bits available. Allocate a new vint */
-	vint_desc = ti_sci_inta_alloc_parent_irq(domain);
-	if (IS_ERR(vint_desc)) {
-		event_desc = ERR_CAST(vint_desc);
+	if (free_bit == MAX_EVENTS_PER_VINT) {
+		event_desc = NULL;
 		goto unlock;
 	}
 
-	free_bit = find_first_zero_bit(vint_desc->event_map,
-				       MAX_EVENTS_PER_VINT);
-	set_bit(free_bit, vint_desc->event_map);
-
-alloc_event:
 	event_desc = ti_sci_inta_alloc_event(vint_desc, free_bit, hwirq);
 	if (IS_ERR(event_desc))
 		clear_bit(free_bit, vint_desc->event_map);
@@ -360,22 +362,6 @@ static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_irq(struct irq_domain *d
 	return event_desc;
 }
 
-/**
- * ti_sci_inta_free_parent_irq() - Free a parent irq to INTA
- * @inta:	Pointer to inta domain.
- * @vint_desc:	Pointer to vint_desc that needs to be freed.
- */
-static void ti_sci_inta_free_parent_irq(struct ti_sci_inta_irq_domain *inta,
-					struct ti_sci_inta_vint_desc *vint_desc)
-{
-	if (find_first_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT) == MAX_EVENTS_PER_VINT) {
-		list_del(&vint_desc->list);
-		ti_sci_release_resource(inta->vint, vint_desc->vint_id);
-		irq_dispose_mapping(vint_desc->parent_virq);
-		kfree(vint_desc);
-	}
-}
-
 /**
  * ti_sci_inta_free_irq() - Free an IRQ within INTA domain
  * @event_desc:	Pointer to event_desc that needs to be freed.
@@ -405,7 +391,6 @@ static void ti_sci_inta_free_irq(struct ti_sci_inta_event_desc *event_desc,
 	event_desc->global_event = TI_SCI_RESOURCE_NULL;
 	event_desc->hwirq = 0;
 
-	ti_sci_inta_free_parent_irq(inta, vint_desc);
 	mutex_unlock(&inta->vint_mutex);
 }
 
@@ -414,8 +399,7 @@ static void ti_sci_inta_free_irq(struct ti_sci_inta_event_desc *event_desc,
  * @data: Pointer to corresponding irq_data
  *
  * Note: This is the core api where the actual allocation happens for input
- *	 hwirq. This allocation involves creating a parent irq for vint.
- *	 If this is done in irq_domain_ops.alloc() then a deadlock is reached
+ *	 hwirq. If this is done in irq_domain_ops.alloc() then a deadlock is reached
  *	 for allocation. So this allocation is being done in request_resources()
  *
  * Return: 0 if all went well else corresponding error.
@@ -437,8 +421,7 @@ static int ti_sci_inta_request_resources(struct irq_data *data)
  * ti_sci_inta_release_resources - Release resources for input irq
  * @data: Pointer to corresponding irq_data
  *
- * Note: Corresponding to request_resources(), all the unmapping and deletion
- *	 of parent vint irqs happens in this api.
+ * Note: Corresponding to request_resources(), release event mapping
  */
 static void ti_sci_inta_release_resources(struct irq_data *data)
 {
@@ -701,8 +684,9 @@ static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	inta->num_vints = ti_sci_get_num_resources(inta->vint);
 	domain = irq_domain_add_linear(dev_of_node(dev),
-				       ti_sci_get_num_resources(inta->vint),
+				       inta->num_vints,
 				       &ti_sci_inta_irq_domain_ops, inta);
 	if (!domain) {
 		dev_err(dev, "Failed to allocate IRQ domain\n");
@@ -721,6 +705,12 @@ static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev)
 	INIT_LIST_HEAD(&inta->vint_list);
 	mutex_init(&inta->vint_mutex);
 
+	ret = ti_sci_inta_alloc_parent_irqs(domain);
+	if (ret) {
+		irq_domain_remove(msi_domain);
+		irq_domain_remove(domain);
+	}
+
 	dev_info(dev, "Interrupt Aggregator domain %d created\n", inta->ti_sci_id);
 
 	return 0;

-- 
2.40.0




More information about the linux-arm-kernel mailing list