[PATCH] irqchip/ti-sci-intr: Add support for system suspend/resume PM

From: Aswath Govindraju
Date: Tue Jun 07 2022 - 02:19:44 EST


Add support for system level suspend/resume power management. The
interrupt mappings are stored in an array and restored in the system level
resume routine. Struct ti_sci_resource_desc can have atmost 2 sets for
ranges. Therefore, the mapping array is also formatted such that it can
store two sets of ranges.

Signed-off-by: Aswath Govindraju <a-govindraju@xxxxxx>
---
drivers/irqchip/irq-ti-sci-intr.c | 108 ++++++++++++++++++++++++++++++
1 file changed, 108 insertions(+)

diff --git a/drivers/irqchip/irq-ti-sci-intr.c b/drivers/irqchip/irq-ti-sci-intr.c
index fe8fad22bcf9..a8fc6cfb96ca 100644
--- a/drivers/irqchip/irq-ti-sci-intr.c
+++ b/drivers/irqchip/irq-ti-sci-intr.c
@@ -25,6 +25,7 @@
* @dev: Struct device pointer.
* @ti_sci_id: TI-SCI device identifier
* @type: Specifies the trigger type supported by this Interrupt Router
+ * @mapping: Pointer to out_irq <-> hwirq mapping table
*/
struct ti_sci_intr_irq_domain {
const struct ti_sci_handle *sci;
@@ -32,6 +33,7 @@ struct ti_sci_intr_irq_domain {
struct device *dev;
u32 ti_sci_id;
u32 type;
+ u32 *mapping;
};

static struct irq_chip ti_sci_intr_irq_chip = {
@@ -99,6 +101,23 @@ static int ti_sci_intr_xlate_irq(struct ti_sci_intr_irq_domain *intr, u32 irq)
return -ENOENT;
}

+/**
+ * ti_sci_intr_free_irq - Free the irq entry in the out_irq <-> hwirq mapping table
+ * @intr: IRQ domain corresponding to Interrupt Router
+ * @out_irq: Out irq number
+ */
+static void ti_sci_intr_free_irq(struct ti_sci_intr_irq_domain *intr, u16 out_irq)
+{
+ u16 start = intr->out_irqs->desc->start;
+ u16 num = intr->out_irqs->desc->num;
+ u16 start_sec = intr->out_irqs->desc->start_sec;
+
+ if (out_irq < start + num)
+ intr->mapping[out_irq - start] = 0xFFFFFFFF;
+ else
+ intr->mapping[out_irq - start_sec + num] = 0xFFFFFFFF;
+}
+
/**
* ti_sci_intr_irq_domain_free() - Free the specified IRQs from the domain.
* @domain: Domain to which the irqs belong
@@ -118,11 +137,30 @@ static void ti_sci_intr_irq_domain_free(struct irq_domain *domain,
intr->sci->ops.rm_irq_ops.free_irq(intr->sci,
intr->ti_sci_id, data->hwirq,
intr->ti_sci_id, out_irq);
+ ti_sci_intr_free_irq(intr, out_irq);
ti_sci_release_resource(intr->out_irqs, out_irq);
irq_domain_free_irqs_parent(domain, virq, 1);
irq_domain_reset_irq_data(data);
}

+/**
+ * ti_sci_intr_add_irq - Add the irq entry in the out_irq <-> hwirq mapping table
+ * @intr: IRQ domain corresponding to Interrupt Router
+ * @hwirq: Input irq number
+ * @out_irq: Out irq number
+ */
+static void ti_sci_intr_add_irq(struct ti_sci_intr_irq_domain *intr, u32 hwirq, u16 out_irq)
+{
+ u16 start = intr->out_irqs->desc->start;
+ u16 num = intr->out_irqs->desc->num;
+ u16 start_sec = intr->out_irqs->desc->start_sec;
+
+ if (out_irq < start + num)
+ intr->mapping[out_irq - start] = hwirq;
+ else
+ intr->mapping[out_irq - start_sec + num] = hwirq;
+}
+
/**
* ti_sci_intr_alloc_parent_irq() - Allocate parent IRQ
* @domain: Pointer to the interrupt router IRQ domain
@@ -173,6 +211,9 @@ static int ti_sci_intr_alloc_parent_irq(struct irq_domain *domain,
if (err)
goto err_msg;

+ /* Adding out_irq <-> hwirq to mapping */
+ ti_sci_intr_add_irq(intr, hwirq, out_irq);
+
return out_irq;

err_msg:
@@ -221,6 +262,26 @@ static const struct irq_domain_ops ti_sci_intr_irq_domain_ops = {
.translate = ti_sci_intr_irq_domain_translate,
};

+/**
+ * ti_sci_intr_initialize_mappingg - Initialize the out_irq <-> hwirq mapping table
+ * @intr: IRQ domain corresponding to Interrupt Router
+ */
+static int ti_sci_intr_initialize_mapping(struct ti_sci_intr_irq_domain *intr)
+{
+ int i;
+ int mapping_len = intr->out_irqs->desc->num + intr->out_irqs->desc->num_sec;
+
+ intr->mapping = devm_kzalloc(intr->dev, mapping_len * sizeof(u32), GFP_KERNEL);
+ if (!intr->mapping)
+ return -ENOMEM;
+
+ /* Set all the elements in the array to max value of u32 */
+ for (i = 0; i < mapping_len; i++)
+ intr->mapping[i] = 0xFFFFFFFF;
+
+ return 0;
+}
+
static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev)
{
struct irq_domain *parent_domain, *domain;
@@ -246,6 +307,8 @@ static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev)
return -ENOMEM;

intr->dev = dev;
+ platform_set_drvdata(pdev, intr);
+
ret = of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type",
&intr->type);
if (ret) {
@@ -273,6 +336,10 @@ static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev)
return PTR_ERR(intr->out_irqs);
}

+ ret = ti_sci_intr_initialize_mapping(intr);
+ if (ret)
+ return ret;
+
domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev),
&ti_sci_intr_irq_domain_ops, intr);
if (!domain) {
@@ -285,6 +352,46 @@ static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev)
return 0;
}

+static int ti_sci_intr_restore_irqs(struct ti_sci_intr_irq_domain *intr)
+{
+ int i, err = 0;
+ u16 start = intr->out_irqs->desc->start;
+ u16 num = intr->out_irqs->desc->num;
+ u16 start_sec = intr->out_irqs->desc->start_sec;
+ u16 num_sec = intr->out_irqs->desc->num_sec;
+
+ for (i = 0; i < num + num_sec; i++) {
+ if (intr->mapping[i] == 0xFFFFFFFF)
+ continue;
+
+ if (i < num)
+ err = intr->sci->ops.rm_irq_ops.set_irq(intr->sci,
+ intr->ti_sci_id, intr->mapping[i],
+ intr->ti_sci_id, i + start);
+
+ else
+ err = intr->sci->ops.rm_irq_ops.set_irq(intr->sci,
+ intr->ti_sci_id, intr->mapping[i],
+ intr->ti_sci_id, i + start_sec);
+
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused ti_sci_intr_resume(struct device *dev)
+{
+ struct ti_sci_intr_irq_domain *intr = dev_get_drvdata(dev);
+
+ return ti_sci_intr_restore_irqs(intr);
+}
+
+static const struct dev_pm_ops ti_sci_intr_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, ti_sci_intr_resume)
+};
+
static const struct of_device_id ti_sci_intr_irq_domain_of_match[] = {
{ .compatible = "ti,sci-intr", },
{ /* sentinel */ },
@@ -295,6 +402,7 @@ static struct platform_driver ti_sci_intr_irq_domain_driver = {
.probe = ti_sci_intr_irq_domain_probe,
.driver = {
.name = "ti-sci-intr",
+ .pm = &ti_sci_intr_dev_pm_ops,
.of_match_table = ti_sci_intr_irq_domain_of_match,
},
};
--
2.17.1