2 * POWER platform energy management driver
3 * Copyright (C) 2010 IBM Corporation
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * version 2 as published by the Free Software Foundation.
9 * This pseries platform device driver provides access to
10 * platform energy management capabilities.
13 #include <linux/module.h>
14 #include <linux/types.h>
15 #include <linux/errno.h>
16 #include <linux/init.h>
17 #include <linux/seq_file.h>
18 #include <linux/device.h>
19 #include <linux/cpu.h>
21 #include <asm/cputhreads.h>
23 #include <asm/hvcall.h>
24 #include <asm/firmware.h>
27 #define MODULE_VERS "1.0"
28 #define MODULE_NAME "pseries_energy"
32 static int sysfs_entries
;
36 /* Helper Routines to convert between drc_index to cpu numbers */
38 static u32
cpu_to_drc_index(int cpu
)
40 struct device_node
*dn
= NULL
;
46 dn
= of_find_node_by_path("/cpus");
49 indexes
= of_get_property(dn
, "ibm,drc-indexes", NULL
);
52 /* Convert logical cpu number to core number */
53 i
= cpu_core_index_of_thread(cpu
);
55 * The first element indexes[0] is the number of drc_indexes
56 * returned in the list. Hence i+1 will get the drc_index
57 * corresponding to core number i.
59 WARN_ON(i
> indexes
[0]);
67 printk(KERN_WARNING
"cpu_to_drc_index(%d) failed", cpu
);
71 static int drc_index_to_cpu(u32 drc_index
)
73 struct device_node
*dn
= NULL
;
78 dn
= of_find_node_by_path("/cpus");
81 indexes
= of_get_property(dn
, "ibm,drc-indexes", NULL
);
85 * First element in the array is the number of drc_indexes
86 * returned. Search through the list to find the matching
87 * drc_index and get the core number
89 for (i
= 0; i
< indexes
[0]; i
++) {
90 if (indexes
[i
+ 1] == drc_index
)
93 /* Convert core number to logical cpu number */
94 cpu
= cpu_first_thread_of_core(i
);
101 printk(KERN_WARNING
"drc_index_to_cpu(%d) failed", drc_index
);
106 * pseries hypervisor call H_BEST_ENERGY provides hints to OS on
107 * preferred logical cpus to activate or deactivate for optimized
108 * energy consumption.
111 #define FLAGS_MODE1 0x004E200000080E01UL
112 #define FLAGS_MODE2 0x004E200000080401UL
113 #define FLAGS_ACTIVATE 0x100
115 static ssize_t
get_best_energy_list(char *page
, int activate
)
118 unsigned long retbuf
[PLPAR_HCALL9_BUFSIZE
];
119 unsigned long flags
= 0;
123 buf_page
= (u32
*) get_zeroed_page(GFP_KERNEL
);
129 flags
|= FLAGS_ACTIVATE
;
131 rc
= plpar_hcall9(H_BEST_ENERGY
, retbuf
, flags
, 0, __pa(buf_page
),
133 if (rc
!= H_SUCCESS
) {
134 free_page((unsigned long) buf_page
);
139 for (i
= 0; i
< cnt
; i
++) {
140 cpu
= drc_index_to_cpu(buf_page
[2*i
+1]);
141 if ((cpu_online(cpu
) && !activate
) ||
142 (!cpu_online(cpu
) && activate
))
143 s
+= sprintf(s
, "%d,", cpu
);
145 if (s
> page
) { /* Something to show */
146 s
--; /* Suppress last comma */
147 s
+= sprintf(s
, "\n");
150 free_page((unsigned long) buf_page
);
154 static ssize_t
get_best_energy_data(struct device
*dev
,
155 char *page
, int activate
)
158 unsigned long retbuf
[PLPAR_HCALL9_BUFSIZE
];
159 unsigned long flags
= 0;
163 flags
|= FLAGS_ACTIVATE
;
165 rc
= plpar_hcall9(H_BEST_ENERGY
, retbuf
, flags
,
166 cpu_to_drc_index(dev
->id
),
167 0, 0, 0, 0, 0, 0, 0);
172 return sprintf(page
, "%lu\n", retbuf
[1] >> 32);
175 /* Wrapper functions */
177 static ssize_t
cpu_activate_hint_list_show(struct device
*dev
,
178 struct device_attribute
*attr
, char *page
)
180 return get_best_energy_list(page
, 1);
183 static ssize_t
cpu_deactivate_hint_list_show(struct device
*dev
,
184 struct device_attribute
*attr
, char *page
)
186 return get_best_energy_list(page
, 0);
189 static ssize_t
percpu_activate_hint_show(struct device
*dev
,
190 struct device_attribute
*attr
, char *page
)
192 return get_best_energy_data(dev
, page
, 1);
195 static ssize_t
percpu_deactivate_hint_show(struct device
*dev
,
196 struct device_attribute
*attr
, char *page
)
198 return get_best_energy_data(dev
, page
, 0);
202 * Create sysfs interface:
203 * /sys/devices/system/cpu/pseries_activate_hint_list
204 * /sys/devices/system/cpu/pseries_deactivate_hint_list
205 * Comma separated list of cpus to activate or deactivate
206 * /sys/devices/system/cpu/cpuN/pseries_activate_hint
207 * /sys/devices/system/cpu/cpuN/pseries_deactivate_hint
208 * Per-cpu value of the hint
211 static struct device_attribute attr_cpu_activate_hint_list
=
212 __ATTR(pseries_activate_hint_list
, 0444,
213 cpu_activate_hint_list_show
, NULL
);
215 static struct device_attribute attr_cpu_deactivate_hint_list
=
216 __ATTR(pseries_deactivate_hint_list
, 0444,
217 cpu_deactivate_hint_list_show
, NULL
);
219 static struct device_attribute attr_percpu_activate_hint
=
220 __ATTR(pseries_activate_hint
, 0444,
221 percpu_activate_hint_show
, NULL
);
223 static struct device_attribute attr_percpu_deactivate_hint
=
224 __ATTR(pseries_deactivate_hint
, 0444,
225 percpu_deactivate_hint_show
, NULL
);
227 static int __init
pseries_energy_init(void)
230 struct device
*cpu_dev
;
232 if (!firmware_has_feature(FW_FEATURE_BEST_ENERGY
))
233 return 0; /* H_BEST_ENERGY hcall not supported */
235 /* Create the sysfs files */
236 err
= device_create_file(cpu_subsys
.dev_root
,
237 &attr_cpu_activate_hint_list
);
239 err
= device_create_file(cpu_subsys
.dev_root
,
240 &attr_cpu_deactivate_hint_list
);
244 for_each_possible_cpu(cpu
) {
245 cpu_dev
= get_cpu_device(cpu
);
246 err
= device_create_file(cpu_dev
,
247 &attr_percpu_activate_hint
);
250 err
= device_create_file(cpu_dev
,
251 &attr_percpu_deactivate_hint
);
259 sysfs_entries
= 1; /* Removed entries on cleanup */
264 static void __exit
pseries_energy_cleanup(void)
267 struct device
*cpu_dev
;
272 /* Remove the sysfs files */
273 device_remove_file(cpu_subsys
.dev_root
, &attr_cpu_activate_hint_list
);
274 device_remove_file(cpu_subsys
.dev_root
, &attr_cpu_deactivate_hint_list
);
276 for_each_possible_cpu(cpu
) {
277 cpu_dev
= get_cpu_device(cpu
);
278 sysfs_remove_file(&cpu_dev
->kobj
,
279 &attr_percpu_activate_hint
.attr
);
280 sysfs_remove_file(&cpu_dev
->kobj
,
281 &attr_percpu_deactivate_hint
.attr
);
285 module_init(pseries_energy_init
);
286 module_exit(pseries_energy_cleanup
);
287 MODULE_DESCRIPTION("Driver for pSeries platform energy management");
288 MODULE_AUTHOR("Vaidyanathan Srinivasan");
289 MODULE_LICENSE("GPL");