1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * PowerNV OPAL Sensor-groups interface 4 * 5 * Copyright 2017 IBM Corp. 6 */ 7 8 #define pr_fmt(fmt) "opal-sensor-groups: " fmt 9 10 #include <linux/of.h> 11 #include <linux/kobject.h> 12 #include <linux/slab.h> 13 14 #include <asm/opal.h> 15 16 DEFINE_MUTEX(sg_mutex); 17 18 static struct kobject *sg_kobj; 19 20 struct sg_attr { 21 u32 handle; 22 struct kobj_attribute attr; 23 }; 24 25 static struct sensor_group { 26 char name[20]; 27 struct attribute_group sg; 28 struct sg_attr *sgattrs; 29 } *sgs; 30 31 int sensor_group_enable(u32 handle, bool enable) 32 { 33 struct opal_msg msg; 34 int token, ret; 35 36 token = opal_async_get_token_interruptible(); 37 if (token < 0) 38 return token; 39 40 ret = opal_sensor_group_enable(handle, token, enable); 41 if (ret == OPAL_ASYNC_COMPLETION) { 42 ret = opal_async_wait_response(token, &msg); 43 if (ret) { 44 pr_devel("Failed to wait for the async response\n"); 45 ret = -EIO; 46 goto out; 47 } 48 ret = opal_error_code(opal_get_async_rc(msg)); 49 } else { 50 ret = opal_error_code(ret); 51 } 52 53 out: 54 opal_async_release_token(token); 55 return ret; 56 } 57 EXPORT_SYMBOL_GPL(sensor_group_enable); 58 59 static ssize_t sg_store(struct kobject *kobj, struct kobj_attribute *attr, 60 const char *buf, size_t count) 61 { 62 struct sg_attr *sattr = container_of(attr, struct sg_attr, attr); 63 struct opal_msg msg; 64 u32 data; 65 int ret, token; 66 67 ret = kstrtoint(buf, 0, &data); 68 if (ret) 69 return ret; 70 71 if (data != 1) 72 return -EINVAL; 73 74 token = opal_async_get_token_interruptible(); 75 if (token < 0) { 76 pr_devel("Failed to get token\n"); 77 return token; 78 } 79 80 ret = mutex_lock_interruptible(&sg_mutex); 81 if (ret) 82 goto out_token; 83 84 ret = opal_sensor_group_clear(sattr->handle, token); 85 switch (ret) { 86 case OPAL_ASYNC_COMPLETION: 87 ret = opal_async_wait_response(token, &msg); 88 if (ret) { 89 pr_devel("Failed to wait for the async response\n"); 90 ret = -EIO; 91 goto out; 92 } 93 ret = opal_error_code(opal_get_async_rc(msg)); 94 if (!ret) 95 ret = count; 96 break; 97 case OPAL_SUCCESS: 98 ret = count; 99 break; 100 default: 101 ret = opal_error_code(ret); 102 } 103 104 out: 105 mutex_unlock(&sg_mutex); 106 out_token: 107 opal_async_release_token(token); 108 return ret; 109 } 110 111 static struct sg_ops_info { 112 int opal_no; 113 const char *attr_name; 114 ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, 115 const char *buf, size_t count); 116 } ops_info[] = { 117 { OPAL_SENSOR_GROUP_CLEAR, "clear", sg_store }, 118 }; 119 120 static void add_attr(int handle, struct sg_attr *attr, int index) 121 { 122 attr->handle = handle; 123 sysfs_attr_init(&attr->attr.attr); 124 attr->attr.attr.name = ops_info[index].attr_name; 125 attr->attr.attr.mode = 0220; 126 attr->attr.store = ops_info[index].store; 127 } 128 129 static int add_attr_group(const __be32 *ops, int len, struct sensor_group *sg, 130 u32 handle) 131 { 132 int i, j; 133 int count = 0; 134 135 for (i = 0; i < len; i++) 136 for (j = 0; j < ARRAY_SIZE(ops_info); j++) 137 if (be32_to_cpu(ops[i]) == ops_info[j].opal_no) { 138 add_attr(handle, &sg->sgattrs[count], j); 139 sg->sg.attrs[count] = 140 &sg->sgattrs[count].attr.attr; 141 count++; 142 } 143 144 return sysfs_create_group(sg_kobj, &sg->sg); 145 } 146 147 static int get_nr_attrs(const __be32 *ops, int len) 148 { 149 int i, j; 150 int nr_attrs = 0; 151 152 for (i = 0; i < len; i++) 153 for (j = 0; j < ARRAY_SIZE(ops_info); j++) 154 if (be32_to_cpu(ops[i]) == ops_info[j].opal_no) 155 nr_attrs++; 156 157 return nr_attrs; 158 } 159 160 void __init opal_sensor_groups_init(void) 161 { 162 struct device_node *sg, *node; 163 int i = 0; 164 165 sg = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group"); 166 if (!sg) { 167 pr_devel("Sensor groups node not found\n"); 168 return; 169 } 170 171 sgs = kcalloc(of_get_child_count(sg), sizeof(*sgs), GFP_KERNEL); 172 if (!sgs) 173 return; 174 175 sg_kobj = kobject_create_and_add("sensor_groups", opal_kobj); 176 if (!sg_kobj) { 177 pr_warn("Failed to create sensor group kobject\n"); 178 goto out_sgs; 179 } 180 181 for_each_child_of_node(sg, node) { 182 const __be32 *ops; 183 u32 sgid, len, nr_attrs, chipid; 184 185 ops = of_get_property(node, "ops", &len); 186 if (!ops) 187 continue; 188 189 nr_attrs = get_nr_attrs(ops, len); 190 if (!nr_attrs) 191 continue; 192 193 sgs[i].sgattrs = kcalloc(nr_attrs, sizeof(*sgs[i].sgattrs), 194 GFP_KERNEL); 195 if (!sgs[i].sgattrs) 196 goto out_sgs_sgattrs; 197 198 sgs[i].sg.attrs = kcalloc(nr_attrs + 1, 199 sizeof(*sgs[i].sg.attrs), 200 GFP_KERNEL); 201 202 if (!sgs[i].sg.attrs) { 203 kfree(sgs[i].sgattrs); 204 goto out_sgs_sgattrs; 205 } 206 207 if (of_property_read_u32(node, "sensor-group-id", &sgid)) { 208 pr_warn("sensor-group-id property not found\n"); 209 goto out_sgs_sgattrs; 210 } 211 212 if (!of_property_read_u32(node, "ibm,chip-id", &chipid)) 213 sprintf(sgs[i].name, "%pOFn%d", node, chipid); 214 else 215 sprintf(sgs[i].name, "%pOFn", node); 216 217 sgs[i].sg.name = sgs[i].name; 218 if (add_attr_group(ops, len, &sgs[i], sgid)) { 219 pr_warn("Failed to create sensor attribute group %s\n", 220 sgs[i].sg.name); 221 goto out_sgs_sgattrs; 222 } 223 i++; 224 } 225 226 return; 227 228 out_sgs_sgattrs: 229 while (--i >= 0) { 230 kfree(sgs[i].sgattrs); 231 kfree(sgs[i].sg.attrs); 232 } 233 kobject_put(sg_kobj); 234 out_sgs: 235 kfree(sgs); 236 } 237