1 /* 2 * IBM PowerNV platform sensors for temperature/fan/voltage/power 3 * Copyright (C) 2014 IBM 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. 17 */ 18 19 #define DRVNAME "ibmpowernv" 20 #define pr_fmt(fmt) DRVNAME ": " fmt 21 22 #include <linux/init.h> 23 #include <linux/module.h> 24 #include <linux/kernel.h> 25 #include <linux/hwmon.h> 26 #include <linux/hwmon-sysfs.h> 27 #include <linux/of.h> 28 #include <linux/slab.h> 29 30 #include <linux/platform_device.h> 31 #include <asm/opal.h> 32 #include <linux/err.h> 33 34 #define MAX_ATTR_LEN 32 35 36 /* Sensor suffix name from DT */ 37 #define DT_FAULT_ATTR_SUFFIX "faulted" 38 #define DT_DATA_ATTR_SUFFIX "data" 39 #define DT_THRESHOLD_ATTR_SUFFIX "thrs" 40 41 /* 42 * Enumerates all the types of sensors in the POWERNV platform and does index 43 * into 'struct sensor_group' 44 */ 45 enum sensors { 46 FAN, 47 AMBIENT_TEMP, 48 POWER_SUPPLY, 49 POWER_INPUT, 50 MAX_SENSOR_TYPE, 51 }; 52 53 static struct sensor_group { 54 const char *name; 55 const char *compatible; 56 struct attribute_group group; 57 u32 attr_count; 58 } sensor_groups[] = { 59 {"fan", "ibm,opal-sensor-cooling-fan"}, 60 {"temp", "ibm,opal-sensor-amb-temp"}, 61 {"in", "ibm,opal-sensor-power-supply"}, 62 {"power", "ibm,opal-sensor-power"} 63 }; 64 65 struct sensor_data { 66 u32 id; /* An opaque id of the firmware for each sensor */ 67 enum sensors type; 68 char name[MAX_ATTR_LEN]; 69 struct device_attribute dev_attr; 70 }; 71 72 struct platform_data { 73 const struct attribute_group *attr_groups[MAX_SENSOR_TYPE + 1]; 74 u32 sensors_count; /* Total count of sensors from each group */ 75 }; 76 77 /* Platform device representing all the ibmpowernv sensors */ 78 static struct platform_device *pdevice; 79 80 static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, 81 char *buf) 82 { 83 struct sensor_data *sdata = container_of(devattr, struct sensor_data, 84 dev_attr); 85 ssize_t ret; 86 u32 x; 87 88 ret = opal_get_sensor_data(sdata->id, &x); 89 if (ret) 90 return ret; 91 92 /* Convert temperature to milli-degrees */ 93 if (sdata->type == AMBIENT_TEMP) 94 x *= 1000; 95 /* Convert power to micro-watts */ 96 else if (sdata->type == POWER_INPUT) 97 x *= 1000000; 98 99 return sprintf(buf, "%u\n", x); 100 } 101 102 static int __init get_sensor_index_attr(const char *name, u32 *index, 103 char *attr) 104 { 105 char *hash_pos = strchr(name, '#'); 106 char buf[8] = { 0 }; 107 char *dash_pos; 108 u32 copy_len; 109 int err; 110 111 if (!hash_pos) 112 return -EINVAL; 113 114 dash_pos = strchr(hash_pos, '-'); 115 if (!dash_pos) 116 return -EINVAL; 117 118 copy_len = dash_pos - hash_pos - 1; 119 if (copy_len >= sizeof(buf)) 120 return -EINVAL; 121 122 strncpy(buf, hash_pos + 1, copy_len); 123 124 err = kstrtou32(buf, 10, index); 125 if (err) 126 return err; 127 128 strncpy(attr, dash_pos + 1, MAX_ATTR_LEN); 129 130 return 0; 131 } 132 133 /* 134 * This function translates the DT node name into the 'hwmon' attribute name. 135 * IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc. 136 * which need to be mapped as fan2_input, temp1_max respectively before 137 * populating them inside hwmon device class. 138 */ 139 static int __init create_hwmon_attr_name(struct device *dev, enum sensors type, 140 const char *node_name, 141 char *hwmon_attr_name) 142 { 143 char attr_suffix[MAX_ATTR_LEN]; 144 char *attr_name; 145 u32 index; 146 int err; 147 148 err = get_sensor_index_attr(node_name, &index, attr_suffix); 149 if (err) { 150 dev_err(dev, "Sensor device node name '%s' is invalid\n", 151 node_name); 152 return err; 153 } 154 155 if (!strcmp(attr_suffix, DT_FAULT_ATTR_SUFFIX)) { 156 attr_name = "fault"; 157 } else if (!strcmp(attr_suffix, DT_DATA_ATTR_SUFFIX)) { 158 attr_name = "input"; 159 } else if (!strcmp(attr_suffix, DT_THRESHOLD_ATTR_SUFFIX)) { 160 if (type == AMBIENT_TEMP) 161 attr_name = "max"; 162 else if (type == FAN) 163 attr_name = "min"; 164 else 165 return -ENOENT; 166 } else { 167 return -ENOENT; 168 } 169 170 snprintf(hwmon_attr_name, MAX_ATTR_LEN, "%s%d_%s", 171 sensor_groups[type].name, index, attr_name); 172 return 0; 173 } 174 175 static int __init populate_attr_groups(struct platform_device *pdev) 176 { 177 struct platform_data *pdata = platform_get_drvdata(pdev); 178 const struct attribute_group **pgroups = pdata->attr_groups; 179 struct device_node *opal, *np; 180 enum sensors type; 181 182 opal = of_find_node_by_path("/ibm,opal/sensors"); 183 if (!opal) { 184 dev_err(&pdev->dev, "Opal node 'sensors' not found\n"); 185 return -ENODEV; 186 } 187 188 for_each_child_of_node(opal, np) { 189 if (np->name == NULL) 190 continue; 191 192 for (type = 0; type < MAX_SENSOR_TYPE; type++) 193 if (of_device_is_compatible(np, 194 sensor_groups[type].compatible)) { 195 sensor_groups[type].attr_count++; 196 break; 197 } 198 } 199 200 of_node_put(opal); 201 202 for (type = 0; type < MAX_SENSOR_TYPE; type++) { 203 sensor_groups[type].group.attrs = devm_kzalloc(&pdev->dev, 204 sizeof(struct attribute *) * 205 (sensor_groups[type].attr_count + 1), 206 GFP_KERNEL); 207 if (!sensor_groups[type].group.attrs) 208 return -ENOMEM; 209 210 pgroups[type] = &sensor_groups[type].group; 211 pdata->sensors_count += sensor_groups[type].attr_count; 212 sensor_groups[type].attr_count = 0; 213 } 214 215 return 0; 216 } 217 218 /* 219 * Iterate through the device tree for each child of 'sensors' node, create 220 * a sysfs attribute file, the file is named by translating the DT node name 221 * to the name required by the higher 'hwmon' driver like fan1_input, temp1_max 222 * etc.. 223 */ 224 static int __init create_device_attrs(struct platform_device *pdev) 225 { 226 struct platform_data *pdata = platform_get_drvdata(pdev); 227 const struct attribute_group **pgroups = pdata->attr_groups; 228 struct device_node *opal, *np; 229 struct sensor_data *sdata; 230 u32 sensor_id; 231 enum sensors type; 232 u32 count = 0; 233 int err = 0; 234 235 opal = of_find_node_by_path("/ibm,opal/sensors"); 236 sdata = devm_kzalloc(&pdev->dev, pdata->sensors_count * sizeof(*sdata), 237 GFP_KERNEL); 238 if (!sdata) { 239 err = -ENOMEM; 240 goto exit_put_node; 241 } 242 243 for_each_child_of_node(opal, np) { 244 if (np->name == NULL) 245 continue; 246 247 for (type = 0; type < MAX_SENSOR_TYPE; type++) 248 if (of_device_is_compatible(np, 249 sensor_groups[type].compatible)) 250 break; 251 252 if (type == MAX_SENSOR_TYPE) 253 continue; 254 255 if (of_property_read_u32(np, "sensor-id", &sensor_id)) { 256 dev_info(&pdev->dev, 257 "'sensor-id' missing in the node '%s'\n", 258 np->name); 259 continue; 260 } 261 262 sdata[count].id = sensor_id; 263 sdata[count].type = type; 264 err = create_hwmon_attr_name(&pdev->dev, type, np->name, 265 sdata[count].name); 266 if (err) 267 goto exit_put_node; 268 269 sysfs_attr_init(&sdata[count].dev_attr.attr); 270 sdata[count].dev_attr.attr.name = sdata[count].name; 271 sdata[count].dev_attr.attr.mode = S_IRUGO; 272 sdata[count].dev_attr.show = show_sensor; 273 274 pgroups[type]->attrs[sensor_groups[type].attr_count++] = 275 &sdata[count++].dev_attr.attr; 276 } 277 278 exit_put_node: 279 of_node_put(opal); 280 return err; 281 } 282 283 static int __init ibmpowernv_probe(struct platform_device *pdev) 284 { 285 struct platform_data *pdata; 286 struct device *hwmon_dev; 287 int err; 288 289 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 290 if (!pdata) 291 return -ENOMEM; 292 293 platform_set_drvdata(pdev, pdata); 294 pdata->sensors_count = 0; 295 err = populate_attr_groups(pdev); 296 if (err) 297 return err; 298 299 /* Create sysfs attribute data for each sensor found in the DT */ 300 err = create_device_attrs(pdev); 301 if (err) 302 return err; 303 304 /* Finally, register with hwmon */ 305 hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, DRVNAME, 306 pdata, 307 pdata->attr_groups); 308 309 return PTR_ERR_OR_ZERO(hwmon_dev); 310 } 311 312 static struct platform_driver ibmpowernv_driver = { 313 .driver = { 314 .owner = THIS_MODULE, 315 .name = DRVNAME, 316 }, 317 }; 318 319 static int __init ibmpowernv_init(void) 320 { 321 int err; 322 323 pdevice = platform_device_alloc(DRVNAME, 0); 324 if (!pdevice) { 325 pr_err("Device allocation failed\n"); 326 err = -ENOMEM; 327 goto exit; 328 } 329 330 err = platform_device_add(pdevice); 331 if (err) { 332 pr_err("Device addition failed (%d)\n", err); 333 goto exit_device_put; 334 } 335 336 err = platform_driver_probe(&ibmpowernv_driver, ibmpowernv_probe); 337 if (err) { 338 pr_err("Platfrom driver probe failed\n"); 339 goto exit_device_del; 340 } 341 342 return 0; 343 344 exit_device_del: 345 platform_device_del(pdevice); 346 exit_device_put: 347 platform_device_put(pdevice); 348 exit: 349 return err; 350 } 351 352 static void __exit ibmpowernv_exit(void) 353 { 354 platform_driver_unregister(&ibmpowernv_driver); 355 platform_device_unregister(pdevice); 356 } 357 358 MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>"); 359 MODULE_DESCRIPTION("IBM POWERNV platform sensors"); 360 MODULE_LICENSE("GPL"); 361 362 module_init(ibmpowernv_init); 363 module_exit(ibmpowernv_exit); 364