1*3c14b734SPratik R. Sampat // SPDX-License-Identifier: GPL-2.0-or-later 2*3c14b734SPratik R. Sampat /* 3*3c14b734SPratik R. Sampat * Platform energy and frequency attributes driver 4*3c14b734SPratik R. Sampat * 5*3c14b734SPratik R. Sampat * This driver creates a sys file at /sys/firmware/papr/ which encapsulates a 6*3c14b734SPratik R. Sampat * directory structure containing files in keyword - value pairs that specify 7*3c14b734SPratik R. Sampat * energy and frequency configuration of the system. 8*3c14b734SPratik R. Sampat * 9*3c14b734SPratik R. Sampat * The format of exposing the sysfs information is as follows: 10*3c14b734SPratik R. Sampat * /sys/firmware/papr/energy_scale_info/ 11*3c14b734SPratik R. Sampat * |-- <id>/ 12*3c14b734SPratik R. Sampat * |-- desc 13*3c14b734SPratik R. Sampat * |-- value 14*3c14b734SPratik R. Sampat * |-- value_desc (if exists) 15*3c14b734SPratik R. Sampat * |-- <id>/ 16*3c14b734SPratik R. Sampat * |-- desc 17*3c14b734SPratik R. Sampat * |-- value 18*3c14b734SPratik R. Sampat * |-- value_desc (if exists) 19*3c14b734SPratik R. Sampat * 20*3c14b734SPratik R. Sampat * Copyright 2022 IBM Corp. 21*3c14b734SPratik R. Sampat */ 22*3c14b734SPratik R. Sampat 23*3c14b734SPratik R. Sampat #include <asm/hvcall.h> 24*3c14b734SPratik R. Sampat #include <asm/machdep.h> 25*3c14b734SPratik R. Sampat 26*3c14b734SPratik R. Sampat #include "pseries.h" 27*3c14b734SPratik R. Sampat 28*3c14b734SPratik R. Sampat /* 29*3c14b734SPratik R. Sampat * Flag attributes to fetch either all or one attribute from the HCALL 30*3c14b734SPratik R. Sampat * flag = BE(0) => fetch all attributes with firstAttributeId = 0 31*3c14b734SPratik R. Sampat * flag = BE(1) => fetch a single attribute with firstAttributeId = id 32*3c14b734SPratik R. Sampat */ 33*3c14b734SPratik R. Sampat #define ESI_FLAGS_ALL 0 34*3c14b734SPratik R. Sampat #define ESI_FLAGS_SINGLE (1ull << 63) 35*3c14b734SPratik R. Sampat 36*3c14b734SPratik R. Sampat #define KOBJ_MAX_ATTRS 3 37*3c14b734SPratik R. Sampat 38*3c14b734SPratik R. Sampat #define ESI_HDR_SIZE sizeof(struct h_energy_scale_info_hdr) 39*3c14b734SPratik R. Sampat #define ESI_ATTR_SIZE sizeof(struct energy_scale_attribute) 40*3c14b734SPratik R. Sampat #define CURR_MAX_ESI_ATTRS 8 41*3c14b734SPratik R. Sampat 42*3c14b734SPratik R. Sampat struct energy_scale_attribute { 43*3c14b734SPratik R. Sampat __be64 id; 44*3c14b734SPratik R. Sampat __be64 val; 45*3c14b734SPratik R. Sampat u8 desc[64]; 46*3c14b734SPratik R. Sampat u8 value_desc[64]; 47*3c14b734SPratik R. Sampat } __packed; 48*3c14b734SPratik R. Sampat 49*3c14b734SPratik R. Sampat struct h_energy_scale_info_hdr { 50*3c14b734SPratik R. Sampat __be64 num_attrs; 51*3c14b734SPratik R. Sampat __be64 array_offset; 52*3c14b734SPratik R. Sampat u8 data_header_version; 53*3c14b734SPratik R. Sampat } __packed; 54*3c14b734SPratik R. Sampat 55*3c14b734SPratik R. Sampat struct papr_attr { 56*3c14b734SPratik R. Sampat u64 id; 57*3c14b734SPratik R. Sampat struct kobj_attribute kobj_attr; 58*3c14b734SPratik R. Sampat }; 59*3c14b734SPratik R. Sampat 60*3c14b734SPratik R. Sampat struct papr_group { 61*3c14b734SPratik R. Sampat struct attribute_group pg; 62*3c14b734SPratik R. Sampat struct papr_attr pgattrs[KOBJ_MAX_ATTRS]; 63*3c14b734SPratik R. Sampat }; 64*3c14b734SPratik R. Sampat 65*3c14b734SPratik R. Sampat static struct papr_group *papr_groups; 66*3c14b734SPratik R. Sampat /* /sys/firmware/papr */ 67*3c14b734SPratik R. Sampat static struct kobject *papr_kobj; 68*3c14b734SPratik R. Sampat /* /sys/firmware/papr/energy_scale_info */ 69*3c14b734SPratik R. Sampat static struct kobject *esi_kobj; 70*3c14b734SPratik R. Sampat 71*3c14b734SPratik R. Sampat /* 72*3c14b734SPratik R. Sampat * Energy modes can change dynamically hence making a new hcall each time the 73*3c14b734SPratik R. Sampat * information needs to be retrieved 74*3c14b734SPratik R. Sampat */ 75*3c14b734SPratik R. Sampat static int papr_get_attr(u64 id, struct energy_scale_attribute *esi) 76*3c14b734SPratik R. Sampat { 77*3c14b734SPratik R. Sampat int esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * ESI_ATTR_SIZE); 78*3c14b734SPratik R. Sampat int ret, max_esi_attrs = CURR_MAX_ESI_ATTRS; 79*3c14b734SPratik R. Sampat struct energy_scale_attribute *curr_esi; 80*3c14b734SPratik R. Sampat struct h_energy_scale_info_hdr *hdr; 81*3c14b734SPratik R. Sampat char *buf; 82*3c14b734SPratik R. Sampat 83*3c14b734SPratik R. Sampat buf = kmalloc(esi_buf_size, GFP_KERNEL); 84*3c14b734SPratik R. Sampat if (buf == NULL) 85*3c14b734SPratik R. Sampat return -ENOMEM; 86*3c14b734SPratik R. Sampat 87*3c14b734SPratik R. Sampat retry: 88*3c14b734SPratik R. Sampat ret = plpar_hcall_norets(H_GET_ENERGY_SCALE_INFO, ESI_FLAGS_SINGLE, 89*3c14b734SPratik R. Sampat id, virt_to_phys(buf), 90*3c14b734SPratik R. Sampat esi_buf_size); 91*3c14b734SPratik R. Sampat 92*3c14b734SPratik R. Sampat /* 93*3c14b734SPratik R. Sampat * If the hcall fails with not enough memory for either the 94*3c14b734SPratik R. Sampat * header or data, attempt to allocate more 95*3c14b734SPratik R. Sampat */ 96*3c14b734SPratik R. Sampat if (ret == H_PARTIAL || ret == H_P4) { 97*3c14b734SPratik R. Sampat char *temp_buf; 98*3c14b734SPratik R. Sampat 99*3c14b734SPratik R. Sampat max_esi_attrs += 4; 100*3c14b734SPratik R. Sampat esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * max_esi_attrs); 101*3c14b734SPratik R. Sampat 102*3c14b734SPratik R. Sampat temp_buf = krealloc(buf, esi_buf_size, GFP_KERNEL); 103*3c14b734SPratik R. Sampat if (temp_buf) 104*3c14b734SPratik R. Sampat buf = temp_buf; 105*3c14b734SPratik R. Sampat else 106*3c14b734SPratik R. Sampat return -ENOMEM; 107*3c14b734SPratik R. Sampat 108*3c14b734SPratik R. Sampat goto retry; 109*3c14b734SPratik R. Sampat } 110*3c14b734SPratik R. Sampat 111*3c14b734SPratik R. Sampat if (ret != H_SUCCESS) { 112*3c14b734SPratik R. Sampat pr_warn("hcall failed: H_GET_ENERGY_SCALE_INFO"); 113*3c14b734SPratik R. Sampat ret = -EIO; 114*3c14b734SPratik R. Sampat goto out_buf; 115*3c14b734SPratik R. Sampat } 116*3c14b734SPratik R. Sampat 117*3c14b734SPratik R. Sampat hdr = (struct h_energy_scale_info_hdr *) buf; 118*3c14b734SPratik R. Sampat curr_esi = (struct energy_scale_attribute *) 119*3c14b734SPratik R. Sampat (buf + be64_to_cpu(hdr->array_offset)); 120*3c14b734SPratik R. Sampat 121*3c14b734SPratik R. Sampat if (esi_buf_size < 122*3c14b734SPratik R. Sampat be64_to_cpu(hdr->array_offset) + (be64_to_cpu(hdr->num_attrs) 123*3c14b734SPratik R. Sampat * sizeof(struct energy_scale_attribute))) { 124*3c14b734SPratik R. Sampat ret = -EIO; 125*3c14b734SPratik R. Sampat goto out_buf; 126*3c14b734SPratik R. Sampat } 127*3c14b734SPratik R. Sampat 128*3c14b734SPratik R. Sampat *esi = *curr_esi; 129*3c14b734SPratik R. Sampat 130*3c14b734SPratik R. Sampat out_buf: 131*3c14b734SPratik R. Sampat kfree(buf); 132*3c14b734SPratik R. Sampat 133*3c14b734SPratik R. Sampat return ret; 134*3c14b734SPratik R. Sampat } 135*3c14b734SPratik R. Sampat 136*3c14b734SPratik R. Sampat /* 137*3c14b734SPratik R. Sampat * Extract and export the description of the energy scale attributes 138*3c14b734SPratik R. Sampat */ 139*3c14b734SPratik R. Sampat static ssize_t desc_show(struct kobject *kobj, 140*3c14b734SPratik R. Sampat struct kobj_attribute *kobj_attr, 141*3c14b734SPratik R. Sampat char *buf) 142*3c14b734SPratik R. Sampat { 143*3c14b734SPratik R. Sampat struct papr_attr *pattr = container_of(kobj_attr, struct papr_attr, 144*3c14b734SPratik R. Sampat kobj_attr); 145*3c14b734SPratik R. Sampat struct energy_scale_attribute esi; 146*3c14b734SPratik R. Sampat int ret; 147*3c14b734SPratik R. Sampat 148*3c14b734SPratik R. Sampat ret = papr_get_attr(pattr->id, &esi); 149*3c14b734SPratik R. Sampat if (ret) 150*3c14b734SPratik R. Sampat return ret; 151*3c14b734SPratik R. Sampat 152*3c14b734SPratik R. Sampat return sysfs_emit(buf, "%s\n", esi.desc); 153*3c14b734SPratik R. Sampat } 154*3c14b734SPratik R. Sampat 155*3c14b734SPratik R. Sampat /* 156*3c14b734SPratik R. Sampat * Extract and export the numeric value of the energy scale attributes 157*3c14b734SPratik R. Sampat */ 158*3c14b734SPratik R. Sampat static ssize_t val_show(struct kobject *kobj, 159*3c14b734SPratik R. Sampat struct kobj_attribute *kobj_attr, 160*3c14b734SPratik R. Sampat char *buf) 161*3c14b734SPratik R. Sampat { 162*3c14b734SPratik R. Sampat struct papr_attr *pattr = container_of(kobj_attr, struct papr_attr, 163*3c14b734SPratik R. Sampat kobj_attr); 164*3c14b734SPratik R. Sampat struct energy_scale_attribute esi; 165*3c14b734SPratik R. Sampat int ret; 166*3c14b734SPratik R. Sampat 167*3c14b734SPratik R. Sampat ret = papr_get_attr(pattr->id, &esi); 168*3c14b734SPratik R. Sampat if (ret) 169*3c14b734SPratik R. Sampat return ret; 170*3c14b734SPratik R. Sampat 171*3c14b734SPratik R. Sampat return sysfs_emit(buf, "%llu\n", be64_to_cpu(esi.val)); 172*3c14b734SPratik R. Sampat } 173*3c14b734SPratik R. Sampat 174*3c14b734SPratik R. Sampat /* 175*3c14b734SPratik R. Sampat * Extract and export the value description in string format of the energy 176*3c14b734SPratik R. Sampat * scale attributes 177*3c14b734SPratik R. Sampat */ 178*3c14b734SPratik R. Sampat static ssize_t val_desc_show(struct kobject *kobj, 179*3c14b734SPratik R. Sampat struct kobj_attribute *kobj_attr, 180*3c14b734SPratik R. Sampat char *buf) 181*3c14b734SPratik R. Sampat { 182*3c14b734SPratik R. Sampat struct papr_attr *pattr = container_of(kobj_attr, struct papr_attr, 183*3c14b734SPratik R. Sampat kobj_attr); 184*3c14b734SPratik R. Sampat struct energy_scale_attribute esi; 185*3c14b734SPratik R. Sampat int ret; 186*3c14b734SPratik R. Sampat 187*3c14b734SPratik R. Sampat ret = papr_get_attr(pattr->id, &esi); 188*3c14b734SPratik R. Sampat if (ret) 189*3c14b734SPratik R. Sampat return ret; 190*3c14b734SPratik R. Sampat 191*3c14b734SPratik R. Sampat return sysfs_emit(buf, "%s\n", esi.value_desc); 192*3c14b734SPratik R. Sampat } 193*3c14b734SPratik R. Sampat 194*3c14b734SPratik R. Sampat static struct papr_ops_info { 195*3c14b734SPratik R. Sampat const char *attr_name; 196*3c14b734SPratik R. Sampat ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *kobj_attr, 197*3c14b734SPratik R. Sampat char *buf); 198*3c14b734SPratik R. Sampat } ops_info[KOBJ_MAX_ATTRS] = { 199*3c14b734SPratik R. Sampat { "desc", desc_show }, 200*3c14b734SPratik R. Sampat { "value", val_show }, 201*3c14b734SPratik R. Sampat { "value_desc", val_desc_show }, 202*3c14b734SPratik R. Sampat }; 203*3c14b734SPratik R. Sampat 204*3c14b734SPratik R. Sampat static void add_attr(u64 id, int index, struct papr_attr *attr) 205*3c14b734SPratik R. Sampat { 206*3c14b734SPratik R. Sampat attr->id = id; 207*3c14b734SPratik R. Sampat sysfs_attr_init(&attr->kobj_attr.attr); 208*3c14b734SPratik R. Sampat attr->kobj_attr.attr.name = ops_info[index].attr_name; 209*3c14b734SPratik R. Sampat attr->kobj_attr.attr.mode = 0444; 210*3c14b734SPratik R. Sampat attr->kobj_attr.show = ops_info[index].show; 211*3c14b734SPratik R. Sampat } 212*3c14b734SPratik R. Sampat 213*3c14b734SPratik R. Sampat static int add_attr_group(u64 id, struct papr_group *pg, bool show_val_desc) 214*3c14b734SPratik R. Sampat { 215*3c14b734SPratik R. Sampat int i; 216*3c14b734SPratik R. Sampat 217*3c14b734SPratik R. Sampat for (i = 0; i < KOBJ_MAX_ATTRS; i++) { 218*3c14b734SPratik R. Sampat if (!strcmp(ops_info[i].attr_name, "value_desc") && 219*3c14b734SPratik R. Sampat !show_val_desc) { 220*3c14b734SPratik R. Sampat continue; 221*3c14b734SPratik R. Sampat } 222*3c14b734SPratik R. Sampat add_attr(id, i, &pg->pgattrs[i]); 223*3c14b734SPratik R. Sampat pg->pg.attrs[i] = &pg->pgattrs[i].kobj_attr.attr; 224*3c14b734SPratik R. Sampat } 225*3c14b734SPratik R. Sampat 226*3c14b734SPratik R. Sampat return sysfs_create_group(esi_kobj, &pg->pg); 227*3c14b734SPratik R. Sampat } 228*3c14b734SPratik R. Sampat 229*3c14b734SPratik R. Sampat 230*3c14b734SPratik R. Sampat static int __init papr_init(void) 231*3c14b734SPratik R. Sampat { 232*3c14b734SPratik R. Sampat int esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * ESI_ATTR_SIZE); 233*3c14b734SPratik R. Sampat int ret, idx, i, max_esi_attrs = CURR_MAX_ESI_ATTRS; 234*3c14b734SPratik R. Sampat struct h_energy_scale_info_hdr *esi_hdr; 235*3c14b734SPratik R. Sampat struct energy_scale_attribute *esi_attrs; 236*3c14b734SPratik R. Sampat uint64_t num_attrs; 237*3c14b734SPratik R. Sampat char *esi_buf; 238*3c14b734SPratik R. Sampat 239*3c14b734SPratik R. Sampat if (!firmware_has_feature(FW_FEATURE_LPAR) || 240*3c14b734SPratik R. Sampat !firmware_has_feature(FW_FEATURE_ENERGY_SCALE_INFO)) { 241*3c14b734SPratik R. Sampat return -ENXIO; 242*3c14b734SPratik R. Sampat } 243*3c14b734SPratik R. Sampat 244*3c14b734SPratik R. Sampat esi_buf = kmalloc(esi_buf_size, GFP_KERNEL); 245*3c14b734SPratik R. Sampat if (esi_buf == NULL) 246*3c14b734SPratik R. Sampat return -ENOMEM; 247*3c14b734SPratik R. Sampat /* 248*3c14b734SPratik R. Sampat * hcall( 249*3c14b734SPratik R. Sampat * uint64 H_GET_ENERGY_SCALE_INFO, // Get energy scale info 250*3c14b734SPratik R. Sampat * uint64 flags, // Per the flag request 251*3c14b734SPratik R. Sampat * uint64 firstAttributeId, // The attribute id 252*3c14b734SPratik R. Sampat * uint64 bufferAddress, // Guest physical address of the output buffer 253*3c14b734SPratik R. Sampat * uint64 bufferSize); // The size in bytes of the output buffer 254*3c14b734SPratik R. Sampat */ 255*3c14b734SPratik R. Sampat retry: 256*3c14b734SPratik R. Sampat 257*3c14b734SPratik R. Sampat ret = plpar_hcall_norets(H_GET_ENERGY_SCALE_INFO, ESI_FLAGS_ALL, 0, 258*3c14b734SPratik R. Sampat virt_to_phys(esi_buf), esi_buf_size); 259*3c14b734SPratik R. Sampat 260*3c14b734SPratik R. Sampat /* 261*3c14b734SPratik R. Sampat * If the hcall fails with not enough memory for either the 262*3c14b734SPratik R. Sampat * header or data, attempt to allocate more 263*3c14b734SPratik R. Sampat */ 264*3c14b734SPratik R. Sampat if (ret == H_PARTIAL || ret == H_P4) { 265*3c14b734SPratik R. Sampat char *temp_esi_buf; 266*3c14b734SPratik R. Sampat 267*3c14b734SPratik R. Sampat max_esi_attrs += 4; 268*3c14b734SPratik R. Sampat esi_buf_size = ESI_HDR_SIZE + (CURR_MAX_ESI_ATTRS * max_esi_attrs); 269*3c14b734SPratik R. Sampat 270*3c14b734SPratik R. Sampat temp_esi_buf = krealloc(esi_buf, esi_buf_size, GFP_KERNEL); 271*3c14b734SPratik R. Sampat if (temp_esi_buf) 272*3c14b734SPratik R. Sampat esi_buf = temp_esi_buf; 273*3c14b734SPratik R. Sampat else 274*3c14b734SPratik R. Sampat return -ENOMEM; 275*3c14b734SPratik R. Sampat 276*3c14b734SPratik R. Sampat goto retry; 277*3c14b734SPratik R. Sampat } 278*3c14b734SPratik R. Sampat 279*3c14b734SPratik R. Sampat if (ret != H_SUCCESS) { 280*3c14b734SPratik R. Sampat pr_warn("hcall failed: H_GET_ENERGY_SCALE_INFO, ret: %d\n", ret); 281*3c14b734SPratik R. Sampat goto out_free_esi_buf; 282*3c14b734SPratik R. Sampat } 283*3c14b734SPratik R. Sampat 284*3c14b734SPratik R. Sampat esi_hdr = (struct h_energy_scale_info_hdr *) esi_buf; 285*3c14b734SPratik R. Sampat num_attrs = be64_to_cpu(esi_hdr->num_attrs); 286*3c14b734SPratik R. Sampat esi_attrs = (struct energy_scale_attribute *) 287*3c14b734SPratik R. Sampat (esi_buf + be64_to_cpu(esi_hdr->array_offset)); 288*3c14b734SPratik R. Sampat 289*3c14b734SPratik R. Sampat if (esi_buf_size < 290*3c14b734SPratik R. Sampat be64_to_cpu(esi_hdr->array_offset) + 291*3c14b734SPratik R. Sampat (num_attrs * sizeof(struct energy_scale_attribute))) { 292*3c14b734SPratik R. Sampat goto out_free_esi_buf; 293*3c14b734SPratik R. Sampat } 294*3c14b734SPratik R. Sampat 295*3c14b734SPratik R. Sampat papr_groups = kcalloc(num_attrs, sizeof(*papr_groups), GFP_KERNEL); 296*3c14b734SPratik R. Sampat if (!papr_groups) 297*3c14b734SPratik R. Sampat goto out_free_esi_buf; 298*3c14b734SPratik R. Sampat 299*3c14b734SPratik R. Sampat papr_kobj = kobject_create_and_add("papr", firmware_kobj); 300*3c14b734SPratik R. Sampat if (!papr_kobj) { 301*3c14b734SPratik R. Sampat pr_warn("kobject_create_and_add papr failed\n"); 302*3c14b734SPratik R. Sampat goto out_papr_groups; 303*3c14b734SPratik R. Sampat } 304*3c14b734SPratik R. Sampat 305*3c14b734SPratik R. Sampat esi_kobj = kobject_create_and_add("energy_scale_info", papr_kobj); 306*3c14b734SPratik R. Sampat if (!esi_kobj) { 307*3c14b734SPratik R. Sampat pr_warn("kobject_create_and_add energy_scale_info failed\n"); 308*3c14b734SPratik R. Sampat goto out_kobj; 309*3c14b734SPratik R. Sampat } 310*3c14b734SPratik R. Sampat 311*3c14b734SPratik R. Sampat /* Allocate the groups before registering */ 312*3c14b734SPratik R. Sampat for (idx = 0; idx < num_attrs; idx++) { 313*3c14b734SPratik R. Sampat papr_groups[idx].pg.attrs = kcalloc(KOBJ_MAX_ATTRS + 1, 314*3c14b734SPratik R. Sampat sizeof(*papr_groups[idx].pg.attrs), 315*3c14b734SPratik R. Sampat GFP_KERNEL); 316*3c14b734SPratik R. Sampat if (!papr_groups[idx].pg.attrs) 317*3c14b734SPratik R. Sampat goto out_pgattrs; 318*3c14b734SPratik R. Sampat 319*3c14b734SPratik R. Sampat papr_groups[idx].pg.name = kasprintf(GFP_KERNEL, "%lld", 320*3c14b734SPratik R. Sampat be64_to_cpu(esi_attrs[idx].id)); 321*3c14b734SPratik R. Sampat if (papr_groups[idx].pg.name == NULL) 322*3c14b734SPratik R. Sampat goto out_pgattrs; 323*3c14b734SPratik R. Sampat } 324*3c14b734SPratik R. Sampat 325*3c14b734SPratik R. Sampat for (idx = 0; idx < num_attrs; idx++) { 326*3c14b734SPratik R. Sampat bool show_val_desc = true; 327*3c14b734SPratik R. Sampat 328*3c14b734SPratik R. Sampat /* Do not add the value desc attr if it does not exist */ 329*3c14b734SPratik R. Sampat if (strnlen(esi_attrs[idx].value_desc, 330*3c14b734SPratik R. Sampat sizeof(esi_attrs[idx].value_desc)) == 0) 331*3c14b734SPratik R. Sampat show_val_desc = false; 332*3c14b734SPratik R. Sampat 333*3c14b734SPratik R. Sampat if (add_attr_group(be64_to_cpu(esi_attrs[idx].id), 334*3c14b734SPratik R. Sampat &papr_groups[idx], 335*3c14b734SPratik R. Sampat show_val_desc)) { 336*3c14b734SPratik R. Sampat pr_warn("Failed to create papr attribute group %s\n", 337*3c14b734SPratik R. Sampat papr_groups[idx].pg.name); 338*3c14b734SPratik R. Sampat idx = num_attrs; 339*3c14b734SPratik R. Sampat goto out_pgattrs; 340*3c14b734SPratik R. Sampat } 341*3c14b734SPratik R. Sampat } 342*3c14b734SPratik R. Sampat 343*3c14b734SPratik R. Sampat kfree(esi_buf); 344*3c14b734SPratik R. Sampat return 0; 345*3c14b734SPratik R. Sampat out_pgattrs: 346*3c14b734SPratik R. Sampat for (i = 0; i < idx ; i++) { 347*3c14b734SPratik R. Sampat kfree(papr_groups[i].pg.attrs); 348*3c14b734SPratik R. Sampat kfree(papr_groups[i].pg.name); 349*3c14b734SPratik R. Sampat } 350*3c14b734SPratik R. Sampat kobject_put(esi_kobj); 351*3c14b734SPratik R. Sampat out_kobj: 352*3c14b734SPratik R. Sampat kobject_put(papr_kobj); 353*3c14b734SPratik R. Sampat out_papr_groups: 354*3c14b734SPratik R. Sampat kfree(papr_groups); 355*3c14b734SPratik R. Sampat out_free_esi_buf: 356*3c14b734SPratik R. Sampat kfree(esi_buf); 357*3c14b734SPratik R. Sampat 358*3c14b734SPratik R. Sampat return -ENOMEM; 359*3c14b734SPratik R. Sampat } 360*3c14b734SPratik R. Sampat 361*3c14b734SPratik R. Sampat machine_device_initcall(pseries, papr_init); 362