diff options
Diffstat (limited to 'drivers/hwmon/ibmpowernv.c')
| -rw-r--r-- | drivers/hwmon/ibmpowernv.c | 267 | 
1 files changed, 216 insertions, 51 deletions
| diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index febe8175d36c..4255514b2c72 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -30,8 +30,11 @@  #include <linux/platform_device.h>  #include <asm/opal.h>  #include <linux/err.h> +#include <asm/cputhreads.h> +#include <asm/smp.h>  #define MAX_ATTR_LEN	32 +#define MAX_LABEL_LEN	64  /* Sensor suffix name from DT */  #define DT_FAULT_ATTR_SUFFIX		"faulted" @@ -44,17 +47,20 @@   */  enum sensors {  	FAN, -	AMBIENT_TEMP, +	TEMP,  	POWER_SUPPLY,  	POWER_INPUT,  	MAX_SENSOR_TYPE,  }; +#define INVALID_INDEX (-1U) +  static struct sensor_group {  	const char *name;  	const char *compatible;  	struct attribute_group group;  	u32 attr_count; +	u32 hwmon_index;  } sensor_groups[] = {  	{"fan", "ibm,opal-sensor-cooling-fan"},  	{"temp", "ibm,opal-sensor-amb-temp"}, @@ -64,7 +70,10 @@ static struct sensor_group {  struct sensor_data {  	u32 id; /* An opaque id of the firmware for each sensor */ +	u32 hwmon_index; +	u32 opal_index;  	enum sensors type; +	char label[MAX_LABEL_LEN];  	char name[MAX_ATTR_LEN];  	struct device_attribute dev_attr;  }; @@ -87,7 +96,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,  		return ret;  	/* Convert temperature to milli-degrees */ -	if (sdata->type == AMBIENT_TEMP) +	if (sdata->type == TEMP)  		x *= 1000;  	/* Convert power to micro-watts */  	else if (sdata->type == POWER_INPUT) @@ -96,8 +105,65 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,  	return sprintf(buf, "%u\n", x);  } -static int get_sensor_index_attr(const char *name, u32 *index, -					char *attr) +static ssize_t show_label(struct device *dev, struct device_attribute *devattr, +			  char *buf) +{ +	struct sensor_data *sdata = container_of(devattr, struct sensor_data, +						 dev_attr); + +	return sprintf(buf, "%s\n", sdata->label); +} + +static int __init get_logical_cpu(int hwcpu) +{ +	int cpu; + +	for_each_possible_cpu(cpu) +		if (get_hard_smp_processor_id(cpu) == hwcpu) +			return cpu; + +	return -ENOENT; +} + +static void __init make_sensor_label(struct device_node *np, +				     struct sensor_data *sdata, +				     const char *label) +{ +	u32 id; +	size_t n; + +	n = snprintf(sdata->label, sizeof(sdata->label), "%s", label); + +	/* +	 * Core temp pretty print +	 */ +	if (!of_property_read_u32(np, "ibm,pir", &id)) { +		int cpuid = get_logical_cpu(id); + +		if (cpuid >= 0) +			/* +			 * The digital thermal sensors are associated +			 * with a core. Let's print out the range of +			 * cpu ids corresponding to the hardware +			 * threads of the core. +			 */ +			n += snprintf(sdata->label + n, +				      sizeof(sdata->label) - n, " %d-%d", +				      cpuid, cpuid + threads_per_core - 1); +		else +			n += snprintf(sdata->label + n, +				      sizeof(sdata->label) - n, " phy%d", id); +	} + +	/* +	 * Membuffer pretty print +	 */ +	if (!of_property_read_u32(np, "ibm,chip-id", &id)) +		n += snprintf(sdata->label + n, sizeof(sdata->label) - n, +			      " %d", id & 0xffff); +} + +static int get_sensor_index_attr(const char *name, u32 *index, char *attr)  {  	char *hash_pos = strchr(name, '#');  	char buf[8] = { 0 }; @@ -127,46 +193,90 @@ static int get_sensor_index_attr(const char *name, u32 *index,  	return 0;  } +static const char *convert_opal_attr_name(enum sensors type, +					  const char *opal_attr) +{ +	const char *attr_name = NULL; + +	if (!strcmp(opal_attr, DT_FAULT_ATTR_SUFFIX)) { +		attr_name = "fault"; +	} else if (!strcmp(opal_attr, DT_DATA_ATTR_SUFFIX)) { +		attr_name = "input"; +	} else if (!strcmp(opal_attr, DT_THRESHOLD_ATTR_SUFFIX)) { +		if (type == TEMP) +			attr_name = "max"; +		else if (type == FAN) +			attr_name = "min"; +	} + +	return attr_name; +} +  /*   * This function translates the DT node name into the 'hwmon' attribute name.   * IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc.   * which need to be mapped as fan2_input, temp1_max respectively before   * populating them inside hwmon device class.   */ -static int create_hwmon_attr_name(struct device *dev, enum sensors type, -					 const char *node_name, -					 char *hwmon_attr_name) +static const char *parse_opal_node_name(const char *node_name, +					enum sensors type, u32 *index)  {  	char attr_suffix[MAX_ATTR_LEN]; -	char *attr_name; -	u32 index; +	const char *attr_name;  	int err; -	err = get_sensor_index_attr(node_name, &index, attr_suffix); -	if (err) { -		dev_err(dev, "Sensor device node name '%s' is invalid\n", -			node_name); -		return err; -	} +	err = get_sensor_index_attr(node_name, index, attr_suffix); +	if (err) +		return ERR_PTR(err); -	if (!strcmp(attr_suffix, DT_FAULT_ATTR_SUFFIX)) { -		attr_name = "fault"; -	} else if (!strcmp(attr_suffix, DT_DATA_ATTR_SUFFIX)) { -		attr_name = "input"; -	} else if (!strcmp(attr_suffix, DT_THRESHOLD_ATTR_SUFFIX)) { -		if (type == AMBIENT_TEMP) -			attr_name = "max"; -		else if (type == FAN) -			attr_name = "min"; -		else -			return -ENOENT; -	} else { -		return -ENOENT; +	attr_name = convert_opal_attr_name(type, attr_suffix); +	if (!attr_name) +		return ERR_PTR(-ENOENT); + +	return attr_name; +} + +static int get_sensor_type(struct device_node *np) +{ +	enum sensors type; +	const char *str; + +	for (type = 0; type < MAX_SENSOR_TYPE; type++) { +		if (of_device_is_compatible(np, sensor_groups[type].compatible)) +			return type;  	} -	snprintf(hwmon_attr_name, MAX_ATTR_LEN, "%s%d_%s", -		 sensor_groups[type].name, index, attr_name); -	return 0; +	/* +	 * Let's check if we have a newer device tree +	 */ +	if (!of_device_is_compatible(np, "ibm,opal-sensor")) +		return MAX_SENSOR_TYPE; + +	if (of_property_read_string(np, "sensor-type", &str)) +		return MAX_SENSOR_TYPE; + +	for (type = 0; type < MAX_SENSOR_TYPE; type++) +		if (!strcmp(str, sensor_groups[type].name)) +			return type; + +	return MAX_SENSOR_TYPE; +} + +static u32 get_sensor_hwmon_index(struct sensor_data *sdata, +				  struct sensor_data *sdata_table, int count) +{ +	int i; + +	/* +	 * We don't use the OPAL index on newer device trees +	 */ +	if (sdata->opal_index != INVALID_INDEX) { +		for (i = 0; i < count; i++) +			if (sdata_table[i].opal_index == sdata->opal_index && +			    sdata_table[i].type == sdata->type) +				return sdata_table[i].hwmon_index; +	} +	return ++sensor_groups[sdata->type].hwmon_index;  }  static int populate_attr_groups(struct platform_device *pdev) @@ -178,15 +288,22 @@ static int populate_attr_groups(struct platform_device *pdev)  	opal = of_find_node_by_path("/ibm,opal/sensors");  	for_each_child_of_node(opal, np) { +		const char *label; +  		if (np->name == NULL)  			continue; -		for (type = 0; type < MAX_SENSOR_TYPE; type++) -			if (of_device_is_compatible(np, -					sensor_groups[type].compatible)) { -				sensor_groups[type].attr_count++; -				break; -			} +		type = get_sensor_type(np); +		if (type == MAX_SENSOR_TYPE) +			continue; + +		sensor_groups[type].attr_count++; + +		/* +		 * add a new attribute for labels +		 */ +		if (!of_property_read_string(np, "label", &label)) +			sensor_groups[type].attr_count++;  	}  	of_node_put(opal); @@ -207,6 +324,21 @@ static int populate_attr_groups(struct platform_device *pdev)  	return 0;  } +static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name, +			      ssize_t (*show)(struct device *dev, +					      struct device_attribute *attr, +					      char *buf)) +{ +	snprintf(sdata->name, MAX_ATTR_LEN, "%s%d_%s", +		 sensor_groups[sdata->type].name, sdata->hwmon_index, +		 attr_name); + +	sysfs_attr_init(&sdata->dev_attr.attr); +	sdata->dev_attr.attr.name = sdata->name; +	sdata->dev_attr.attr.mode = S_IRUGO; +	sdata->dev_attr.show = show; +} +  /*   * Iterate through the device tree for each child of 'sensors' node, create   * a sysfs attribute file, the file is named by translating the DT node name @@ -233,18 +365,23 @@ static int create_device_attrs(struct platform_device *pdev)  	}  	for_each_child_of_node(opal, np) { +		const char *attr_name; +		u32 opal_index; +		const char *label; +  		if (np->name == NULL)  			continue; -		for (type = 0; type < MAX_SENSOR_TYPE; type++) -			if (of_device_is_compatible(np, -					sensor_groups[type].compatible)) -				break; - +		type = get_sensor_type(np);  		if (type == MAX_SENSOR_TYPE)  			continue; -		if (of_property_read_u32(np, "sensor-id", &sensor_id)) { +		/* +		 * Newer device trees use a "sensor-data" property +		 * name for input. +		 */ +		if (of_property_read_u32(np, "sensor-id", &sensor_id) && +		    of_property_read_u32(np, "sensor-data", &sensor_id)) {  			dev_info(&pdev->dev,  				 "'sensor-id' missing in the node '%s'\n",  				 np->name); @@ -253,18 +390,46 @@ static int create_device_attrs(struct platform_device *pdev)  		sdata[count].id = sensor_id;  		sdata[count].type = type; -		err = create_hwmon_attr_name(&pdev->dev, type, np->name, -					     sdata[count].name); -		if (err) -			goto exit_put_node; -		sysfs_attr_init(&sdata[count].dev_attr.attr); -		sdata[count].dev_attr.attr.name = sdata[count].name; -		sdata[count].dev_attr.attr.mode = S_IRUGO; -		sdata[count].dev_attr.show = show_sensor; +		/* +		 * If we can not parse the node name, it means we are +		 * running on a newer device tree. We can just forget +		 * about the OPAL index and use a defaut value for the +		 * hwmon attribute name +		 */ +		attr_name = parse_opal_node_name(np->name, type, &opal_index); +		if (IS_ERR(attr_name)) { +			attr_name = "input"; +			opal_index = INVALID_INDEX; +		} + +		sdata[count].opal_index = opal_index; +		sdata[count].hwmon_index = +			get_sensor_hwmon_index(&sdata[count], sdata, count); + +		create_hwmon_attr(&sdata[count], attr_name, show_sensor);  		pgroups[type]->attrs[sensor_groups[type].attr_count++] =  				&sdata[count++].dev_attr.attr; + +		if (!of_property_read_string(np, "label", &label)) { +			/* +			 * For the label attribute, we can reuse the +			 * "properties" of the previous "input" +			 * attribute. They are related to the same +			 * sensor. +			 */ +			sdata[count].type = type; +			sdata[count].opal_index = sdata[count - 1].opal_index; +			sdata[count].hwmon_index = sdata[count - 1].hwmon_index; + +			make_sensor_label(np, &sdata[count], label); + +			create_hwmon_attr(&sdata[count], "label", show_label); + +			pgroups[type]->attrs[sensor_groups[type].attr_count++] = +				&sdata[count++].dev_attr.attr; +		}  	}  exit_put_node: |