1212#include <linux/of.h>
1313#include <linux/of_irq.h>
1414#include <linux/platform_device.h>
15+ #include <linux/pm_domain.h>
16+ #include <linux/pm_runtime.h>
1517#include <linux/printk.h>
18+ #include <linux/syscore_ops.h>
1619
1720#include "irq-riscv-aplic-main.h"
1821
22+ static LIST_HEAD (aplics );
23+
24+ static void aplic_restore_states (struct aplic_priv * priv )
25+ {
26+ struct aplic_saved_regs * saved_regs = & priv -> saved_hw_regs ;
27+ struct aplic_src_ctrl * srcs ;
28+ void __iomem * regs ;
29+ u32 nr_irqs , i ;
30+
31+ regs = priv -> regs ;
32+ writel (saved_regs -> domaincfg , regs + APLIC_DOMAINCFG );
33+ #ifdef CONFIG_RISCV_M_MODE
34+ writel (saved_regs -> msiaddr , regs + APLIC_xMSICFGADDR );
35+ writel (saved_regs -> msiaddrh , regs + APLIC_xMSICFGADDRH );
36+ #endif
37+ /*
38+ * The sourcecfg[i] has to be restored prior to the target[i], interrupt-pending and
39+ * interrupt-enable bits. The AIA specification states that "Whenever interrupt source i is
40+ * inactive in an interrupt domain, the corresponding interrupt-pending and interrupt-enable
41+ * bits within the domain are read-only zeros, and register target[i] is also read-only
42+ * zero."
43+ */
44+ nr_irqs = priv -> nr_irqs ;
45+ for (i = 0 ; i < nr_irqs ; i ++ ) {
46+ srcs = & priv -> saved_hw_regs .srcs [i ];
47+ writel (srcs -> sourcecfg , regs + APLIC_SOURCECFG_BASE + i * sizeof (u32 ));
48+ writel (srcs -> target , regs + APLIC_TARGET_BASE + i * sizeof (u32 ));
49+ }
50+
51+ for (i = 0 ; i <= nr_irqs ; i += 32 ) {
52+ srcs = & priv -> saved_hw_regs .srcs [i ];
53+ writel (-1U , regs + APLIC_CLRIE_BASE + (i / 32 ) * sizeof (u32 ));
54+ writel (srcs -> ie , regs + APLIC_SETIE_BASE + (i / 32 ) * sizeof (u32 ));
55+
56+ /* Re-trigger the interrupts if it forwards interrupts to target harts by MSIs */
57+ if (!priv -> nr_idcs )
58+ writel (readl (regs + APLIC_CLRIP_BASE + (i / 32 ) * sizeof (u32 )),
59+ regs + APLIC_SETIP_BASE + (i / 32 ) * sizeof (u32 ));
60+ }
61+
62+ if (priv -> nr_idcs )
63+ aplic_direct_restore_states (priv );
64+ }
65+
66+ static void aplic_save_states (struct aplic_priv * priv )
67+ {
68+ struct aplic_src_ctrl * srcs ;
69+ void __iomem * regs ;
70+ u32 i , nr_irqs ;
71+
72+ regs = priv -> regs ;
73+ nr_irqs = priv -> nr_irqs ;
74+ /* The valid interrupt source IDs range from 1 to N, where N is priv->nr_irqs */
75+ for (i = 0 ; i < nr_irqs ; i ++ ) {
76+ srcs = & priv -> saved_hw_regs .srcs [i ];
77+ srcs -> target = readl (regs + APLIC_TARGET_BASE + i * sizeof (u32 ));
78+
79+ if (i % 32 )
80+ continue ;
81+
82+ srcs -> ie = readl (regs + APLIC_SETIE_BASE + (i / 32 ) * sizeof (u32 ));
83+ }
84+
85+ /* Save the nr_irqs bit if needed */
86+ if (!(nr_irqs % 32 )) {
87+ srcs = & priv -> saved_hw_regs .srcs [nr_irqs ];
88+ srcs -> ie = readl (regs + APLIC_SETIE_BASE + (nr_irqs / 32 ) * sizeof (u32 ));
89+ }
90+ }
91+
92+ static int aplic_syscore_suspend (void * data )
93+ {
94+ struct aplic_priv * priv ;
95+
96+ list_for_each_entry (priv , & aplics , head )
97+ aplic_save_states (priv );
98+
99+ return 0 ;
100+ }
101+
102+ static void aplic_syscore_resume (void * data )
103+ {
104+ struct aplic_priv * priv ;
105+
106+ list_for_each_entry (priv , & aplics , head )
107+ aplic_restore_states (priv );
108+ }
109+
110+ static struct syscore_ops aplic_syscore_ops = {
111+ .suspend = aplic_syscore_suspend ,
112+ .resume = aplic_syscore_resume ,
113+ };
114+
115+ static struct syscore aplic_syscore = {
116+ .ops = & aplic_syscore_ops ,
117+ };
118+
119+ static int aplic_pm_notifier (struct notifier_block * nb , unsigned long action , void * data )
120+ {
121+ struct aplic_priv * priv = container_of (nb , struct aplic_priv , genpd_nb );
122+
123+ switch (action ) {
124+ case GENPD_NOTIFY_PRE_OFF :
125+ aplic_save_states (priv );
126+ break ;
127+ case GENPD_NOTIFY_ON :
128+ aplic_restore_states (priv );
129+ break ;
130+ default :
131+ break ;
132+ }
133+
134+ return 0 ;
135+ }
136+
137+ static void aplic_pm_remove (void * data )
138+ {
139+ struct aplic_priv * priv = data ;
140+ struct device * dev = priv -> dev ;
141+
142+ list_del (& priv -> head );
143+ if (dev -> pm_domain )
144+ dev_pm_genpd_remove_notifier (dev );
145+ }
146+
147+ static int aplic_pm_add (struct device * dev , struct aplic_priv * priv )
148+ {
149+ struct aplic_src_ctrl * srcs ;
150+ int ret ;
151+
152+ srcs = devm_kzalloc (dev , (priv -> nr_irqs + 1 ) * sizeof (* srcs ), GFP_KERNEL );
153+ if (!srcs )
154+ return - ENOMEM ;
155+
156+ priv -> saved_hw_regs .srcs = srcs ;
157+ list_add (& priv -> head , & aplics );
158+ if (dev -> pm_domain ) {
159+ priv -> genpd_nb .notifier_call = aplic_pm_notifier ;
160+ ret = dev_pm_genpd_add_notifier (dev , & priv -> genpd_nb );
161+ if (ret )
162+ goto remove_head ;
163+
164+ ret = devm_pm_runtime_enable (dev );
165+ if (ret )
166+ goto remove_notifier ;
167+ }
168+
169+ return devm_add_action_or_reset (dev , aplic_pm_remove , priv );
170+
171+ remove_notifier :
172+ dev_pm_genpd_remove_notifier (dev );
173+ remove_head :
174+ list_del (& priv -> head );
175+ return ret ;
176+ }
177+
19178void aplic_irq_unmask (struct irq_data * d )
20179{
21180 struct aplic_priv * priv = irq_data_get_irq_chip_data (d );
@@ -60,6 +219,8 @@ int aplic_irq_set_type(struct irq_data *d, unsigned int type)
60219 sourcecfg += (d -> hwirq - 1 ) * sizeof (u32 );
61220 writel (val , sourcecfg );
62221
222+ priv -> saved_hw_regs .srcs [d -> hwirq - 1 ].sourcecfg = val ;
223+
63224 return 0 ;
64225}
65226
@@ -82,6 +243,7 @@ int aplic_irqdomain_translate(struct irq_fwspec *fwspec, u32 gsi_base,
82243
83244void aplic_init_hw_global (struct aplic_priv * priv , bool msi_mode )
84245{
246+ struct aplic_saved_regs * saved_regs = & priv -> saved_hw_regs ;
85247 u32 val ;
86248#ifdef CONFIG_RISCV_M_MODE
87249 u32 valh ;
@@ -95,6 +257,8 @@ void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode)
95257 valh |= FIELD_PREP (APLIC_xMSICFGADDRH_HHXS , priv -> msicfg .hhxs );
96258 writel (val , priv -> regs + APLIC_xMSICFGADDR );
97259 writel (valh , priv -> regs + APLIC_xMSICFGADDRH );
260+ saved_regs -> msiaddr = val ;
261+ saved_regs -> msiaddrh = valh ;
98262 }
99263#endif
100264
@@ -106,6 +270,8 @@ void aplic_init_hw_global(struct aplic_priv *priv, bool msi_mode)
106270 writel (val , priv -> regs + APLIC_DOMAINCFG );
107271 if (readl (priv -> regs + APLIC_DOMAINCFG ) != val )
108272 dev_warn (priv -> dev , "unable to write 0x%x in domaincfg\n" , val );
273+
274+ saved_regs -> domaincfg = val ;
109275}
110276
111277static void aplic_init_hw_irqs (struct aplic_priv * priv )
@@ -176,7 +342,7 @@ int aplic_setup_priv(struct aplic_priv *priv, struct device *dev, void __iomem *
176342 /* Setup initial state APLIC interrupts */
177343 aplic_init_hw_irqs (priv );
178344
179- return 0 ;
345+ return aplic_pm_add ( dev , priv ) ;
180346}
181347
182348static int aplic_probe (struct platform_device * pdev )
@@ -209,6 +375,8 @@ static int aplic_probe(struct platform_device *pdev)
209375 if (rc )
210376 dev_err_probe (dev , rc , "failed to setup APLIC in %s mode\n" ,
211377 msi_mode ? "MSI" : "direct" );
378+ else
379+ register_syscore (& aplic_syscore );
212380
213381#ifdef CONFIG_ACPI
214382 if (!acpi_disabled )
0 commit comments