brintos

brintos / linux-shallow public Read only

0
0
Text · 16.8 KiB · 70ca833 Raw
720 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * IBM PowerNV platform sensors for temperature/fan/voltage/power4 * Copyright (C) 2014 IBM5 */6 7#define DRVNAME		"ibmpowernv"8#define pr_fmt(fmt)	DRVNAME ": " fmt9 10#include <linux/init.h>11#include <linux/module.h>12#include <linux/kernel.h>13#include <linux/hwmon.h>14#include <linux/hwmon-sysfs.h>15#include <linux/of.h>16#include <linux/slab.h>17 18#include <linux/platform_device.h>19#include <asm/opal.h>20#include <linux/err.h>21#include <asm/cputhreads.h>22#include <asm/smp.h>23 24#define MAX_ATTR_LEN	3225#define MAX_LABEL_LEN	6426 27/* Sensor suffix name from DT */28#define DT_FAULT_ATTR_SUFFIX		"faulted"29#define DT_DATA_ATTR_SUFFIX		"data"30#define DT_THRESHOLD_ATTR_SUFFIX	"thrs"31 32/*33 * Enumerates all the types of sensors in the POWERNV platform and does index34 * into 'struct sensor_group'35 */36enum sensors {37	FAN,38	TEMP,39	POWER_SUPPLY,40	POWER_INPUT,41	CURRENT,42	ENERGY,43	MAX_SENSOR_TYPE,44};45 46#define INVALID_INDEX (-1U)47 48/*49 * 'compatible' string properties for sensor types as defined in old50 * PowerNV firmware (skiboot). These are ordered as 'enum sensors'.51 */52static const char * const legacy_compatibles[] = {53	"ibm,opal-sensor-cooling-fan",54	"ibm,opal-sensor-amb-temp",55	"ibm,opal-sensor-power-supply",56	"ibm,opal-sensor-power"57};58 59static struct sensor_group {60	const char *name; /* matches property 'sensor-type' */61	struct attribute_group group;62	u32 attr_count;63	u32 hwmon_index;64} sensor_groups[] = {65	{ "fan"   },66	{ "temp"  },67	{ "in"    },68	{ "power" },69	{ "curr"  },70	{ "energy" },71};72 73struct sensor_data {74	u32 id; /* An opaque id of the firmware for each sensor */75	u32 hwmon_index;76	u32 opal_index;77	enum sensors type;78	char label[MAX_LABEL_LEN];79	char name[MAX_ATTR_LEN];80	struct device_attribute dev_attr;81	struct sensor_group_data *sgrp_data;82};83 84struct sensor_group_data {85	struct mutex mutex;86	u32 gid;87	bool enable;88};89 90struct platform_data {91	const struct attribute_group *attr_groups[MAX_SENSOR_TYPE + 1];92	struct sensor_group_data *sgrp_data;93	u32 sensors_count; /* Total count of sensors from each group */94	u32 nr_sensor_groups; /* Total number of sensor groups */95};96 97static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,98			   char *buf)99{100	struct sensor_data *sdata = container_of(devattr, struct sensor_data,101						 dev_attr);102	ssize_t ret;103	u64 x;104 105	if (sdata->sgrp_data && !sdata->sgrp_data->enable)106		return -ENODATA;107 108	ret =  opal_get_sensor_data_u64(sdata->id, &x);109 110	if (ret)111		return ret;112 113	/* Convert temperature to milli-degrees */114	if (sdata->type == TEMP)115		x *= 1000;116	/* Convert power to micro-watts */117	else if (sdata->type == POWER_INPUT)118		x *= 1000000;119 120	return sprintf(buf, "%llu\n", x);121}122 123static ssize_t show_enable(struct device *dev,124			   struct device_attribute *devattr, char *buf)125{126	struct sensor_data *sdata = container_of(devattr, struct sensor_data,127						 dev_attr);128 129	return sprintf(buf, "%u\n", sdata->sgrp_data->enable);130}131 132static ssize_t store_enable(struct device *dev,133			    struct device_attribute *devattr,134			    const char *buf, size_t count)135{136	struct sensor_data *sdata = container_of(devattr, struct sensor_data,137						 dev_attr);138	struct sensor_group_data *sgrp_data = sdata->sgrp_data;139	int ret;140	bool data;141 142	ret = kstrtobool(buf, &data);143	if (ret)144		return ret;145 146	ret = mutex_lock_interruptible(&sgrp_data->mutex);147	if (ret)148		return ret;149 150	if (data != sgrp_data->enable) {151		ret =  sensor_group_enable(sgrp_data->gid, data);152		if (!ret)153			sgrp_data->enable = data;154	}155 156	if (!ret)157		ret = count;158 159	mutex_unlock(&sgrp_data->mutex);160	return ret;161}162 163static ssize_t show_label(struct device *dev, struct device_attribute *devattr,164			  char *buf)165{166	struct sensor_data *sdata = container_of(devattr, struct sensor_data,167						 dev_attr);168 169	return sprintf(buf, "%s\n", sdata->label);170}171 172static int get_logical_cpu(int hwcpu)173{174	int cpu;175 176	for_each_possible_cpu(cpu)177		if (get_hard_smp_processor_id(cpu) == hwcpu)178			return cpu;179 180	return -ENOENT;181}182 183static void make_sensor_label(struct device_node *np,184			      struct sensor_data *sdata, const char *label)185{186	u32 id;187	size_t n;188 189	n = scnprintf(sdata->label, sizeof(sdata->label), "%s", label);190 191	/*192	 * Core temp pretty print193	 */194	if (!of_property_read_u32(np, "ibm,pir", &id)) {195		int cpuid = get_logical_cpu(id);196 197		if (cpuid >= 0)198			/*199			 * The digital thermal sensors are associated200			 * with a core.201			 */202			n += scnprintf(sdata->label + n,203				      sizeof(sdata->label) - n, " %d",204				      cpuid);205		else206			n += scnprintf(sdata->label + n,207				      sizeof(sdata->label) - n, " phy%d", id);208	}209 210	/*211	 * Membuffer pretty print212	 */213	if (!of_property_read_u32(np, "ibm,chip-id", &id))214		n += scnprintf(sdata->label + n, sizeof(sdata->label) - n,215			      " %d", id & 0xffff);216}217 218static int get_sensor_index_attr(const char *name, u32 *index, char *attr)219{220	char *hash_pos = strchr(name, '#');221	char buf[8] = { 0 };222	char *dash_pos;223	u32 copy_len;224	int err;225 226	if (!hash_pos)227		return -EINVAL;228 229	dash_pos = strchr(hash_pos, '-');230	if (!dash_pos)231		return -EINVAL;232 233	copy_len = dash_pos - hash_pos - 1;234	if (copy_len >= sizeof(buf))235		return -EINVAL;236 237	memcpy(buf, hash_pos + 1, copy_len);238 239	err = kstrtou32(buf, 10, index);240	if (err)241		return err;242 243	strscpy(attr, dash_pos + 1, MAX_ATTR_LEN);244 245	return 0;246}247 248static const char *convert_opal_attr_name(enum sensors type,249					  const char *opal_attr)250{251	const char *attr_name = NULL;252 253	if (!strcmp(opal_attr, DT_FAULT_ATTR_SUFFIX)) {254		attr_name = "fault";255	} else if (!strcmp(opal_attr, DT_DATA_ATTR_SUFFIX)) {256		attr_name = "input";257	} else if (!strcmp(opal_attr, DT_THRESHOLD_ATTR_SUFFIX)) {258		if (type == TEMP)259			attr_name = "max";260		else if (type == FAN)261			attr_name = "min";262	}263 264	return attr_name;265}266 267/*268 * This function translates the DT node name into the 'hwmon' attribute name.269 * IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc.270 * which need to be mapped as fan2_input, temp1_max respectively before271 * populating them inside hwmon device class.272 */273static const char *parse_opal_node_name(const char *node_name,274					enum sensors type, u32 *index)275{276	char attr_suffix[MAX_ATTR_LEN];277	const char *attr_name;278	int err;279 280	err = get_sensor_index_attr(node_name, index, attr_suffix);281	if (err)282		return ERR_PTR(err);283 284	attr_name = convert_opal_attr_name(type, attr_suffix);285	if (!attr_name)286		return ERR_PTR(-ENOENT);287 288	return attr_name;289}290 291static int get_sensor_type(struct device_node *np)292{293	enum sensors type;294	const char *str;295 296	for (type = 0; type < ARRAY_SIZE(legacy_compatibles); type++) {297		if (of_device_is_compatible(np, legacy_compatibles[type]))298			return type;299	}300 301	/*302	 * Let's check if we have a newer device tree303	 */304	if (!of_device_is_compatible(np, "ibm,opal-sensor"))305		return MAX_SENSOR_TYPE;306 307	if (of_property_read_string(np, "sensor-type", &str))308		return MAX_SENSOR_TYPE;309 310	for (type = 0; type < MAX_SENSOR_TYPE; type++)311		if (!strcmp(str, sensor_groups[type].name))312			return type;313 314	return MAX_SENSOR_TYPE;315}316 317static u32 get_sensor_hwmon_index(struct sensor_data *sdata,318				  struct sensor_data *sdata_table, int count)319{320	int i;321 322	/*323	 * We don't use the OPAL index on newer device trees324	 */325	if (sdata->opal_index != INVALID_INDEX) {326		for (i = 0; i < count; i++)327			if (sdata_table[i].opal_index == sdata->opal_index &&328			    sdata_table[i].type == sdata->type)329				return sdata_table[i].hwmon_index;330	}331	return ++sensor_groups[sdata->type].hwmon_index;332}333 334static int init_sensor_group_data(struct platform_device *pdev,335				  struct platform_data *pdata)336{337	struct sensor_group_data *sgrp_data;338	struct device_node *groups, *sgrp;339	int count = 0, ret = 0;340	enum sensors type;341 342	groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group");343	if (!groups)344		return ret;345 346	for_each_child_of_node(groups, sgrp) {347		type = get_sensor_type(sgrp);348		if (type != MAX_SENSOR_TYPE)349			pdata->nr_sensor_groups++;350	}351 352	if (!pdata->nr_sensor_groups)353		goto out;354 355	sgrp_data = devm_kcalloc(&pdev->dev, pdata->nr_sensor_groups,356				 sizeof(*sgrp_data), GFP_KERNEL);357	if (!sgrp_data) {358		ret = -ENOMEM;359		goto out;360	}361 362	for_each_child_of_node(groups, sgrp) {363		u32 gid;364 365		type = get_sensor_type(sgrp);366		if (type == MAX_SENSOR_TYPE)367			continue;368 369		if (of_property_read_u32(sgrp, "sensor-group-id", &gid))370			continue;371 372		if (of_count_phandle_with_args(sgrp, "sensors", NULL) <= 0)373			continue;374 375		sensor_groups[type].attr_count++;376		sgrp_data[count].gid = gid;377		mutex_init(&sgrp_data[count].mutex);378		sgrp_data[count++].enable = false;379	}380 381	pdata->sgrp_data = sgrp_data;382out:383	of_node_put(groups);384	return ret;385}386 387static struct sensor_group_data *get_sensor_group(struct platform_data *pdata,388						  struct device_node *node,389						  enum sensors gtype)390{391	struct sensor_group_data *sgrp_data = pdata->sgrp_data;392	struct device_node *groups, *sgrp;393 394	groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group");395	if (!groups)396		return NULL;397 398	for_each_child_of_node(groups, sgrp) {399		struct of_phandle_iterator it;400		u32 gid;401		int rc, i;402		enum sensors type;403 404		type = get_sensor_type(sgrp);405		if (type != gtype)406			continue;407 408		if (of_property_read_u32(sgrp, "sensor-group-id", &gid))409			continue;410 411		of_for_each_phandle(&it, rc, sgrp, "sensors", NULL, 0)412			if (it.phandle == node->phandle) {413				of_node_put(it.node);414				break;415			}416 417		if (rc)418			continue;419 420		for (i = 0; i < pdata->nr_sensor_groups; i++)421			if (gid == sgrp_data[i].gid) {422				of_node_put(sgrp);423				of_node_put(groups);424				return &sgrp_data[i];425			}426	}427 428	of_node_put(groups);429	return NULL;430}431 432static int populate_attr_groups(struct platform_device *pdev)433{434	struct platform_data *pdata = platform_get_drvdata(pdev);435	const struct attribute_group **pgroups = pdata->attr_groups;436	struct device_node *opal, *np;437	enum sensors type;438	int ret;439 440	ret = init_sensor_group_data(pdev, pdata);441	if (ret)442		return ret;443 444	opal = of_find_node_by_path("/ibm,opal/sensors");445	for_each_child_of_node(opal, np) {446		const char *label;447 448		type = get_sensor_type(np);449		if (type == MAX_SENSOR_TYPE)450			continue;451 452		sensor_groups[type].attr_count++;453 454		/*455		 * add attributes for labels, min and max456		 */457		if (!of_property_read_string(np, "label", &label))458			sensor_groups[type].attr_count++;459		if (of_property_present(np, "sensor-data-min"))460			sensor_groups[type].attr_count++;461		if (of_property_present(np, "sensor-data-max"))462			sensor_groups[type].attr_count++;463	}464 465	of_node_put(opal);466 467	for (type = 0; type < MAX_SENSOR_TYPE; type++) {468		sensor_groups[type].group.attrs = devm_kcalloc(&pdev->dev,469					sensor_groups[type].attr_count + 1,470					sizeof(struct attribute *),471					GFP_KERNEL);472		if (!sensor_groups[type].group.attrs)473			return -ENOMEM;474 475		pgroups[type] = &sensor_groups[type].group;476		pdata->sensors_count += sensor_groups[type].attr_count;477		sensor_groups[type].attr_count = 0;478	}479 480	return 0;481}482 483static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name,484			      ssize_t (*show)(struct device *dev,485					      struct device_attribute *attr,486					      char *buf),487			    ssize_t (*store)(struct device *dev,488					     struct device_attribute *attr,489					     const char *buf, size_t count))490{491	snprintf(sdata->name, MAX_ATTR_LEN, "%s%d_%s",492		 sensor_groups[sdata->type].name, sdata->hwmon_index,493		 attr_name);494 495	sysfs_attr_init(&sdata->dev_attr.attr);496	sdata->dev_attr.attr.name = sdata->name;497	sdata->dev_attr.show = show;498	if (store) {499		sdata->dev_attr.store = store;500		sdata->dev_attr.attr.mode = 0664;501	} else {502		sdata->dev_attr.attr.mode = 0444;503	}504}505 506static void populate_sensor(struct sensor_data *sdata, int od, int hd, int sid,507			    const char *attr_name, enum sensors type,508			    const struct attribute_group *pgroup,509			    struct sensor_group_data *sgrp_data,510			    ssize_t (*show)(struct device *dev,511					    struct device_attribute *attr,512					    char *buf),513			    ssize_t (*store)(struct device *dev,514					     struct device_attribute *attr,515					     const char *buf, size_t count))516{517	sdata->id = sid;518	sdata->type = type;519	sdata->opal_index = od;520	sdata->hwmon_index = hd;521	create_hwmon_attr(sdata, attr_name, show, store);522	pgroup->attrs[sensor_groups[type].attr_count++] = &sdata->dev_attr.attr;523	sdata->sgrp_data = sgrp_data;524}525 526static char *get_max_attr(enum sensors type)527{528	switch (type) {529	case POWER_INPUT:530		return "input_highest";531	default:532		return "highest";533	}534}535 536static char *get_min_attr(enum sensors type)537{538	switch (type) {539	case POWER_INPUT:540		return "input_lowest";541	default:542		return "lowest";543	}544}545 546/*547 * Iterate through the device tree for each child of 'sensors' node, create548 * a sysfs attribute file, the file is named by translating the DT node name549 * to the name required by the higher 'hwmon' driver like fan1_input, temp1_max550 * etc..551 */552static int create_device_attrs(struct platform_device *pdev)553{554	struct platform_data *pdata = platform_get_drvdata(pdev);555	const struct attribute_group **pgroups = pdata->attr_groups;556	struct device_node *opal, *np;557	struct sensor_data *sdata;558	u32 count = 0;559	u32 group_attr_id[MAX_SENSOR_TYPE] = {0};560 561	sdata = devm_kcalloc(&pdev->dev,562			     pdata->sensors_count, sizeof(*sdata),563			     GFP_KERNEL);564	if (!sdata)565		return -ENOMEM;566 567	opal = of_find_node_by_path("/ibm,opal/sensors");568	for_each_child_of_node(opal, np) {569		struct sensor_group_data *sgrp_data;570		const char *attr_name;571		u32 opal_index, hw_id;572		u32 sensor_id;573		const char *label;574		enum sensors type;575 576		type = get_sensor_type(np);577		if (type == MAX_SENSOR_TYPE)578			continue;579 580		/*581		 * Newer device trees use a "sensor-data" property582		 * name for input.583		 */584		if (of_property_read_u32(np, "sensor-id", &sensor_id) &&585		    of_property_read_u32(np, "sensor-data", &sensor_id)) {586			dev_info(&pdev->dev,587				 "'sensor-id' missing in the node '%pOFn'\n",588				 np);589			continue;590		}591 592		sdata[count].id = sensor_id;593		sdata[count].type = type;594 595		/*596		 * If we can not parse the node name, it means we are597		 * running on a newer device tree. We can just forget598		 * about the OPAL index and use a defaut value for the599		 * hwmon attribute name600		 */601		attr_name = parse_opal_node_name(np->name, type, &opal_index);602		if (IS_ERR(attr_name)) {603			attr_name = "input";604			opal_index = INVALID_INDEX;605		}606 607		hw_id = get_sensor_hwmon_index(&sdata[count], sdata, count);608		sgrp_data = get_sensor_group(pdata, np, type);609		populate_sensor(&sdata[count], opal_index, hw_id, sensor_id,610				attr_name, type, pgroups[type], sgrp_data,611				show_sensor, NULL);612		count++;613 614		if (!of_property_read_string(np, "label", &label)) {615			/*616			 * For the label attribute, we can reuse the617			 * "properties" of the previous "input"618			 * attribute. They are related to the same619			 * sensor.620			 */621 622			make_sensor_label(np, &sdata[count], label);623			populate_sensor(&sdata[count], opal_index, hw_id,624					sensor_id, "label", type, pgroups[type],625					NULL, show_label, NULL);626			count++;627		}628 629		if (!of_property_read_u32(np, "sensor-data-max", &sensor_id)) {630			attr_name = get_max_attr(type);631			populate_sensor(&sdata[count], opal_index, hw_id,632					sensor_id, attr_name, type,633					pgroups[type], sgrp_data, show_sensor,634					NULL);635			count++;636		}637 638		if (!of_property_read_u32(np, "sensor-data-min", &sensor_id)) {639			attr_name = get_min_attr(type);640			populate_sensor(&sdata[count], opal_index, hw_id,641					sensor_id, attr_name, type,642					pgroups[type], sgrp_data, show_sensor,643					NULL);644			count++;645		}646 647		if (sgrp_data && !sgrp_data->enable) {648			sgrp_data->enable = true;649			hw_id = ++group_attr_id[type];650			populate_sensor(&sdata[count], opal_index, hw_id,651					sgrp_data->gid, "enable", type,652					pgroups[type], sgrp_data, show_enable,653					store_enable);654			count++;655		}656	}657 658	of_node_put(opal);659	return 0;660}661 662static int ibmpowernv_probe(struct platform_device *pdev)663{664	struct platform_data *pdata;665	struct device *hwmon_dev;666	int err;667 668	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);669	if (!pdata)670		return -ENOMEM;671 672	platform_set_drvdata(pdev, pdata);673	pdata->sensors_count = 0;674	pdata->nr_sensor_groups = 0;675	err = populate_attr_groups(pdev);676	if (err)677		return err;678 679	/* Create sysfs attribute data for each sensor found in the DT */680	err = create_device_attrs(pdev);681	if (err)682		return err;683 684	/* Finally, register with hwmon */685	hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, DRVNAME,686							   pdata,687							   pdata->attr_groups);688 689	return PTR_ERR_OR_ZERO(hwmon_dev);690}691 692static const struct platform_device_id opal_sensor_driver_ids[] = {693	{694		.name = "opal-sensor",695	},696	{ }697};698MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids);699 700static const struct of_device_id opal_sensor_match[] = {701	{ .compatible	= "ibm,opal-sensor" },702	{ },703};704MODULE_DEVICE_TABLE(of, opal_sensor_match);705 706static struct platform_driver ibmpowernv_driver = {707	.probe		= ibmpowernv_probe,708	.id_table	= opal_sensor_driver_ids,709	.driver		= {710		.name	= DRVNAME,711		.of_match_table	= opal_sensor_match,712	},713};714 715module_platform_driver(ibmpowernv_driver);716 717MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");718MODULE_DESCRIPTION("IBM POWERNV platform sensors");719MODULE_LICENSE("GPL");720