All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC linux v4 5/6] hwmon: Add OCC sensor groups
@ 2016-10-13 21:43 eajames.ibm
  0 siblings, 0 replies; only message in thread
From: eajames.ibm @ 2016-10-13 21:43 UTC (permalink / raw)
  To: openbmc; +Cc: Edward A. James

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 --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:
-- 
1.9.1

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2016-10-13 21:43 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-13 21:43 [RFC linux v4 5/6] hwmon: Add OCC sensor groups eajames.ibm

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.