[RFC linux v4 5/6] hwmon: Add OCC sensor groups
eajames.ibm at gmail.com
eajames.ibm at gmail.com
Fri Oct 14 08:43:01 AEDT 2016
From: "Edward A. James" <eajames at 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 at 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
More information about the openbmc
mailing list