0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/module.h>
0011 #include <linux/types.h>
0012 #include <linux/errno.h>
0013 #include <linux/init.h>
0014 #include <linux/seq_file.h>
0015 #include <linux/device.h>
0016 #include <linux/cpu.h>
0017 #include <linux/of.h>
0018 #include <asm/cputhreads.h>
0019 #include <asm/page.h>
0020 #include <asm/hvcall.h>
0021 #include <asm/firmware.h>
0022 #include <asm/prom.h>
0023
0024
0025 #define MODULE_VERS "1.0"
0026 #define MODULE_NAME "pseries_energy"
0027
0028
0029
0030 static int sysfs_entries;
0031
0032
0033
0034
0035
0036 static u32 cpu_to_drc_index(int cpu)
0037 {
0038 struct device_node *dn = NULL;
0039 struct property *info;
0040 int thread_index;
0041 int rc = 1;
0042 u32 ret = 0;
0043
0044 dn = of_find_node_by_path("/cpus");
0045 if (dn == NULL)
0046 goto err;
0047
0048
0049 thread_index = cpu_core_index_of_thread(cpu);
0050
0051 info = of_find_property(dn, "ibm,drc-info", NULL);
0052 if (info) {
0053 struct of_drc_info drc;
0054 int j;
0055 u32 num_set_entries;
0056 const __be32 *value;
0057
0058 value = of_prop_next_u32(info, NULL, &num_set_entries);
0059 if (!value)
0060 goto err_of_node_put;
0061 else
0062 value++;
0063
0064 for (j = 0; j < num_set_entries; j++) {
0065
0066 of_read_drc_info_cell(&info, &value, &drc);
0067 if (strncmp(drc.drc_type, "CPU", 3))
0068 goto err;
0069
0070 if (thread_index < drc.last_drc_index)
0071 break;
0072 }
0073
0074 ret = drc.drc_index_start + (thread_index * drc.sequential_inc);
0075 } else {
0076 u32 nr_drc_indexes, thread_drc_index;
0077
0078
0079
0080
0081
0082
0083
0084 rc = of_property_read_u32_index(dn, "ibm,drc-indexes",
0085 0, &nr_drc_indexes);
0086 if (rc)
0087 goto err_of_node_put;
0088
0089 WARN_ON_ONCE(thread_index > nr_drc_indexes);
0090 rc = of_property_read_u32_index(dn, "ibm,drc-indexes",
0091 thread_index + 1,
0092 &thread_drc_index);
0093 if (rc)
0094 goto err_of_node_put;
0095
0096 ret = thread_drc_index;
0097 }
0098
0099 rc = 0;
0100
0101 err_of_node_put:
0102 of_node_put(dn);
0103 err:
0104 if (rc)
0105 printk(KERN_WARNING "cpu_to_drc_index(%d) failed", cpu);
0106 return ret;
0107 }
0108
0109 static int drc_index_to_cpu(u32 drc_index)
0110 {
0111 struct device_node *dn = NULL;
0112 struct property *info;
0113 const int *indexes;
0114 int thread_index = 0, cpu = 0;
0115 int rc = 1;
0116
0117 dn = of_find_node_by_path("/cpus");
0118 if (dn == NULL)
0119 goto err;
0120 info = of_find_property(dn, "ibm,drc-info", NULL);
0121 if (info) {
0122 struct of_drc_info drc;
0123 int j;
0124 u32 num_set_entries;
0125 const __be32 *value;
0126
0127 value = of_prop_next_u32(info, NULL, &num_set_entries);
0128 if (!value)
0129 goto err_of_node_put;
0130 else
0131 value++;
0132
0133 for (j = 0; j < num_set_entries; j++) {
0134
0135 of_read_drc_info_cell(&info, &value, &drc);
0136 if (strncmp(drc.drc_type, "CPU", 3))
0137 goto err;
0138
0139 if (drc_index > drc.last_drc_index) {
0140 cpu += drc.num_sequential_elems;
0141 continue;
0142 }
0143 cpu += ((drc_index - drc.drc_index_start) /
0144 drc.sequential_inc);
0145
0146 thread_index = cpu_first_thread_of_core(cpu);
0147 rc = 0;
0148 break;
0149 }
0150 } else {
0151 unsigned long int i;
0152
0153 indexes = of_get_property(dn, "ibm,drc-indexes", NULL);
0154 if (indexes == NULL)
0155 goto err_of_node_put;
0156
0157
0158
0159
0160
0161 for (i = 0; i < indexes[0]; i++) {
0162 if (indexes[i + 1] == drc_index)
0163 break;
0164 }
0165
0166 thread_index = cpu_first_thread_of_core(i);
0167 rc = 0;
0168 }
0169
0170 err_of_node_put:
0171 of_node_put(dn);
0172 err:
0173 if (rc)
0174 printk(KERN_WARNING "drc_index_to_cpu(%d) failed", drc_index);
0175 return thread_index;
0176 }
0177
0178
0179
0180
0181
0182
0183
0184 #define FLAGS_MODE1 0x004E200000080E01UL
0185 #define FLAGS_MODE2 0x004E200000080401UL
0186 #define FLAGS_ACTIVATE 0x100
0187
0188 static ssize_t get_best_energy_list(char *page, int activate)
0189 {
0190 int rc, cnt, i, cpu;
0191 unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
0192 unsigned long flags = 0;
0193 u32 *buf_page;
0194 char *s = page;
0195
0196 buf_page = (u32 *) get_zeroed_page(GFP_KERNEL);
0197 if (!buf_page)
0198 return -ENOMEM;
0199
0200 flags = FLAGS_MODE1;
0201 if (activate)
0202 flags |= FLAGS_ACTIVATE;
0203
0204 rc = plpar_hcall9(H_BEST_ENERGY, retbuf, flags, 0, __pa(buf_page),
0205 0, 0, 0, 0, 0, 0);
0206 if (rc != H_SUCCESS) {
0207 free_page((unsigned long) buf_page);
0208 return -EINVAL;
0209 }
0210
0211 cnt = retbuf[0];
0212 for (i = 0; i < cnt; i++) {
0213 cpu = drc_index_to_cpu(buf_page[2*i+1]);
0214 if ((cpu_online(cpu) && !activate) ||
0215 (!cpu_online(cpu) && activate))
0216 s += sprintf(s, "%d,", cpu);
0217 }
0218 if (s > page) {
0219 s--;
0220 s += sprintf(s, "\n");
0221 }
0222
0223 free_page((unsigned long) buf_page);
0224 return s-page;
0225 }
0226
0227 static ssize_t get_best_energy_data(struct device *dev,
0228 char *page, int activate)
0229 {
0230 int rc;
0231 unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
0232 unsigned long flags = 0;
0233
0234 flags = FLAGS_MODE2;
0235 if (activate)
0236 flags |= FLAGS_ACTIVATE;
0237
0238 rc = plpar_hcall9(H_BEST_ENERGY, retbuf, flags,
0239 cpu_to_drc_index(dev->id),
0240 0, 0, 0, 0, 0, 0, 0);
0241
0242 if (rc != H_SUCCESS)
0243 return -EINVAL;
0244
0245 return sprintf(page, "%lu\n", retbuf[1] >> 32);
0246 }
0247
0248
0249
0250 static ssize_t cpu_activate_hint_list_show(struct device *dev,
0251 struct device_attribute *attr, char *page)
0252 {
0253 return get_best_energy_list(page, 1);
0254 }
0255
0256 static ssize_t cpu_deactivate_hint_list_show(struct device *dev,
0257 struct device_attribute *attr, char *page)
0258 {
0259 return get_best_energy_list(page, 0);
0260 }
0261
0262 static ssize_t percpu_activate_hint_show(struct device *dev,
0263 struct device_attribute *attr, char *page)
0264 {
0265 return get_best_energy_data(dev, page, 1);
0266 }
0267
0268 static ssize_t percpu_deactivate_hint_show(struct device *dev,
0269 struct device_attribute *attr, char *page)
0270 {
0271 return get_best_energy_data(dev, page, 0);
0272 }
0273
0274
0275
0276
0277
0278
0279
0280
0281
0282
0283
0284 static struct device_attribute attr_cpu_activate_hint_list =
0285 __ATTR(pseries_activate_hint_list, 0444,
0286 cpu_activate_hint_list_show, NULL);
0287
0288 static struct device_attribute attr_cpu_deactivate_hint_list =
0289 __ATTR(pseries_deactivate_hint_list, 0444,
0290 cpu_deactivate_hint_list_show, NULL);
0291
0292 static struct device_attribute attr_percpu_activate_hint =
0293 __ATTR(pseries_activate_hint, 0444,
0294 percpu_activate_hint_show, NULL);
0295
0296 static struct device_attribute attr_percpu_deactivate_hint =
0297 __ATTR(pseries_deactivate_hint, 0444,
0298 percpu_deactivate_hint_show, NULL);
0299
0300 static int __init pseries_energy_init(void)
0301 {
0302 int cpu, err;
0303 struct device *cpu_dev;
0304
0305 if (!firmware_has_feature(FW_FEATURE_BEST_ENERGY))
0306 return 0;
0307
0308
0309 err = device_create_file(cpu_subsys.dev_root,
0310 &attr_cpu_activate_hint_list);
0311 if (!err)
0312 err = device_create_file(cpu_subsys.dev_root,
0313 &attr_cpu_deactivate_hint_list);
0314
0315 if (err)
0316 return err;
0317 for_each_possible_cpu(cpu) {
0318 cpu_dev = get_cpu_device(cpu);
0319 err = device_create_file(cpu_dev,
0320 &attr_percpu_activate_hint);
0321 if (err)
0322 break;
0323 err = device_create_file(cpu_dev,
0324 &attr_percpu_deactivate_hint);
0325 if (err)
0326 break;
0327 }
0328
0329 if (err)
0330 return err;
0331
0332 sysfs_entries = 1;
0333 return 0;
0334
0335 }
0336
0337 static void __exit pseries_energy_cleanup(void)
0338 {
0339 int cpu;
0340 struct device *cpu_dev;
0341
0342 if (!sysfs_entries)
0343 return;
0344
0345
0346 device_remove_file(cpu_subsys.dev_root, &attr_cpu_activate_hint_list);
0347 device_remove_file(cpu_subsys.dev_root, &attr_cpu_deactivate_hint_list);
0348
0349 for_each_possible_cpu(cpu) {
0350 cpu_dev = get_cpu_device(cpu);
0351 sysfs_remove_file(&cpu_dev->kobj,
0352 &attr_percpu_activate_hint.attr);
0353 sysfs_remove_file(&cpu_dev->kobj,
0354 &attr_percpu_deactivate_hint.attr);
0355 }
0356 }
0357
0358 module_init(pseries_energy_init);
0359 module_exit(pseries_energy_cleanup);
0360 MODULE_DESCRIPTION("Driver for pSeries platform energy management");
0361 MODULE_AUTHOR("Vaidyanathan Srinivasan");
0362 MODULE_LICENSE("GPL");