Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1600902 > unrolled thread
| Started by | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| First post | 2017-03-14 22:00 +0100 |
| Last post | 2017-03-14 22:00 +0100 |
| Articles | 3 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH linux v9 0/5] drivers: hwmon: Add On-Chip Controller driver Eddie James <eajames@linux.vnet.ibm.com> - 2017-03-14 22:00 +0100
[PATCH linux v9 4/5] hwmon: occ: Add callbacks for parsing P8 OCC datastructures Eddie James <eajames@linux.vnet.ibm.com> - 2017-03-14 22:00 +0100
[PATCH linux v9 2/5] hwmon: occ: Add sysfs interface Eddie James <eajames@linux.vnet.ibm.com> - 2017-03-14 22:00 +0100
| From | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-03-14 22:00 +0100 |
| Subject | [PATCH linux v9 0/5] drivers: hwmon: Add On-Chip Controller driver |
| Message-ID | <tl1HY-5G2-9@gated-at.bofh.it> |
From: "Edward A. James" <eajames@us.ibm.com> This patchset adds a hwmon driver to support the OCC (On-Chip Controller) on the IBM POWER8 and POWER9 processors, from a BMC (Baseboard Management Controller). The OCC is an embedded processor that provides real time power and thermal monitoring. The driver provides an interface on a BMC to poll OCC sensor data, set user power caps, and perform some basic OCC error handling. It interfaces with userspace through hwmon. The driver is currently functional only for the OCC on POWER8 chips. Communicating with the POWER9 OCC requries FSI support. since v8: * drop P9 sensors patch. This patch was mainly included to demonstrate why we abstracted out the sensor-specific code into the occ_p8 functions. However, since we haven't submitted the transfer protocol to the OCC for P9 yet, we shouldn't submit this unused P9 code yet. * fix string access in hwmon read_string() function. Thanks Guenter. * change parameter to const char ** in read_string() function to match upcoming hwmon code. Edward A. James (5): hwmon: Add core On-Chip Controller support for POWER CPUs hwmon: occ: Add sysfs interface hwmon: occ: Add I2C transport implementation for SCOM operations hwmon: occ: Add callbacks for parsing P8 OCC datastructures hwmon: occ: Add hwmon implementation for the P8 OCC Documentation/devicetree/bindings/hwmon/occ.txt | 13 + Documentation/hwmon/occ | 113 ++++++ MAINTAINERS | 7 + drivers/hwmon/Kconfig | 2 + drivers/hwmon/Makefile | 1 + drivers/hwmon/occ/Kconfig | 28 ++ drivers/hwmon/occ/Makefile | 3 + drivers/hwmon/occ/occ.c | 440 ++++++++++++++++++++++++ drivers/hwmon/occ/occ.h | 77 +++++ drivers/hwmon/occ/occ_p8.c | 256 ++++++++++++++ drivers/hwmon/occ/occ_p8.h | 25 ++ drivers/hwmon/occ/occ_p8_i2c.c | 99 ++++++ drivers/hwmon/occ/occ_scom_i2c.c | 69 ++++ drivers/hwmon/occ/occ_scom_i2c.h | 21 ++ drivers/hwmon/occ/occ_sysfs.c | 253 ++++++++++++++ drivers/hwmon/occ/occ_sysfs.h | 25 ++ drivers/hwmon/occ/scom.h | 42 +++ 17 files changed, 1474 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/occ.txt create mode 100644 Documentation/hwmon/occ create mode 100644 drivers/hwmon/occ/Kconfig create mode 100644 drivers/hwmon/occ/Makefile create mode 100644 drivers/hwmon/occ/occ.c create mode 100644 drivers/hwmon/occ/occ.h create mode 100644 drivers/hwmon/occ/occ_p8.c create mode 100644 drivers/hwmon/occ/occ_p8.h create mode 100644 drivers/hwmon/occ/occ_p8_i2c.c create mode 100644 drivers/hwmon/occ/occ_scom_i2c.c create mode 100644 drivers/hwmon/occ/occ_scom_i2c.h create mode 100644 drivers/hwmon/occ/occ_sysfs.c create mode 100644 drivers/hwmon/occ/occ_sysfs.h create mode 100644 drivers/hwmon/occ/scom.h -- 1.8.3.1
[toc] | [next] | [standalone]
| From | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-03-14 22:00 +0100 |
| Subject | [PATCH linux v9 4/5] hwmon: occ: Add callbacks for parsing P8 OCC datastructures |
| Message-ID | <tl1HY-5G2-27@gated-at.bofh.it> |
| In reply to | #1600902 |
From: "Edward A. James" <eajames@us.ibm.com>
Add functions to parse the data structures that are specific to the OCC
on the POWER8 processor. These are the sensor data structures, including
temperature, frequency, power, and "caps."
Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
Documentation/hwmon/occ | 9 ++
drivers/hwmon/occ/Makefile | 2 +-
drivers/hwmon/occ/occ_p8.c | 256 +++++++++++++++++++++++++++++++++++++++++++++
drivers/hwmon/occ/occ_p8.h | 25 +++++
4 files changed, 291 insertions(+), 1 deletion(-)
create mode 100644 drivers/hwmon/occ/occ_p8.c
create mode 100644 drivers/hwmon/occ/occ_p8.h
diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
index 580af26..4ec8842 100644
--- a/Documentation/hwmon/occ
+++ b/Documentation/hwmon/occ
@@ -27,6 +27,15 @@ 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.
+The format for the POWER8 OCC sensor data can be found in the P8 OCC
+specification:
+github.com/open-power/docs/blob/master/occ/OCC_OpenPwr_FW_Interfaces.pdf
+This document provides the details of the OCC sensors: power, frequency,
+temperature, and caps. These sensor formats are specific to the POWER8 OCC. A
+number of data structures, such as command format, response headers, and the
+like, are also defined in this specification, and are common to both POWER8 and
+POWER9 OCCs.
+
sysfs Entries
-------------
diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
index 9cc36e7..f59cca4 100644
--- a/drivers/hwmon/occ/Makefile
+++ b/drivers/hwmon/occ/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_SENSORS_IBM_OCC) += occ.o occ_sysfs.o occ_scom_i2c.o
+obj-$(CONFIG_SENSORS_IBM_OCC) += occ.o occ_sysfs.o occ_scom_i2c.o occ_p8.o
diff --git a/drivers/hwmon/occ/occ_p8.c b/drivers/hwmon/occ/occ_p8.c
new file mode 100644
index 0000000..ae8b453
--- /dev/null
+++ b/drivers/hwmon/occ/occ_p8.c
@@ -0,0 +1,256 @@
+/*
+ * occ_p8.c - OCC hwmon driver
+ *
+ * This file contains the Power8-specific methods and data structures for
+ * the OCC hwmon driver.
+ *
+ * Copyright 2017 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.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include "occ.h"
+#include "occ_p8.h"
+
+/* P8 OCC sensor data format */
+struct p8_occ_sensor {
+ u16 sensor_id;
+ u16 value;
+};
+
+struct p8_power_sensor {
+ u16 sensor_id;
+ u32 update_tag;
+ u32 accumulator;
+ u16 value;
+};
+
+struct p8_caps_sensor {
+ u16 curr_powercap;
+ u16 curr_powerreading;
+ u16 norm_powercap;
+ u16 max_powercap;
+ u16 min_powercap;
+ u16 user_powerlimit;
+};
+
+/* value, sensor_id */
+#define FREQ_SENSOR_CONFIG (HWMON_I_INPUT | HWMON_I_LABEL)
+#define TEMP_SENSOR_CONFIG (HWMON_T_INPUT | HWMON_T_LABEL)
+
+/* value, sensor_id, accumulator, update_tag */
+#define POWER_SENSOR_CONFIG \
+( \
+ HWMON_P_INPUT | HWMON_P_LABEL | HWMON_P_AVERAGE | \
+ HWMON_P_AVERAGE_INTERVAL \
+)
+
+/* curr_powercap, max_powercap, min_powercap, norm_powercap, user_powerlimit */
+#define CAPS_SENSOR_CONFIG \
+( \
+ HWMON_P_CAP | HWMON_P_CAP_MAX | HWMON_P_CAP_MIN | HWMON_P_MAX | \
+ HWMON_P_ALARM \
+)
+
+static const u32 p8_sensor_hwmon_configs[MAX_OCC_SENSOR_TYPE] = {
+ FREQ_SENSOR_CONFIG,
+ TEMP_SENSOR_CONFIG,
+ POWER_SENSOR_CONFIG,
+ CAPS_SENSOR_CONFIG,
+};
+
+void p8_parse_sensor(u8 *data, void *sensor, int sensor_type, int off,
+ int snum)
+{
+ switch (sensor_type) {
+ case FREQ:
+ case TEMP:
+ {
+ struct p8_occ_sensor *os =
+ &(((struct p8_occ_sensor *)sensor)[snum]);
+
+ os->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
+ os->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 2]));
+ }
+ break;
+ case POWER:
+ {
+ struct p8_power_sensor *ps =
+ &(((struct p8_power_sensor *)sensor)[snum]);
+
+ ps->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
+ ps->update_tag =
+ be32_to_cpu(get_unaligned((u32 *)&data[off + 2]));
+ ps->accumulator =
+ be32_to_cpu(get_unaligned((u32 *)&data[off + 6]));
+ ps->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 10]));
+ }
+ break;
+ case CAPS:
+ {
+ struct p8_caps_sensor *cs =
+ &(((struct p8_caps_sensor *)sensor)[snum]);
+
+ cs->curr_powercap =
+ be16_to_cpu(get_unaligned((u16 *)&data[off]));
+ cs->curr_powerreading =
+ be16_to_cpu(get_unaligned((u16 *)&data[off + 2]));
+ cs->norm_powercap =
+ be16_to_cpu(get_unaligned((u16 *)&data[off + 4]));
+ cs->max_powercap =
+ be16_to_cpu(get_unaligned((u16 *)&data[off + 6]));
+ cs->min_powercap =
+ be16_to_cpu(get_unaligned((u16 *)&data[off + 8]));
+ cs->user_powerlimit =
+ be16_to_cpu(get_unaligned((u16 *)&data[off + 10]));
+ }
+ break;
+ };
+}
+
+void *p8_alloc_sensor(struct device *dev, int sensor_type, int num_sensors)
+{
+ switch (sensor_type) {
+ case FREQ:
+ case TEMP:
+ return devm_kzalloc(dev, num_sensors *
+ sizeof(struct p8_occ_sensor), GFP_KERNEL);
+ case POWER:
+ return devm_kzalloc(dev, num_sensors *
+ sizeof(struct p8_power_sensor),
+ GFP_KERNEL);
+ case CAPS:
+ return devm_kzalloc(dev, num_sensors *
+ sizeof(struct p8_caps_sensor), GFP_KERNEL);
+ default:
+ return NULL;
+ }
+}
+
+int p8_get_sensor(struct occ *driver, int sensor_type, int sensor_num,
+ u32 hwmon, long *val)
+{
+ int rc = 0;
+ void *sensor;
+
+ if (sensor_type == POWER) {
+ if (hwmon == hwmon_power_cap || hwmon == hwmon_power_cap_max ||
+ hwmon == hwmon_power_cap_min || hwmon == hwmon_power_max ||
+ hwmon == hwmon_power_alarm)
+ sensor_type = CAPS;
+ }
+
+ sensor = occ_get_sensor(driver, sensor_type);
+ if (!sensor)
+ return -ENODEV;
+
+ switch (sensor_type) {
+ case FREQ:
+ case TEMP:
+ {
+ struct p8_occ_sensor *os =
+ &(((struct p8_occ_sensor *)sensor)[sensor_num]);
+
+ if (hwmon == hwmon_in_input || hwmon == hwmon_temp_input)
+ *val = os->value;
+ else if (hwmon == hwmon_in_label || hwmon == hwmon_temp_label)
+ *val = os->sensor_id;
+ else
+ rc = -EOPNOTSUPP;
+ }
+ break;
+ case POWER:
+ {
+ struct p8_power_sensor *ps =
+ &(((struct p8_power_sensor *)sensor)[sensor_num]);
+
+ switch (hwmon) {
+ case hwmon_power_input:
+ *val = ps->value;
+ break;
+ case hwmon_power_label:
+ *val = ps->sensor_id;
+ break;
+ case hwmon_power_average:
+ *val = ps->accumulator;
+ break;
+ case hwmon_power_average_interval:
+ *val = ps->update_tag;
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ }
+ }
+ break;
+ case CAPS:
+ {
+ struct p8_caps_sensor *cs =
+ &(((struct p8_caps_sensor *)sensor)[sensor_num]);
+
+ switch (hwmon) {
+ case hwmon_power_cap:
+ *val = cs->curr_powercap;
+ break;
+ case hwmon_power_cap_max:
+ *val = cs->max_powercap;
+ break;
+ case hwmon_power_cap_min:
+ *val = cs->min_powercap;
+ break;
+ case hwmon_power_max:
+ *val = cs->norm_powercap;
+ break;
+ case hwmon_power_alarm:
+ *val = cs->user_powerlimit;
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ }
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static const struct occ_ops p8_ops = {
+ .parse_sensor = p8_parse_sensor,
+ .alloc_sensor = p8_alloc_sensor,
+ .get_sensor = p8_get_sensor,
+};
+
+static const struct occ_init_data p8_init = {
+ .command_addr = 0xFFFF6000,
+ .response_addr = 0xFFFF7000,
+ .ops = &p8_ops,
+};
+
+const u32 *p8_get_sensor_hwmon_configs()
+{
+ return p8_sensor_hwmon_configs;
+}
+
+struct occ *p8_occ_init(struct device *dev, void *bus,
+ const struct occ_bus_ops *bus_ops)
+{
+ return occ_init(dev, bus, bus_ops, &p8_init);
+}
+
+MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
+MODULE_DESCRIPTION("P8 OCC sensors");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/occ_p8.h b/drivers/hwmon/occ/occ_p8.h
new file mode 100644
index 0000000..5f9cceb
--- /dev/null
+++ b/drivers/hwmon/occ/occ_p8.h
@@ -0,0 +1,25 @@
+/*
+ * occ_p8.h - OCC hwmon driver
+ *
+ * This file contains Power8-specific function prototypes
+ *
+ * Copyright 2017 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.
+ */
+
+#ifndef __OCC_P8_H__
+#define __OCC_P8_H__
+
+struct device;
+struct occ;
+struct occ_bus_ops;
+
+const u32 *p8_get_sensor_hwmon_configs(void);
+struct occ *p8_occ_init(struct device *dev, void *bus,
+ const struct occ_bus_ops *bus_ops);
+
+#endif /* __OCC_P8_H__ */
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-03-14 22:00 +0100 |
| Subject | [PATCH linux v9 2/5] hwmon: occ: Add sysfs interface |
| Message-ID | <tl1HY-5G2-31@gated-at.bofh.it> |
| In reply to | #1600902 |
From: "Edward A. James" <eajames@us.ibm.com>
Add a generic mechanism to expose the sensors provided by the OCC in
sysfs.
Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
Documentation/hwmon/occ | 62 +++++++++++
drivers/hwmon/occ/Makefile | 2 +-
drivers/hwmon/occ/occ_sysfs.c | 253 ++++++++++++++++++++++++++++++++++++++++++
drivers/hwmon/occ/occ_sysfs.h | 25 +++++
4 files changed, 341 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 d1c863b..580af26 100644
--- a/Documentation/hwmon/occ
+++ b/Documentation/hwmon/occ
@@ -27,6 +27,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. <x> 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<x>_input - frequency value
+ in<x>_label - sensor id
+temperature:
+ POWER8:
+ temp<x>_input - temperature value
+ temp<x>_label - sensor id
+ POWER9 (in addition to above):
+ temp<x>_type - FRU type
+
+power:
+ POWER8:
+ power<x>_input - power value
+ power<x>_label - sensor id
+ power<x>_average - accumulator
+ power<x>_average_interval - update tag (number of samples in
+ accumulator)
+ POWER9:
+ power<x>_input - power value
+ power<x>_label - sensor id
+ power<x>_average_min - accumulator[0]
+ power<x>_average_max - accumulator[1] (64 bits total)
+ power<x>_average_interval - update tag
+ power<x>_reset_history - (function_id | (apss_channel << 8)
+
+caps:
+ POWER8:
+ power<x>_cap - current powercap
+ power<x>_cap_max - max powercap
+ power<x>_cap_min - min powercap
+ power<x>_max - normal powercap
+ power<x>_alarm - user powercap, r/w
+ POWER9:
+ power<x>_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 3ed79a5..67b5367 100644
--- a/drivers/hwmon/occ/Makefile
+++ b/drivers/hwmon/occ/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_SENSORS_IBM_OCC) += occ.o
+obj-$(CONFIG_SENSORS_IBM_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..50b20e2
--- /dev/null
+++ b/drivers/hwmon/occ/occ_sysfs.c
@@ -0,0 +1,253 @@
+/*
+ * occ_sysfs.c - OCC sysfs interface
+ *
+ * This file contains the methods and data structures for implementing the OCC
+ * hwmon sysfs entries.
+ *
+ * Copyright 2017 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include "occ.h"
+#include "occ_sysfs.h"
+
+#define OCC_HWMON_NAME_LENGTH 32
+
+struct occ_sysfs {
+ struct device *dev;
+ struct occ *occ;
+
+ char label_buffer[OCC_HWMON_NAME_LENGTH + 1];
+ 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, const char **str)
+{
+ int rc;
+ unsigned long val = 0;
+ struct occ_sysfs *driver = dev_get_drvdata(dev);
+
+ 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;
+
+ /* just use one label buffer for all sensors. works with current hwmon
+ * implementation. only alternative is to store a buffer for each
+ * sensor, which gets expensive quickly.
+ */
+ rc = snprintf(driver->label_buffer, OCC_HWMON_NAME_LENGTH, "%lu", val);
+ if (rc > 0)
+ rc = 0;
+
+ *str = driver->label_buffer;
+
+ return rc;
+}
+
+static int occ_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ int rc;
+ 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) {
+ dev_err(dev, "set user powercap failed:%d\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 0644;
+ break;
+ case hwmon_in:
+ if (BIT(attr) & driver->sensor_hwmon_configs[0])
+ return 0444;
+ break;
+ case hwmon_temp:
+ if (BIT(attr) & driver->sensor_hwmon_configs[1])
+ return 0444;
+ break;
+ case hwmon_power:
+ /* user power limit */
+ if (attr == hwmon_power_alarm)
+ return 0644;
+ else if ((BIT(attr) & driver->sensor_hwmon_configs[2]) ||
+ (BIT(attr) & driver->sensor_hwmon_configs[3]))
+ return 0444;
+ 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;
+}
+
+MODULE_AUTHOR("Eddie James <eajames@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..fcd4f59a
--- /dev/null
+++ b/drivers/hwmon/occ/occ_sysfs.h
@@ -0,0 +1,25 @@
+/*
+ * occ_sysfs.h - OCC sysfs interface
+ *
+ * This file contains the data structures and function prototypes for the OCC
+ * hwmon sysfs entries.
+ *
+ * Copyright 2017 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.
+ */
+
+#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__ */
--
1.8.3.1
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web