From patchwork Thu Jan 26 23:19:14 2017 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: 9540309 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 F40F1601D7 for ; Thu, 26 Jan 2017 23:20:34 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E890A27F9F for ; Thu, 26 Jan 2017 23:20:34 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DD23328306; Thu, 26 Jan 2017 23:20:34 +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 3429E27F9F for ; Thu, 26 Jan 2017 23:20:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752867AbdAZXTn (ORCPT ); Thu, 26 Jan 2017 18:19:43 -0500 Received: from mail-qt0-f194.google.com ([209.85.216.194]:35632 "EHLO mail-qt0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752363AbdAZXTl (ORCPT ); Thu, 26 Jan 2017 18:19:41 -0500 Received: by mail-qt0-f194.google.com with SMTP id s58so1235109qtc.2; Thu, 26 Jan 2017 15:19:40 -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=vNE7F0L1Qh2i5FQtmjZ6taANFUM9n4wUrCEmNFJi+lw=; b=KTSgQ4nw9X9ERW1A7K5BNiSh5frbzKLrNclCzA9DlZBXBs+HKJqGx5znP1Mo34jxF0 6bLBch+TbLNmlI6oVswdcS7ekX71H46TpvYHeGr1OdfpBdDe+v0yJW/kXqciQIo7REKK BjBRVvWqkB/tZc97gLvhNc6qB7xjxnLWqOK1dv8CYHobhGufzaNslpA33r1yo+h58Qap q29RIvOi7lVNtsuTv/JnEhVP/NSmZR4xyAqj3PqM8+lb76suFLVIEdMEbslIRYvg/zE6 35UXamX4yj5Uis3Gwg77xHslOxCtDkAyHc2F2shlBTi1OAGKqmONxCCwg2nFPoNh/33C 3Rpw== 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=vNE7F0L1Qh2i5FQtmjZ6taANFUM9n4wUrCEmNFJi+lw=; b=P0VVA6uKGF0Ufjjqr37fEOFIhLlhcMBzX2c5nj05DKIO8F44o9FatpNVSfEgJwCbLJ NGWxPz3N1rUArPzGR8PXrqrB/avNzeelg5D2JIAQe5BrBkwxFLUwD2HPHjF2yZel4sTn f8gweqPrBG9t6IbaDoAISXRWS8QGwGEhXCMdKZQ+FYJWh0pm7gK9y00jOJmr2pHMsoQU IvJHP9ErU3Y6M8ETFo32Ay5zKUi8L8+h91fUObYMqqC3/Z6O+naw6fCensaCGRsp06B9 m3/UbSMmIgxR1/kcD0OsdQ9jrnBmd6SfVMQQQ7K0iuG4LvOMP51DqdnYGRj+xlnvqe9n 4ukA== X-Gm-Message-State: AIkVDXJwCwAOVoD4UDzS3JHRnmrGVSP46k/woV2keJ4OkOgYcYgWrRxcTWILl+O62eyq2w== X-Received: by 10.55.108.193 with SMTP id h184mr5413707qkc.155.1485472780123; Thu, 26 Jan 2017 15:19:40 -0800 (PST) Received: from eajames-austin-w350.austin.ibm.com ([32.97.110.56]) by smtp.gmail.com with ESMTPSA id x62sm2484865qkg.31.2017.01.26.15.19.37 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 26 Jan 2017 15:19:39 -0800 (PST) From: eajames.ibm@gmail.com To: linux@roeck-us.net Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-doc@vger.kernel.org, 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, benh@kernel.crashing.org, "Edward A. James" Subject: [PATCH linux v4 2/6] hwmon: occ: Add sysfs interface Date: Thu, 26 Jan 2017 17:19:14 -0600 Message-Id: <1485472758-11003-3-git-send-email-eajames.ibm@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1485472758-11003-1-git-send-email-eajames.ibm@gmail.com> References: <1485472758-11003-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 --- Documentation/hwmon/occ | 62 ++++++++++ drivers/hwmon/occ/Makefile | 2 +- drivers/hwmon/occ/occ_sysfs.c | 259 ++++++++++++++++++++++++++++++++++++++++++ drivers/hwmon/occ/occ_sysfs.h | 30 +++++ 4 files changed, 352 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..d0bdf06 100644 --- a/Documentation/hwmon/occ +++ b/Documentation/hwmon/occ @@ -25,6 +25,68 @@ 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 a number of sysfs files for each type of sensor. The +sensor-specific files vary depending on the processor type, though many of the +attributes are common for both the POWER8 and POWER9. + +The hwmon interface cannot define every type of sensor that may be used. +Therefore, the frequency sensor on the OCC uses the "input" type sensor defined +by the hwmon interface, rather than defining a new type of custom sensor. + +Below are detailed the names and meaning of each sensor file for both types of +processors. All sensors are read-only unless otherwise specified. indicates +the hwmon index. sensor id indicates the unique internal OCC identifer. Please +see the POWER OCC specification for details on all these sensor values. + +frequency: + all processors: + in_input - frequency value + in_label - sensor id +temperature: + POWER8: + temp_input - temperature value + temp_label - sensor id + POWER9 (in addition to above): + temp_type - FRU type + +power: + POWER8: + power_input - power value + power_label - sensor id + power_average - accumulator + power_average_interval - update tag (number of samples in + accumulator) + POWER9: + power_input - power value + power_label - sensor id + power_average_min - accumulator[0] + power_average_max - accumulator[1] (64 bits total) + power_average_interval - update tag + power_reset_history - (function_id | (apss_channel << 8) + +caps: + POWER8: + power_cap - current powercap + power_cap_max - max powercap + power_cap_min - min powercap + power_max - normal powercap + power_alarm - user powercap, r/w + POWER9: + power_cap_alarm - user powercap source + +The driver also provides two sysfs entries through hwmon to better +control the driver and monitor the master OCC. Though there may be multiple +OCCs present on the system, these two files are only present for the "master" +OCC. + name - read the name of the driver + update_interval - read or write the minimum interval for polling the + OCC. + 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..ea2aa04 --- /dev/null +++ b/drivers/hwmon/occ/occ_sysfs.c @@ -0,0 +1,259 @@ +/* + * 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.h" +#include "occ_sysfs.h" + +#define RESP_RETURN_CMD_INVAL 0x13 +#define OCC_HWMON_NAME_LENGTH 32 + +struct occ_sysfs { + struct device *dev; + struct occ *occ; + + char hwmon_name[OCC_HWMON_NAME_LENGTH + 1]; + const u32 *sensor_hwmon_configs; + struct hwmon_channel_info **occ_sensors; + struct hwmon_chip_info occ_info; + u16 user_powercap; +}; + +static int occ_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + int rc; + struct occ_sysfs *driver = dev_get_drvdata(dev); + struct occ *occ = driver->occ; + + switch (type) { + case hwmon_in: + rc = occ_get_sensor_field(occ, FREQ, channel, attr, val); + break; + case hwmon_temp: + rc = occ_get_sensor_field(occ, TEMP, channel, attr, val); + break; + case hwmon_power: + rc = occ_get_sensor_field(occ, POWER, channel, attr, val); + break; + default: + rc = -EOPNOTSUPP; + } + + return rc; +} + +static int occ_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, char **str) +{ + int rc; + unsigned long val = 0; + + if (!((type == hwmon_in && attr == hwmon_in_label) || + (type == hwmon_temp && attr == hwmon_temp_label) || + (type == hwmon_power && attr == hwmon_power_label))) + return -EOPNOTSUPP; + + /* will fetch the "label", the sensor_id */ + rc = occ_hwmon_read(dev, type, attr, channel, &val); + if (rc < 0) + return rc; + + rc = snprintf(*str, PAGE_SIZE - 1, "%lu", val); + if (rc > 0) + rc = 0; + + return rc; +} + +static int occ_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + int rc = 0; + struct occ_sysfs *driver = dev_get_drvdata(dev); + + if (type == hwmon_power && attr == hwmon_power_alarm) { + rc = occ_set_user_powercap(driver->occ, val); + if (rc) { + if (rc == RESP_RETURN_CMD_INVAL) { + dev_err(dev, + "set invalid powercap value: %ld\n", + val); + return -EINVAL; + } + + dev_err(dev, "set user powercap failed: 0x:%x\n", rc); + return rc; + } + + driver->user_powercap = val; + + return rc; + } + + return -EOPNOTSUPP; +} + +static umode_t occ_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct occ_sysfs *driver = data; + + switch (type) { + case hwmon_chip: + if (attr == hwmon_chip_update_interval) + return S_IRUGO | S_IWUSR; + break; + case hwmon_in: + if (BIT(attr) & driver->sensor_hwmon_configs[0]) + return S_IRUGO; + break; + case hwmon_temp: + if (BIT(attr) & driver->sensor_hwmon_configs[1]) + return S_IRUGO; + break; + case hwmon_power: + /* user power limit */ + if (attr == hwmon_power_alarm) + return S_IRUGO | S_IWUSR; + else if ((BIT(attr) & driver->sensor_hwmon_configs[2]) || + (BIT(attr) & driver->sensor_hwmon_configs[3])) + return S_IRUGO; + break; + default: + return 0; + } + + return 0; +} + +static const struct hwmon_ops occ_hwmon_ops = { + .is_visible = occ_is_visible, + .read = occ_hwmon_read, + .read_string = occ_hwmon_read_string, + .write = occ_hwmon_write, +}; + +static const enum hwmon_sensor_types occ_sensor_types[MAX_OCC_SENSOR_TYPE] = { + hwmon_in, + hwmon_temp, + hwmon_power, + hwmon_power +}; + +struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ, + const u32 *sensor_hwmon_configs, + const char *name) +{ + int rc, i, j, num_sensors, index = 0, id; + char *brk; + struct occ_blocks *resp = NULL; + u32 *sensor_config; + struct occ_sysfs *hwmon = devm_kzalloc(dev, sizeof(struct occ_sysfs), + GFP_KERNEL); + if (!hwmon) + return ERR_PTR(-ENOMEM); + + /* need space for null-termination */ + hwmon->occ_sensors = + devm_kzalloc(dev, sizeof(struct hwmon_channel_info *) * + (MAX_OCC_SENSOR_TYPE + 1), GFP_KERNEL); + if (!hwmon->occ_sensors) + return ERR_PTR(-ENOMEM); + + hwmon->occ = occ; + hwmon->sensor_hwmon_configs = sensor_hwmon_configs; + hwmon->occ_info.ops = &occ_hwmon_ops; + hwmon->occ_info.info = + (const struct hwmon_channel_info **)hwmon->occ_sensors; + + occ_get_response_blocks(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(occ); + if (rc) + return ERR_PTR(rc); + + for (i = 0; i < MAX_OCC_SENSOR_TYPE; i++) { + id = resp->sensor_block_id[i]; + if (id < 0) + continue; + + num_sensors = resp->blocks[id].header.num_sensors; + /* need null-termination */ + sensor_config = devm_kzalloc(dev, + sizeof(u32) * (num_sensors + 1), + GFP_KERNEL); + if (!sensor_config) + return ERR_PTR(-ENOMEM); + + for (j = 0; j < num_sensors; j++) + sensor_config[j] = sensor_hwmon_configs[i]; + + hwmon->occ_sensors[index] = + devm_kzalloc(dev, sizeof(struct hwmon_channel_info), + GFP_KERNEL); + if (!hwmon->occ_sensors[index]) + return ERR_PTR(-ENOMEM); + + hwmon->occ_sensors[index]->type = occ_sensor_types[i]; + hwmon->occ_sensors[index]->config = sensor_config; + index++; + } + + /* search for bad chars */ + strncpy(hwmon->hwmon_name, name, OCC_HWMON_NAME_LENGTH); + brk = strpbrk(hwmon->hwmon_name, "-* \t\n"); + while (brk) { + *brk = '_'; + brk = strpbrk(brk, "-* \t\n"); + } + + hwmon->dev = devm_hwmon_device_register_with_info(dev, + hwmon->hwmon_name, + hwmon, + &hwmon->occ_info, + NULL); + if (IS_ERR(hwmon->dev)) { + dev_err(dev, "cannot register hwmon device %s: %ld\n", + hwmon->hwmon_name, PTR_ERR(hwmon->dev)); + return ERR_CAST(hwmon->dev); + } + + return hwmon; +} +EXPORT_SYMBOL(occ_sysfs_start); + +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..462f9d2 --- /dev/null +++ b/drivers/hwmon/occ/occ_sysfs.h @@ -0,0 +1,30 @@ +/* + * 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__ + +struct occ; +struct device; +struct occ_sysfs; + +struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ, + const u32 *sensor_hwmon_configs, + const char *name); +#endif /* __OCC_SYSFS_H__ */