diff mbox

[RFC,linux,v4,5/6] hwmon: Add OCC sensor groups

Message ID 1476394981-17226-1-git-send-email-eajames.ibm@gmail.com
State Changes Requested, archived
Delegated to: Joel Stanley
Headers show

Commit Message

eajames.ibm@gmail.com Oct. 13, 2016, 9:43 p.m. UTC
From: "Edward A. James" <eajames@us.ibm.com>

Add sensor groups for all frequency, temperature, and power sensors on the
OCC. Sysfs attributes for the sensor label and input.

Signed-off-by: Edward A. James <eajames@us.ibm.com>
---
 drivers/hwmon/occ/power8_occ.c | 203 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 202 insertions(+), 1 deletion(-)
diff mbox

Patch

diff --git a/drivers/hwmon/occ/power8_occ.c b/drivers/hwmon/occ/power8_occ.c
index f0f14ec..d264bf5 100644
--- a/drivers/hwmon/occ/power8_occ.c
+++ b/drivers/hwmon/occ/power8_occ.c
@@ -53,6 +53,8 @@ 
 #define SENSOR_BLOCK_NUM_OFFSET	43
 #define SENSOR_BLOCK_OFFSET	45
 
+#define MAX_SENSOR_ATTR_LEN	32
+
 enum sensor_type {
 	FREQ = 0,
 	TEMP,
@@ -124,6 +126,20 @@  struct occ_response {
 	int sensor_block_id[MAX_OCC_SENSOR_TYPE];
 };
 
+struct sensor_attr_data {
+	enum sensor_type type;
+	u32 hwmon_index;
+	u32 attr_id;
+	char name[MAX_SENSOR_ATTR_LEN];
+	struct device_attribute dev_attr;
+};
+
+struct sensor_group {
+	char *name;
+	struct sensor_attr_data *sattr;
+	struct attribute_group group;
+};
+
 struct power8_driver {
 	struct occ_driver *driver;
 	struct device *dev;
@@ -133,6 +149,7 @@  struct power8_driver {
 	struct mutex update_lock;
 	bool valid;
 	struct occ_response occ_response;
+	struct sensor_group sensor_groups[MAX_OCC_SENSOR_TYPE];
 };
 
 static void deinit_occ_resp_buf(struct occ_response *resp)
@@ -560,6 +577,88 @@  static int occ_update_device(struct power8_driver *driver)
 	return rc;
 }
 
+static void *occ_get_sensor(struct power8_driver *driver,
+			    enum sensor_type t)
+{
+	int rc;
+
+	rc = occ_update_device(driver);
+	if (rc != 0) {
+		dev_dbg(driver->dev, "ERROR: cannot get occ sensor data: %d\n",
+			rc);
+		return NULL;
+	}
+
+	return occ_get_sensor_by_type(&driver->occ_response, t);
+}
+
+static int occ_get_sensor_value(struct power8_driver *driver,
+				enum sensor_type t, int index)
+{
+	void *sensor;
+
+	if (t == CAPS)
+		return -1;
+
+	sensor = occ_get_sensor(driver, t);
+	if (!sensor)
+		return -1;
+
+	if (t == POWER)
+		return ((struct power_sensor *)sensor)[index].value;
+
+	return ((struct occ_sensor *)sensor)[index].value;
+}
+
+static int occ_get_sensor_id(struct power8_driver *driver,
+			     enum sensor_type t, int index)
+{
+	void *sensor;
+
+	if (t == CAPS)
+		return -1;
+
+	sensor = occ_get_sensor(driver, t);
+	if (!sensor)
+		return -1;
+
+	if (t == POWER)
+		return ((struct power_sensor *)sensor)[index].sensor_id;
+
+	return ((struct occ_sensor *)sensor)[index].sensor_id;
+}
+
+static ssize_t show_input(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	int val;
+	struct sensor_attr_data *sdata = container_of(attr,
+						      struct sensor_attr_data,
+						      dev_attr);
+	struct power8_driver *driver = dev_get_drvdata(dev);
+
+	val = occ_get_sensor_value(driver, sdata->type,
+				   sdata->hwmon_index - 1);
+	if (sdata->type == TEMP)
+		val *= 1000;	/* in millidegree Celsius */
+
+	return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
+}
+
+static ssize_t show_label(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	int val;
+	struct sensor_attr_data *sdata = container_of(attr,
+						      struct sensor_attr_data,
+						      dev_attr);
+	struct power8_driver *driver = dev_get_drvdata(dev);
+
+	val = occ_get_sensor_id(driver, sdata->type, sdata->hwmon_index - 1);
+
+	return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
+}
+
 static ssize_t show_update_interval(struct device *dev,
 				    struct device_attribute *attr, char *buf)
 {
@@ -639,6 +738,90 @@  static ssize_t store_user_powercap(struct device *dev,
 static DEVICE_ATTR(user_powercap, S_IWUSR | S_IRUGO, show_user_powercap,
 		   store_user_powercap);
 
+static void deinit_sensor_groups(struct device *dev,
+				 struct sensor_group *sensor_groups)
+{
+	int cnt;
+
+	for (cnt = 0; cnt < MAX_OCC_SENSOR_TYPE; cnt++) {
+		if (sensor_groups[cnt].group.attrs)
+			devm_kfree(dev, sensor_groups[cnt].group.attrs);
+		if (sensor_groups[cnt].sattr)
+			devm_kfree(dev, sensor_groups[cnt].sattr);
+		sensor_groups[cnt].group.attrs = NULL;
+		sensor_groups[cnt].sattr = NULL;
+	}
+}
+
+static void sensor_attr_init(struct sensor_attr_data *sdata,
+			     char *sensor_group_name,
+			     char *attr_name,
+			     ssize_t (*show)(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf))
+{
+	sysfs_attr_init(&sdata->dev_attr.attr);
+
+	snprintf(sdata->name, MAX_SENSOR_ATTR_LEN, "%s%d_%s",
+		 sensor_group_name, sdata->hwmon_index, attr_name);
+	sdata->dev_attr.attr.name = sdata->name;
+	sdata->dev_attr.attr.mode = S_IRUGO;
+	sdata->dev_attr.show = show;
+}
+
+static int create_sensor_group(struct power8_driver *driver,
+			       enum sensor_type type, int sensor_num)
+{
+	struct device *dev = driver->dev;
+	struct sensor_group *sensor_groups = driver->sensor_groups;
+	struct sensor_attr_data *sdata;
+	int rc, cnt;
+
+	/* each sensor has 'label' and 'input' attributes */
+	sensor_groups[type].group.attrs =
+		devm_kzalloc(dev, sizeof(struct attribute *) *
+			     sensor_num * 2 + 1, GFP_KERNEL);
+	if (!sensor_groups[type].group.attrs) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	sensor_groups[type].sattr =
+		devm_kzalloc(dev, sizeof(struct sensor_attr_data) *
+			     sensor_num * 2, GFP_KERNEL);
+	if (!sensor_groups[type].sattr) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	for (cnt = 0; cnt < sensor_num; cnt++) {
+		sdata = &sensor_groups[type].sattr[cnt];
+		/* hwmon attributes index starts from 1 */
+		sdata->hwmon_index = cnt + 1;
+		sdata->type = type;
+		sensor_attr_init(sdata, sensor_groups[type].name, "input",
+				 show_input);
+		sensor_groups[type].group.attrs[cnt] = &sdata->dev_attr.attr;
+
+		sdata = &sensor_groups[type].sattr[cnt + sensor_num];
+		sdata->hwmon_index = cnt + 1;
+		sdata->type = type;
+		sensor_attr_init(sdata, sensor_groups[type].name, "label",
+				 show_label);
+		sensor_groups[type].group.attrs[cnt + sensor_num] =
+			&sdata->dev_attr.attr;
+	}
+
+	rc = sysfs_create_group(&dev->kobj, &sensor_groups[type].group);
+	if (rc)
+		goto err;
+
+	return 0;
+err:
+	deinit_sensor_groups(dev, sensor_groups);
+	return rc;
+}
+
 static void occ_remove_hwmon_attrs(struct power8_driver *driver)
 {
 	struct device *dev = driver->dev;
@@ -650,8 +833,9 @@  static void occ_remove_hwmon_attrs(struct power8_driver *driver)
 
 static int occ_create_hwmon_attrs(struct power8_driver *driver)
 {
-	int i, rc;
+	int i, rc, id, sensor_num;
 	struct device *dev = driver->dev;
+	struct sensor_group *sensor_groups = driver->sensor_groups;
 	struct occ_response *resp = &driver->occ_response;
 
 	for (i = 0; i < MAX_OCC_SENSOR_TYPE; ++i)
@@ -681,6 +865,23 @@  static int occ_create_hwmon_attrs(struct power8_driver *driver)
 			goto error;
 	}
 
+	sensor_groups[FREQ].name = "freq";
+	sensor_groups[TEMP].name = "temp";
+	sensor_groups[POWER].name = "power";
+	sensor_groups[CAPS].name =  "caps";
+
+	for (i = 0; i < MAX_OCC_SENSOR_TYPE; i++) {
+		id = resp->sensor_block_id[i];
+		if (id < 0)
+			continue;
+
+		sensor_num = resp->blocks[id].sensor_num;
+		if (i != CAPS)
+			rc = create_sensor_group(driver, i, sensor_num);
+		if (rc)
+			goto error;
+	}
+
 	return 0;
 
 error: