summaryrefslogtreecommitdiff
path: root/drivers/hwmon/ibmpowernv.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2015-04-20 11:32:26 +1000
committerDave Airlie <airlied@redhat.com>2015-04-20 13:05:20 +1000
commit2c33ce009ca2389dbf0535d0672214d09738e35e (patch)
tree6186a6458c3c160385d794a23eaf07c786a9e61b /drivers/hwmon/ibmpowernv.c
parentcec32a47010647e8b0603726ebb75b990a4057a4 (diff)
parent09d51602cf84a1264946711dd4ea0dddbac599a1 (diff)
Merge Linus master into drm-next
The merge is clean, but the arm build fails afterwards, due to API changes in the regulator tree. I've included the patch into the merge to fix the build. Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/hwmon/ibmpowernv.c')
-rw-r--r--drivers/hwmon/ibmpowernv.c267
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: