Hi Guenter,
On 03/09/2017 05:40 PM, Guenter Roeck wrote:
On Thu, Mar 09, 2017 at 05:19:15PM +0530, Shilpasri G Bhat wrote:
Add support to read power and temperature sensors from OCC inband
sensors which are copied to main memory by OCC.
Is this supposed to be an alternative to the submission from
Eddie James ? If so, is there a reason to consider such an
alternative ?
This is not an alternative submission. Eddie James' hwmon OCC driver is to
export the sensors in BMC which is a service processor for POWER{8,9} servers.
This patch adds a hwmon driver in the POWER9 server.
Thanks and Regards,
Shilpa
Thanks,
Guenter
Signed-off-by: Shilpasri G Bhat <shilpa.bhat@xxxxxxxxxxxxxxxxxx>
CC: Rob Herring <robh+dt@xxxxxxxxxx>
CC: Mark Rutland <mark.rutland@xxxxxxx>
CC: Jean Delvare <jdelvare@xxxxxxxx>
CC: Guenter Roeck <linux@xxxxxxxxxxxx>
CC: Jonathan Corbet <corbet@xxxxxxx>
CC: devicetree@xxxxxxxxxxxxxxx
CC: linux-hwmon@xxxxxxxxxxxxxxxxxxx
CC: linux-doc@xxxxxxxxxxxxxxx
---
.../devicetree/bindings/hwmon/ibmpowernv-occ.txt | 4 +
Documentation/hwmon/ibmpowernv-occ | 24 ++
drivers/hwmon/Kconfig | 11 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/ibmpowernv-occ.c | 302 +++++++++++++++++++++
5 files changed, 342 insertions(+)
create mode 100644 Documentation/devicetree/bindings/hwmon/ibmpowernv-occ.txt
create mode 100644 Documentation/hwmon/ibmpowernv-occ
create mode 100644 drivers/hwmon/ibmpowernv-occ.c
diff --git a/Documentation/devicetree/bindings/hwmon/ibmpowernv-occ.txt b/Documentation/devicetree/bindings/hwmon/ibmpowernv-occ.txt
new file mode 100644
index 0000000..d03f744
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/ibmpowernv-occ.txt
@@ -0,0 +1,4 @@
+IBM POWERNV OCC inband platform sensors
+
+Required device-tree property:
+- compatible: "ibm,p9-occ-inband-sensor"
diff --git a/Documentation/hwmon/ibmpowernv-occ b/Documentation/hwmon/ibmpowernv-occ
new file mode 100644
index 0000000..151028b
--- /dev/null
+++ b/Documentation/hwmon/ibmpowernv-occ
@@ -0,0 +1,24 @@
+Kernel driver ibmpowernv-occ
+=============================
+
+Supported systems:
+ * P9 server based on POWERNV platform
+
+Description
+------------
+
+This driver exports the power and temperature sensors from OCC inband
+sensors on P9 POWERNV platforms.
+
+Sysfs attributes
+----------------
+
+powerX_input Latest power reading
+powerX_input_highest Minimum power
+powerX_input_lowest Maximum power
+powerX_label Sensor name
+
+tempX_input Latest temperature reading
+tempX_max Minimum temperature
+tempX_min Maximum temperature
+tempX_label Sensor name
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 0649d53f3..3b1dbb9 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -598,6 +598,17 @@ config SENSORS_IBMPOWERNV
This driver can also be built as a module. If so, the module
will be called ibmpowernv.
+config SENSORS_IBMPOWERNV_OCC
+ tristate "IBM POWERNV OCC Inband platform sensors"
+ depends on PPC_POWERNV
+ default y
+ help
+ If you say yes here you get support for the temperature/power
+ OCC inband sensors on your PowerNV platform.
+
+ This driver can also be built as a module. If so, the module
+ will be called ibmpowernv-occ.
+
config SENSORS_IIO_HWMON
tristate "Hwmon driver that uses channels specified via iio maps"
depends on IIO
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 5509edf..0da2207 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o
+obj-$(CONFIG_SENSORS_IBMPOWERNV_OCC)+= ibmpowernv-occ.o
obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o
obj-$(CONFIG_SENSORS_INA209) += ina209.o
obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o
diff --git a/drivers/hwmon/ibmpowernv-occ.c b/drivers/hwmon/ibmpowernv-occ.c
new file mode 100644
index 0000000..97b1bbe
--- /dev/null
+++ b/drivers/hwmon/ibmpowernv-occ.c
@@ -0,0 +1,302 @@
+/*
+ * IBM PowerNV platform OCC inband sensors for temperature/power
+ * Copyright (C) 2017 IBM
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ */
+
+#define DRVNAME "ibmpowernv_occ"
+#define pr_fmt(fmt) DRVNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+#include <asm/opal.h>
+
+#define MAX_HWMON_ATTR_LEN 32
+#define MAX_HWMON_LABEL_LEN (MAX_OCC_SENSOR_NAME_LEN * 2)
+#define HWMON_ATTRS_PER_SENSOR 16
+#define TO_MILLI_UNITS(x) ((x) * 1000)
+#define TO_MICRO_UNITS(x) ((x) * 1000000)
+
+enum sensors {
+ TEMP,
+ POWER,
+ MAX_SENSOR_TYPE,
+};
+
+static struct sensor_type {
+ const char *name;
+ int hwmon_id;
+} sensor_types[] = {
+ { "temp"},
+ { "power"},
+};
+
+static struct sensor_data {
+ u32 occ_id;
+ u64 offset; /* Offset to ping/pong reading buffer */
+ enum sensors type;
+ char label[MAX_HWMON_LABEL_LEN];
+ char name[MAX_HWMON_ATTR_LEN];
+ struct device_attribute attr;
+} *sdata;
+
+static struct attribute_group sensor_attrs_group;
+__ATTRIBUTE_GROUPS(sensor_attrs);
+
+#define show(file_name) \
+static ssize_t ibmpowernv_occ_show_##file_name \
+(struct device *dev, struct device_attribute *dattr, char *buf) \
+{ \
+ struct sensor_data *sdata = container_of(dattr, \
+ struct sensor_data, \
+ attr); \
+ u64 val; \
+ int ret; \
+ ret = opal_occ_sensor_get_##file_name(sdata->occ_id, \
+ sdata->offset, \
+ &val); \
+ if (ret) \
+ return ret; \
+ if (sdata->type == TEMP) \
+ val = TO_MILLI_UNITS(val); \
+ else if (sdata->type == POWER) \
+ val = TO_MICRO_UNITS(val); \
+ return sprintf(buf, "%llu\n", val); \
+}
+
+show(sample);
+show(max);
+show(min);
+show(js_min);
+show(js_max);
+show(csm_min);
+show(csm_max);
+show(prof_min);
+show(prof_max);
+
+static struct sensor_view_groups {
+ const char *name;
+ ssize_t (*show_sample)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+ ssize_t (*show_min)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+ ssize_t (*show_max)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+} sensor_views[] = {
+ {
+ .name = "",
+ .show_sample = ibmpowernv_occ_show_sample,
+ .show_min = ibmpowernv_occ_show_min,
+ .show_max = ibmpowernv_occ_show_max
+ },
+ {
+ .name = "_JS",
+ .show_sample = ibmpowernv_occ_show_sample,
+ .show_min = ibmpowernv_occ_show_js_min,
+ .show_max = ibmpowernv_occ_show_js_max
+ },
+ { .name = "_CSM",
+ .show_sample = ibmpowernv_occ_show_sample,
+ .show_min = ibmpowernv_occ_show_csm_min,
+ .show_max = ibmpowernv_occ_show_csm_max
+ },
+ { .name = "_Prof",
+ .show_sample = ibmpowernv_occ_show_sample,
+ .show_min = ibmpowernv_occ_show_prof_min,
+ .show_max = ibmpowernv_occ_show_prof_max
+ },
+};
+
+static ssize_t ibmpowernv_occ_show_label(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct sensor_data *sdata = container_of(dattr, struct sensor_data,
+ attr);
+
+ return sprintf(buf, "%s\n", sdata->label);
+}
+
+static int ibmpowernv_occ_get_sensor_type(enum occ_sensor_type type)
+{
+ switch (type) {
+ case OCC_SENSOR_TYPE_POWER:
+ return POWER;
+ case OCC_SENSOR_TYPE_TEMPERATURE:
+ return TEMP;
+ default:
+ return MAX_SENSOR_TYPE;
+ }
+
+ return MAX_SENSOR_TYPE;
+}
+
+static void ibmpowernv_occ_add_sdata(struct occ_hwmon_sensor sensor,
+ struct sensor_data *sdata, char *name,
+ int hwmon_id, enum sensors type,
+ ssize_t (*show)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf))
+{
+ sdata->type = type;
+ sdata->occ_id = sensor.occ_id;
+ sdata->offset = sensor.offset;
+ snprintf(sdata->name, MAX_HWMON_ATTR_LEN, "%s%d_%s",
+ sensor_types[type].name, hwmon_id, name);
+ sysfs_attr_init(&sdata->attr.attr);
+ sdata->attr.attr.name = sdata->name;
+ sdata->attr.attr.mode = 0444;
+ sdata->attr.show = show;
+}
+
+static void ibmpowernv_occ_add_sensor_attrs(struct occ_hwmon_sensor sensor,
+ int index)
+{
+ struct attribute **attrs = sensor_attrs_group.attrs;
+ char attr_str[MAX_HWMON_ATTR_LEN];
+ enum sensors type = ibmpowernv_occ_get_sensor_type(sensor.type);
+ int i;
+
+ index *= HWMON_ATTRS_PER_SENSOR;
+ for (i = 0; i < ARRAY_SIZE(sensor_views); i++) {
+ int hid = ++sensor_types[type].hwmon_id;
+
+ /* input */
+ ibmpowernv_occ_add_sdata(sensor, &sdata[index], "input", hid,
+ type, sensor_views[i].show_sample);
+ attrs[index] = &sdata[index].attr.attr;
+ index++;
+
+ /* min */
+ if (type == POWER)
+ snprintf(attr_str, MAX_HWMON_ATTR_LEN, "%s",
+ "input_lowest");
+ else
+ snprintf(attr_str, MAX_HWMON_ATTR_LEN, "%s", "min");
+
+ ibmpowernv_occ_add_sdata(sensor, &sdata[index], attr_str, hid,
+ type, sensor_views[i].show_min);
+ attrs[index] = &sdata[index].attr.attr;
+ index++;
+
+ /* max */
+ if (type == POWER)
+ snprintf(attr_str, MAX_HWMON_ATTR_LEN, "%s",
+ "input_highest");
+ else
+ snprintf(attr_str, MAX_HWMON_ATTR_LEN, "%s", "max");
+
+ ibmpowernv_occ_add_sdata(sensor, &sdata[index], attr_str, hid,
+ type, sensor_views[i].show_max);
+ attrs[index] = &sdata[index].attr.attr;
+ index++;
+
+ /* label */
+ snprintf(sdata[index].label, MAX_HWMON_LABEL_LEN, "%s%s",
+ sensor.name, sensor_views[i].name);
+ ibmpowernv_occ_add_sdata(sensor, &sdata[index], "label", hid,
+ type, ibmpowernv_occ_show_label);
+ attrs[index] = &sdata[index].attr.attr;
+ index++;
+ }
+}
+
+static int ibmpowernv_occ_add_device_attrs(struct platform_device *pdev)
+{
+ struct attribute **attrs;
+ struct occ_hwmon_sensor *slist = NULL;
+ int nr_sensors = 0, i;
+ int ret = -ENOMEM;
+
+ slist = opal_occ_sensor_get_hwmon_list(&nr_sensors);
+ if (!nr_sensors)
+ return -ENODEV;
+
+ if (!slist)
+ return ret;
+
+ sdata = devm_kzalloc(&pdev->dev, nr_sensors * sizeof(*sdata) *
+ HWMON_ATTRS_PER_SENSOR, GFP_KERNEL);
+ if (!sdata)
+ goto out;
+
+ attrs = devm_kzalloc(&pdev->dev, nr_sensors * sizeof(*attrs) *
+ HWMON_ATTRS_PER_SENSOR, GFP_KERNEL);
+ if (!attrs)
+ goto out;
+
+ sensor_attrs_group.attrs = attrs;
+ for (i = 0; i < nr_sensors; i++)
+ ibmpowernv_occ_add_sensor_attrs(slist[i], i);
+
+ ret = 0;
+out:
+ kfree(slist);
+ return ret;
+}
+
+static int ibmpowernv_occ_probe(struct platform_device *pdev)
+{
+ struct device *hwmon_dev;
+ int err;
+
+ err = ibmpowernv_occ_add_device_attrs(pdev);
+ if (err)
+ goto out;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, DRVNAME,
+ NULL,
+ sensor_attrs_groups);
+
+ err = PTR_ERR_OR_ZERO(hwmon_dev);
+out:
+ if (err)
+ pr_warn("Failed to initialize Hwmon OCC inband sensors\n");
+
+ return err;
+}
+
+static const struct platform_device_id occ_sensor_ids[] = {
+ { .name = "occ-inband-sensor" },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, occ_sensor_ids);
+
+static const struct of_device_id occ_sensor_of_ids[] = {
+ { .compatible = "ibm,p9-occ-inband-sensor" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, occ_sensor_of_ids);
+
+static struct platform_driver ibmpowernv_occ_driver = {
+ .probe = ibmpowernv_occ_probe,
+ .id_table = occ_sensor_ids,
+ .driver = {
+ .name = DRVNAME,
+ .of_match_table = occ_sensor_of_ids,
+ },
+};
+
+module_platform_driver(ibmpowernv_occ_driver);
+
+MODULE_AUTHOR("Shilpasri G Bhat <shilpa.bhat@xxxxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("IBM POWERNV platform OCC inband sensors");
+MODULE_LICENSE("GPL");
--
1.8.3.1