Blame view

arch/arm/kernel/cpuidle.c 3.96 KB
fcaf20360   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
e1689795a   Robert Lee   cpuidle: Add comm...
2
3
  /*
   * Copyright 2012 Linaro Ltd.
e1689795a   Robert Lee   cpuidle: Add comm...
4
5
6
   */
  
  #include <linux/cpuidle.h>
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
7
8
  #include <linux/of.h>
  #include <linux/of_device.h>
eeebc3bb4   Daniel Lezcano   ARM: cpuidle: Rem...
9
  #include <asm/cpuidle.h>
e1689795a   Robert Lee   cpuidle: Add comm...
10

449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
11
12
13
  extern struct of_cpuidle_method __cpuidle_method_of_table[];
  
  static const struct of_cpuidle_method __cpuidle_method_of_table_sentinel
33def8498   Joe Perches   treewide: Convert...
14
  	__used __section("__cpuidle_method_of_table_end");
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
15

7619751f8   Kees Cook   ARM: 8595/2: appl...
16
  static struct cpuidle_ops cpuidle_ops[NR_CPUS] __ro_after_init;
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
17

9a309d6fd   Daniel Lezcano   ARM: cpuidle: Doc...
18
19
20
21
22
23
24
25
26
27
28
  /**
   * arm_cpuidle_simple_enter() - a wrapper to cpu_do_idle()
   * @dev: not used
   * @drv: not used
   * @index: not used
   *
   * A trivial wrapper to allow the cpu_do_idle function to be assigned as a
   * cpuidle callback by matching the function signature.
   *
   * Returns the index passed as parameter
   */
e1689795a   Robert Lee   cpuidle: Add comm...
29
30
31
32
33
34
35
  int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
  		struct cpuidle_driver *drv, int index)
  {
  	cpu_do_idle();
  
  	return index;
  }
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
36

9a309d6fd   Daniel Lezcano   ARM: cpuidle: Doc...
37
38
39
40
41
42
43
  /**
   * arm_cpuidle_suspend() - function to enter low power idle states
   * @index: an integer used as an identifier for the low level PM callbacks
   *
   * This function calls the underlying arch specific low level PM code as
   * registered at the init time.
   *
c3fbbf930   Jisheng Zhang   ARM: 8586/1: cpui...
44
   * Returns the result of the suspend callback.
9a309d6fd   Daniel Lezcano   ARM: cpuidle: Doc...
45
   */
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
46
47
  int arm_cpuidle_suspend(int index)
  {
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
48
  	int cpu = smp_processor_id();
c3fbbf930   Jisheng Zhang   ARM: 8586/1: cpui...
49
  	return cpuidle_ops[cpu].suspend(index);
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
50
  }
9a309d6fd   Daniel Lezcano   ARM: cpuidle: Doc...
51
52
53
54
55
56
57
58
59
  /**
   * arm_cpuidle_get_ops() - find a registered cpuidle_ops by name
   * @method: the method name
   *
   * Search in the __cpuidle_method_of_table array the cpuidle ops matching the
   * method name.
   *
   * Returns a struct cpuidle_ops pointer, NULL if not found.
   */
4cfd55202   Jisheng Zhang   ARM: cpuidle: con...
60
  static const struct cpuidle_ops *__init arm_cpuidle_get_ops(const char *method)
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
61
62
63
64
65
66
67
68
69
  {
  	struct of_cpuidle_method *m = __cpuidle_method_of_table;
  
  	for (; m->method; m++)
  		if (!strcmp(m->method, method))
  			return m->ops;
  
  	return NULL;
  }
9a309d6fd   Daniel Lezcano   ARM: cpuidle: Doc...
70
71
72
73
74
75
76
  /**
   * arm_cpuidle_read_ops() - Initialize the cpuidle ops with the device tree
   * @dn: a pointer to a struct device node corresponding to a cpu node
   * @cpu: the cpu identifier
   *
   * Get the method name defined in the 'enable-method' property, retrieve the
   * associated cpuidle_ops and do a struct copy. This copy is needed because all
4cfd55202   Jisheng Zhang   ARM: cpuidle: con...
77
   * cpuidle_ops are tagged __initconst and will be unloaded after the init
9a309d6fd   Daniel Lezcano   ARM: cpuidle: Doc...
78
79
80
   * process.
   *
   * Return 0 on sucess, -ENOENT if no 'enable-method' is defined, -EOPNOTSUPP if
c3fbbf930   Jisheng Zhang   ARM: 8586/1: cpui...
81
82
   * no cpuidle_ops is registered for the 'enable-method', or if either init or
   * suspend callback isn't defined.
9a309d6fd   Daniel Lezcano   ARM: cpuidle: Doc...
83
   */
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
84
85
86
  static int __init arm_cpuidle_read_ops(struct device_node *dn, int cpu)
  {
  	const char *enable_method;
4cfd55202   Jisheng Zhang   ARM: cpuidle: con...
87
  	const struct cpuidle_ops *ops;
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
88
89
90
91
92
93
94
  
  	enable_method = of_get_property(dn, "enable-method", NULL);
  	if (!enable_method)
  		return -ENOENT;
  
  	ops = arm_cpuidle_get_ops(enable_method);
  	if (!ops) {
a8e65e06e   Rob Herring   ARM: Convert to u...
95
96
97
  		pr_warn("%pOF: unsupported enable-method property: %s
  ",
  			dn, enable_method);
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
98
99
  		return -EOPNOTSUPP;
  	}
c3fbbf930   Jisheng Zhang   ARM: 8586/1: cpui...
100
101
102
  	if (!ops->init || !ops->suspend) {
  		pr_warn("cpuidle_ops '%s': no init or suspend callback
  ",
f222a7695   Jisheng Zhang   ARM: 8585/1: cpui...
103
104
105
  			enable_method);
  		return -EOPNOTSUPP;
  	}
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
106
107
108
109
110
111
112
113
  	cpuidle_ops[cpu] = *ops; /* structure copy */
  
  	pr_notice("cpuidle: enable-method property '%s'"
  		  " found operations
  ", enable_method);
  
  	return 0;
  }
9a309d6fd   Daniel Lezcano   ARM: cpuidle: Doc...
114
115
116
117
118
119
120
121
122
123
124
  /**
   * arm_cpuidle_init() - Initialize cpuidle_ops for a specific cpu
   * @cpu: the cpu to be initialized
   *
   * Initialize the cpuidle ops with the device for the cpu and then call
   * the cpu's idle initialization callback. This may fail if the underlying HW
   * is not operational.
   *
   * Returns:
   *  0 on success,
   *  -ENODEV if it fails to find the cpu node in the device tree,
f222a7695   Jisheng Zhang   ARM: 8585/1: cpui...
125
126
   *  -EOPNOTSUPP if it does not find a registered and valid cpuidle_ops for
   *  this cpu,
9a309d6fd   Daniel Lezcano   ARM: cpuidle: Doc...
127
128
129
130
   *  -ENOENT if it fails to find an 'enable-method' property,
   *  -ENXIO if the HW reports a failure or a misconfiguration,
   *  -ENOMEM if the HW report an memory allocation failure 
   */
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
131
132
133
134
135
136
137
138
139
  int __init arm_cpuidle_init(int cpu)
  {
  	struct device_node *cpu_node = of_cpu_device_node_get(cpu);
  	int ret;
  
  	if (!cpu_node)
  		return -ENODEV;
  
  	ret = arm_cpuidle_read_ops(cpu_node, cpu);
f222a7695   Jisheng Zhang   ARM: 8585/1: cpui...
140
  	if (!ret)
449e056c7   Daniel Lezcano   ARM: cpuidle: Add...
141
142
143
144
145
146
  		ret = cpuidle_ops[cpu].init(cpu_node, cpu);
  
  	of_node_put(cpu_node);
  
  	return ret;
  }