* [PATCH v2 0/2] riscv-imsic/aplic: Save and restore the registers @ 2025-08-06 8:27 Nick Hu 2025-08-06 8:27 ` [PATCH v2 1/2] irqchip/riscv-imsic: Restore the IMSIC registers Nick Hu 2025-08-06 8:27 ` [PATCH v2 2/2] irqchip/riscv-aplic: Save and restore APLIC registers Nick Hu 0 siblings, 2 replies; 9+ messages in thread From: Nick Hu @ 2025-08-06 8:27 UTC (permalink / raw) To: anup, Alexandre Ghiti, linux-riscv Cc: Nick Hu, Paul Walmsley, Palmer Dabbelt, Albert Ou Since the APLIC and IMSIC may be powered down when the CPUs enter a deep sleep state, it is necessary to implement save and restore functions to ensure their states are correctly preserved and restored. Changes in V2: - Address the compile error from kernel test robot when CONFIG_RISCV_M_MODE is enabled. - Apply the suggestion from Anup [1] Link: - [1] https://lore.kernel.org/linux-riscv/20250709025516.28051-1-nick.hu@sifive.com/ Nick Hu (2): irqchip/riscv-imsic: Restore the IMSIC registers irqchip/riscv-aplic: Save and restore APLIC registers drivers/irqchip/irq-riscv-aplic-direct.c | 11 ++ drivers/irqchip/irq-riscv-aplic-main.c | 158 ++++++++++++++++++++++- drivers/irqchip/irq-riscv-aplic-main.h | 11 ++ drivers/irqchip/irq-riscv-imsic-early.c | 41 ++++-- 4 files changed, 212 insertions(+), 9 deletions(-) -- 2.17.1 _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 1/2] irqchip/riscv-imsic: Restore the IMSIC registers 2025-08-06 8:27 [PATCH v2 0/2] riscv-imsic/aplic: Save and restore the registers Nick Hu @ 2025-08-06 8:27 ` Nick Hu 2025-08-06 9:28 ` Nutty Liu 2025-08-06 12:08 ` Thomas Gleixner 2025-08-06 8:27 ` [PATCH v2 2/2] irqchip/riscv-aplic: Save and restore APLIC registers Nick Hu 1 sibling, 2 replies; 9+ messages in thread From: Nick Hu @ 2025-08-06 8:27 UTC (permalink / raw) To: anup, Alexandre Ghiti, linux-riscv, linux-kernel Cc: Nick Hu, Thomas Gleixner, Paul Walmsley, Palmer Dabbelt, Albert Ou When the system woken up from the low power state, the IMSIC might be in the reset state. Therefore adding the CPU PM callbacks to restore the IMSIC register when the cpu resume from the low power state. Signed-off-by: Nick Hu <nick.hu@sifive.com> Reviewed-by: Yong-Xuan Wang <yongxuan.wang@sifive.com> Reviewed-by: Cyan Yang <cyan.yang@sifive.com> Reviewed-by: Anup Patel <anup@brainfault.org> --- drivers/irqchip/irq-riscv-imsic-early.c | 40 ++++++++++++++++++++----- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c index d9ae87808651..62bcbcae8bd4 100644 --- a/drivers/irqchip/irq-riscv-imsic-early.c +++ b/drivers/irqchip/irq-riscv-imsic-early.c @@ -7,6 +7,7 @@ #define pr_fmt(fmt) "riscv-imsic: " fmt #include <linux/acpi.h> #include <linux/cpu.h> +#include <linux/cpu_pm.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> @@ -109,14 +110,8 @@ static void imsic_handle_irq(struct irq_desc *desc) chained_irq_exit(chip, desc); } -static int imsic_starting_cpu(unsigned int cpu) +static void imsic_restore(void) { - /* Mark per-CPU IMSIC state as online */ - imsic_state_online(); - - /* Enable per-CPU parent interrupt */ - enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq)); - /* Setup IPIs */ imsic_ipi_starting_cpu(); @@ -128,6 +123,19 @@ static int imsic_starting_cpu(unsigned int cpu) /* Enable local interrupt delivery */ imsic_local_delivery(true); +} + +static int imsic_starting_cpu(unsigned int cpu) +{ + /* Mark per-CPU IMSIC state as online */ + imsic_state_online(); + + /* Enable per-CPU parent interrupt */ + enable_percpu_irq(imsic_parent_irq, + irq_get_trigger_type(imsic_parent_irq)); + + /* Restore the imsic reg */ + imsic_restore(); return 0; } @@ -143,6 +151,22 @@ static int imsic_dying_cpu(unsigned int cpu) return 0; } +static int imsic_pm_notifier(struct notifier_block *self, unsigned long cmd, void *v) +{ + switch (cmd) { + case CPU_PM_EXIT: + /* Restore the imsic reg */ + imsic_restore(); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block imsic_pm_notifier_block = { + .notifier_call = imsic_pm_notifier, +}; + static int __init imsic_early_probe(struct fwnode_handle *fwnode) { struct irq_domain *domain; @@ -180,7 +204,7 @@ static int __init imsic_early_probe(struct fwnode_handle *fwnode) cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_IMSIC_STARTING, "irqchip/riscv/imsic:starting", imsic_starting_cpu, imsic_dying_cpu); - return 0; + return cpu_pm_register_notifier(&imsic_pm_notifier_block); } static int __init imsic_early_dt_init(struct device_node *node, struct device_node *parent) -- 2.17.1 _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] irqchip/riscv-imsic: Restore the IMSIC registers 2025-08-06 8:27 ` [PATCH v2 1/2] irqchip/riscv-imsic: Restore the IMSIC registers Nick Hu @ 2025-08-06 9:28 ` Nutty Liu 2025-08-06 12:08 ` Thomas Gleixner 1 sibling, 0 replies; 9+ messages in thread From: Nutty Liu @ 2025-08-06 9:28 UTC (permalink / raw) To: Nick Hu, anup, Alexandre Ghiti, linux-riscv, linux-kernel Cc: Thomas Gleixner, Paul Walmsley, Palmer Dabbelt, Albert Ou On 8/6/2025 4:27 PM, Nick Hu wrote: > When the system woken up from the low power state, the IMSIC might be in > the reset state. Therefore adding the CPU PM callbacks to restore the > IMSIC register when the cpu resume from the low power state. > > Signed-off-by: Nick Hu <nick.hu@sifive.com> > Reviewed-by: Yong-Xuan Wang <yongxuan.wang@sifive.com> > Reviewed-by: Cyan Yang <cyan.yang@sifive.com> > Reviewed-by: Anup Patel <anup@brainfault.org> > --- > drivers/irqchip/irq-riscv-imsic-early.c | 40 ++++++++++++++++++++----- > 1 file changed, 32 insertions(+), 8 deletions(-) Reviewed-by: Nutty Liu <liujingqi@lanxincomputing.com> Thanks, Nutty > diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c > index d9ae87808651..62bcbcae8bd4 100644 > --- a/drivers/irqchip/irq-riscv-imsic-early.c > +++ b/drivers/irqchip/irq-riscv-imsic-early.c > @@ -7,6 +7,7 @@ > #define pr_fmt(fmt) "riscv-imsic: " fmt > #include <linux/acpi.h> > #include <linux/cpu.h> > +#include <linux/cpu_pm.h> > #include <linux/interrupt.h> > #include <linux/io.h> > #include <linux/irq.h> > @@ -109,14 +110,8 @@ static void imsic_handle_irq(struct irq_desc *desc) > chained_irq_exit(chip, desc); > } > > -static int imsic_starting_cpu(unsigned int cpu) > +static void imsic_restore(void) > { > - /* Mark per-CPU IMSIC state as online */ > - imsic_state_online(); > - > - /* Enable per-CPU parent interrupt */ > - enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq)); > - > /* Setup IPIs */ > imsic_ipi_starting_cpu(); > > @@ -128,6 +123,19 @@ static int imsic_starting_cpu(unsigned int cpu) > > /* Enable local interrupt delivery */ > imsic_local_delivery(true); > +} > + > +static int imsic_starting_cpu(unsigned int cpu) > +{ > + /* Mark per-CPU IMSIC state as online */ > + imsic_state_online(); > + > + /* Enable per-CPU parent interrupt */ > + enable_percpu_irq(imsic_parent_irq, > + irq_get_trigger_type(imsic_parent_irq)); > + > + /* Restore the imsic reg */ > + imsic_restore(); > > return 0; > } > @@ -143,6 +151,22 @@ static int imsic_dying_cpu(unsigned int cpu) > return 0; > } > > +static int imsic_pm_notifier(struct notifier_block *self, unsigned long cmd, void *v) > +{ > + switch (cmd) { > + case CPU_PM_EXIT: > + /* Restore the imsic reg */ > + imsic_restore(); > + break; > + } > + > + return NOTIFY_OK; > +} > + > +static struct notifier_block imsic_pm_notifier_block = { > + .notifier_call = imsic_pm_notifier, > +}; > + > static int __init imsic_early_probe(struct fwnode_handle *fwnode) > { > struct irq_domain *domain; > @@ -180,7 +204,7 @@ static int __init imsic_early_probe(struct fwnode_handle *fwnode) > cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_IMSIC_STARTING, "irqchip/riscv/imsic:starting", > imsic_starting_cpu, imsic_dying_cpu); > > - return 0; > + return cpu_pm_register_notifier(&imsic_pm_notifier_block); > } > > static int __init imsic_early_dt_init(struct device_node *node, struct device_node *parent) _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] irqchip/riscv-imsic: Restore the IMSIC registers 2025-08-06 8:27 ` [PATCH v2 1/2] irqchip/riscv-imsic: Restore the IMSIC registers Nick Hu 2025-08-06 9:28 ` Nutty Liu @ 2025-08-06 12:08 ` Thomas Gleixner 2025-08-15 2:18 ` Nick Hu 1 sibling, 1 reply; 9+ messages in thread From: Thomas Gleixner @ 2025-08-06 12:08 UTC (permalink / raw) To: Nick Hu, anup, Alexandre Ghiti, linux-riscv, linux-kernel Cc: Nick Hu, Paul Walmsley, Palmer Dabbelt, Albert Ou On Wed, Aug 06 2025 at 16:27, Nick Hu wrote: "Restore the IMSIC registers" does not tell me anything useful. When are they restored? > When the system woken up from the low power state, the IMSIC might be in is woken up from a low power state > the reset state. The real important information is: When the system enters a low power state the IMSIC might be reset, but on exit nothing restores the registers, which prevents interrupt delivery. Or something like that. > Therefore adding the CPU PM callbacks to restore the IMSIC register > when the cpu resume from the low power state. This is not a valid sentence. Solve this by registering a CPU power management notifier, which restores the IMSIC on exit. Or such. See https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#changelog for further explanation. > diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c > index d9ae87808651..62bcbcae8bd4 100644 > --- a/drivers/irqchip/irq-riscv-imsic-early.c > +++ b/drivers/irqchip/irq-riscv-imsic-early.c > @@ -7,6 +7,7 @@ > #define pr_fmt(fmt) "riscv-imsic: " fmt > #include <linux/acpi.h> > #include <linux/cpu.h> > +#include <linux/cpu_pm.h> > #include <linux/interrupt.h> This does neither apply against Linus tree nor against tip > -static int imsic_starting_cpu(unsigned int cpu) > +static void imsic_restore(void) This function is used for both setup _and_ restore, so naming it restore() is misleading at best. > { > - /* Mark per-CPU IMSIC state as online */ > - imsic_state_online(); > - > - /* Enable per-CPU parent interrupt */ > - enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq)); > - > /* Setup IPIs */ > imsic_ipi_starting_cpu(); > > @@ -128,6 +123,19 @@ static int imsic_starting_cpu(unsigned int cpu) > > /* Enable local interrupt delivery */ > imsic_local_delivery(true); > +} > + > +static int imsic_starting_cpu(unsigned int cpu) > +{ > + /* Mark per-CPU IMSIC state as online */ > + imsic_state_online(); > + > + /* Enable per-CPU parent interrupt */ > + enable_percpu_irq(imsic_parent_irq, > + irq_get_trigger_type(imsic_parent_irq)); No line break required. You have 100 characters. > + > + /* Restore the imsic reg */ One IMSIC register? Can you please write proper sentences and write words out? This is not twitter. Also use IMSIC uppercase as the rest of the code does in the comments. Thanks, tglx _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] irqchip/riscv-imsic: Restore the IMSIC registers 2025-08-06 12:08 ` Thomas Gleixner @ 2025-08-15 2:18 ` Nick Hu 0 siblings, 0 replies; 9+ messages in thread From: Nick Hu @ 2025-08-15 2:18 UTC (permalink / raw) To: Thomas Gleixner Cc: anup, Alexandre Ghiti, linux-riscv, linux-kernel, Paul Walmsley, Palmer Dabbelt, Albert Ou On Wed, Aug 6, 2025 at 8:08 PM Thomas Gleixner <tglx@linutronix.de> wrote: > > On Wed, Aug 06 2025 at 16:27, Nick Hu wrote: > > "Restore the IMSIC registers" does not tell me anything useful. When are > they restored? > I’ll update it to: “Preserve the IMSIC states after PM” > > When the system woken up from the low power state, the IMSIC might be in > > is woken up from a low power state > > > the reset state. > > The real important information is: > > When the system enters a low power state the IMSIC might be reset, > but on exit nothing restores the registers, which prevents interrupt > delivery. > > Or something like that. > > > Therefore adding the CPU PM callbacks to restore the IMSIC register > > when the cpu resume from the low power state. > > This is not a valid sentence. > > Solve this by registering a CPU power management notifier, which > restores the IMSIC on exit. > > Or such. > > See > > https://www.kernel.org/doc/html/latest/process/maintainer-tip.html#changelog > > for further explanation. > I'll correct it in the next version. Thanks. > > diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c > > index d9ae87808651..62bcbcae8bd4 100644 > > --- a/drivers/irqchip/irq-riscv-imsic-early.c > > +++ b/drivers/irqchip/irq-riscv-imsic-early.c > > @@ -7,6 +7,7 @@ > > #define pr_fmt(fmt) "riscv-imsic: " fmt > > #include <linux/acpi.h> > > #include <linux/cpu.h> > > +#include <linux/cpu_pm.h> > > #include <linux/interrupt.h> > > This does neither apply against Linus tree nor against tip > > > -static int imsic_starting_cpu(unsigned int cpu) > > +static void imsic_restore(void) > > This function is used for both setup _and_ restore, so naming it > restore() is misleading at best. > Will rename it to imsic_regs_init() > > { > > - /* Mark per-CPU IMSIC state as online */ > > - imsic_state_online(); > > - > > - /* Enable per-CPU parent interrupt */ > > - enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq)); > > - > > /* Setup IPIs */ > > imsic_ipi_starting_cpu(); > > > > @@ -128,6 +123,19 @@ static int imsic_starting_cpu(unsigned int cpu) > > > > /* Enable local interrupt delivery */ > > imsic_local_delivery(true); > > +} > > + > > +static int imsic_starting_cpu(unsigned int cpu) > > +{ > > + /* Mark per-CPU IMSIC state as online */ > > + imsic_state_online(); > > + > > + /* Enable per-CPU parent interrupt */ > > + enable_percpu_irq(imsic_parent_irq, > > + irq_get_trigger_type(imsic_parent_irq)); > > No line break required. You have 100 characters. > > > + > > + /* Restore the imsic reg */ > > One IMSIC register? Can you please write proper sentences and write > words out? This is not twitter. Also use IMSIC uppercase as the rest of > the code does in the comments. > Sorry, I’ll be more careful. > Thanks, > > tglx _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 2/2] irqchip/riscv-aplic: Save and restore APLIC registers 2025-08-06 8:27 [PATCH v2 0/2] riscv-imsic/aplic: Save and restore the registers Nick Hu 2025-08-06 8:27 ` [PATCH v2 1/2] irqchip/riscv-imsic: Restore the IMSIC registers Nick Hu @ 2025-08-06 8:27 ` Nick Hu 2025-08-06 9:47 ` Nutty Liu 2025-08-06 12:52 ` Thomas Gleixner 1 sibling, 2 replies; 9+ messages in thread From: Nick Hu @ 2025-08-06 8:27 UTC (permalink / raw) To: anup, Alexandre Ghiti, linux-riscv, linux-kernel Cc: Nick Hu, Thomas Gleixner, Paul Walmsley, Palmer Dabbelt, Albert Ou The APLIC may be powered down when the CPUs enter a deep sleep state. Therefore adding the APLIC save and restore functions to save and restore the states of APLIC. Signed-off-by: Nick Hu <nick.hu@sifive.com> Reviewed-by: Yong-Xuan Wang <yongxuan.wang@sifive.com> Reviewed-by: Cyan Yang <cyan.yang@sifive.com> --- drivers/irqchip/irq-riscv-aplic-direct.c | 11 ++ drivers/irqchip/irq-riscv-aplic-main.c | 158 ++++++++++++++++++++++- drivers/irqchip/irq-riscv-aplic-main.h | 11 ++ 3 files changed, 179 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-riscv-aplic-direct.c b/drivers/irqchip/irq-riscv-aplic-direct.c index 205ad61d15e4..61b9ac2e1b7b 100644 --- a/drivers/irqchip/irq-riscv-aplic-direct.c +++ b/drivers/irqchip/irq-riscv-aplic-direct.c @@ -8,6 +8,7 @@ #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/cpu.h> +#include <linux/cpumask.h> #include <linux/interrupt.h> #include <linux/irqchip.h> #include <linux/irqchip/chained_irq.h> @@ -171,6 +172,16 @@ static void aplic_idc_set_delivery(struct aplic_idc *idc, bool en) writel(de, idc->regs + APLIC_IDC_IDELIVERY); } +void aplic_direct_restore(struct aplic_priv *priv) +{ + struct aplic_direct *direct = + container_of(priv, struct aplic_direct, priv); + int cpu; + + for_each_cpu(cpu, &direct->lmask) + aplic_idc_set_delivery(per_cpu_ptr(&aplic_idcs, cpu), true); +} + static int aplic_direct_dying_cpu(unsigned int cpu) { if (aplic_direct_parent_irq) diff --git a/drivers/irqchip/irq-riscv-aplic-main.c b/drivers/irqchip/irq-riscv-aplic-main.c index 93e7c51f944a..91fe3305934d 100644 --- a/drivers/irqchip/irq-riscv-aplic-main.c +++ b/drivers/irqchip/irq-riscv-aplic-main.c @@ -12,10 +12,143 @@ #include <linux/of.h> #include <linux/of_irq.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> #include <linux/printk.h> +#include <linux/syscore_ops.h> #include "irq-riscv-aplic-main.h" +static LIST_HEAD(aplics); + +static void aplic_restore(struct aplic_priv *priv) +{ + int i; + u32 clrip; + + writel(priv->saved_domaincfg, priv->regs + APLIC_DOMAINCFG); +#ifdef CONFIG_RISCV_M_MODE + writel(priv->saved_msiaddr, priv->regs + APLIC_xMSICFGADDR); + writel(priv->saved_msiaddrh, priv->regs + APLIC_xMSICFGADDRH); +#endif + for (i = 1; i <= priv->nr_irqs; i++) { + writel(priv->saved_sourcecfg[i - 1], + priv->regs + APLIC_SOURCECFG_BASE + + (i - 1) * sizeof(u32)); + writel(priv->saved_target[i - 1], + priv->regs + APLIC_TARGET_BASE + + (i - 1) * sizeof(u32)); + } + + for (i = 0; i <= priv->nr_irqs; i += 32) { + writel(-1U, priv->regs + APLIC_CLRIE_BASE + + (i / 32) * sizeof(u32)); + writel(priv->saved_ie[i / 32], + priv->regs + APLIC_SETIE_BASE + + (i / 32) * sizeof(u32)); + } + + if (priv->nr_idcs) { + aplic_direct_restore(priv); + } else { + /* Re-trigger the interrupts */ + for (i = 0; i <= priv->nr_irqs; i += 32) { + clrip = readl(priv->regs + APLIC_CLRIP_BASE + + (i / 32) * sizeof(u32)); + writel(clrip, priv->regs + APLIC_SETIP_BASE + + (i / 32) * sizeof(u32)); + } + } +} + +static void aplic_save(struct aplic_priv *priv) +{ + int i; + + for (i = 1; i <= priv->nr_irqs; i++) { + priv->saved_target[i - 1] = readl(priv->regs + + APLIC_TARGET_BASE + + (i - 1) * sizeof(u32)); + } + + for (i = 0; i <= priv->nr_irqs; i += 32) { + priv->saved_ie[i / 32] = readl(priv->regs + + APLIC_SETIE_BASE + + (i / 32) * sizeof(u32)); + } +} + +static int aplic_syscore_suspend(void) +{ + struct aplic_priv *priv; + + list_for_each_entry(priv, &aplics, head) { + aplic_save(priv); + } + + return 0; +} + +static void aplic_syscore_resume(void) +{ + struct aplic_priv *priv; + + list_for_each_entry(priv, &aplics, head) { + aplic_restore(priv); + } +} + +static struct syscore_ops aplic_syscore_ops = { + .suspend = aplic_syscore_suspend, + .resume = aplic_syscore_resume, +}; + +static int aplic_pm_notifier(struct notifier_block *nb, unsigned long action, void *data) +{ + struct aplic_priv *priv = container_of(nb, struct aplic_priv, genpd_nb); + + switch (action) { + case GENPD_NOTIFY_PRE_OFF: + aplic_save(priv); + break; + case GENPD_NOTIFY_ON: + aplic_restore(priv); + break; + default: + break; + } + + return 0; +} + +static void aplic_remove(void *data) +{ + struct aplic_priv *priv = data; + + list_del(&priv->head); + dev_pm_genpd_remove_notifier(priv->dev); +} + +static int aplic_add(struct device *dev, struct aplic_priv *priv) +{ + int ret; + + list_add(&priv->head, &aplics); + /* Add genpd notifier */ + priv->genpd_nb.notifier_call = aplic_pm_notifier; + ret = dev_pm_genpd_add_notifier(dev, &priv->genpd_nb); + if (ret && ret != -ENODEV && ret != -EOPNOTSUPP) { + list_del(&priv->head); + return ret; + } + + ret = devm_add_action_or_reset(dev, aplic_remove, priv); + if (ret) + return ret; + + return devm_pm_runtime_enable(dev); +} + void aplic_irq_unmask(struct irq_data *d) { struct aplic_priv *priv = irq_data_get_irq_chip_data(d); @@ -59,6 +192,7 @@ int aplic_irq_set_type(struct irq_data *d, unsigned int type) sourcecfg = priv->regs + APLIC_SOURCECFG_BASE; sourcecfg += (d->hwirq - 1) * sizeof(u32); writel(val, sourcecfg); + priv->saved_sourcecfg[d->hwirq - 1] = val; return 0; } @@ -95,6 +229,8 @@ void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode) valh |= FIELD_PREP(APLIC_xMSICFGADDRH_HHXS, priv->msicfg.hhxs); writel(val, priv->regs + APLIC_xMSICFGADDR); writel(valh, priv->regs + APLIC_xMSICFGADDRH); + priv->saved_msiaddr = val; + priv->saved_msiaddrh = valh; } #endif @@ -106,6 +242,7 @@ void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode) writel(val, priv->regs + APLIC_DOMAINCFG); if (readl(priv->regs + APLIC_DOMAINCFG) != val) dev_warn(priv->dev, "unable to write 0x%x in domaincfg\n", val); + priv->saved_domaincfg = val; } static void aplic_init_hw_irqs(struct aplic_priv *priv) @@ -176,7 +313,24 @@ int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem * /* Setup initial state APLIC interrupts */ aplic_init_hw_irqs(priv); - return 0; + /* For power management */ + priv->saved_target = devm_kzalloc(dev, priv->nr_irqs * sizeof(u32), + GFP_KERNEL); + if (!priv->saved_target) + return -ENOMEM; + + priv->saved_sourcecfg = devm_kzalloc(dev, priv->nr_irqs * sizeof(u32), + GFP_KERNEL); + if (!priv->saved_sourcecfg) + return -ENOMEM; + + priv->saved_ie = devm_kzalloc(dev, + DIV_ROUND_UP(priv->nr_irqs, 32) * sizeof(u32), + GFP_KERNEL); + if (!priv->saved_ie) + return -ENOMEM; + + return aplic_add(dev, priv); } static int aplic_probe(struct platform_device *pdev) @@ -209,6 +363,8 @@ static int aplic_probe(struct platform_device *pdev) if (rc) dev_err_probe(dev, rc, "failed to setup APLIC in %s mode\n", msi_mode ? "MSI" : "direct"); + else + register_syscore_ops(&aplic_syscore_ops); #ifdef CONFIG_ACPI if (!acpi_disabled) diff --git a/drivers/irqchip/irq-riscv-aplic-main.h b/drivers/irqchip/irq-riscv-aplic-main.h index b0ad8cde69b1..f27d5ff1c741 100644 --- a/drivers/irqchip/irq-riscv-aplic-main.h +++ b/drivers/irqchip/irq-riscv-aplic-main.h @@ -24,6 +24,7 @@ struct aplic_msicfg { }; struct aplic_priv { + struct list_head head; struct device *dev; u32 gsi_base; u32 nr_irqs; @@ -31,6 +32,15 @@ struct aplic_priv { u32 acpi_aplic_id; void __iomem *regs; struct aplic_msicfg msicfg; + struct notifier_block genpd_nb; + u32 *saved_target; + u32 *saved_sourcecfg; + u32 *saved_ie; + u32 saved_domaincfg; +#ifdef CONFIG_RISCV_M_MODE + u32 saved_msiaddr; + u32 saved_msiaddrh; +#endif }; void aplic_irq_unmask(struct irq_data *d); @@ -39,6 +49,7 @@ int aplic_irq_set_type(struct irq_data *d, unsigned int type); int aplic_irqdomain_translate(struct irq_fwspec *fwspec, u32 gsi_base, unsigned long *hwirq, unsigned int *type); void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode); +void aplic_direct_restore(struct aplic_priv *priv); int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *regs); int aplic_direct_setup(struct device *dev, void __iomem *regs); #ifdef CONFIG_RISCV_APLIC_MSI -- 2.17.1 _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] irqchip/riscv-aplic: Save and restore APLIC registers 2025-08-06 8:27 ` [PATCH v2 2/2] irqchip/riscv-aplic: Save and restore APLIC registers Nick Hu @ 2025-08-06 9:47 ` Nutty Liu 2025-08-06 12:52 ` Thomas Gleixner 1 sibling, 0 replies; 9+ messages in thread From: Nutty Liu @ 2025-08-06 9:47 UTC (permalink / raw) To: Nick Hu, anup, Alexandre Ghiti, linux-riscv, linux-kernel Cc: Thomas Gleixner, Paul Walmsley, Palmer Dabbelt, Albert Ou On 8/6/2025 4:27 PM, Nick Hu wrote: > The APLIC may be powered down when the CPUs enter a deep sleep state. > Therefore adding the APLIC save and restore functions to save and > restore the states of APLIC. > > Signed-off-by: Nick Hu <nick.hu@sifive.com> > Reviewed-by: Yong-Xuan Wang <yongxuan.wang@sifive.com> > Reviewed-by: Cyan Yang <cyan.yang@sifive.com> > --- > drivers/irqchip/irq-riscv-aplic-direct.c | 11 ++ > drivers/irqchip/irq-riscv-aplic-main.c | 158 ++++++++++++++++++++++- > drivers/irqchip/irq-riscv-aplic-main.h | 11 ++ > 3 files changed, 179 insertions(+), 1 deletion(-) Reviewed-by: Nutty Liu <liujingqi@lanxincomputing.com> Thanks, Nutty > diff --git a/drivers/irqchip/irq-riscv-aplic-direct.c b/drivers/irqchip/irq-riscv-aplic-direct.c > index 205ad61d15e4..61b9ac2e1b7b 100644 > --- a/drivers/irqchip/irq-riscv-aplic-direct.c > +++ b/drivers/irqchip/irq-riscv-aplic-direct.c > @@ -8,6 +8,7 @@ > #include <linux/bitfield.h> > #include <linux/bitops.h> > #include <linux/cpu.h> > +#include <linux/cpumask.h> > #include <linux/interrupt.h> > #include <linux/irqchip.h> > #include <linux/irqchip/chained_irq.h> > @@ -171,6 +172,16 @@ static void aplic_idc_set_delivery(struct aplic_idc *idc, bool en) > writel(de, idc->regs + APLIC_IDC_IDELIVERY); > } > > +void aplic_direct_restore(struct aplic_priv *priv) > +{ > + struct aplic_direct *direct = > + container_of(priv, struct aplic_direct, priv); > + int cpu; > + > + for_each_cpu(cpu, &direct->lmask) > + aplic_idc_set_delivery(per_cpu_ptr(&aplic_idcs, cpu), true); > +} > + > static int aplic_direct_dying_cpu(unsigned int cpu) > { > if (aplic_direct_parent_irq) > diff --git a/drivers/irqchip/irq-riscv-aplic-main.c b/drivers/irqchip/irq-riscv-aplic-main.c > index 93e7c51f944a..91fe3305934d 100644 > --- a/drivers/irqchip/irq-riscv-aplic-main.c > +++ b/drivers/irqchip/irq-riscv-aplic-main.c > @@ -12,10 +12,143 @@ > #include <linux/of.h> > #include <linux/of_irq.h> > #include <linux/platform_device.h> > +#include <linux/pm_domain.h> > +#include <linux/pm_runtime.h> > #include <linux/printk.h> > +#include <linux/syscore_ops.h> > > #include "irq-riscv-aplic-main.h" > > +static LIST_HEAD(aplics); > + > +static void aplic_restore(struct aplic_priv *priv) > +{ > + int i; > + u32 clrip; > + > + writel(priv->saved_domaincfg, priv->regs + APLIC_DOMAINCFG); > +#ifdef CONFIG_RISCV_M_MODE > + writel(priv->saved_msiaddr, priv->regs + APLIC_xMSICFGADDR); > + writel(priv->saved_msiaddrh, priv->regs + APLIC_xMSICFGADDRH); > +#endif > + for (i = 1; i <= priv->nr_irqs; i++) { > + writel(priv->saved_sourcecfg[i - 1], > + priv->regs + APLIC_SOURCECFG_BASE + > + (i - 1) * sizeof(u32)); > + writel(priv->saved_target[i - 1], > + priv->regs + APLIC_TARGET_BASE + > + (i - 1) * sizeof(u32)); > + } > + > + for (i = 0; i <= priv->nr_irqs; i += 32) { > + writel(-1U, priv->regs + APLIC_CLRIE_BASE + > + (i / 32) * sizeof(u32)); > + writel(priv->saved_ie[i / 32], > + priv->regs + APLIC_SETIE_BASE + > + (i / 32) * sizeof(u32)); > + } > + > + if (priv->nr_idcs) { > + aplic_direct_restore(priv); > + } else { > + /* Re-trigger the interrupts */ > + for (i = 0; i <= priv->nr_irqs; i += 32) { > + clrip = readl(priv->regs + APLIC_CLRIP_BASE + > + (i / 32) * sizeof(u32)); > + writel(clrip, priv->regs + APLIC_SETIP_BASE + > + (i / 32) * sizeof(u32)); > + } > + } > +} > + > +static void aplic_save(struct aplic_priv *priv) > +{ > + int i; > + > + for (i = 1; i <= priv->nr_irqs; i++) { > + priv->saved_target[i - 1] = readl(priv->regs + > + APLIC_TARGET_BASE + > + (i - 1) * sizeof(u32)); > + } > + > + for (i = 0; i <= priv->nr_irqs; i += 32) { > + priv->saved_ie[i / 32] = readl(priv->regs + > + APLIC_SETIE_BASE + > + (i / 32) * sizeof(u32)); > + } > +} > + > +static int aplic_syscore_suspend(void) > +{ > + struct aplic_priv *priv; > + > + list_for_each_entry(priv, &aplics, head) { > + aplic_save(priv); > + } > + > + return 0; > +} > + > +static void aplic_syscore_resume(void) > +{ > + struct aplic_priv *priv; > + > + list_for_each_entry(priv, &aplics, head) { > + aplic_restore(priv); > + } > +} > + > +static struct syscore_ops aplic_syscore_ops = { > + .suspend = aplic_syscore_suspend, > + .resume = aplic_syscore_resume, > +}; > + > +static int aplic_pm_notifier(struct notifier_block *nb, unsigned long action, void *data) > +{ > + struct aplic_priv *priv = container_of(nb, struct aplic_priv, genpd_nb); > + > + switch (action) { > + case GENPD_NOTIFY_PRE_OFF: > + aplic_save(priv); > + break; > + case GENPD_NOTIFY_ON: > + aplic_restore(priv); > + break; > + default: > + break; > + } > + > + return 0; > +} > + > +static void aplic_remove(void *data) > +{ > + struct aplic_priv *priv = data; > + > + list_del(&priv->head); > + dev_pm_genpd_remove_notifier(priv->dev); > +} > + > +static int aplic_add(struct device *dev, struct aplic_priv *priv) > +{ > + int ret; > + > + list_add(&priv->head, &aplics); > + /* Add genpd notifier */ > + priv->genpd_nb.notifier_call = aplic_pm_notifier; > + ret = dev_pm_genpd_add_notifier(dev, &priv->genpd_nb); > + if (ret && ret != -ENODEV && ret != -EOPNOTSUPP) { > + list_del(&priv->head); > + return ret; > + } > + > + ret = devm_add_action_or_reset(dev, aplic_remove, priv); > + if (ret) > + return ret; > + > + return devm_pm_runtime_enable(dev); > +} > + > void aplic_irq_unmask(struct irq_data *d) > { > struct aplic_priv *priv = irq_data_get_irq_chip_data(d); > @@ -59,6 +192,7 @@ int aplic_irq_set_type(struct irq_data *d, unsigned int type) > sourcecfg = priv->regs + APLIC_SOURCECFG_BASE; > sourcecfg += (d->hwirq - 1) * sizeof(u32); > writel(val, sourcecfg); > + priv->saved_sourcecfg[d->hwirq - 1] = val; > > return 0; > } > @@ -95,6 +229,8 @@ void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode) > valh |= FIELD_PREP(APLIC_xMSICFGADDRH_HHXS, priv->msicfg.hhxs); > writel(val, priv->regs + APLIC_xMSICFGADDR); > writel(valh, priv->regs + APLIC_xMSICFGADDRH); > + priv->saved_msiaddr = val; > + priv->saved_msiaddrh = valh; > } > #endif > > @@ -106,6 +242,7 @@ void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode) > writel(val, priv->regs + APLIC_DOMAINCFG); > if (readl(priv->regs + APLIC_DOMAINCFG) != val) > dev_warn(priv->dev, "unable to write 0x%x in domaincfg\n", val); > + priv->saved_domaincfg = val; > } > > static void aplic_init_hw_irqs(struct aplic_priv *priv) > @@ -176,7 +313,24 @@ int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem * > /* Setup initial state APLIC interrupts */ > aplic_init_hw_irqs(priv); > > - return 0; > + /* For power management */ > + priv->saved_target = devm_kzalloc(dev, priv->nr_irqs * sizeof(u32), > + GFP_KERNEL); > + if (!priv->saved_target) > + return -ENOMEM; > + > + priv->saved_sourcecfg = devm_kzalloc(dev, priv->nr_irqs * sizeof(u32), > + GFP_KERNEL); > + if (!priv->saved_sourcecfg) > + return -ENOMEM; > + > + priv->saved_ie = devm_kzalloc(dev, > + DIV_ROUND_UP(priv->nr_irqs, 32) * sizeof(u32), > + GFP_KERNEL); > + if (!priv->saved_ie) > + return -ENOMEM; > + > + return aplic_add(dev, priv); > } > > static int aplic_probe(struct platform_device *pdev) > @@ -209,6 +363,8 @@ static int aplic_probe(struct platform_device *pdev) > if (rc) > dev_err_probe(dev, rc, "failed to setup APLIC in %s mode\n", > msi_mode ? "MSI" : "direct"); > + else > + register_syscore_ops(&aplic_syscore_ops); > > #ifdef CONFIG_ACPI > if (!acpi_disabled) > diff --git a/drivers/irqchip/irq-riscv-aplic-main.h b/drivers/irqchip/irq-riscv-aplic-main.h > index b0ad8cde69b1..f27d5ff1c741 100644 > --- a/drivers/irqchip/irq-riscv-aplic-main.h > +++ b/drivers/irqchip/irq-riscv-aplic-main.h > @@ -24,6 +24,7 @@ struct aplic_msicfg { > }; > > struct aplic_priv { > + struct list_head head; > struct device *dev; > u32 gsi_base; > u32 nr_irqs; > @@ -31,6 +32,15 @@ struct aplic_priv { > u32 acpi_aplic_id; > void __iomem *regs; > struct aplic_msicfg msicfg; > + struct notifier_block genpd_nb; > + u32 *saved_target; > + u32 *saved_sourcecfg; > + u32 *saved_ie; > + u32 saved_domaincfg; > +#ifdef CONFIG_RISCV_M_MODE > + u32 saved_msiaddr; > + u32 saved_msiaddrh; > +#endif > }; > > void aplic_irq_unmask(struct irq_data *d); > @@ -39,6 +49,7 @@ int aplic_irq_set_type(struct irq_data *d, unsigned int type); > int aplic_irqdomain_translate(struct irq_fwspec *fwspec, u32 gsi_base, > unsigned long *hwirq, unsigned int *type); > void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode); > +void aplic_direct_restore(struct aplic_priv *priv); > int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *regs); > int aplic_direct_setup(struct device *dev, void __iomem *regs); > #ifdef CONFIG_RISCV_APLIC_MSI _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] irqchip/riscv-aplic: Save and restore APLIC registers 2025-08-06 8:27 ` [PATCH v2 2/2] irqchip/riscv-aplic: Save and restore APLIC registers Nick Hu 2025-08-06 9:47 ` Nutty Liu @ 2025-08-06 12:52 ` Thomas Gleixner 2025-08-15 7:33 ` Nick Hu 1 sibling, 1 reply; 9+ messages in thread From: Thomas Gleixner @ 2025-08-06 12:52 UTC (permalink / raw) To: Nick Hu, anup, Alexandre Ghiti, linux-riscv, linux-kernel Cc: Nick Hu, Paul Walmsley, Palmer Dabbelt, Albert Ou On Wed, Aug 06 2025 at 16:27, Nick Hu wrote: > The APLIC may be powered down when the CPUs enter a deep sleep state. > Therefore adding the APLIC save and restore functions to save and > restore the states of APLIC. Same comments vs. subject and change log. > > +void aplic_direct_restore(struct aplic_priv *priv) > +{ > + struct aplic_direct *direct = > + container_of(priv, struct aplic_direct, priv); No line break required. > + int cpu; > + > + for_each_cpu(cpu, &direct->lmask) > + aplic_idc_set_delivery(per_cpu_ptr(&aplic_idcs, cpu), true); > +static LIST_HEAD(aplics); > + > +static void aplic_restore(struct aplic_priv *priv) > +{ > + int i; > + u32 clrip; See the documentation I linked you to and look for the chapter about variable declarations. > + writel(priv->saved_domaincfg, priv->regs + APLIC_DOMAINCFG); > +#ifdef CONFIG_RISCV_M_MODE > + writel(priv->saved_msiaddr, priv->regs + APLIC_xMSICFGADDR); > + writel(priv->saved_msiaddrh, priv->regs + APLIC_xMSICFGADDRH); > +#endif > + for (i = 1; i <= priv->nr_irqs; i++) { > + writel(priv->saved_sourcecfg[i - 1], > + priv->regs + APLIC_SOURCECFG_BASE + > + (i - 1) * sizeof(u32)); > + writel(priv->saved_target[i - 1], > + priv->regs + APLIC_TARGET_BASE + > + (i - 1) * sizeof(u32)); Sorry, but this is incomprehensible garbage. Why are you starting with i = 1 when you subtract 1 from i at every usage site? u32 __iomem *regs = priv->regs; for (i = 0; i < priv->nr_irqs; i++, regs++) { writel(priv->saved_sourcecfg[i], regs + APLIC_SOURCECFG_BASE); writel(priv->saved_target[i], regs + APLIC_TARGET_BASE); } See? > + } > + > + for (i = 0; i <= priv->nr_irqs; i += 32) { Off by one. This needs to be i < priv->nr_irqs, no? > + writel(-1U, priv->regs + APLIC_CLRIE_BASE + > + (i / 32) * sizeof(u32)); > + writel(priv->saved_ie[i / 32], > + priv->regs + APLIC_SETIE_BASE + > + (i / 32) * sizeof(u32)); > + } > + > + if (priv->nr_idcs) { > + aplic_direct_restore(priv); > + } else { > + /* Re-trigger the interrupts */ > + for (i = 0; i <= priv->nr_irqs; i += 32) { Same here > + clrip = readl(priv->regs + APLIC_CLRIP_BASE + > + (i / 32) * sizeof(u32)); > + writel(clrip, priv->regs + APLIC_SETIP_BASE + > + (i / 32) * sizeof(u32)); > + } And this can properly be combined into: u32 __iomem *regs = priv->regs; for (i = 0; i < priv->nr_irqs; i += 32, regs++) { writel(-1U, regs + APLIC_CLRIE_BASE); writel(priv->saved_ie[i / 32], regs + APLIC_SETIE_BASE); if (!priv->nr_idcs) writel(readl(regs + APLOC_CLRIP_BASE), regs + APLIC_SETIP_BASE); } if (priv->nr_idcs) aplic_direct_restore(priv); No? > +} > + > +static void aplic_save(struct aplic_priv *priv) > +{ > + int i; > + > + for (i = 1; i <= priv->nr_irqs; i++) { > + priv->saved_target[i - 1] = readl(priv->regs + > + APLIC_TARGET_BASE + > + (i - 1) * sizeof(u32)); > + } Oh well... > + > + for (i = 0; i <= priv->nr_irqs; i += 32) { Off by one again ... > + priv->saved_ie[i / 32] = readl(priv->regs + > + APLIC_SETIE_BASE + > + (i / 32) * sizeof(u32)); > + } > +} > + > +static int aplic_syscore_suspend(void) > +{ > + struct aplic_priv *priv; > + > + list_for_each_entry(priv, &aplics, head) { > + aplic_save(priv); > + } Superflous brackets > + > + return 0; > +} > + > +static void aplic_syscore_resume(void) > +{ > + struct aplic_priv *priv; > + > + list_for_each_entry(priv, &aplics, head) { > + aplic_restore(priv); > + } Ditto > +} > + > +static struct syscore_ops aplic_syscore_ops = { > + .suspend = aplic_syscore_suspend, > + .resume = aplic_syscore_resume, See documentation about struct definitions and initializers > +}; > + > +static int aplic_pm_notifier(struct notifier_block *nb, unsigned long action, void *data) > +{ > + struct aplic_priv *priv = container_of(nb, struct aplic_priv, genpd_nb); > + > + switch (action) { > + case GENPD_NOTIFY_PRE_OFF: > + aplic_save(priv); > + break; > + case GENPD_NOTIFY_ON: > + aplic_restore(priv); > + break; > + default: > + break; > + } > + > + return 0; > +} > + > +static void aplic_remove(void *data) > +{ > + struct aplic_priv *priv = data; > + > + list_del(&priv->head); How is this list operation serialized? > + dev_pm_genpd_remove_notifier(priv->dev); > +} > + > +static int aplic_add(struct device *dev, struct aplic_priv *priv) > +{ > + int ret; > + > + list_add(&priv->head, &aplics); > + /* Add genpd notifier */ > + priv->genpd_nb.notifier_call = aplic_pm_notifier; > + ret = dev_pm_genpd_add_notifier(dev, &priv->genpd_nb); > + if (ret && ret != -ENODEV && ret != -EOPNOTSUPP) { What is this magic about? Lacks explanation and rationale. > + list_del(&priv->head); > + return ret; ... > + /* For power management */ > + priv->saved_target = devm_kzalloc(dev, priv->nr_irqs * sizeof(u32), > + GFP_KERNEL); > + if (!priv->saved_target) > + return -ENOMEM; > + > + priv->saved_sourcecfg = devm_kzalloc(dev, priv->nr_irqs * sizeof(u32), > + GFP_KERNEL); > + if (!priv->saved_sourcecfg) > + return -ENOMEM; > + > + priv->saved_ie = devm_kzalloc(dev, > + DIV_ROUND_UP(priv->nr_irqs, 32) * sizeof(u32), > + GFP_KERNEL); > + if (!priv->saved_ie) > + return -ENOMEM; Seriously? You allocate all of this seperately? Ever heard about the concept of data structures? struct savedregs { u32 target; u32 source; u32 ie; }; priv->savedregs = devm_kcalloc(dev, priv->nr_irqs, sizeof(*priv->savedregs, GFP_KERNEL); Yes, I know you don't need as many ie registers, but that's not a problem at all. You just need to write/read to/from index 0, 32, 64 ... And your restore muck boils down to a single loop: u32 __iomem *regs = priv->regs; for (i = 0; i < priv->nr_irqs; i++, regs++) { writel(priv->savedregs->source[i], regs + APLIC_SOURCECFG_BASE); writel(priv->savedregs->target[i], regs + APLIC_TARGET_BASE); if (i % 32) continue; writel(-1U, regs + APLIC_CLRIE_BASE); writel(priv->saved_ie[i], regs + APLIC_SETIE_BASE); if (!priv->nr_idcs) writel(readl(regs + APLOC_CLRIP_BASE), regs + APLIC_SETIP_BASE); } That's too comprehensible, right? > struct aplic_priv { > + struct list_head head; Why needs this to be at the top? There is no reason at all. Not that it matters much as this data structure is a trainwreck vs. cacheline efficiency anyway. > struct device *dev; > u32 gsi_base; > u32 nr_irqs; > @@ -31,6 +32,15 @@ struct aplic_priv { > u32 acpi_aplic_id; > void __iomem *regs; > struct aplic_msicfg msicfg; > + struct notifier_block genpd_nb; > + u32 *saved_target; So you aligned @head and @genpd_nb, but then you use random formatting to turn this into visual clutter. I'm amazed that this lot has received three Reviewed-by tags and nobody found even the slightest issue with it. Oh well. Thanks, tglx _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] irqchip/riscv-aplic: Save and restore APLIC registers 2025-08-06 12:52 ` Thomas Gleixner @ 2025-08-15 7:33 ` Nick Hu 0 siblings, 0 replies; 9+ messages in thread From: Nick Hu @ 2025-08-15 7:33 UTC (permalink / raw) To: Thomas Gleixner Cc: anup, Alexandre Ghiti, linux-riscv, linux-kernel, Paul Walmsley, Palmer Dabbelt, Albert Ou On Wed, Aug 6, 2025 at 8:52 PM Thomas Gleixner <tglx@linutronix.de> wrote: > > On Wed, Aug 06 2025 at 16:27, Nick Hu wrote: > > The APLIC may be powered down when the CPUs enter a deep sleep state. > > Therefore adding the APLIC save and restore functions to save and > > restore the states of APLIC. > > Same comments vs. subject and change log. > > > > > +void aplic_direct_restore(struct aplic_priv *priv) > > +{ > > + struct aplic_direct *direct = > > + container_of(priv, struct aplic_direct, priv); > > No line break required. > > > + int cpu; > > + > > + for_each_cpu(cpu, &direct->lmask) > > + aplic_idc_set_delivery(per_cpu_ptr(&aplic_idcs, cpu), true); > > +static LIST_HEAD(aplics); > > + > > +static void aplic_restore(struct aplic_priv *priv) > > +{ > > + int i; > > + u32 clrip; > > See the documentation I linked you to and look for the chapter about > variable declarations. > > > + writel(priv->saved_domaincfg, priv->regs + APLIC_DOMAINCFG); > > +#ifdef CONFIG_RISCV_M_MODE > > + writel(priv->saved_msiaddr, priv->regs + APLIC_xMSICFGADDR); > > + writel(priv->saved_msiaddrh, priv->regs + APLIC_xMSICFGADDRH); > > +#endif > > + for (i = 1; i <= priv->nr_irqs; i++) { > > + writel(priv->saved_sourcecfg[i - 1], > > + priv->regs + APLIC_SOURCECFG_BASE + > > + (i - 1) * sizeof(u32)); > > + writel(priv->saved_target[i - 1], > > + priv->regs + APLIC_TARGET_BASE + > > + (i - 1) * sizeof(u32)); > > Sorry, but this is incomprehensible garbage. > > Why are you starting with i = 1 when you subtract 1 from i at every > usage site? > The AIA specification states, “The number zero is not a valid interrupt identity number at an APLIC,” so I thought it was intuitive to start with i = 1 up to `priv->nr_irqs`. However, you are right, I should start from 0 so the index doesn’t need to be offset by 1 in every iteration. > u32 __iomem *regs = priv->regs; > for (i = 0; i < priv->nr_irqs; i++, regs++) { > writel(priv->saved_sourcecfg[i], regs + APLIC_SOURCECFG_BASE); > writel(priv->saved_target[i], regs + APLIC_TARGET_BASE); > } > > See? > > > + } > > + > > + for (i = 0; i <= priv->nr_irqs; i += 32) { > > Off by one. This needs to be i < priv->nr_irqs, no? > We need to preserve bits 1 through priv->nr_irqs of the SETIE register, so this should be i <= priv->nr_irqs. > > + writel(-1U, priv->regs + APLIC_CLRIE_BASE + > > + (i / 32) * sizeof(u32)); > > + writel(priv->saved_ie[i / 32], > > + priv->regs + APLIC_SETIE_BASE + > > + (i / 32) * sizeof(u32)); > > + } > > + > > + if (priv->nr_idcs) { > > + aplic_direct_restore(priv); > > + } else { > > + /* Re-trigger the interrupts */ > > + for (i = 0; i <= priv->nr_irqs; i += 32) { > > Same here > > > + clrip = readl(priv->regs + APLIC_CLRIP_BASE + > > + (i / 32) * sizeof(u32)); > > + writel(clrip, priv->regs + APLIC_SETIP_BASE + > > + (i / 32) * sizeof(u32)); > > + } > > And this can properly be combined into: > > u32 __iomem *regs = priv->regs; > > for (i = 0; i < priv->nr_irqs; i += 32, regs++) { > writel(-1U, regs + APLIC_CLRIE_BASE); > writel(priv->saved_ie[i / 32], regs + APLIC_SETIE_BASE); > > if (!priv->nr_idcs) > writel(readl(regs + APLOC_CLRIP_BASE), regs + APLIC_SETIP_BASE); > } > > if (priv->nr_idcs) > aplic_direct_restore(priv); > > No? > I'll update it in the next version. > > +} > > + > > +static void aplic_save(struct aplic_priv *priv) > > +{ > > + int i; > > + > > + for (i = 1; i <= priv->nr_irqs; i++) { > > + priv->saved_target[i - 1] = readl(priv->regs + > > + APLIC_TARGET_BASE + > > + (i - 1) * sizeof(u32)); > > + } > > Oh well... > > > + > > + for (i = 0; i <= priv->nr_irqs; i += 32) { > > Off by one again ... > > > + priv->saved_ie[i / 32] = readl(priv->regs + > > + APLIC_SETIE_BASE + > > + (i / 32) * sizeof(u32)); > > + } > > +} > > + > > +static int aplic_syscore_suspend(void) > > +{ > > + struct aplic_priv *priv; > > + > > + list_for_each_entry(priv, &aplics, head) { > > + aplic_save(priv); > > + } > > Superflous brackets > > > + > > + return 0; > > +} > > + > > +static void aplic_syscore_resume(void) > > +{ > > + struct aplic_priv *priv; > > + > > + list_for_each_entry(priv, &aplics, head) { > > + aplic_restore(priv); > > + } > > Ditto > > > +} > > + > > +static struct syscore_ops aplic_syscore_ops = { > > + .suspend = aplic_syscore_suspend, > > + .resume = aplic_syscore_resume, > > See documentation about struct definitions and initializers > > > +}; > > + > > +static int aplic_pm_notifier(struct notifier_block *nb, unsigned long action, void *data) > > +{ > > + struct aplic_priv *priv = container_of(nb, struct aplic_priv, genpd_nb); > > + > > + switch (action) { > > + case GENPD_NOTIFY_PRE_OFF: > > + aplic_save(priv); > > + break; > > + case GENPD_NOTIFY_ON: > > + aplic_restore(priv); > > + break; > > + default: > > + break; > > + } > > + > > + return 0; > > +} > > + > > +static void aplic_remove(void *data) > > +{ > > + struct aplic_priv *priv = data; > > + > > + list_del(&priv->head); > > How is this list operation serialized? > I'll add a lock to prevent the race from binding/unbinding the driver at runtime, Thanks. > > + dev_pm_genpd_remove_notifier(priv->dev); > > +} > > + > > +static int aplic_add(struct device *dev, struct aplic_priv *priv) > > +{ > > + int ret; > > + > > + list_add(&priv->head, &aplics); > > + /* Add genpd notifier */ > > + priv->genpd_nb.notifier_call = aplic_pm_notifier; > > + ret = dev_pm_genpd_add_notifier(dev, &priv->genpd_nb); > > + if (ret && ret != -ENODEV && ret != -EOPNOTSUPP) { > > What is this magic about? Lacks explanation and rationale. > I’ll add the explanation: It is okay when the platforms doesn't enable CONFIG_PM_GENERIC_DOMAINS or define the `power-domains` property in the APLIC DT node. > > + list_del(&priv->head); > > + return ret; > > ... > > > + /* For power management */ > > + priv->saved_target = devm_kzalloc(dev, priv->nr_irqs * sizeof(u32), > > + GFP_KERNEL); > > + if (!priv->saved_target) > > + return -ENOMEM; > > + > > + priv->saved_sourcecfg = devm_kzalloc(dev, priv->nr_irqs * sizeof(u32), > > + GFP_KERNEL); > > + if (!priv->saved_sourcecfg) > > + return -ENOMEM; > > + > > + priv->saved_ie = devm_kzalloc(dev, > > + DIV_ROUND_UP(priv->nr_irqs, 32) * sizeof(u32), > > + GFP_KERNEL); > > + if (!priv->saved_ie) > > + return -ENOMEM; > > Seriously? You allocate all of this seperately? Ever heard about the > concept of data structures? > > struct savedregs { > u32 target; > u32 source; > u32 ie; > }; > > priv->savedregs = devm_kcalloc(dev, priv->nr_irqs, sizeof(*priv->savedregs, GFP_KERNEL); > > Yes, I know you don't need as many ie registers, but that's not a > problem at all. You just need to write/read to/from index 0, 32, 64 ... > > And your restore muck boils down to a single loop: > > u32 __iomem *regs = priv->regs; > for (i = 0; i < priv->nr_irqs; i++, regs++) { > writel(priv->savedregs->source[i], regs + APLIC_SOURCECFG_BASE); > writel(priv->savedregs->target[i], regs + APLIC_TARGET_BASE); > > if (i % 32) > continue; > > writel(-1U, regs + APLIC_CLRIE_BASE); > writel(priv->saved_ie[i], regs + APLIC_SETIE_BASE); > if (!priv->nr_idcs) > writel(readl(regs + APLOC_CLRIP_BASE), regs + APLIC_SETIP_BASE); > } > > That's too comprehensible, right? > I can update it to: u32 __iomem *regs = priv->regs; for (i = 0; i <= priv->nr_irqs; i++) { if (i < priv->nr_irqs) { writel(priv->savedregs->source[i], regs + APLIC_SOURCECFG_BASE + i); writel(priv->savedregs->target[i], regs + APLIC_TARGET_BASE + i); } if (i % 32) continue; writel(-1U, regs + APLIC_CLRIE_BASE + i / 32); writel(priv->savedregs->ie[i], regs + APLIC_SETIE_BASE + i / 32); if (!priv->nr_idcs) writel(readl(regs + APLOC_CLRIP_BASE + i / 32), regs + APLIC_SETIP_BASE + i / 32); } > > struct aplic_priv { > > + struct list_head head; > > Why needs this to be at the top? > > There is no reason at all. Not that it matters much as this data > structure is a trainwreck vs. cacheline efficiency anyway. > Does it make sense to you if I put the hot data together ? Like: struct aplic_source_regs { u32 *target; u32 *sourcecfg; u32 *ie; }; struct aplic_saved_regs { u32 domaincfg; #ifdef CONFIG_RISCV_M_MODE u32 msiaddr; u32 msiaddrh; #endif struct aplic_source_regs *srcs }; struct aplic_priv { struct list_head head; struct aplic_saved_regs saved_regs; void __iomem *regs; u32 nr_irqs; u32 nr_idcs; u32 gsi_base; u32 acpi_aplic_id; struct device *dev; struct aplic_msicfg msicfg; struct notifier_block genpd_nb; }; Or any other suggestion? > > struct device *dev; > > u32 gsi_base; > > u32 nr_irqs; > > @@ -31,6 +32,15 @@ struct aplic_priv { > > u32 acpi_aplic_id; > > void __iomem *regs; > > struct aplic_msicfg msicfg; > > + struct notifier_block genpd_nb; > > + u32 *saved_target; > > So you aligned @head and @genpd_nb, but then you use random formatting > to turn this into visual clutter. > Sorry about that. > I'm amazed that this lot has received three Reviewed-by tags and nobody > found even the slightest issue with it. Oh well. > > Thanks, > > tglx > _______________________________________________ linux-riscv mailing list linux-riscv@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-riscv ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-08-15 7:34 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-08-06 8:27 [PATCH v2 0/2] riscv-imsic/aplic: Save and restore the registers Nick Hu 2025-08-06 8:27 ` [PATCH v2 1/2] irqchip/riscv-imsic: Restore the IMSIC registers Nick Hu 2025-08-06 9:28 ` Nutty Liu 2025-08-06 12:08 ` Thomas Gleixner 2025-08-15 2:18 ` Nick Hu 2025-08-06 8:27 ` [PATCH v2 2/2] irqchip/riscv-aplic: Save and restore APLIC registers Nick Hu 2025-08-06 9:47 ` Nutty Liu 2025-08-06 12:52 ` Thomas Gleixner 2025-08-15 7:33 ` Nick Hu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox