[PATCH linux 2/7] hwmon: occ: Add sysfs interface

Andrew Jeffery andrew at aj.id.au
Tue Dec 20 11:14:35 AEDT 2016


On Fri, 2016-12-16 at 14:34 -0600, eajames.ibm at gmail.com wrote:
> > From: "Edward A. James" <eajames at us.ibm.com>
> 
> Add a generic mechanism to expose the sensors provided by the OCC in
> sysfs.
> 
> > Signed-off-by: Edward A. James <eajames at us.ibm.com>
> > Signed-off-by: Andrew Jeffery <andrew at aj.id.au>
> ---
>  Documentation/hwmon/occ       |  48 +++++
>  drivers/hwmon/occ/Makefile    |   2 +-
>  drivers/hwmon/occ/occ_sysfs.c | 492 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/hwmon/occ/occ_sysfs.h |  52 +++++
>  4 files changed, 593 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/hwmon/occ/occ_sysfs.c
>  create mode 100644 drivers/hwmon/occ/occ_sysfs.h
> 
> diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
> index 79d1642..1ee8689 100644
> --- a/Documentation/hwmon/occ
> +++ b/Documentation/hwmon/occ
> @@ -25,6 +25,54 @@ Currently, all versions of the OCC support four types of sensor data: power,
>  temperature, frequency, and "caps," which indicate limits and thresholds used
>  internally on the OCC.
>  
> +sysfs Entries
> +-------------
> +
> +The OCC driver uses the hwmon sysfs framework to provide data to userspace.
> +
> +The driver exports two sysfs files for each frequency, temperature, and power
> +sensor. These are "input" and "label". The input file contains the value of the
> +sensor. The label file contains the sensor id. The sensor id is the unique
> +internal OCC identifier. Sensor ids may be provided by the OCC specification.
> +The names of these files will be in the following format:
> > +	<sensor type><sensor index>_input
> > +	<sensor type><sensor index>_label
> +Sensor types will be one of "temp", "freq", or "power". The sensor index is
> +an index to differentiate different sensor files. For example, a single
> +temperature sensor will have two sysfs files: temp1_input and temp1_label.
> +
> +Caps sensors are exported differently. For each caps sensor, the driver will
> +export 6 entries:
> > +	curr_powercap - current power cap in watts
> > +	curr_powerreading - current power output in watts
> > +	norm_powercap - power cap without redundant power
> > +	max_powercap - maximum power cap that can be set in watts
> > +	min_powercap - minimum power cap that can be set in watts
> > +	user_powerlimit - power limit specified by the user in watts
> +In addition, the OCC driver for P9 will export a 7th entry:
> > +	user_powerlimit_source - can be one of two values depending on who set
> > +		the user_powerlimit. 0x1 - out of band from BMC or host. 0x2 -
> > +		in band from other source.
> +The format for these files is caps<sensor index>_<entry type>. For example,
> +caps1_curr_powercap.
> +
> +The driver also provides a number of sysfs entries through hwmon to better
> +control the driver and monitor the OCC.
> > +	powercap - read or write the OCC user power limit in watts.
> > +	name - read the name of the driver
> > +	update_interval - read or write the minimum interval for polling the
> > +		OCC.
> +
> +The driver also exports a single sysfs file through the communication protocol
> +device (see BMC - Host Communications). The filename is "online" and represents
> +the status of the OCC with respect to the driver. The OCC can be in one of two
> +states: OCC polling enabled or OCC polling disabled. The purpose of this file
> +is to control the behavior of the driver and it's hwmon sysfs entries, not to
> +infer any information about the state of the physical OCC. Reading the file
> +returns either a 0 (polling disabled) or 1 (polling enabled). Writing 1 to the
> +file enables OCC polling in the driver if communications can be established
> +with the OCC. Writing a 0 to the driver disables OCC polling.

Again, this is great. There's some overlap with
Documentation/hwmon/sysfs-interface, but nothing to worry about.

The rest looks good to me as well.

Reviewed-by: Andrew Jeffery <andrew at aj.id.au>

> +
>  BMC - Host Communications
>  -------------------------
>  
> diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
> index 93cb52f..a6881f9 100644
> --- a/drivers/hwmon/occ/Makefile
> +++ b/drivers/hwmon/occ/Makefile
> @@ -1 +1 @@
> -obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o
> +obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o occ_sysfs.o
> diff --git a/drivers/hwmon/occ/occ_sysfs.c b/drivers/hwmon/occ/occ_sysfs.c
> new file mode 100644
> index 0000000..b0e063da
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_sysfs.c
> @@ -0,0 +1,492 @@
> +/*
> + * occ_sysfs.c - OCC sysfs interface
> + *
> + * This file contains the methods and data structures for implementing the OCC
> + * hwmon sysfs entries.
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/jiffies.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/err.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +
> +#include "occ_sysfs.h"
> +
> > +#define MAX_SENSOR_ATTR_LEN	32
> +
> > +#define RESP_RETURN_CMD_INVAL	0x13
> +
> +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;
> +};
> +
> +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 occ_sysfs *driver = dev_get_drvdata(dev);
> +
> > +	val = occ_get_sensor_value(driver->occ, sdata->type,
> > +				   sdata->hwmon_index - 1);
> > +	if (sdata->type == TEMP)
> > > +		val *= 1000;	/* in millidegree Celsius */
> +
> > +	return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
> +}
> +
> +/* show_label provides the OCC sensor id. The sensor id will be either a
> + * 2-byte (for P8) or 4-byte (for P9) value. The sensor id is a way to
> + * identify what each sensor represents, according to the OCC specification.
> + */
> +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 occ_sysfs *driver = dev_get_drvdata(dev);
> +
> > +	val = occ_get_sensor_id(driver->occ, sdata->type,
> > +				sdata->hwmon_index - 1);
> +
> > +	return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
> +}
> +
> +static ssize_t show_caps(struct device *dev,
> > +			 struct device_attribute *attr, char *buf)
> +{
> > +	int val;
> > +	struct caps_sensor *sensor;
> > +	struct sensor_attr_data *sdata = container_of(attr,
> > +						      struct sensor_attr_data,
> > +						      dev_attr);
> > +	struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> > +	sensor = occ_get_sensor(driver->occ, CAPS);
> > +	if (!sensor) {
> > +		val = -1;
> > +		return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
> > +	}
> +
> > +	val = occ_get_caps_value(driver->occ, sensor, sdata->hwmon_index - 1,
> > +				 sdata->attr_id);
> +
> > +	return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
> +}
> +
> +static ssize_t show_update_interval(struct device *dev,
> > +				    struct device_attribute *attr, char *buf)
> +{
> > +	struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> > +	return snprintf(buf, PAGE_SIZE - 1, "%lu\n", driver->update_interval);
> +}
> +
> +static ssize_t store_update_interval(struct device *dev,
> > +				     struct device_attribute *attr,
> > +				     const char *buf, size_t count)
> +{
> > +	struct occ_sysfs *driver = dev_get_drvdata(dev);
> > +	unsigned long val;
> > +	int rc;
> +
> > +	rc = kstrtoul(buf, 10, &val);
> > +	if (rc)
> > +		return rc;
> +
> > +	driver->update_interval = val;
> > +	occ_set_update_interval(driver->occ, val);
> +
> > +	return count;
> +}
> +
> +static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_update_interval,
> > +		   store_update_interval);
> +
> +static ssize_t show_name(struct device *dev,
> > +			 struct device_attribute *attr, char *buf)
> +{
> > +	return snprintf(buf, PAGE_SIZE - 1, "occ\n");
> +}
> +
> +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> +
> +static ssize_t show_user_powercap(struct device *dev,
> > +				  struct device_attribute *attr, char *buf)
> +{
> > +	struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> > +	return snprintf(buf, PAGE_SIZE - 1, "%u\n", driver->user_powercap);
> +}
> +
> +static ssize_t store_user_powercap(struct device *dev,
> > +				   struct device_attribute *attr,
> > +				   const char *buf, size_t count)
> +{
> > +	struct occ_sysfs *driver = dev_get_drvdata(dev);
> > +	u16 val;
> > +	int rc;
> +
> > +	rc = kstrtou16(buf, 10, &val);
> > +	if (rc)
> > +		return rc;
> +
> > +	dev_dbg(dev, "set user powercap to: %d\n", val);
> > +	rc = occ_set_user_powercap(driver->occ, val);
> > +	if (rc) {
> > +		dev_err(dev, "set user powercap failed: 0x%x\n", rc);
> > +		if (rc == RESP_RETURN_CMD_INVAL) {
> > +			dev_err(dev, "set invalid powercap value: %d\n", val);
> > +			return -EINVAL;
> > +		}
> +
> > +		return rc;
> > +	}
> +
> > +	driver->user_powercap = val;
> +
> > +	return count;
> +}
> +
> +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 i;
> +
> > +	for (i = 0; i < MAX_OCC_SENSOR_TYPE; i++) {
> > +		if (sensor_groups[i].group.attrs)
> > +			devm_kfree(dev, sensor_groups[i].group.attrs);
> > +		if (sensor_groups[i].sattr)
> > +			devm_kfree(dev, sensor_groups[i].sattr);
> > +		sensor_groups[i].group.attrs = NULL;
> > +		sensor_groups[i].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 occ_sysfs *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, i;
> +
> > +	/* 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 (i = 0; i < sensor_num; i++) {
> > +		sdata = &sensor_groups[type].sattr[i];
> > +		/* hwmon attributes index starts from 1 */
> > +		sdata->hwmon_index = i + 1;
> > +		sdata->type = type;
> > +		sensor_attr_init(sdata, sensor_groups[type].name, "input",
> > +				 show_input);
> > +		sensor_groups[type].group.attrs[i] = &sdata->dev_attr.attr;
> +
> > +		sdata = &sensor_groups[type].sattr[i + sensor_num];
> > +		sdata->hwmon_index = i + 1;
> > +		sdata->type = type;
> > +		sensor_attr_init(sdata, sensor_groups[type].name, "label",
> > +				 show_label);
> > +		sensor_groups[type].group.attrs[i + 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 caps_sensor_attr_init(struct sensor_attr_data *sdata,
> > +				  char *attr_name, uint32_t hwmon_index,
> > +				  uint32_t attr_id)
> +{
> > +	sdata->type = CAPS;
> > +	sdata->hwmon_index = hwmon_index;
> > +	sdata->attr_id = attr_id;
> +
> > +	snprintf(sdata->name, MAX_SENSOR_ATTR_LEN, "%s%d_%s",
> > +		 "caps", 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_caps;
> +}
> +
> +static int create_caps_sensor_group(struct occ_sysfs *driver, int sensor_num)
> +{
> > +	struct device *dev = driver->dev;
> > +	struct sensor_group *sensor_groups = driver->sensor_groups;
> > +	int field_num = driver->num_caps_fields;
> > +	struct sensor_attr_data *sdata;
> > +	int i, j, rc;
> +
> > +	sensor_groups[CAPS].group.attrs =
> > +		devm_kzalloc(dev, sizeof(struct attribute *) * sensor_num *
> > +			     field_num + 1, GFP_KERNEL);
> > +	if (!sensor_groups[CAPS].group.attrs) {
> > +		rc = -ENOMEM;
> > +		goto err;
> > +	}
> +
> > +	sensor_groups[CAPS].sattr =
> > +		devm_kzalloc(dev, sizeof(struct sensor_attr_data) *
> > +			     sensor_num * field_num, GFP_KERNEL);
> > +	if (!sensor_groups[CAPS].sattr) {
> > +		rc = -ENOMEM;
> > +		goto err;
> > +	}
> +
> > +	for (j = 0; j < sensor_num; ++j) {
> > +		for (i = 0; i < field_num; ++i) {
> > +			sdata = &sensor_groups[CAPS].sattr[j * field_num + i];
> > +			caps_sensor_attr_init(sdata,
> > +					      driver->caps_names[i], j + 1, i);
> > +			sensor_groups[CAPS].group.attrs[j * field_num + i] =
> > +				&sdata->dev_attr.attr;
> > +		}
> > +	}
> +
> > +	rc = sysfs_create_group(&dev->kobj, &sensor_groups[CAPS].group);
> > +	if (rc)
> > +		goto err;
> +
> > +	return rc;
> +err:
> > +	deinit_sensor_groups(dev, sensor_groups);
> > +	return rc;
> +}
> +
> +static void occ_remove_hwmon_attrs(struct occ_sysfs *driver)
> +{
> > +	struct device *dev = driver->dev;
> +
> > +	device_remove_file(dev, &dev_attr_user_powercap);
> > +	device_remove_file(dev, &dev_attr_update_interval);
> > +	device_remove_file(dev, &dev_attr_name);
> +}
> +
> +static int occ_create_hwmon_attrs(struct occ_sysfs *driver)
> +{
> > +	int i, rc, id, sensor_num;
> > +	struct device *dev = driver->dev;
> > +	struct sensor_group *sensor_groups = driver->sensor_groups;
> > +	struct occ_blocks *resp = NULL;
> +
> > +	occ_get_response_blocks(driver->occ, &resp);
> +
> > +	for (i = 0; i < MAX_OCC_SENSOR_TYPE; ++i)
> > +		resp->sensor_block_id[i] = -1;
> +
> > +	/* read sensor data from occ */
> > +	rc = occ_update_device(driver->occ);
> > +	if (rc) {
> > +		dev_err(dev, "cannot get occ sensor data: %d\n", rc);
> > +		return rc;
> > +	}
> > +	if (!resp->blocks)
> > +		return -ENOMEM;
> +
> > +	rc = device_create_file(dev, &dev_attr_name);
> > +	if (rc)
> > +		goto error;
> +
> > +	rc = device_create_file(dev, &dev_attr_update_interval);
> > +	if (rc)
> > +		goto error;
> +
> > +	if (resp->sensor_block_id[CAPS] >= 0) {
> > +		/* user powercap: only for master OCC */
> > +		rc = device_create_file(dev, &dev_attr_user_powercap);
> > +		if (rc)
> > +			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].header.sensor_num;
> > +		if (i == CAPS)
> > +			rc = create_caps_sensor_group(driver, sensor_num);
> > +		else
> > +			rc = create_sensor_group(driver, i, sensor_num);
> > +		if (rc)
> > +			goto error;
> > +	}
> +
> > +	return 0;
> +
> +error:
> > +	dev_err(dev, "cannot create hwmon attributes: %d\n", rc);
> > +	occ_remove_hwmon_attrs(driver);
> > +	return rc;
> +}
> +
> +static ssize_t show_occ_online(struct device *dev,
> > +			       struct device_attribute *attr, char *buf)
> +{
> > +	struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> > +	return snprintf(buf, PAGE_SIZE - 1, "%u\n", driver->occ_online);
> +}
> +
> +static ssize_t store_occ_online(struct device *dev,
> > +				struct device_attribute *attr,
> > +				const char *buf, size_t count)
> +{
> > +	struct occ_sysfs *driver = dev_get_drvdata(dev);
> > +	unsigned long val;
> > +	int rc;
> +
> > +	rc = kstrtoul(buf, 10, &val);
> > +	if (rc)
> > +		return rc;
> +
> > +	if (val == 1) {
> > +		if (driver->occ_online)
> > +			return count;
> +
> > +		driver->dev = hwmon_device_register(dev);
> > +		if (IS_ERR(driver->dev))
> > +			return PTR_ERR(driver->dev);
> +
> > +		dev_set_drvdata(driver->dev, driver);
> +
> > +		rc = occ_create_hwmon_attrs(driver);
> > +		if (rc) {
> > +			hwmon_device_unregister(driver->dev);
> > +			driver->dev = NULL;
> > +			return rc;
> > +		}
> > +	} else if (val == 0) {
> > +		if (!driver->occ_online)
> > +			return count;
> +
> > +		occ_remove_hwmon_attrs(driver);
> > +		hwmon_device_unregister(driver->dev);
> > +		driver->dev = NULL;
> > +	} else
> > +		return -EINVAL;
> +
> > +	driver->occ_online = val;
> > +	return count;
> +}
> +
> +static DEVICE_ATTR(online, S_IWUSR | S_IRUGO, show_occ_online,
> > +		   store_occ_online);
> +
> +struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ,
> > +				  struct occ_sysfs_config *config)
> +{
> > +	struct occ_sysfs *hwmon = devm_kzalloc(dev, sizeof(struct occ_sysfs),
> > +					       GFP_KERNEL);
> > +	int rc;
> +
> > +	if (!hwmon)
> > +		return ERR_PTR(-ENOMEM);
> +
> > +	hwmon->occ = occ;
> > +	hwmon->num_caps_fields = config->num_caps_fields;
> > +	hwmon->caps_names = config->caps_names;
> +
> > +	dev_set_drvdata(dev, hwmon);
> +
> > +	rc = device_create_file(dev, &dev_attr_online);
> > +	if (rc)
> > +		return ERR_PTR(rc);
> +
> > +	return hwmon;
> +}
> +EXPORT_SYMBOL(occ_sysfs_start);
> +
> +int occ_sysfs_stop(struct device *dev, struct occ_sysfs *driver)
> +{
> > +	if (driver->dev) {
> > +		occ_remove_hwmon_attrs(driver);
> > +		hwmon_device_unregister(driver->dev);
> > +	}
> +
> > +	device_remove_file(driver->dev, &dev_attr_online);
> +
> > +	devm_kfree(dev, driver);
> +
> > +	return 0;
> +}
> +EXPORT_SYMBOL(occ_sysfs_stop);
> +
> > +MODULE_AUTHOR("Eddie James <eajames at us.ibm.com>");
> +MODULE_DESCRIPTION("OCC sysfs driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/occ/occ_sysfs.h b/drivers/hwmon/occ/occ_sysfs.h
> new file mode 100644
> index 0000000..2a8044f
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_sysfs.h
> @@ -0,0 +1,52 @@
> +/*
> + * occ_sysfs.h - OCC sysfs interface
> + *
> + * This file contains the data structures and function prototypes for the OCC
> + * hwmon sysfs entries.
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * 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.
> + */
> +
> +#ifndef __OCC_SYSFS_H__
> +#define __OCC_SYSFS_H__
> +
> +#include "occ.h"
> +
> +struct sensor_group {
> > +	char *name;
> > +	struct sensor_attr_data *sattr;
> > +	struct attribute_group group;
> +};
> +
> +struct occ_sysfs_config {
> > +	unsigned int num_caps_fields;
> > +	char **caps_names;
> +};
> +
> +struct occ_sysfs {
> > +	struct device *dev;
> > +	struct occ *occ;
> +
> > +	u16 user_powercap;
> > +	bool occ_online;
> > +	struct sensor_group sensor_groups[MAX_OCC_SENSOR_TYPE];
> > +	unsigned long update_interval;
> > +	unsigned int num_caps_fields;
> > +	char **caps_names;
> +};
> +
> +struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ,
> > +				  struct occ_sysfs_config *config);
> +int occ_sysfs_stop(struct device *dev, struct occ_sysfs *driver);
> +
> +#endif /* __OCC_SYSFS_H__ */
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: This is a digitally signed message part
URL: <http://lists.ozlabs.org/pipermail/openbmc/attachments/20161220/a53abbe1/attachment-0001.sig>


More information about the openbmc mailing list