From patchwork Fri Dec 30 17:56:04 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: eajames.ibm@gmail.com X-Patchwork-Id: 9492359 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 3191762ABB for ; Fri, 30 Dec 2016 17:58:01 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 260F3223B2 for ; Fri, 30 Dec 2016 17:58:01 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 194B0228C9; Fri, 30 Dec 2016 17:58:01 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EAF9A223B2 for ; Fri, 30 Dec 2016 17:57:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753403AbcL3R4o (ORCPT ); Fri, 30 Dec 2016 12:56:44 -0500 Received: from mail-it0-f66.google.com ([209.85.214.66]:35372 "EHLO mail-it0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751690AbcL3R4k (ORCPT ); Fri, 30 Dec 2016 12:56:40 -0500 Received: by mail-it0-f66.google.com with SMTP id b123so42260031itb.2; Fri, 30 Dec 2016 09:56:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=b7S+8tdaXzW9ok+6A1OGsL6T7Y1eOoK+oicYWMottvA=; b=resjf2zcKvru1MZul6FpBv7Z1PmsdxWW8vCivKDR5CqH3V7ICwCSOAVFctMiDFBpaP D67CFZfZ+JLBKbfJxzfhjjNTbLLhb+uDCpkwmrM0907FX3wgL4V4cdyDrWdBxI9mrQOb UC0khTPI4tsdJ9CiSTzFJ86/RIhtJBGiWeFM/99bsrHN9rNuXGEGBaJ5XV2lkIg2jH9F cHRlyTKfkP3Ysg/30jjm/dkjj4gVu7TZzWQi4yYpB3PRKVIamnToURk8Jx1eMKbV8ekf /OYZDpjGwaVE4AnzjbcU0ZZr6CuvugfjIVNZ9ecYazfx0VReIMEGuYqVw7YCfrF3deXa yz7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=b7S+8tdaXzW9ok+6A1OGsL6T7Y1eOoK+oicYWMottvA=; b=MeIOReMbMxNFpqQfZk+YMwXfWyR06mfX9dnDViufpcYVsCpHLLEZnOrvyHDjbbwICq 37VzvAByk9JwJGhGHXXlg15ua56sG62m8NrAPLWvFTa2IA4puCAoNtOb5OmMUzCjbR7O jInl0TBFjQAEmKaZt5akm86u6eqqxkvSp3Oh/6v/kWeYU6QYnWCO4HkdOXNRIJNd+X1V v46AIXhTArs5PoiyizRdcDD0lBrVEYR/X8XXFWXzMGMH4C6W+X9K7Z00l7N7UdHvSE49 /b+okhZySTZQlUmcRsT1PZ1dcGwIgWVTfhcxzZ6NFqjF/wdoDvFu7ylR6F/p2su4A5+9 BuQg== X-Gm-Message-State: AIkVDXLWCh+5qasSrev46ksEBWOoJARUo6WBLIHNjgkYr+wPaLwDeh/xf3VwLdahTt5Vhw== X-Received: by 10.36.107.79 with SMTP id v76mr36149871itc.32.1483120598910; Fri, 30 Dec 2016 09:56:38 -0800 (PST) Received: from eajames-austin-w350.ibm.com ([32.97.110.51]) by smtp.gmail.com with ESMTPSA id d77sm4524611itc.10.2016.12.30.09.56.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 30 Dec 2016 09:56:38 -0800 (PST) From: eajames.ibm@gmail.com To: linux@roeck-us.net Cc: jdelvare@suse.com, corbet@lwn.net, mark.rutland@arm.com, robh+dt@kernel.org, wsa@the-dreams.de, andrew@aj.id.au, joel@jms.id.au, devicetree@vger.kernel.org, linux-doc@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org, "Edward A. James" Subject: [PATCH linux 2/6] hwmon: occ: Add sysfs interface Date: Fri, 30 Dec 2016 11:56:04 -0600 Message-Id: <1483120568-21082-3-git-send-email-eajames.ibm@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1483120568-21082-1-git-send-email-eajames.ibm@gmail.com> References: <1483120568-21082-1-git-send-email-eajames.ibm@gmail.com> Sender: linux-hwmon-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-hwmon@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: "Edward A. James" Add a generic mechanism to expose the sensors provided by the OCC in sysfs. Signed-off-by: Edward A. James Signed-off-by: Andrew Jeffery Reviewed-by: Andrew Jeffery --- 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: + _input + _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_. 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. + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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__ */