diff mbox

[v2,5/6] irqchip: gicv3-its: add support for power down

Message ID 1423992723-5028-6-git-send-email-wuyun.wu@huawei.com (mailing list archive)
State New, archived
Headers show

Commit Message

Abel Wu Feb. 15, 2015, 9:32 a.m. UTC
It's unsafe to change the configurations of an activated ITS directly
since this will lead to unpredictable results. This patch guarantees
a safe quiescent status before initializing an ITS.

Signed-off-by: Yun Wu <wuyun.wu@huawei.com>
---
 drivers/irqchip/irq-gic-v3-its.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

--
1.8.0

Comments

Marc Zyngier Feb. 17, 2015, 9:29 a.m. UTC | #1
On Sun, 15 Feb 2015 09:32:02 +0000
Yun Wu <wuyun.wu@huawei.com> wrote:

> It's unsafe to change the configurations of an activated ITS directly
> since this will lead to unpredictable results. This patch guarantees
> a safe quiescent status before initializing an ITS.

Please change the title of this patch to reflect what it actually
does. Nothing here is about powering down anything.

> Signed-off-by: Yun Wu <wuyun.wu@huawei.com>
> ---
>  drivers/irqchip/irq-gic-v3-its.c | 32
> ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
> 
> diff --git a/drivers/irqchip/irq-gic-v3-its.c
> b/drivers/irqchip/irq-gic-v3-its.c index 42c03b2..29eb665 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -1321,6 +1321,31 @@ static const struct irq_domain_ops
> its_domain_ops = { .deactivate		=
> its_irq_domain_deactivate, };
> 
> +static int its_check_quiesced(void __iomem *base)
> +{
> +	u32 count = 1000000;	/* 1s */
> +	u32 val;
> +
> +	val = readl_relaxed(base + GITS_CTLR);
> +	if (val & GITS_CTLR_QUIESCENT)
> +		return 0;
> +
> +	/* Disable the generation of all interrupts to this ITS */
> +	val &= ~GITS_CTLR_ENABLE;
> +	writel_relaxed(val, base + GITS_CTLR);
> +
> +	/* Poll GITS_CTLR and wait until ITS becomes quiescent */
> +	while (count--) {
> +		val = readl_relaxed(base + GITS_CTLR);
> +		if (val & GITS_CTLR_QUIESCENT)
> +			return 0;
> +		cpu_relax();
> +		udelay(1);
> +	}

You're now introducing a third variant of a 1s timeout loop. Notice a
pattern?

Thanks,

	M.
Abel Wu Feb. 17, 2015, 10:15 a.m. UTC | #2
On 2015/2/17 17:29, Marc Zyngier wrote:

> On Sun, 15 Feb 2015 09:32:02 +0000
> Yun Wu <wuyun.wu@huawei.com> wrote:
> 
>> It's unsafe to change the configurations of an activated ITS directly
>> since this will lead to unpredictable results. This patch guarantees
>> a safe quiescent status before initializing an ITS.
> 
> Please change the title of this patch to reflect what it actually
> does. Nothing here is about powering down anything.

My miss, I will fix this in next version.

> 
>> Signed-off-by: Yun Wu <wuyun.wu@huawei.com>
>> ---
>>  drivers/irqchip/irq-gic-v3-its.c | 32
>> ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
>>
>> diff --git a/drivers/irqchip/irq-gic-v3-its.c
>> b/drivers/irqchip/irq-gic-v3-its.c index 42c03b2..29eb665 100644
>> --- a/drivers/irqchip/irq-gic-v3-its.c
>> +++ b/drivers/irqchip/irq-gic-v3-its.c
>> @@ -1321,6 +1321,31 @@ static const struct irq_domain_ops
>> its_domain_ops = { .deactivate		=
>> its_irq_domain_deactivate, };
>>
>> +static int its_check_quiesced(void __iomem *base)
>> +{
>> +	u32 count = 1000000;	/* 1s */
>> +	u32 val;
>> +
>> +	val = readl_relaxed(base + GITS_CTLR);
>> +	if (val & GITS_CTLR_QUIESCENT)
>> +		return 0;
>> +
>> +	/* Disable the generation of all interrupts to this ITS */
>> +	val &= ~GITS_CTLR_ENABLE;
>> +	writel_relaxed(val, base + GITS_CTLR);
>> +
>> +	/* Poll GITS_CTLR and wait until ITS becomes quiescent */
>> +	while (count--) {
>> +		val = readl_relaxed(base + GITS_CTLR);
>> +		if (val & GITS_CTLR_QUIESCENT)
>> +			return 0;
>> +		cpu_relax();
>> +		udelay(1);
>> +	}
> 
> You're now introducing a third variant of a 1s timeout loop. Notice a
> pattern?
> 

I am not sure I know exactly what you suggest. Do you mean I should code
like below to keep the coding style same as the other 2 loops?

	while (1) {
		val = readl_relaxed(base + GITS_CTLR);
		if (val & GITS_CTLR_QUIESCENT)
			return 0;

		count--;
		if (!count)
			return -EBUSY;

		cpu_relax();
		udelay(1);
	}

Thanks,
	Abel
Marc Zyngier Feb. 17, 2015, 11:11 a.m. UTC | #3
On Tue, 17 Feb 2015 10:15:15 +0000
"Yun Wu (Abel)" <wuyun.wu@huawei.com> wrote:

> On 2015/2/17 17:29, Marc Zyngier wrote:
> 
> > On Sun, 15 Feb 2015 09:32:02 +0000
> > Yun Wu <wuyun.wu@huawei.com> wrote:
> > 
> >> It's unsafe to change the configurations of an activated ITS
> >> directly since this will lead to unpredictable results. This patch
> >> guarantees a safe quiescent status before initializing an ITS.
> > 
> > Please change the title of this patch to reflect what it actually
> > does. Nothing here is about powering down anything.
> 
> My miss, I will fix this in next version.
> 
> > 
> >> Signed-off-by: Yun Wu <wuyun.wu@huawei.com>
> >> ---
> >>  drivers/irqchip/irq-gic-v3-its.c | 32
> >> ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
> >>
> >> diff --git a/drivers/irqchip/irq-gic-v3-its.c
> >> b/drivers/irqchip/irq-gic-v3-its.c index 42c03b2..29eb665 100644
> >> --- a/drivers/irqchip/irq-gic-v3-its.c
> >> +++ b/drivers/irqchip/irq-gic-v3-its.c
> >> @@ -1321,6 +1321,31 @@ static const struct irq_domain_ops
> >> its_domain_ops = { .deactivate		=
> >> its_irq_domain_deactivate, };
> >>
> >> +static int its_check_quiesced(void __iomem *base)
> >> +{
> >> +	u32 count = 1000000;	/* 1s */
> >> +	u32 val;
> >> +
> >> +	val = readl_relaxed(base + GITS_CTLR);
> >> +	if (val & GITS_CTLR_QUIESCENT)
> >> +		return 0;
> >> +
> >> +	/* Disable the generation of all interrupts to this ITS */
> >> +	val &= ~GITS_CTLR_ENABLE;
> >> +	writel_relaxed(val, base + GITS_CTLR);
> >> +
> >> +	/* Poll GITS_CTLR and wait until ITS becomes quiescent */
> >> +	while (count--) {
> >> +		val = readl_relaxed(base + GITS_CTLR);
> >> +		if (val & GITS_CTLR_QUIESCENT)
> >> +			return 0;
> >> +		cpu_relax();
> >> +		udelay(1);
> >> +	}
> > 
> > You're now introducing a third variant of a 1s timeout loop. Notice
> > a pattern?
> > 
> 
> I am not sure I know exactly what you suggest. Do you mean I should
> code like below to keep the coding style same as the other 2 loops?
> 
> 	while (1) {
> 		val = readl_relaxed(base + GITS_CTLR);
> 		if (val & GITS_CTLR_QUIESCENT)
> 			return 0;
> 
> 		count--;
> 		if (!count)
> 			return -EBUSY;
> 
> 		cpu_relax();
> 		udelay(1);
> 	}

That'd be a good start. But given that this is starting to be a common
construct, it could probably be rewritten as:

static int its_poll_timeout(struct its_node *its, void *data,
                            int (*fn)(struct its_node *its,
                                      void *data))
{
	while (1) {
		if (!fn(its, data))
			return 0;

		...
	}
}

and have the call sites to provide the right utility function. We also
have two similar timeout loops in the main GICv3 driver, so there
should be room for improvement.

Thoughts?

Thanks,

	M.
Abel Wu Feb. 17, 2015, 12:27 p.m. UTC | #4
On 2015/2/17 19:11, Marc Zyngier wrote:

> On Tue, 17 Feb 2015 10:15:15 +0000
> "Yun Wu (Abel)" <wuyun.wu@huawei.com> wrote:
> 
>> On 2015/2/17 17:29, Marc Zyngier wrote:
>>
>>> On Sun, 15 Feb 2015 09:32:02 +0000
>>> Yun Wu <wuyun.wu@huawei.com> wrote:
>>>
>>>> It's unsafe to change the configurations of an activated ITS
>>>> directly since this will lead to unpredictable results. This patch
>>>> guarantees a safe quiescent status before initializing an ITS.
>>>
>>> Please change the title of this patch to reflect what it actually
>>> does. Nothing here is about powering down anything.
>>
>> My miss, I will fix this in next version.
>>
>>>
>>>> Signed-off-by: Yun Wu <wuyun.wu@huawei.com>
>>>> ---
>>>>  drivers/irqchip/irq-gic-v3-its.c | 32
>>>> ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
>>>>
>>>> diff --git a/drivers/irqchip/irq-gic-v3-its.c
>>>> b/drivers/irqchip/irq-gic-v3-its.c index 42c03b2..29eb665 100644
>>>> --- a/drivers/irqchip/irq-gic-v3-its.c
>>>> +++ b/drivers/irqchip/irq-gic-v3-its.c
>>>> @@ -1321,6 +1321,31 @@ static const struct irq_domain_ops
>>>> its_domain_ops = { .deactivate		=
>>>> its_irq_domain_deactivate, };
>>>>
>>>> +static int its_check_quiesced(void __iomem *base)
>>>> +{
>>>> +	u32 count = 1000000;	/* 1s */
>>>> +	u32 val;
>>>> +
>>>> +	val = readl_relaxed(base + GITS_CTLR);
>>>> +	if (val & GITS_CTLR_QUIESCENT)
>>>> +		return 0;
>>>> +
>>>> +	/* Disable the generation of all interrupts to this ITS */
>>>> +	val &= ~GITS_CTLR_ENABLE;
>>>> +	writel_relaxed(val, base + GITS_CTLR);
>>>> +
>>>> +	/* Poll GITS_CTLR and wait until ITS becomes quiescent */
>>>> +	while (count--) {
>>>> +		val = readl_relaxed(base + GITS_CTLR);
>>>> +		if (val & GITS_CTLR_QUIESCENT)
>>>> +			return 0;
>>>> +		cpu_relax();
>>>> +		udelay(1);
>>>> +	}
>>>
>>> You're now introducing a third variant of a 1s timeout loop. Notice
>>> a pattern?
>>>
>>
>> I am not sure I know exactly what you suggest. Do you mean I should
>> code like below to keep the coding style same as the other 2 loops?
>>
>> 	while (1) {
>> 		val = readl_relaxed(base + GITS_CTLR);
>> 		if (val & GITS_CTLR_QUIESCENT)
>> 			return 0;
>>
>> 		count--;
>> 		if (!count)
>> 			return -EBUSY;
>>
>> 		cpu_relax();
>> 		udelay(1);
>> 	}
> 
> That'd be a good start. But given that this is starting to be a common
> construct, it could probably be rewritten as:
> 
> static int its_poll_timeout(struct its_node *its, void *data,
>                             int (*fn)(struct its_node *its,
>                                       void *data))
> {
> 	while (1) {
> 		if (!fn(its, data))
> 			return 0;
> 
> 		...
> 	}
> }
> 
> and have the call sites to provide the right utility function. We also
> have two similar timeout loops in the main GICv3 driver, so there
> should be room for improvement.
> 
> Thoughts?
> 

It looks fine to me. I will make some improvement in the next version after
Chinese Spring Festival. :)

Thanks,
	Abel
Abel Wu March 4, 2015, 3:10 a.m. UTC | #5
On 2015/2/17 20:27, Yun Wu (Abel) wrote:

> On 2015/2/17 19:11, Marc Zyngier wrote:
> 
>> On Tue, 17 Feb 2015 10:15:15 +0000
>> "Yun Wu (Abel)" <wuyun.wu@huawei.com> wrote:
>>
>>> On 2015/2/17 17:29, Marc Zyngier wrote:
>>>
>>>> On Sun, 15 Feb 2015 09:32:02 +0000
>>>> Yun Wu <wuyun.wu@huawei.com> wrote:
>>>>
>>>>> It's unsafe to change the configurations of an activated ITS
>>>>> directly since this will lead to unpredictable results. This patch
>>>>> guarantees a safe quiescent status before initializing an ITS.
>>>>
>>>> Please change the title of this patch to reflect what it actually
>>>> does. Nothing here is about powering down anything.
>>>
>>> My miss, I will fix this in next version.
>>>
>>>>
>>>>> Signed-off-by: Yun Wu <wuyun.wu@huawei.com>
>>>>> ---
>>>>>  drivers/irqchip/irq-gic-v3-its.c | 32
>>>>> ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
>>>>>
>>>>> diff --git a/drivers/irqchip/irq-gic-v3-its.c
>>>>> b/drivers/irqchip/irq-gic-v3-its.c index 42c03b2..29eb665 100644
>>>>> --- a/drivers/irqchip/irq-gic-v3-its.c
>>>>> +++ b/drivers/irqchip/irq-gic-v3-its.c
>>>>> @@ -1321,6 +1321,31 @@ static const struct irq_domain_ops
>>>>> its_domain_ops = { .deactivate		=
>>>>> its_irq_domain_deactivate, };
>>>>>
>>>>> +static int its_check_quiesced(void __iomem *base)
>>>>> +{
>>>>> +	u32 count = 1000000;	/* 1s */
>>>>> +	u32 val;
>>>>> +
>>>>> +	val = readl_relaxed(base + GITS_CTLR);
>>>>> +	if (val & GITS_CTLR_QUIESCENT)
>>>>> +		return 0;
>>>>> +
>>>>> +	/* Disable the generation of all interrupts to this ITS */
>>>>> +	val &= ~GITS_CTLR_ENABLE;
>>>>> +	writel_relaxed(val, base + GITS_CTLR);
>>>>> +
>>>>> +	/* Poll GITS_CTLR and wait until ITS becomes quiescent */
>>>>> +	while (count--) {
>>>>> +		val = readl_relaxed(base + GITS_CTLR);
>>>>> +		if (val & GITS_CTLR_QUIESCENT)
>>>>> +			return 0;
>>>>> +		cpu_relax();
>>>>> +		udelay(1);
>>>>> +	}
>>>>
>>>> You're now introducing a third variant of a 1s timeout loop. Notice
>>>> a pattern?
>>>>
>>>
>>> I am not sure I know exactly what you suggest. Do you mean I should
>>> code like below to keep the coding style same as the other 2 loops?
>>>
>>> 	while (1) {
>>> 		val = readl_relaxed(base + GITS_CTLR);
>>> 		if (val & GITS_CTLR_QUIESCENT)
>>> 			return 0;
>>>
>>> 		count--;
>>> 		if (!count)
>>> 			return -EBUSY;
>>>
>>> 		cpu_relax();
>>> 		udelay(1);
>>> 	}
>>
>> That'd be a good start. But given that this is starting to be a common
>> construct, it could probably be rewritten as:
>>
>> static int its_poll_timeout(struct its_node *its, void *data,
>>                             int (*fn)(struct its_node *its,
>>                                       void *data))
>> {
>> 	while (1) {
>> 		if (!fn(its, data))
>> 			return 0;
>>
>> 		...
>> 	}
>> }
>>
>> and have the call sites to provide the right utility function. We also
>> have two similar timeout loops in the main GICv3 driver, so there
>> should be room for improvement.
>>
>> Thoughts?
>>


Hi Marc,

Currently I didn't make any improvement on providing a unified utility
function to do the timeout loops, because I haven't found a proper way
yet. And to achieve this, at least three aspects I can imagine are
needed to be considered.

Refactoring the existing loop functions comes first. A prototype is
showed below.

	static T1 func_prototype(T2 node, char *msg, void **args)
	{
		u32 count = 1000000;

		do_something_here(node, args);

		while (1) {
			if (condition_satisfied(node, args))
				return (T1)SUCCESS;

			count--;
			if (!count) {
				print_err_msg(msg);
				return (T1)FAIL;
			}

			cpu_relax();
			udelay(1);
		}
	}

Obviously it will make things complicated to move do_something_here() and
print_err_msg() outside of func_prototype(), because func_prototype() could
be called sereval places. But the two functions are different from each
loop function, so...

	static T1 func_prototype(T2 node, char *msg, void **args)
	{
		u32 count = 1000000;

		do_something_here(node, args);

		while (count--) {
			if (condition_satisfied(node, args))
				return (T1)SUCCESS;

			cpu_relax();
			udelay(1);
		}

		print_err_msg(msg);
		return (T1)FAIL;
	}

Now we can unify the loop part,

	static bool condition_satisfied(T2 node, void **args);

	static u32 poll_timeout(T2 node, void **args,
				bool (*fn)(T2, void **))
	{
		u32 count = 1000000;

		while (count--) {
			if (fn(node, args))
				break;

			cpu_relax();
			udelay(1);
		}

		return count;
	}

	static T1 func_prototype(T2 node, char *msg, void **args)
	{
		do_something_here(node, args);

		if (poll_timeout(node, args, condition_satisfied)) {
			return (T1)SUCCESS;
		} else {
			print_err_msg(msg);
			return (T1)FAIL;
		}
	}

Look at what I have done, turn the original N loop functions to 2*N+1 functions.
It can hardly be called improvement.. :(

The 2nd and 3rd aspects are return value and list of parameters respectively.
Using (void *) may help a lot, I think.

Thoughts?

Thanks,
	Abel
diff mbox

Patch

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 42c03b2..29eb665 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -1321,6 +1321,31 @@  static const struct irq_domain_ops its_domain_ops = {
 	.deactivate		= its_irq_domain_deactivate,
 };

+static int its_check_quiesced(void __iomem *base)
+{
+	u32 count = 1000000;	/* 1s */
+	u32 val;
+
+	val = readl_relaxed(base + GITS_CTLR);
+	if (val & GITS_CTLR_QUIESCENT)
+		return 0;
+
+	/* Disable the generation of all interrupts to this ITS */
+	val &= ~GITS_CTLR_ENABLE;
+	writel_relaxed(val, base + GITS_CTLR);
+
+	/* Poll GITS_CTLR and wait until ITS becomes quiescent */
+	while (count--) {
+		val = readl_relaxed(base + GITS_CTLR);
+		if (val & GITS_CTLR_QUIESCENT)
+			return 0;
+		cpu_relax();
+		udelay(1);
+	}
+
+	return -EBUSY;
+}
+
 static int its_probe(struct device_node *node, struct irq_domain *parent)
 {
 	struct resource res;
@@ -1349,6 +1374,13 @@  static int its_probe(struct device_node *node, struct irq_domain *parent)
 		goto out_unmap;
 	}

+	err = its_check_quiesced(its_base);
+	if (err) {
+		pr_warn("%s: failed to quiesce, behaviour unpredictable\n",
+			node->full_name);
+		goto out_unmap;
+	}
+
 	pr_info("ITS: %s\n", node->full_name);

 	its = kzalloc(sizeof(*its), GFP_KERNEL);