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 static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, 78 char *buf) 79 { 80 struct sensor_data *sdata = container_of(devattr, struct sensor_data, 81 dev_attr); 82 ssize_t ret; 83 u32 x; 84 85 ret = opal_get_sensor_data(sdata->id, &x); 86 if (ret) 87 return ret; 88 89 /* Convert temperature to milli-degrees */ 90 if (sdata->type == AMBIENT_TEMP) 91 x *= 1000; 92 /* Convert power to micro-watts */ 93 else if (sdata->type == POWER_INPUT) 94 x *= 1000000; 95 96 return sprintf(buf, "%u\n", x); 97 } 98 99 static int get_sensor_index_attr(const char *name, u32 *index, 100 char *attr) 101 { 102 char *hash_pos = strchr(name, '#'); 103 char buf[8] = { 0 }; 104 char *dash_pos; 105 u32 copy_len; 106 int err; 107 108 if (!hash_pos) 109 return -EINVAL; 110 111 dash_pos = strchr(hash_pos, '-'); 112 if (!dash_pos) 113 return -EINVAL; 114 115 copy_len = dash_pos - hash_pos - 1; 116 if (copy_len >= sizeof(buf)) 117 return -EINVAL; 118 119 strncpy(buf, hash_pos + 1, copy_len); 120 121 err = kstrtou32(buf, 10, index); 122 if (err) 123 return err; 124 125 strncpy(attr, dash_pos + 1, MAX_ATTR_LEN); 126 127 return 0; 128 } 129 130 /* 131 * This function translates the DT node name into the 'hwmon' attribute name. 132 * IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc. 133 * which need to be mapped as fan2_input, temp1_max respectively before 134 * populating them inside hwmon device class. 135 */ 136 static int create_hwmon_attr_name(struct device *dev, enum sensors type, 137 const char *node_name, 138 char *hwmon_attr_name) 139 { 140 char attr_suffix[MAX_ATTR_LEN]; 141 char *attr_name; 142 u32 index; 143 int err; 144 145 err = get_sensor_index_attr(node_name, &index, attr_suffix); 146 if (err) { 147 dev_err(dev, "Sensor device node name '%s' is invalid\n", 148 node_name); 149 return err; 150 } 151 152 if (!strcmp(attr_suffix, DT_FAULT_ATTR_SUFFIX)) { 153 attr_name = "fault"; 154 } else if (!strcmp(attr_suffix, DT_DATA_ATTR_SUFFIX)) { 155 attr_name = "input"; 156 } else if (!strcmp(attr_suffix, DT_THRESHOLD_ATTR_SUFFIX)) { 157 if (type == AMBIENT_TEMP) 158 attr_name = "max"; 159 else if (type == FAN) 160 attr_name = "min"; 161 else 162 return -ENOENT; 163 } else { 164 return -ENOENT; 165 } 166 167 snprintf(hwmon_attr_name, MAX_ATTR_LEN, "%s%d_%s", 168 sensor_groups[type].name, index, attr_name); 169 return 0; 170 } 171 172 static int populate_attr_groups(struct platform_device *pdev) 173 { 174 struct platform_data *pdata = platform_get_drvdata(pdev); 175 const struct attribute_group **pgroups = pdata->attr_groups; 176 struct device_node *opal, *np; 177 enum sensors type; 178 179 opal = of_find_node_by_path("/ibm,opal/sensors"); 180 for_each_child_of_node(opal, np) { 181 if (np->name == NULL) 182 continue; 183 184 for (type = 0; type < MAX_SENSOR_TYPE; type++) 185 if (of_device_is_compatible(np, 186 sensor_groups[type].compatible)) { 187 sensor_groups[type].attr_count++; 188 break; 189 } 190 } 191 192 of_node_put(opal); 193 194 for (type = 0; type < MAX_SENSOR_TYPE; type++) { 195 sensor_groups[type].group.attrs = devm_kzalloc(&pdev->dev, 196 sizeof(struct attribute *) * 197 (sensor_groups[type].attr_count + 1), 198 GFP_KERNEL); 199 if (!sensor_groups[type].group.attrs) 200 return -ENOMEM; 201 202 pgroups[type] = &sensor_groups[type].group; 203 pdata->sensors_count += sensor_groups[type].attr_count; 204 sensor_groups[type].attr_count = 0; 205 } 206 207 return 0; 208 } 209 210 /* 211 * Iterate through the device tree for each child of 'sensors' node, create 212 * a sysfs attribute file, the file is named by translating the DT node name 213 * to the name required by the higher 'hwmon' driver like fan1_input, temp1_max 214 * etc.. 215 */ 216 static int create_device_attrs(struct platform_device *pdev) 217 { 218 struct platform_data *pdata = platform_get_drvdata(pdev); 219 const struct attribute_group **pgroups = pdata->attr_groups; 220 struct device_node *opal, *np; 221 struct sensor_data *sdata; 222 u32 sensor_id; 223 enum sensors type; 224 u32 count = 0; 225 int err = 0; 226 227 opal = of_find_node_by_path("/ibm,opal/sensors"); 228 sdata = devm_kzalloc(&pdev->dev, pdata->sensors_count * sizeof(*sdata), 229 GFP_KERNEL); 230 if (!sdata) { 231 err = -ENOMEM; 232 goto exit_put_node; 233 } 234 235 for_each_child_of_node(opal, np) { 236 if (np->name == NULL) 237 continue; 238 239 for (type = 0; type < MAX_SENSOR_TYPE; type++) 240 if (of_device_is_compatible(np, 241 sensor_groups[type].compatible)) 242 break; 243 244 if (type == MAX_SENSOR_TYPE) 245 continue; 246 247 if (of_property_read_u32(np, "sensor-id", &sensor_id)) { 248 dev_info(&pdev->dev, 249 "'sensor-id' missing in the node '%s'\n", 250 np->name); 251 continue; 252 } 253 254 sdata[count].id = sensor_id; 255 sdata[count].type = type; 256 err = create_hwmon_attr_name(&pdev->dev, type, np->name, 257 sdata[count].name); 258 if (err) 259 goto exit_put_node; 260 261 sysfs_attr_init(&sdata[count].dev_attr.attr); 262 sdata[count].dev_attr.attr.name = sdata[count].name; 263 sdata[count].dev_attr.attr.mode = S_IRUGO; 264 sdata[count].dev_attr.show = show_sensor; 265 266 pgroups[type]->attrs[sensor_groups[type].attr_count++] = 267 &sdata[count++].dev_attr.attr; 268 } 269 270 exit_put_node: 271 of_node_put(opal); 272 return err; 273 } 274 275 static int ibmpowernv_probe(struct platform_device *pdev) 276 { 277 struct platform_data *pdata; 278 struct device *hwmon_dev; 279 int err; 280 281 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 282 if (!pdata) 283 return -ENOMEM; 284 285 platform_set_drvdata(pdev, pdata); 286 pdata->sensors_count = 0; 287 err = populate_attr_groups(pdev); 288 if (err) 289 return err; 290 291 /* Create sysfs attribute data for each sensor found in the DT */ 292 err = create_device_attrs(pdev); 293 if (err) 294 return err; 295 296 /* Finally, register with hwmon */ 297 hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, DRVNAME, 298 pdata, 299 pdata->attr_groups); 300 301 return PTR_ERR_OR_ZERO(hwmon_dev); 302 } 303 304 static const struct platform_device_id opal_sensor_driver_ids[] = { 305 { 306 .name = "opal-sensor", 307 }, 308 { } 309 }; 310 MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids); 311 312 static struct platform_driver ibmpowernv_driver = { 313 .probe = ibmpowernv_probe, 314 .id_table = opal_sensor_driver_ids, 315 .driver = { 316 .name = DRVNAME, 317 }, 318 }; 319 320 module_platform_driver(ibmpowernv_driver); 321 322 MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>"); 323 MODULE_DESCRIPTION("IBM POWERNV platform sensors"); 324 MODULE_LICENSE("GPL"); 325