irqchip/gic-v3: Ensure GICR_CTLR.EnableLPI=0 is observed before enabling
Subsystems (2)
PATCH v2 1/1 irqchip/gic-v3: Ensure GICR_CTLR.EnableLPI=0 is observed before enabling
The definition of the GICR_CTLR.RWP control bit was expanded to indicate status of changing GICR_CTLR.EnableLPI from 1 to 0 is being in progress or completed. Software must observe GICR_CTLR.RWP==0 after clearing GICR_CTLR.EnableLPI from 1 to 0 and before writing GICR_PENDBASER and/or GICR_PROPBASER, otherwise behavior is UNPREDICTABLE.
---
Changes since v1:
-Moved LPI disable code to a seperate function as Marc suggested. -Mark's suggestion to use readl_relaxed_poll_timeout() helper functions.
drivers/irqchip/irq-gic-v3-its.c | 66 ++++++++++++++++++++++++++++++-------- include/linux/irqchip/arm-gic-v3.h | 1 + 2 files changed, 53 insertions(+), 14 deletions(-)
drivers/irqchip/irq-gic-v3-its.c +52 -14
@@ -33,6 +33,8 @@#include <linux/of_platform.h> #include <linux/percpu.h> #include <linux/slab.h> +#include <linux/time64.h> +#include <linux/iopoll.h> #include <linux/irqchip.h> #include <linux/irqchip/arm-gic-v3.h>
@@ -1875,16 +1877,6 @@ static void its_cpu_init_lpis(void)gic_data_rdist ()->pend_page = pend_page; } -/* Disable LPIs */ - val =readl_relaxed (rbase +GICR_CTLR ); - val &= ~GICR_CTLR_ENABLE_LPIS ; -writel_relaxed (val, rbase +GICR_CTLR ); - -/* -* Make sure any change to the table is observable by the GIC. -*/ -dsb (sy); -/* set PROPBASE */ val = (page_to_phys(gic_rdists->prop_page) | GICR_PROPBASER_InnerShareable |
@@ -3288,13 +3280,59 @@ static bool gic_rdists_supports_plpis(void)return !!(gic_read_typer (gic_data_rdist_rd_base () +GICR_TYPER ) &GICR_TYPER_PLPIS ); } +static int redist_disable_lpis (void ) +{ +void __iomem *rbase =gic_data_rdist_rd_base (); +u64 val; +int ret; + +if (!gic_rdists_supports_plpis ()) { +pr_info ("CPU%d: LPIs not supported\n" ,smp_processor_id ()); +return -ENXIO ; + } + + val =readl_relaxed (rbase +GICR_CTLR ); +if (!(val &GICR_CTLR_ENABLE_LPIS )) +return 0 ; + +/* Disable LPIs */ + val &= ~GICR_CTLR_ENABLE_LPIS ; +writel_relaxed (val, rbase +GICR_CTLR ); + +/* Make sure any change to GICR_CTLR is observable by the GIC */ +dsb (sy); + +/* Wait for GICR_CTLR.GICR_CTLR_ENABLE_LPIS==0 or timeout */ + ret =readl_relaxed_poll_timeout_atomic (rbase +GICR_CTLR , val, + !(val &GICR_CTLR_ENABLE_LPIS ),1 , +USEC_PER_SEC ); +if (ret) { +pr_err ("CPU%d: Failed to disable LPIs\n" ,smp_processor_id ()); +return -EBUSY ; + } + +/* Wait for GICR_CTLR.RWP==0 or timeout */ + ret =readl_relaxed_poll_timeout_atomic (rbase +GICR_CTLR , val, + !(val &GICR_CTLR_RWP ),1 , +USEC_PER_SEC ); +if (ret) { +pr_err ("CPU%d: Failed to observe RWP==0 after enabling LPIs\n" , +smp_processor_id ()); +return -EBUSY ; + } + +return 0 ; +} +int its_cpu_init (void ) { +int ret; +if (!list_empty (&its_nodes)) { -if (!gic_rdists_supports_plpis ()) { -pr_info ("CPU%d: LPIs not supported\n" ,smp_processor_id ()); -return -ENXIO ; - } + ret =redist_disable_lpis (); +if (ret) +return ret; +its_cpu_init_lpis ();its_cpu_init_collection (); }
include/linux/irqchip/arm-gic-v3.h +1 -0
@@ -106,6 +106,7 @@#define GICR_PIDR2 GICD_PIDR2#define GICR_CTLR_ENABLE_LPIS (1UL << 0) +#define GICR_CTLR_RWP (1UL << 3)#define GICR_TYPER_CPU_NUMBER (r) (((r) >> 8) & 0xffff)
Signature · 2 lines
Qualcomm Datacenter Technologies, Inc. on behalf of the Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
Quoted 66 lines · On Thu, 15 Mar 2018 09:31:27 -0500 Shanker Donthineni…
The definition of the GICR_CTLR.RWP control bit was expanded to indicate status of changing GICR_CTLR.EnableLPI from 1 to 0 is being in progress or completed. Software must observe GICR_CTLR.RWP==0 after clearing GICR_CTLR.EnableLPI from 1 to 0 and before writing
GICR_PENDBASERand/orGICR_PROPBASER, otherwise behavior is UNPREDICTABLE.Signed-off-by Shanker Donthineni <[email protected]>---
Changes since v1:
-Moved LPI disable code to a seperate function as Marc suggested. -Mark's suggestion to usereadl_relaxed_poll_timeout()helper functions.drivers/irqchip/irq-gic-v3-its.c | 66 ++++++++++++++++++++++++++++++-------- include/linux/irqchip/arm-gic-v3.h | 1 + 2 files changed, 53 insertions(+), 14 deletions(-)drivers/irqchip/irq-gic-v3-its.c +18
-10@@ -33,6 +33,8 @@#include <linux/of_platform.h> #include <linux/percpu.h> #include <linux/slab.h> +#include <linux/time64.h> +#include <linux/iopoll.h> #include <linux/irqchip.h> #include <linux/irqchip/arm-gic-v3.h> @@ -1875,16 +1877,6 @@ static void its_cpu_init_lpis(void)gic_data_rdist ()->pend_page = pend_page; } -/* Disable LPIs */ - val =readl_relaxed (rbase +GICR_CTLR ); - val &= ~GICR_CTLR_ENABLE_LPIS ; -writel_relaxed (val, rbase +GICR_CTLR ); - -/* -* Make sure any change to the table is observable by the GIC. -*/ -dsb (sy); -/* set PROPBASE */ val = (page_to_phys(gic_rdists->prop_page) | GICR_PROPBASER_InnerShareable |@@ -3288,13 +3280,59 @@ static bool gic_rdists_supports_plpis(void)return !!(gic_read_typer (gic_data_rdist_rd_base () +GICR_TYPER ) &GICR_TYPER_PLPIS ); } +static int redist_disable_lpis (void ) +{ +void __iomem *rbase =gic_data_rdist_rd_base (); +u64 val; +int ret; + +if (!gic_rdists_supports_plpis ()) { +pr_info ("CPU%d: LPIs not supported\n" ,smp_processor_id ()); +return -ENXIO ; + } + + val =readl_relaxed (rbase +GICR_CTLR ); +if (!(val &GICR_CTLR_ENABLE_LPIS )) +return 0 ; + +/* Disable LPIs */
drivers/irqchip/irq-gic-v3-its.c static bool gic_rdists_supports_plpis(void) · line 3308
Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +10
-0+ val &= ~GICR_CTLR_ENABLE_LPIS ; +writel_relaxed (val, rbase +GICR_CTLR ); + +/* Make sure any change to GICR_CTLR is observable by the GIC */ +dsb (sy); + +/* Wait for GICR_CTLR.GICR_CTLR_ENABLE_LPIS==0 or timeout */ + ret =readl_relaxed_poll_timeout_atomic (rbase +GICR_CTLR , val, + !(val &GICR_CTLR_ENABLE_LPIS ),1 , +USEC_PER_SEC );
Why do you need to poll on EnableLPIs? It is supposed to be immediately written. It seems to me that the sequence should be:
- Check EnableLPis
- If set to 1, scream about bootloader being braindead
- Write EnableLPIs to 0
- Read back EnableLpis:
- If still set 1, bail out, we're doomed. Scream about the HW being braindead
- Poll RWP
- If timeout, bail out, the HW has locked up. Scream again.
I think this should cover the whole thing. An alternative would be to place the poll between the write and read-back, but that doesn't change anything.
Quoted 13 lines
drivers/irqchip/irq-gic-v3-its.c +13
-0+if (ret) { +pr_err ("CPU%d: Failed to disable LPIs\n" ,smp_processor_id ()); +return -EBUSY ; + } + +/* Wait for GICR_CTLR.RWP==0 or timeout */ + ret =readl_relaxed_poll_timeout_atomic (rbase +GICR_CTLR , val, + !(val &GICR_CTLR_RWP ),1 , +USEC_PER_SEC ); +if (ret) { +pr_err ("CPU%d: Failed to observe RWP==0 after enabling LPIs\n" , +smp_processor_id ()); +return -EBUSY ;
drivers/irqchip/irq-gic-v3-its.c static bool gic_rdists_supports_plpis(void) · line 3331
Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +7
-0+ } + +return 0 ; +} +int its_cpu_init (void ) { +int ret; +if (!list_empty (&its_nodes)) {
nit: move 'ret' here.
Quoted 16 lines
drivers/irqchip/irq-gic-v3-its.c +4
-4-if (!gic_rdists_supports_plpis ()) { -pr_info ("CPU%d: LPIs not supported\n" ,smp_processor_id ()); -return -ENXIO ; - } + ret =redist_disable_lpis (); +if (ret) +return ret; +its_cpu_init_lpis ();its_cpu_init_collection (); }include/linux/irqchip/arm-gic-v3.h +0
-0
include/linux/irqchip/arm-gic-v3.h line 112
Quoted 7 lines
include/linux/irqchip/arm-gic-v3.h +1
-0@@ -106,6 +106,7 @@#define GICR_PIDR2 GICD_PIDR2#define GICR_CTLR_ENABLE_LPIS (1UL << 0) +#define GICR_CTLR_RWP (1UL << 3)#define GICR_TYPER_CPU_NUMBER (r) (((r) >> 8) & 0xffff)
Thanks,
M.
Signature · 1 line
Without deviation from the norm, progress is not possible.
Hi Marc,
Quoted 74 lines · Marc Zyngier
Quoted 71 lines · On Thu, 15 Mar 2018 09:31:27 -0500 Shanker Donthineni…
The definition of the GICR_CTLR.RWP control bit was expanded to indicate status of changing GICR_CTLR.EnableLPI from 1 to 0 is being in progress or completed. Software must observe GICR_CTLR.RWP==0 after clearing GICR_CTLR.EnableLPI from 1 to 0 and before writing
GICR_PENDBASERand/orGICR_PROPBASER, otherwise behavior is UNPREDICTABLE.Signed-off-by Shanker Donthineni <[email protected]>---
Changes since v1:
-Moved LPI disable code to a seperate function as Marc suggested. -Mark's suggestion to usereadl_relaxed_poll_timeout()helper functions.drivers/irqchip/irq-gic-v3-its.c | 66 ++++++++++++++++++++++++++++++-------- include/linux/irqchip/arm-gic-v3.h | 1 + 2 files changed, 53 insertions(+), 14 deletions(-)drivers/irqchip/irq-gic-v3-its.c +23
-10@@ -33,6 +33,8 @@#include <linux/of_platform.h> #include <linux/percpu.h> #include <linux/slab.h> +#include <linux/time64.h> +#include <linux/iopoll.h> #include <linux/irqchip.h> #include <linux/irqchip/arm-gic-v3.h> @@ -1875,16 +1877,6 @@ static void its_cpu_init_lpis(void)gic_data_rdist ()->pend_page = pend_page; } -/* Disable LPIs */ - val =readl_relaxed (rbase +GICR_CTLR ); - val &= ~GICR_CTLR_ENABLE_LPIS ; -writel_relaxed (val, rbase +GICR_CTLR ); - -/* -* Make sure any change to the table is observable by the GIC. -*/ -dsb (sy); -/* set PROPBASE */ val = (page_to_phys(gic_rdists->prop_page) | GICR_PROPBASER_InnerShareable |@@ -3288,13 +3280,59 @@ static bool gic_rdists_supports_plpis(void)return !!(gic_read_typer (gic_data_rdist_rd_base () +GICR_TYPER ) &GICR_TYPER_PLPIS ); } +static int redist_disable_lpis (void ) +{ +void __iomem *rbase =gic_data_rdist_rd_base (); +u64 val; +int ret; + +if (!gic_rdists_supports_plpis ()) { +pr_info ("CPU%d: LPIs not supported\n" ,smp_processor_id ()); +return -ENXIO ; + } + + val =readl_relaxed (rbase +GICR_CTLR ); +if (!(val &GICR_CTLR_ENABLE_LPIS )) +return 0 ; + +/* Disable LPIs */ + val &= ~GICR_CTLR_ENABLE_LPIS ; +writel_relaxed (val, rbase +GICR_CTLR ); + +/* Make sure any change to GICR_CTLR is observable by the GIC */ +dsb (sy);
Quoted 9 lines
drivers/irqchip/irq-gic-v3-its.c static bool gic_rdists_supports_plpis(void) · line 3308
Quoted 5 lines
drivers/irqchip/irq-gic-v3-its.c +5
-0+ +/* Wait for GICR_CTLR.GICR_CTLR_ENABLE_LPIS==0 or timeout */ + ret =readl_relaxed_poll_timeout_atomic (rbase +GICR_CTLR , val, + !(val &GICR_CTLR_ENABLE_LPIS ),1 , +USEC_PER_SEC );Why do you need to poll on EnableLPIs? It is supposed to be immediately written. It seems to me that the sequence should be:
Agree we don't need to poll EnableLPIs here. I'll remove.
Quoted 9 lines
- Check EnableLPis
- If set to 1, scream about bootloader being braindead
- Write EnableLPIs to 0
- Read back EnableLpis:
- If still set 1, bail out, we're doomed. Scream about the HW being braindead
- Poll RWP
- If timeout, bail out, the HW has locked up. Scream again.
Quoted 4 lines
I think this should cover the whole thing. An alternative would be to place the poll between the write and read-back, but that doesn't change anything.
I'll prefer your second approach. Please comment on the below change before I post v3.
static int redist_disable_lpis(void)
{
void __iomem *rbase = gic_data_rdist_rd_base();
u64 val;
int ret;
if (!gic_rdists_supports_plpis()) {
pr_info("CPU%d: LPIs not supported\n", smp_processor_id());
return -ENXIO;
}
if (!(readl(rbase + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS))
return 0;
pr_warn("CPU%d: Trying to disable LPIs\n", smp_processor_id());
val &= ~GICR_CTLR_ENABLE_LPIS;
writel_relaxed(val, rbase + GICR_CTLR);
/* Make sure any change to GICR_CTLR is observable by the GIC */
dsb(sy);
/**
* Software must observe RWP==0 after clearing GICR_CTLR.EnableLPIs
* from 1 to 0 before programming GICR_PEND{PROP}BASER registers.
* Bail out the driver probe() in case of timeout.
*/
ret = readl_relaxed_poll_timeout_atomic(rbase + GICR_CTLR, val,
!(val & GICR_CTLR_RWP), 1,
USEC_PER_SEC);
if (ret) {
pr_err("CPU%d: EnableLPIs not observed\n", smp_processor_id());
return ret;
}
/**
* After it has been written to 1, it is IMPLEMENTATION DEFINED whether
* the bit GICR_CTLR.EnableLPI becomes RES1 or can be cleared to 0.
* Bail out the driver probe() on systems where it's RES1.
*/
if (readl_relaxed(rbase + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS) {
pr_err("CPU%d: Failed to disable LPIs\n", smp_processor_id());
return -EPERM;
}
return 0;
}Quoted 54 lines
Quoted 13 lines
drivers/irqchip/irq-gic-v3-its.c +13
-0+if (ret) { +pr_err ("CPU%d: Failed to disable LPIs\n" ,smp_processor_id ()); +return -EBUSY ; + } + +/* Wait for GICR_CTLR.RWP==0 or timeout */ + ret =readl_relaxed_poll_timeout_atomic (rbase +GICR_CTLR , val, + !(val &GICR_CTLR_RWP ),1 , +USEC_PER_SEC ); +if (ret) { +pr_err ("CPU%d: Failed to observe RWP==0 after enabling LPIs\n" , +smp_processor_id ()); +return -EBUSY ;drivers/irqchip/irq-gic-v3-its.c static bool gic_rdists_supports_plpis(void) · line 3331
Quoted 10 lines
drivers/irqchip/irq-gic-v3-its.c +7
-0+ } + +return 0 ; +} +int its_cpu_init (void ) { +int ret; +if (!list_empty (&its_nodes)) {nit: move 'ret' here.
Quoted 16 lines
drivers/irqchip/irq-gic-v3-its.c +4
-4-if (!gic_rdists_supports_plpis ()) { -pr_info ("CPU%d: LPIs not supported\n" ,smp_processor_id ()); -return -ENXIO ; - } + ret =redist_disable_lpis (); +if (ret) +return ret; +its_cpu_init_lpis ();its_cpu_init_collection (); }include/linux/irqchip/arm-gic-v3.h +0
-0include/linux/irqchip/arm-gic-v3.h line 112
Quoted 7 lines
include/linux/irqchip/arm-gic-v3.h +1
-0@@ -106,6 +106,7 @@#define GICR_PIDR2 GICD_PIDR2#define GICR_CTLR_ENABLE_LPIS (1UL << 0) +#define GICR_CTLR_RWP (1UL << 3)#define GICR_TYPER_CPU_NUMBER (r) (((r) >> 8) & 0xffff)Thanks,
M.
Signature · 3 lines
Shanker Donthineni Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
Quoted 108 lines · Shanker Donthineni
Hi Marc,
Quoted 74 lines · Marc Zyngier
Quoted 71 lines · On Thu, 15 Mar 2018 09:31:27 -0500 Shanker Donthineni…
The definition of the GICR_CTLR.RWP control bit was expanded to indicate status of changing GICR_CTLR.EnableLPI from 1 to 0 is being in progress or completed. Software must observe GICR_CTLR.RWP==0 after clearing GICR_CTLR.EnableLPI from 1 to 0 and before writing
GICR_PENDBASERand/orGICR_PROPBASER, otherwise behavior is UNPREDICTABLE.Signed-off-by Shanker Donthineni <[email protected]>---
Changes since v1:
-Moved LPI disable code to a seperate function as Marc suggested. -Mark's suggestion to usereadl_relaxed_poll_timeout()helper functions.drivers/irqchip/irq-gic-v3-its.c | 66 ++++++++++++++++++++++++++++++-------- include/linux/irqchip/arm-gic-v3.h | 1 + 2 files changed, 53 insertions(+), 14 deletions(-)drivers/irqchip/irq-gic-v3-its.c +23
-10@@ -33,6 +33,8 @@#include <linux/of_platform.h> #include <linux/percpu.h> #include <linux/slab.h> +#include <linux/time64.h> +#include <linux/iopoll.h> #include <linux/irqchip.h> #include <linux/irqchip/arm-gic-v3.h> @@ -1875,16 +1877,6 @@ static void its_cpu_init_lpis(void)gic_data_rdist ()->pend_page = pend_page; } -/* Disable LPIs */ - val =readl_relaxed (rbase +GICR_CTLR ); - val &= ~GICR_CTLR_ENABLE_LPIS ; -writel_relaxed (val, rbase +GICR_CTLR ); - -/* -* Make sure any change to the table is observable by the GIC. -*/ -dsb (sy); -/* set PROPBASE */ val = (page_to_phys(gic_rdists->prop_page) | GICR_PROPBASER_InnerShareable |@@ -3288,13 +3280,59 @@ static bool gic_rdists_supports_plpis(void)return !!(gic_read_typer (gic_data_rdist_rd_base () +GICR_TYPER ) &GICR_TYPER_PLPIS ); } +static int redist_disable_lpis (void ) +{ +void __iomem *rbase =gic_data_rdist_rd_base (); +u64 val; +int ret; + +if (!gic_rdists_supports_plpis ()) { +pr_info ("CPU%d: LPIs not supported\n" ,smp_processor_id ()); +return -ENXIO ; + } + + val =readl_relaxed (rbase +GICR_CTLR ); +if (!(val &GICR_CTLR_ENABLE_LPIS )) +return 0 ; + +/* Disable LPIs */ + val &= ~GICR_CTLR_ENABLE_LPIS ; +writel_relaxed (val, rbase +GICR_CTLR ); + +/* Make sure any change to GICR_CTLR is observable by the GIC */ +dsb (sy);Quoted 9 lines
drivers/irqchip/irq-gic-v3-its.c static bool gic_rdists_supports_plpis(void) · line 3308
Quoted 5 lines
drivers/irqchip/irq-gic-v3-its.c +5
-0+ +/* Wait for GICR_CTLR.GICR_CTLR_ENABLE_LPIS==0 or timeout */ + ret =readl_relaxed_poll_timeout_atomic (rbase +GICR_CTLR , val, + !(val &GICR_CTLR_ENABLE_LPIS ),1 , +USEC_PER_SEC );Why do you need to poll on EnableLPIs? It is supposed to be immediately written. It seems to me that the sequence should be:
Agree we don't need to poll EnableLPIs here. I'll remove.
Quoted 9 lines
- Check EnableLPis
- If set to 1, scream about bootloader being braindead
- Write EnableLPIs to 0
- Read back EnableLpis:
- If still set 1, bail out, we're doomed. Scream about the HW being braindead
- Poll RWP
- If timeout, bail out, the HW has locked up. Scream again.Quoted 4 lines
I think this should cover the whole thing. An alternative would be to place the poll between the write and read-back, but that doesn't change anything.
I'll prefer your second approach. Please comment on the below change before I post v3.
static int
redist_disable_lpis(void)
Quoted 14 lines
{
void __iomem *rbase = gic_data_rdist_rd_base();
u64 val;
int ret;
if (!gic_rdists_supports_plpis()) {
pr_info("CPU%d: LPIs not supported\n", smp_processor_id());
return -ENXIO;
}
if (!(readl(rbase + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS))
return 0;
pr_warn("CPU%d: Trying to disable LPIs\n", smp_processor_id());I'd be much more clear:
pr_warn("CPU%d: Booted with LPIs enabled, memory probably corrupted\n",
smp_processor_id());
add_taint(TAINT_CRAP, LOCKDEP_STILL_OK);Quoted 8 lines
val &= ~GICR_CTLR_ENABLE_LPIS;
writel_relaxed(val, rbase + GICR_CTLR);
/* Make sure any change to GICR_CTLR is observable by the GIC */
dsb(sy);
/**
* Software must observe RWP==0 after clearing GICR_CTLR.EnableLPIsQuoted 8 lines
* from 1 to 0 before programming GICR_PEND{PROP}BASER registers.
* Bail out the driver probe() in case of timeout.
*/
ret = readl_relaxed_poll_timeout_atomic(rbase + GICR_CTLR, val,
!(val & GICR_CTLR_RWP), 1,
USEC_PER_SEC);
if (ret) {
pr_err("CPU%d: EnableLPIs not observed\n", smp_processor_id());Let's call a spade a spade: the redistributor hasn't reacted after a whole second, it has probably locked-up. Just say so, because your message doesn't mean much to me.
Quoted 11 lines
return ret;
}
/**
* After it has been written to 1, it is IMPLEMENTATION DEFINED whether
* the bit GICR_CTLR.EnableLPI becomes RES1 or can be cleared to 0.
* Bail out the driver probe() on systems where it's RES1.
*/
if (readl_relaxed(rbase + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS) {
pr_err("CPU%d: Failed to disable LPIs\n", smp_processor_id());
return -EPERM;EPERM? EBUSY was a better approximation...
Thanks,
M.
Signature · 1 line
Jazz is not dead, it just smell funny.
Hi Marc,
Quoted 148 lines · Marc Zyngier
Quoted 108 lines · Shanker Donthineni
Hi Marc,
Quoted 74 lines · Marc Zyngier
Quoted 71 lines · On Thu, 15 Mar 2018 09:31:27 -0500 Shanker Donthineni…
The definition of the GICR_CTLR.RWP control bit was expanded to indicate status of changing GICR_CTLR.EnableLPI from 1 to 0 is being in progress or completed. Software must observe GICR_CTLR.RWP==0 after clearing GICR_CTLR.EnableLPI from 1 to 0 and before writing
GICR_PENDBASERand/orGICR_PROPBASER, otherwise behavior is UNPREDICTABLE.Signed-off-by Shanker Donthineni <[email protected]>---
Changes since v1:
-Moved LPI disable code to a seperate function as Marc suggested. -Mark's suggestion to usereadl_relaxed_poll_timeout()helper functions.drivers/irqchip/irq-gic-v3-its.c | 66 ++++++++++++++++++++++++++++++-------- include/linux/irqchip/arm-gic-v3.h | 1 + 2 files changed, 53 insertions(+), 14 deletions(-)drivers/irqchip/irq-gic-v3-its.c +23
-10@@ -33,6 +33,8 @@#include <linux/of_platform.h> #include <linux/percpu.h> #include <linux/slab.h> +#include <linux/time64.h> +#include <linux/iopoll.h> #include <linux/irqchip.h> #include <linux/irqchip/arm-gic-v3.h> @@ -1875,16 +1877,6 @@ static void its_cpu_init_lpis(void)gic_data_rdist ()->pend_page = pend_page; } -/* Disable LPIs */ - val =readl_relaxed (rbase +GICR_CTLR ); - val &= ~GICR_CTLR_ENABLE_LPIS ; -writel_relaxed (val, rbase +GICR_CTLR ); - -/* -* Make sure any change to the table is observable by the GIC. -*/ -dsb (sy); -/* set PROPBASE */ val = (page_to_phys(gic_rdists->prop_page) | GICR_PROPBASER_InnerShareable |@@ -3288,13 +3280,59 @@ static bool gic_rdists_supports_plpis(void)return !!(gic_read_typer (gic_data_rdist_rd_base () +GICR_TYPER ) &GICR_TYPER_PLPIS ); } +static int redist_disable_lpis (void ) +{ +void __iomem *rbase =gic_data_rdist_rd_base (); +u64 val; +int ret; + +if (!gic_rdists_supports_plpis ()) { +pr_info ("CPU%d: LPIs not supported\n" ,smp_processor_id ()); +return -ENXIO ; + } + + val =readl_relaxed (rbase +GICR_CTLR ); +if (!(val &GICR_CTLR_ENABLE_LPIS )) +return 0 ; + +/* Disable LPIs */ + val &= ~GICR_CTLR_ENABLE_LPIS ; +writel_relaxed (val, rbase +GICR_CTLR ); + +/* Make sure any change to GICR_CTLR is observable by the GIC */ +dsb (sy);Quoted 9 lines
drivers/irqchip/irq-gic-v3-its.c static bool gic_rdists_supports_plpis(void) · line 3308
Quoted 5 lines
drivers/irqchip/irq-gic-v3-its.c +5
-0+ +/* Wait for GICR_CTLR.GICR_CTLR_ENABLE_LPIS==0 or timeout */ + ret =readl_relaxed_poll_timeout_atomic (rbase +GICR_CTLR , val, + !(val &GICR_CTLR_ENABLE_LPIS ),1 , +USEC_PER_SEC );Why do you need to poll on EnableLPIs? It is supposed to be immediately written. It seems to me that the sequence should be:
Agree we don't need to poll EnableLPIs here. I'll remove.
Quoted 9 lines
- Check EnableLPis
- If set to 1, scream about bootloader being braindead
- Write EnableLPIs to 0
- Read back EnableLpis:
- If still set 1, bail out, we're doomed. Scream about the HW being braindead
- Poll RWP
- If timeout, bail out, the HW has locked up. Scream again.Quoted 4 lines
I think this should cover the whole thing. An alternative would be to place the poll between the write and read-back, but that doesn't change anything.
I'll prefer your second approach. Please comment on the below change before I post v3.
static int
redist_disable_lpis(void)Quoted 14 lines
{ void __iomem *rbase = gic_data_rdist_rd_base(); u64 val; int ret; if (!gic_rdists_supports_plpis()) { pr_info("CPU%d: LPIs not supported\n", smp_processor_id()); return -ENXIO; } if (!(readl(rbase + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS)) return 0; pr_warn("CPU%d: Trying to disable LPIs\n", smp_processor_id());I'd be much more clear:
pr_warn("CPU%d: Booted with LPIs enabled, memory probably corrupted\n", smp_processor_id()); add_taint(TAINT_CRAP, LOCKDEP_STILL_OK);Quoted 8 lines
val &= ~GICR_CTLR_ENABLE_LPIS; writel_relaxed(val, rbase + GICR_CTLR); /* Make sure any change to GICR_CTLR is observable by the GIC */ dsb(sy); /** * Software must observe RWP==0 after clearing GICR_CTLR.EnableLPIsQuoted 8 lines
* from 1 to 0 before programming GICR_PEND{PROP}BASER registers. * Bail out the driver probe() in case of timeout. */ ret = readl_relaxed_poll_timeout_atomic(rbase + GICR_CTLR, val, !(val & GICR_CTLR_RWP), 1, USEC_PER_SEC); if (ret) { pr_err("CPU%d: EnableLPIs not observed\n", smp_processor_id());
Quoted 4 lines
Let's call a spade a spade: the redistributor hasn't reacted after a whole second, it has probably locked-up. Just say so, because your message doesn't mean much to me.
Sure I'll change, and also readX_relaxed_poll_XXX() helper macros aren't usable during GIC/ITS probe time, noticed kernel crashes when I tested KEXEC/KDUMP kernel on QDF2400. I believe this's because of the timer subsystem "timer_init()" is initialized after calling init_IRQ(). I've to go back to v1 change for handling timeout.
while (readl_relaxed(rbase + GICR_CTLR) & GICR_CTLR_RWP) {
if (!timeout) {
pr_err("CPU%d: Failed to observe RWP==0 after disabling LPIs\n",
smp_processor_id());
return -ETIMEDOUT;
}
udelay(1);
timeout--;
cpu_relax();
}Quoted 18 lines
Quoted 11 lines
return ret; } /** * After it has been written to 1, it is IMPLEMENTATION DEFINED whether * the bit GICR_CTLR.EnableLPI becomes RES1 or can be cleared to 0. * Bail out the driver probe() on systems where it's RES1. */ if (readl_relaxed(rbase + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS) { pr_err("CPU%d: Failed to disable LPIs\n", smp_processor_id()); return -EPERM;EPERM? EBUSY was a better approximation...
Thanks,
M.
Signature · 3 lines
Shanker Donthineni Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.