diff mbox

[v2,2/5] irqchip/gic-v3-its: add ability to save/restore ITS state

Message ID 20180126073835.16575-3-dbasehore@chromium.org (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Derek Basehore Jan. 26, 2018, 7:38 a.m. UTC
Some platforms power off GIC logic in suspend, so we need to
save/restore state. The distributor and redistributor registers need
to be handled in platform code due to access permissions on those
registers, but the ITS registers can be restored in the kernel.

Signed-off-by: Derek Basehore <dbasehore@chromium.org>
---
 drivers/irqchip/irq-gic-v3-its.c | 86 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)

Comments

Brian Norris Jan. 26, 2018, 8:59 p.m. UTC | #1
One trivial comment:

On Thu, Jan 25, 2018 at 11:38:32PM -0800, Derek Basehore wrote:
> Some platforms power off GIC logic in suspend, so we need to
> save/restore state. The distributor and redistributor registers need
> to be handled in platform code due to access permissions on those
> registers, but the ITS registers can be restored in the kernel.
> 
> Signed-off-by: Derek Basehore <dbasehore@chromium.org>
> ---
>  drivers/irqchip/irq-gic-v3-its.c | 86 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 86 insertions(+)
> 
> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
> index 06f025fd5726..4727b447610f 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c

...

> @@ -3042,6 +3054,75 @@ static void its_enable_quirks(struct its_node *its)
>  	gic_enable_quirks(iidr, its_quirks, its);
>  }
>  
> +int its_save_disable(void)

This (and its_restore_enable()) should be static, now that you're only
using them in this file.

Brian

> +{
> +	struct its_node *its;
> +	int err = 0;
> +
> +	spin_lock(&its_lock);
> +	list_for_each_entry(its, &its_nodes, entry) {
> +		if (its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE) {
> +			struct its_ctx *ctx = &its->its_ctx;
> +			void __iomem *base = its->base;
> +
> +			ctx->ctlr = readl_relaxed(base + GITS_CTLR);
> +			err = its_force_quiescent(base);
> +			if (err) {
> +				writel_relaxed(ctx->ctlr, base + GITS_CTLR);
> +				goto err;
> +			}
> +
> +			ctx->cbaser = gits_read_cbaser(base + GITS_CBASER);
> +		}
> +	}
> +
> +err:
> +	if (err) {
> +		list_for_each_entry_continue_reverse(its, &its_nodes, entry) {
> +			if (its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE) {
> +				struct its_ctx *ctx = &its->its_ctx;
> +				void __iomem *base = its->base;
> +
> +				writel_relaxed(ctx->ctlr, base + GITS_CTLR);
> +			}
> +		}
> +	}
> +
> +	spin_unlock(&its_lock);
> +
> +	return err;
> +}
> +
> +void its_restore_enable(void)
> +{
> +	struct its_node *its;
> +
> +	spin_lock(&its_lock);
> +	list_for_each_entry(its, &its_nodes, entry) {
> +		struct its_ctx *ctx = &its->its_ctx;
> +		struct its_baser *baser;
> +		void __iomem *base;
> +		int i;
> +
> +		if (its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE) {
> +			base = its->base;
> +			gits_write_cbaser(ctx->cbaser, base + GITS_CBASER);
> +			/* Restore GITS_BASER from the value cache. */
> +			for (i = 0; i < GITS_BASER_NR_REGS; i++) {
> +				baser = &its->tables[i];
> +				its_write_baser(its, baser, baser->val);
> +			}
> +			writel_relaxed(ctx->ctlr, base + GITS_CTLR);
> +		}
> +	}
> +	spin_unlock(&its_lock);
> +}
> +
> +static struct syscore_ops its_syscore_ops = {
> +	.suspend = its_save_disable,
> +	.resume = its_restore_enable,
> +};
> +
>  static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
>  {
>  	struct irq_domain *inner_domain;
Derek Basehore Jan. 26, 2018, 9:13 p.m. UTC | #2
On Fri, Jan 26, 2018 at 12:59 PM, Brian Norris <briannorris@chromium.org> wrote:
> One trivial comment:
>
> On Thu, Jan 25, 2018 at 11:38:32PM -0800, Derek Basehore wrote:
>> Some platforms power off GIC logic in suspend, so we need to
>> save/restore state. The distributor and redistributor registers need
>> to be handled in platform code due to access permissions on those
>> registers, but the ITS registers can be restored in the kernel.
>>
>> Signed-off-by: Derek Basehore <dbasehore@chromium.org>
>> ---
>>  drivers/irqchip/irq-gic-v3-its.c | 86 ++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 86 insertions(+)
>>
>> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
>> index 06f025fd5726..4727b447610f 100644
>> --- a/drivers/irqchip/irq-gic-v3-its.c
>> +++ b/drivers/irqchip/irq-gic-v3-its.c
>
> ...
>
>> @@ -3042,6 +3054,75 @@ static void its_enable_quirks(struct its_node *its)
>>       gic_enable_quirks(iidr, its_quirks, its);
>>  }
>>
>> +int its_save_disable(void)
>
> This (and its_restore_enable()) should be static, now that you're only
> using them in this file.

Oops. I'll change that and add a pr_err in the its_force_quiescent
error case in the next patch set.

>
> Brian
>
>> +{
>> +     struct its_node *its;
>> +     int err = 0;
>> +
>> +     spin_lock(&its_lock);
>> +     list_for_each_entry(its, &its_nodes, entry) {
>> +             if (its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE) {
>> +                     struct its_ctx *ctx = &its->its_ctx;
>> +                     void __iomem *base = its->base;
>> +
>> +                     ctx->ctlr = readl_relaxed(base + GITS_CTLR);
>> +                     err = its_force_quiescent(base);
>> +                     if (err) {
>> +                             writel_relaxed(ctx->ctlr, base + GITS_CTLR);
>> +                             goto err;
>> +                     }
>> +
>> +                     ctx->cbaser = gits_read_cbaser(base + GITS_CBASER);
>> +             }
>> +     }
>> +
>> +err:
>> +     if (err) {
>> +             list_for_each_entry_continue_reverse(its, &its_nodes, entry) {
>> +                     if (its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE) {
>> +                             struct its_ctx *ctx = &its->its_ctx;
>> +                             void __iomem *base = its->base;
>> +
>> +                             writel_relaxed(ctx->ctlr, base + GITS_CTLR);
>> +                     }
>> +             }
>> +     }
>> +
>> +     spin_unlock(&its_lock);
>> +
>> +     return err;
>> +}
>> +
>> +void its_restore_enable(void)
>> +{
>> +     struct its_node *its;
>> +
>> +     spin_lock(&its_lock);
>> +     list_for_each_entry(its, &its_nodes, entry) {
>> +             struct its_ctx *ctx = &its->its_ctx;
>> +             struct its_baser *baser;
>> +             void __iomem *base;
>> +             int i;
>> +
>> +             if (its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE) {
>> +                     base = its->base;
>> +                     gits_write_cbaser(ctx->cbaser, base + GITS_CBASER);
>> +                     /* Restore GITS_BASER from the value cache. */
>> +                     for (i = 0; i < GITS_BASER_NR_REGS; i++) {
>> +                             baser = &its->tables[i];
>> +                             its_write_baser(its, baser, baser->val);
>> +                     }
>> +                     writel_relaxed(ctx->ctlr, base + GITS_CTLR);
>> +             }
>> +     }
>> +     spin_unlock(&its_lock);
>> +}
>> +
>> +static struct syscore_ops its_syscore_ops = {
>> +     .suspend = its_save_disable,
>> +     .resume = its_restore_enable,
>> +};
>> +
>>  static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
>>  {
>>       struct irq_domain *inner_domain;
diff mbox

Patch

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 06f025fd5726..4727b447610f 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -33,6 +33,7 @@ 
 #include <linux/of_platform.h>
 #include <linux/percpu.h>
 #include <linux/slab.h>
+#include <linux/syscore_ops.h>
 
 #include <linux/irqchip.h>
 #include <linux/irqchip/arm-gic-v3.h>
@@ -46,6 +47,7 @@ 
 #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING		(1ULL << 0)
 #define ITS_FLAGS_WORKAROUND_CAVIUM_22375	(1ULL << 1)
 #define ITS_FLAGS_WORKAROUND_CAVIUM_23144	(1ULL << 2)
+#define ITS_FLAGS_SAVE_SUSPEND_STATE		(1ULL << 3)
 
 #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING	(1 << 0)
 
@@ -83,6 +85,15 @@  struct its_baser {
 	u32		psz;
 };
 
+/*
+ * Saved ITS state - this is where saved state for the ITS is stored
+ * when it's disabled during system suspend.
+ */
+struct its_ctx {
+	u64			cbaser;
+	u32			ctlr;
+};
+
 struct its_device;
 
 /*
@@ -101,6 +112,7 @@  struct its_node {
 	struct its_collection	*collections;
 	struct fwnode_handle	*fwnode_handle;
 	u64			(*get_msi_base)(struct its_device *its_dev);
+	struct its_ctx		its_ctx;
 	struct list_head	its_device_list;
 	u64			flags;
 	unsigned long		list_nr;
@@ -3042,6 +3054,75 @@  static void its_enable_quirks(struct its_node *its)
 	gic_enable_quirks(iidr, its_quirks, its);
 }
 
+int its_save_disable(void)
+{
+	struct its_node *its;
+	int err = 0;
+
+	spin_lock(&its_lock);
+	list_for_each_entry(its, &its_nodes, entry) {
+		if (its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE) {
+			struct its_ctx *ctx = &its->its_ctx;
+			void __iomem *base = its->base;
+
+			ctx->ctlr = readl_relaxed(base + GITS_CTLR);
+			err = its_force_quiescent(base);
+			if (err) {
+				writel_relaxed(ctx->ctlr, base + GITS_CTLR);
+				goto err;
+			}
+
+			ctx->cbaser = gits_read_cbaser(base + GITS_CBASER);
+		}
+	}
+
+err:
+	if (err) {
+		list_for_each_entry_continue_reverse(its, &its_nodes, entry) {
+			if (its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE) {
+				struct its_ctx *ctx = &its->its_ctx;
+				void __iomem *base = its->base;
+
+				writel_relaxed(ctx->ctlr, base + GITS_CTLR);
+			}
+		}
+	}
+
+	spin_unlock(&its_lock);
+
+	return err;
+}
+
+void its_restore_enable(void)
+{
+	struct its_node *its;
+
+	spin_lock(&its_lock);
+	list_for_each_entry(its, &its_nodes, entry) {
+		struct its_ctx *ctx = &its->its_ctx;
+		struct its_baser *baser;
+		void __iomem *base;
+		int i;
+
+		if (its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE) {
+			base = its->base;
+			gits_write_cbaser(ctx->cbaser, base + GITS_CBASER);
+			/* Restore GITS_BASER from the value cache. */
+			for (i = 0; i < GITS_BASER_NR_REGS; i++) {
+				baser = &its->tables[i];
+				its_write_baser(its, baser, baser->val);
+			}
+			writel_relaxed(ctx->ctlr, base + GITS_CTLR);
+		}
+	}
+	spin_unlock(&its_lock);
+}
+
+static struct syscore_ops its_syscore_ops = {
+	.suspend = its_save_disable,
+	.resume = its_restore_enable,
+};
+
 static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
 {
 	struct irq_domain *inner_domain;
@@ -3261,6 +3342,9 @@  static int __init its_probe_one(struct resource *res,
 		ctlr |= GITS_CTLR_ImDe;
 	writel_relaxed(ctlr, its->base + GITS_CTLR);
 
+	if (fwnode_property_present(handle, "reset-on-suspend"))
+		its->flags |= ITS_FLAGS_SAVE_SUSPEND_STATE;
+
 	err = its_init_domain(handle, its);
 	if (err)
 		goto out_free_tables;
@@ -3515,5 +3599,7 @@  int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
 		}
 	}
 
+	register_syscore_ops(&its_syscore_ops);
+
 	return 0;
 }