0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023 #include <asm/hvcall.h>
0024 #include <asm/machdep.h>
0025 #include <asm/firmware.h>
0026
0027 #include "pseries.h"
0028
0029
0030
0031
0032
0033
0034 #define ESI_FLAGS_ALL 0
0035 #define ESI_FLAGS_SINGLE (1ull << 63)
0036
0037 #define KOBJ_MAX_ATTRS 3
0038
0039 #define ESI_HDR_SIZE sizeof(struct h_energy_scale_info_hdr)
0040 #define ESI_ATTR_SIZE sizeof(struct energy_scale_attribute)
0041 #define CURR_MAX_ESI_ATTRS 8
0042
0043 struct energy_scale_attribute {
0044 __be64 id;
0045 __be64 val;
0046 u8 desc[64];
0047 u8 value_desc[64];
0048 } __packed;
0049
0050 struct h_energy_scale_info_hdr {
0051 __be64 num_attrs;
0052 __be64 array_offset;
0053 u8 data_header_version;
0054 } __packed;
0055
0056 struct papr_attr {
0057 u64 id;
0058 struct kobj_attribute kobj_attr;
0059 };
0060
0061 struct papr_group {
0062 struct attribute_group pg;
0063 struct papr_attr pgattrs[KOBJ_MAX_ATTRS];
0064 };
0065
0066 static struct papr_group *papr_groups;
0067
0068 static struct kobject *papr_kobj;
0069
0070 static struct kobject *esi_kobj;
0071
0072
0073
0074
0075
0076 static int papr_get_attr(u64 id, struct energy_scale_attribute *esi)
0077 {
0078 int esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * ESI_ATTR_SIZE);
0079 int ret, max_esi_attrs = CURR_MAX_ESI_ATTRS;
0080 struct energy_scale_attribute *curr_esi;
0081 struct h_energy_scale_info_hdr *hdr;
0082 char *buf;
0083
0084 buf = kmalloc(esi_buf_size, GFP_KERNEL);
0085 if (buf == NULL)
0086 return -ENOMEM;
0087
0088 retry:
0089 ret = plpar_hcall_norets(H_GET_ENERGY_SCALE_INFO, ESI_FLAGS_SINGLE,
0090 id, virt_to_phys(buf),
0091 esi_buf_size);
0092
0093
0094
0095
0096
0097 if (ret == H_PARTIAL || ret == H_P4) {
0098 char *temp_buf;
0099
0100 max_esi_attrs += 4;
0101 esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * max_esi_attrs);
0102
0103 temp_buf = krealloc(buf, esi_buf_size, GFP_KERNEL);
0104 if (temp_buf)
0105 buf = temp_buf;
0106 else
0107 return -ENOMEM;
0108
0109 goto retry;
0110 }
0111
0112 if (ret != H_SUCCESS) {
0113 pr_warn("hcall failed: H_GET_ENERGY_SCALE_INFO");
0114 ret = -EIO;
0115 goto out_buf;
0116 }
0117
0118 hdr = (struct h_energy_scale_info_hdr *) buf;
0119 curr_esi = (struct energy_scale_attribute *)
0120 (buf + be64_to_cpu(hdr->array_offset));
0121
0122 if (esi_buf_size <
0123 be64_to_cpu(hdr->array_offset) + (be64_to_cpu(hdr->num_attrs)
0124 * sizeof(struct energy_scale_attribute))) {
0125 ret = -EIO;
0126 goto out_buf;
0127 }
0128
0129 *esi = *curr_esi;
0130
0131 out_buf:
0132 kfree(buf);
0133
0134 return ret;
0135 }
0136
0137
0138
0139
0140 static ssize_t desc_show(struct kobject *kobj,
0141 struct kobj_attribute *kobj_attr,
0142 char *buf)
0143 {
0144 struct papr_attr *pattr = container_of(kobj_attr, struct papr_attr,
0145 kobj_attr);
0146 struct energy_scale_attribute esi;
0147 int ret;
0148
0149 ret = papr_get_attr(pattr->id, &esi);
0150 if (ret)
0151 return ret;
0152
0153 return sysfs_emit(buf, "%s\n", esi.desc);
0154 }
0155
0156
0157
0158
0159 static ssize_t val_show(struct kobject *kobj,
0160 struct kobj_attribute *kobj_attr,
0161 char *buf)
0162 {
0163 struct papr_attr *pattr = container_of(kobj_attr, struct papr_attr,
0164 kobj_attr);
0165 struct energy_scale_attribute esi;
0166 int ret;
0167
0168 ret = papr_get_attr(pattr->id, &esi);
0169 if (ret)
0170 return ret;
0171
0172 return sysfs_emit(buf, "%llu\n", be64_to_cpu(esi.val));
0173 }
0174
0175
0176
0177
0178
0179 static ssize_t val_desc_show(struct kobject *kobj,
0180 struct kobj_attribute *kobj_attr,
0181 char *buf)
0182 {
0183 struct papr_attr *pattr = container_of(kobj_attr, struct papr_attr,
0184 kobj_attr);
0185 struct energy_scale_attribute esi;
0186 int ret;
0187
0188 ret = papr_get_attr(pattr->id, &esi);
0189 if (ret)
0190 return ret;
0191
0192 return sysfs_emit(buf, "%s\n", esi.value_desc);
0193 }
0194
0195 static struct papr_ops_info {
0196 const char *attr_name;
0197 ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *kobj_attr,
0198 char *buf);
0199 } ops_info[KOBJ_MAX_ATTRS] = {
0200 { "desc", desc_show },
0201 { "value", val_show },
0202 { "value_desc", val_desc_show },
0203 };
0204
0205 static void add_attr(u64 id, int index, struct papr_attr *attr)
0206 {
0207 attr->id = id;
0208 sysfs_attr_init(&attr->kobj_attr.attr);
0209 attr->kobj_attr.attr.name = ops_info[index].attr_name;
0210 attr->kobj_attr.attr.mode = 0444;
0211 attr->kobj_attr.show = ops_info[index].show;
0212 }
0213
0214 static int add_attr_group(u64 id, struct papr_group *pg, bool show_val_desc)
0215 {
0216 int i;
0217
0218 for (i = 0; i < KOBJ_MAX_ATTRS; i++) {
0219 if (!strcmp(ops_info[i].attr_name, "value_desc") &&
0220 !show_val_desc) {
0221 continue;
0222 }
0223 add_attr(id, i, &pg->pgattrs[i]);
0224 pg->pg.attrs[i] = &pg->pgattrs[i].kobj_attr.attr;
0225 }
0226
0227 return sysfs_create_group(esi_kobj, &pg->pg);
0228 }
0229
0230
0231 static int __init papr_init(void)
0232 {
0233 int esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * ESI_ATTR_SIZE);
0234 int ret, idx, i, max_esi_attrs = CURR_MAX_ESI_ATTRS;
0235 struct h_energy_scale_info_hdr *esi_hdr;
0236 struct energy_scale_attribute *esi_attrs;
0237 uint64_t num_attrs;
0238 char *esi_buf;
0239
0240 if (!firmware_has_feature(FW_FEATURE_LPAR) ||
0241 !firmware_has_feature(FW_FEATURE_ENERGY_SCALE_INFO)) {
0242 return -ENXIO;
0243 }
0244
0245 esi_buf = kmalloc(esi_buf_size, GFP_KERNEL);
0246 if (esi_buf == NULL)
0247 return -ENOMEM;
0248
0249
0250
0251
0252
0253
0254
0255
0256 retry:
0257
0258 ret = plpar_hcall_norets(H_GET_ENERGY_SCALE_INFO, ESI_FLAGS_ALL, 0,
0259 virt_to_phys(esi_buf), esi_buf_size);
0260
0261
0262
0263
0264
0265 if (ret == H_PARTIAL || ret == H_P4) {
0266 char *temp_esi_buf;
0267
0268 max_esi_attrs += 4;
0269 esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * max_esi_attrs);
0270
0271 temp_esi_buf = krealloc(esi_buf, esi_buf_size, GFP_KERNEL);
0272 if (temp_esi_buf)
0273 esi_buf = temp_esi_buf;
0274 else
0275 return -ENOMEM;
0276
0277 goto retry;
0278 }
0279
0280 if (ret != H_SUCCESS) {
0281 pr_warn("hcall failed: H_GET_ENERGY_SCALE_INFO, ret: %d\n", ret);
0282 goto out_free_esi_buf;
0283 }
0284
0285 esi_hdr = (struct h_energy_scale_info_hdr *) esi_buf;
0286 num_attrs = be64_to_cpu(esi_hdr->num_attrs);
0287 esi_attrs = (struct energy_scale_attribute *)
0288 (esi_buf + be64_to_cpu(esi_hdr->array_offset));
0289
0290 if (esi_buf_size <
0291 be64_to_cpu(esi_hdr->array_offset) +
0292 (num_attrs * sizeof(struct energy_scale_attribute))) {
0293 goto out_free_esi_buf;
0294 }
0295
0296 papr_groups = kcalloc(num_attrs, sizeof(*papr_groups), GFP_KERNEL);
0297 if (!papr_groups)
0298 goto out_free_esi_buf;
0299
0300 papr_kobj = kobject_create_and_add("papr", firmware_kobj);
0301 if (!papr_kobj) {
0302 pr_warn("kobject_create_and_add papr failed\n");
0303 goto out_papr_groups;
0304 }
0305
0306 esi_kobj = kobject_create_and_add("energy_scale_info", papr_kobj);
0307 if (!esi_kobj) {
0308 pr_warn("kobject_create_and_add energy_scale_info failed\n");
0309 goto out_kobj;
0310 }
0311
0312
0313 for (idx = 0; idx < num_attrs; idx++) {
0314 papr_groups[idx].pg.attrs = kcalloc(KOBJ_MAX_ATTRS + 1,
0315 sizeof(*papr_groups[idx].pg.attrs),
0316 GFP_KERNEL);
0317 if (!papr_groups[idx].pg.attrs)
0318 goto out_pgattrs;
0319
0320 papr_groups[idx].pg.name = kasprintf(GFP_KERNEL, "%lld",
0321 be64_to_cpu(esi_attrs[idx].id));
0322 if (papr_groups[idx].pg.name == NULL)
0323 goto out_pgattrs;
0324 }
0325
0326 for (idx = 0; idx < num_attrs; idx++) {
0327 bool show_val_desc = true;
0328
0329
0330 if (strnlen(esi_attrs[idx].value_desc,
0331 sizeof(esi_attrs[idx].value_desc)) == 0)
0332 show_val_desc = false;
0333
0334 if (add_attr_group(be64_to_cpu(esi_attrs[idx].id),
0335 &papr_groups[idx],
0336 show_val_desc)) {
0337 pr_warn("Failed to create papr attribute group %s\n",
0338 papr_groups[idx].pg.name);
0339 idx = num_attrs;
0340 goto out_pgattrs;
0341 }
0342 }
0343
0344 kfree(esi_buf);
0345 return 0;
0346 out_pgattrs:
0347 for (i = 0; i < idx ; i++) {
0348 kfree(papr_groups[i].pg.attrs);
0349 kfree(papr_groups[i].pg.name);
0350 }
0351 kobject_put(esi_kobj);
0352 out_kobj:
0353 kobject_put(papr_kobj);
0354 out_papr_groups:
0355 kfree(papr_groups);
0356 out_free_esi_buf:
0357 kfree(esi_buf);
0358
0359 return -ENOMEM;
0360 }
0361
0362 machine_device_initcall(pseries, papr_init);