Blame view

drivers/cpuidle/cpuidle-arm.c 3.67 KB
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
1
  /*
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
2
   * ARM/ARM64 generic CPU idle driver.
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
3
4
5
6
7
8
9
10
   *
   * Copyright (C) 2014 ARM Ltd.
   * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
11
  #define pr_fmt(fmt) "CPUidle arm: " fmt
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
12
13
14
15
16
17
18
  
  #include <linux/cpuidle.h>
  #include <linux/cpumask.h>
  #include <linux/cpu_pm.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/of.h>
a0d46a3df   Daniel Lezcano   ARM: cpuidle: Reg...
19
  #include <linux/slab.h>
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
20
21
  
  #include <asm/cpuidle.h>
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
22
23
24
25
  
  #include "dt_idle_states.h"
  
  /*
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
26
   * arm_enter_idle_state - Programs CPU to enter the specified state
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
27
28
29
30
31
32
33
34
   *
   * dev: cpuidle device
   * drv: cpuidle driver
   * idx: state index
   *
   * Called from the CPUidle framework to program the device to the
   * specified target state selected by the governor.
   */
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
35
36
  static int arm_enter_idle_state(struct cpuidle_device *dev,
  				struct cpuidle_driver *drv, int idx)
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
37
  {
220276e09   Sudeep Holla   cpuidle: introduc...
38
39
40
41
42
43
  	/*
  	 * Pass idle state index to arm_cpuidle_suspend which in turn
  	 * will call the CPU ops suspend protocol with idle index as a
  	 * parameter.
  	 */
  	return CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, idx);
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
44
  }
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
45
46
  static struct cpuidle_driver arm_idle_driver = {
  	.name = "arm_idle",
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
47
48
49
50
51
52
53
54
55
  	.owner = THIS_MODULE,
  	/*
  	 * State at index 0 is standby wfi and considered standard
  	 * on all ARM platforms. If in some platforms simple wfi
  	 * can't be used as "state 0", DT bindings must be implemented
  	 * to work around this issue and allow installing a special
  	 * handler for idle state index 0.
  	 */
  	.states[0] = {
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
56
  		.enter                  = arm_enter_idle_state,
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
57
58
59
  		.exit_latency           = 1,
  		.target_residency       = 1,
  		.power_usage		= UINT_MAX,
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
60
  		.name                   = "WFI",
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
61
  		.desc                   = "ARM WFI",
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
62
63
  	}
  };
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
64
  static const struct of_device_id arm_idle_state_match[] __initconst = {
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
65
  	{ .compatible = "arm,idle-state",
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
66
  	  .data = arm_enter_idle_state },
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
67
68
69
70
  	{ },
  };
  
  /*
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
71
   * arm_idle_init
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
72
   *
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
73
   * Registers the arm specific cpuidle driver with the cpuidle
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
74
75
76
   * framework. It relies on core code to parse the idle states
   * and initialize them using driver data structures accordingly.
   */
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
77
  static int __init arm_idle_init(void)
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
78
79
  {
  	int cpu, ret;
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
80
  	struct cpuidle_driver *drv = &arm_idle_driver;
a0d46a3df   Daniel Lezcano   ARM: cpuidle: Reg...
81
  	struct cpuidle_device *dev;
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
82
83
84
85
86
87
88
  
  	/*
  	 * Initialize idle states data, starting at index 1.
  	 * This driver is DT only, if no DT idle states are detected (ret == 0)
  	 * let the driver initialization fail accordingly since there is no
  	 * reason to initialize the idle driver if only wfi is supported.
  	 */
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
89
  	ret = dt_init_idle_driver(drv, arm_idle_state_match, 1);
18f95a364   Lorenzo Pieralisi   drivers: cpuidle:...
90
  	if (ret <= 0)
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
91
  		return ret ? : -ENODEV;
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
92

a0d46a3df   Daniel Lezcano   ARM: cpuidle: Reg...
93
94
95
96
97
98
  	ret = cpuidle_register_driver(drv);
  	if (ret) {
  		pr_err("Failed to register cpuidle driver
  ");
  		return ret;
  	}
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
99
100
101
102
103
  	/*
  	 * Call arch CPU operations in order to initialize
  	 * idle states suspend back-end specific data
  	 */
  	for_each_possible_cpu(cpu) {
c9d621614   Daniel Lezcano   ARM64: cpuidle: R...
104
  		ret = arm_cpuidle_init(cpu);
a0d46a3df   Daniel Lezcano   ARM: cpuidle: Reg...
105
106
107
108
109
110
111
  
  		/*
  		 * Skip the cpuidle device initialization if the reported
  		 * failure is a HW misconfiguration/breakage (-ENXIO).
  		 */
  		if (ret == -ENXIO)
  			continue;
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
112
113
114
  		if (ret) {
  			pr_err("CPU %d failed to init idle CPU ops
  ", cpu);
a0d46a3df   Daniel Lezcano   ARM: cpuidle: Reg...
115
116
117
118
119
120
121
  			goto out_fail;
  		}
  
  		dev = kzalloc(sizeof(*dev), GFP_KERNEL);
  		if (!dev) {
  			pr_err("Failed to allocate cpuidle device
  ");
af48d7bc3   Christophe Jaillet   ARM: cpuidle: Fix...
122
  			ret = -ENOMEM;
a0d46a3df   Daniel Lezcano   ARM: cpuidle: Reg...
123
124
125
126
127
128
129
130
131
132
133
  			goto out_fail;
  		}
  		dev->cpu = cpu;
  
  		ret = cpuidle_register_device(dev);
  		if (ret) {
  			pr_err("Failed to register cpuidle device for CPU %d
  ",
  			       cpu);
  			kfree(dev);
  			goto out_fail;
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
134
135
  		}
  	}
a0d46a3df   Daniel Lezcano   ARM: cpuidle: Reg...
136
137
138
139
140
141
142
143
144
145
146
  	return 0;
  out_fail:
  	while (--cpu >= 0) {
  		dev = per_cpu(cpuidle_devices, cpu);
  		cpuidle_unregister_device(dev);
  		kfree(dev);
  	}
  
  	cpuidle_unregister_driver(drv);
  
  	return ret;
3299b63de   Lorenzo Pieralisi   drivers: cpuidle:...
147
  }
69e6cb3d2   Daniel Lezcano   ARM64: cpuidle: R...
148
  device_initcall(arm_idle_init);