19
19
#include <linux/cpu_pm.h>
20
20
#include <linux/qcom_scm.h>
21
21
22
- #include <asm/cpuidle.h>
23
22
#include <asm/proc-fns.h>
24
23
#include <asm/suspend.h>
25
24
25
+ #include "dt_idle_states.h"
26
+
26
27
#define MAX_PMIC_DATA 2
27
28
#define MAX_SEQ_DATA 64
28
29
#define SPM_CTL_INDEX 0x7f
@@ -62,6 +63,7 @@ struct spm_reg_data {
62
63
};
63
64
64
65
struct spm_driver_data {
66
+ struct cpuidle_driver cpuidle_driver ;
65
67
void __iomem * reg_base ;
66
68
const struct spm_reg_data * reg_data ;
67
69
};
@@ -107,11 +109,6 @@ static const struct spm_reg_data spm_reg_8064_cpu = {
107
109
.start_index [PM_SLEEP_MODE_SPC ] = 2 ,
108
110
};
109
111
110
- static DEFINE_PER_CPU (struct spm_driver_data * , cpu_spm_drv ) ;
111
-
112
- typedef int (* idle_fn )(void );
113
- static DEFINE_PER_CPU (idle_fn * , qcom_idle_ops ) ;
114
-
115
112
static inline void spm_register_write (struct spm_driver_data * drv ,
116
113
enum spm_reg reg , u32 val )
117
114
{
@@ -172,10 +169,9 @@ static int qcom_pm_collapse(unsigned long int unused)
172
169
return -1 ;
173
170
}
174
171
175
- static int qcom_cpu_spc (void )
172
+ static int qcom_cpu_spc (struct spm_driver_data * drv )
176
173
{
177
174
int ret ;
178
- struct spm_driver_data * drv = __this_cpu_read (cpu_spm_drv );
179
175
180
176
spm_set_low_power_mode (drv , PM_SLEEP_MODE_SPC );
181
177
ret = cpu_suspend (0 , qcom_pm_collapse );
@@ -190,94 +186,49 @@ static int qcom_cpu_spc(void)
190
186
return ret ;
191
187
}
192
188
193
- static int qcom_idle_enter (unsigned long index )
189
+ static int spm_enter_idle_state (struct cpuidle_device * dev ,
190
+ struct cpuidle_driver * drv , int idx )
194
191
{
195
- return __this_cpu_read (qcom_idle_ops )[index ]();
192
+ struct spm_driver_data * data = container_of (drv , struct spm_driver_data ,
193
+ cpuidle_driver );
194
+
195
+ return CPU_PM_CPU_IDLE_ENTER_PARAM (qcom_cpu_spc , idx , data );
196
196
}
197
197
198
- static const struct of_device_id qcom_idle_state_match [] __initconst = {
199
- { .compatible = "qcom,idle-state-spc" , .data = qcom_cpu_spc },
198
+ static struct cpuidle_driver qcom_spm_idle_driver = {
199
+ .name = "qcom_spm" ,
200
+ .owner = THIS_MODULE ,
201
+ .states [0 ] = {
202
+ .enter = spm_enter_idle_state ,
203
+ .exit_latency = 1 ,
204
+ .target_residency = 1 ,
205
+ .power_usage = UINT_MAX ,
206
+ .name = "WFI" ,
207
+ .desc = "ARM WFI" ,
208
+ }
209
+ };
210
+
211
+ static const struct of_device_id qcom_idle_state_match [] = {
212
+ { .compatible = "qcom,idle-state-spc" , .data = spm_enter_idle_state },
200
213
{ },
201
214
};
202
215
203
- static int __init qcom_cpuidle_init (struct device_node * cpu_node , int cpu )
216
+ static int spm_cpuidle_init (struct cpuidle_driver * drv , int cpu )
204
217
{
205
- const struct of_device_id * match_id ;
206
- struct device_node * state_node ;
207
- int i ;
208
- int state_count = 1 ;
209
- idle_fn idle_fns [CPUIDLE_STATE_MAX ];
210
- idle_fn * fns ;
211
- cpumask_t mask ;
212
- bool use_scm_power_down = false;
213
-
214
- if (!qcom_scm_is_available ())
215
- return - EPROBE_DEFER ;
216
-
217
- for (i = 0 ; ; i ++ ) {
218
- state_node = of_parse_phandle (cpu_node , "cpu-idle-states" , i );
219
- if (!state_node )
220
- break ;
221
-
222
- if (!of_device_is_available (state_node ))
223
- continue ;
224
-
225
- if (i == CPUIDLE_STATE_MAX ) {
226
- pr_warn ("%s: cpuidle states reached max possible\n" ,
227
- __func__ );
228
- break ;
229
- }
230
-
231
- match_id = of_match_node (qcom_idle_state_match , state_node );
232
- if (!match_id )
233
- return - ENODEV ;
234
-
235
- idle_fns [state_count ] = match_id -> data ;
236
-
237
- /* Check if any of the states allow power down */
238
- if (match_id -> data == qcom_cpu_spc )
239
- use_scm_power_down = true;
240
-
241
- state_count ++ ;
242
- }
243
-
244
- if (state_count == 1 )
245
- goto check_spm ;
246
-
247
- fns = devm_kcalloc (get_cpu_device (cpu ), state_count , sizeof (* fns ),
248
- GFP_KERNEL );
249
- if (!fns )
250
- return - ENOMEM ;
251
-
252
- for (i = 1 ; i < state_count ; i ++ )
253
- fns [i ] = idle_fns [i ];
218
+ int ret ;
254
219
255
- if (use_scm_power_down ) {
256
- /* We have atleast one power down mode */
257
- cpumask_clear (& mask );
258
- cpumask_set_cpu (cpu , & mask );
259
- qcom_scm_set_warm_boot_addr (cpu_resume_arm , & mask );
260
- }
220
+ memcpy (drv , & qcom_spm_idle_driver , sizeof (* drv ));
221
+ drv -> cpumask = (struct cpumask * )cpumask_of (cpu );
261
222
262
- per_cpu (qcom_idle_ops , cpu ) = fns ;
223
+ /* Parse idle states from device tree */
224
+ ret = dt_init_idle_driver (drv , qcom_idle_state_match , 1 );
225
+ if (ret <= 0 )
226
+ return ret ? : - ENODEV ;
263
227
264
- /*
265
- * SPM probe for the cpu should have happened by now, if the
266
- * SPM device does not exist, return -ENXIO to indicate that the
267
- * cpu does not support idle states.
268
- */
269
- check_spm :
270
- return per_cpu (cpu_spm_drv , cpu ) ? 0 : - ENXIO ;
228
+ /* We have atleast one power down mode */
229
+ return qcom_scm_set_warm_boot_addr (cpu_resume_arm , drv -> cpumask );
271
230
}
272
231
273
- static const struct cpuidle_ops qcom_cpuidle_ops __initconst = {
274
- .suspend = qcom_idle_enter ,
275
- .init = qcom_cpuidle_init ,
276
- };
277
-
278
- CPUIDLE_METHOD_OF_DECLARE (qcom_idle_v1 , "qcom,kpss-acc-v1" , & qcom_cpuidle_ops );
279
- CPUIDLE_METHOD_OF_DECLARE (qcom_idle_v2 , "qcom,kpss-acc-v2" , & qcom_cpuidle_ops );
280
-
281
232
static struct spm_driver_data * spm_get_drv (struct platform_device * pdev ,
282
233
int * spm_cpu )
283
234
{
@@ -323,11 +274,15 @@ static int spm_dev_probe(struct platform_device *pdev)
323
274
struct resource * res ;
324
275
const struct of_device_id * match_id ;
325
276
void __iomem * addr ;
326
- int cpu ;
277
+ int cpu , ret ;
278
+
279
+ if (!qcom_scm_is_available ())
280
+ return - EPROBE_DEFER ;
327
281
328
282
drv = spm_get_drv (pdev , & cpu );
329
283
if (!drv )
330
284
return - EINVAL ;
285
+ platform_set_drvdata (pdev , drv );
331
286
332
287
res = platform_get_resource (pdev , IORESOURCE_MEM , 0 );
333
288
drv -> reg_base = devm_ioremap_resource (& pdev -> dev , res );
@@ -340,6 +295,10 @@ static int spm_dev_probe(struct platform_device *pdev)
340
295
341
296
drv -> reg_data = match_id -> data ;
342
297
298
+ ret = spm_cpuidle_init (& drv -> cpuidle_driver , cpu );
299
+ if (ret )
300
+ return ret ;
301
+
343
302
/* Write the SPM sequences first.. */
344
303
addr = drv -> reg_base + drv -> reg_data -> reg_offset [SPM_REG_SEQ_ENTRY ];
345
304
__iowrite32_copy (addr , drv -> reg_data -> seq ,
@@ -362,13 +321,20 @@ static int spm_dev_probe(struct platform_device *pdev)
362
321
/* Set up Standby as the default low power mode */
363
322
spm_set_low_power_mode (drv , PM_SLEEP_MODE_STBY );
364
323
365
- per_cpu (cpu_spm_drv , cpu ) = drv ;
324
+ return cpuidle_register (& drv -> cpuidle_driver , NULL );
325
+ }
326
+
327
+ static int spm_dev_remove (struct platform_device * pdev )
328
+ {
329
+ struct spm_driver_data * drv = platform_get_drvdata (pdev );
366
330
331
+ cpuidle_unregister (& drv -> cpuidle_driver );
367
332
return 0 ;
368
333
}
369
334
370
335
static struct platform_driver spm_driver = {
371
336
.probe = spm_dev_probe ,
337
+ .remove = spm_dev_remove ,
372
338
.driver = {
373
339
.name = "saw" ,
374
340
.of_match_table = spm_match_table ,
0 commit comments