2017-11-20 23:58:54

by Eddie James

[permalink] [raw]
Subject: [PATCH v3 00/12] hwmon: Add On-Chip Controller hwmon driver

From: "Edward A. James" <[email protected]>

This series adds a hwmon driver to support the OCC on POWER8 and POWER9
processors. The OCC is an embedded processor that provides realtime power and
thermal monitoring and management.

This driver has two different platform drivers as a "base" for the
hwmon interface, as the means of communicating with the OCC on P8 and P9 is
completely different. For P8, the driver is an I2C client driver. For P9 the
driver is an FSI-based OCC client driver, and uses the OCC driver in-kernel
API. The OCC driver is on the LKML (latest https://lkml.org/lkml/2017/11/20/631).

Changes since v2:
* Add sysfs_notify for the error and throttling attributes when change is
detected.
* Removed occs_present counting of devices bound.
* Improved remove() of P9 driver to avoid bad behavior with relation to OCC
driver when unbound.
* Added default cases (return EINVAL) for all sensor show functions.
* Added temperature fault sensor.
* Added back dt binding documentation for P9 to address checkpatch warning.
* Added occs_present attribute from the poll response.

Changes since v1:
* Remove wait loop in P9 code, as that is now handled by FSI OCC driver.
* Removed dt binding documentation for P9, FSI OCC driver will probe OCC hwmon
driver automatically.
* Moved OCC response code definitions to the OCC include file.
* Fixed includes.
* Changed some structure fields to __beXX as that is what they are.
* Changed some errnos.
* Removed some dev_err().
* Refactored P8 code a bit to use #defined addresses and magic values, and
changed "goto retry" to a loop.
* Refactored error handling a bit.

Edward A. James (12):
Documentation: hwmon: Add OCC documentation
Documentation: ABI: Add occ-hwmon driver sysfs documentation
dt-bindings: i2c: Add P8 OCC hwmon device documentation
dt-bindings: fsi: Add P9 OCC hwmon device documentation
hwmon (occ): Add On-Chip Controller (OCC) hwmon driver
hwmon (occ): Add command transport method for P8 and P9
hwmon (occ): Parse OCC poll response
hwmon (occ): Add sensor types and versions
hwmon (occ): Add sensor attributes and register hwmon device
hwmon (occ): Add non-hwmon attributes
hwmon (occ): Add error handling
hwmon (occ): Add sysfs notification for errors and throttling

Documentation/ABI/testing/sysfs-driver-occ-hwmon | 85 ++
.../devicetree/bindings/fsi/ibm,p9-occ-hwmon.txt | 16 +
.../devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt | 25 +
Documentation/hwmon/occ | 75 ++
drivers/hwmon/Kconfig | 2 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/occ/Kconfig | 28 +
drivers/hwmon/occ/Makefile | 11 +
drivers/hwmon/occ/common.c | 1387 ++++++++++++++++++++
drivers/hwmon/occ/common.h | 124 ++
drivers/hwmon/occ/p8_i2c.c | 262 ++++
drivers/hwmon/occ/p9_sbe.c | 161 +++
12 files changed, 2177 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-driver-occ-hwmon
create mode 100644 Documentation/devicetree/bindings/fsi/ibm,p9-occ-hwmon.txt
create mode 100644 Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.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/common.c
create mode 100644 drivers/hwmon/occ/common.h
create mode 100644 drivers/hwmon/occ/p8_i2c.c
create mode 100644 drivers/hwmon/occ/p9_sbe.c

--
1.8.3.1


From 1584611077088958265@xxx Mon Nov 20 18:34:45 +0000 2017
X-GM-THRID: 1584611077088958265
X-Gmail-Labels: Inbox,Category Forums,HistoricalUnread


2017-11-20 23:58:05

by Eddie James

[permalink] [raw]
Subject: [PATCH v3 01/12] Documentation: hwmon: Add OCC documentation

From: "Edward A. James" <[email protected]>

Document the hwmon interface for the OCC.

Signed-off-by: Edward A. James <[email protected]>
---
Documentation/hwmon/occ | 75 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)
create mode 100644 Documentation/hwmon/occ

diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
new file mode 100644
index 0000000..c88d0f5
--- /dev/null
+++ b/Documentation/hwmon/occ
@@ -0,0 +1,75 @@
+Kernel driver occ-hwmon
+=======================
+
+Supported chips:
+ * POWER8
+ * POWER9
+
+Author: Eddie James <[email protected]>
+
+Description
+-----------
+
+This driver supports hardware monitoring for the On-Chip Controller (OCC)
+embedded on POWER processors. The OCC is a device that collects and aggregates
+sensor data from the processor and the system. The OCC can provide the raw
+sensor data as well as perform thermal and power management on the system.
+
+The P8 version of this driver is a client driver of I2C. It may be probed
+manually if an "ibm,p8-occ-hwmon" compatible device is found under the
+appropriate I2C bus node in the device-tree.
+
+The P9 version of this driver is a client driver of the FSI-based OCC driver.
+It will be probed automatically by the FSI-based OCC driver. Please see the
+device-tree bindings documentation for that driver for details on probing
+an OCC device (Documentation/devicetree/bindings/fsi/ibm,p9-occ.txt).
+
+Sysfs entries
+-------------
+
+The following attributes are supported. All attributes are read-only unless
+specified.
+
+temp[1-n]_label OCC sensor id.
+temp[1-n]_input Measured temperature in millidegrees C.
+[with temperature sensor version 2+]
+ temp[1-n]_fru_type Given FRU (Field Replaceable Unit) type.
+ temp[1-n]_fault Temperature sensor fault.
+
+freq[1-n]_label OCC sensor id.
+freq[1-n]_input Measured frequency.
+
+power[1-n]_label OCC sensor id.
+power[1-n]_input Measured power in microwatts.
+power[1-n]_update_tag Number of 250us samples represented in accumulator.
+power[1-n]_accumulator Accumulation of 250us power readings.
+[with power sensor version 2+]
+ power[1-n]_function_id Identifies what the power reading is for.
+ power[1-n]_apss_channel Indicates APSS channel.
+
+[power version 0xa0 only]
+power1_id OCC sensor id.
+power[1-n]_label Sensor type, "system", "proc", "vdd", or "vdn".
+power[1-n]_input Most recent power reading in microwatts.
+power[1-n]_update_tag Number of samples in the accumulator.
+power[1-n]_accumulator Accumulation of power readings.
+[with sensor type "system" and "proc" only]
+ power[1-n]_update_time Time in us that the power value is read.
+
+caps1_current Current OCC power cap in watts.
+caps1_reading Current system output power in watts.
+caps1_norm Power cap without redundant power.
+caps1_max Maximum power cap.
+[caps version 1 and 2 only]
+ caps1_min Minimum power cap.
+[caps version 3+]
+ caps1_min_hard Hard minimum cap that can be set and held.
+ caps1_min_soft Soft minimum cap below hard, not guaranteed.
+caps1_user The powercap specified by the user. Will be 0 if no
+ user powercap exists. This attribute is read-write.
+[caps version 1+]
+ caps1_user_source Indicates how the user power limit was set.
+
+extn[1-n]_label ASCII id or sensor id.
+extn[1-n]_flags Indicates type of label attribute.
+extn[1-n]_input Data.
--
1.8.3.1


From 1586238535860770780@xxx Fri Dec 08 17:42:31 +0000 2017
X-GM-THRID: 1586051908163953638
X-Gmail-Labels: Inbox,Category Forums,HistoricalUnread

2017-11-20 23:57:08

by Eddie James

[permalink] [raw]
Subject: [PATCH v3 06/12] hwmon (occ): Add command transport method for P8 and P9

From: "Edward A. James" <[email protected]>

For the P8 OCC, add the procedure to send a command to the OCC over I2C
bus. This involves writing the OCC command registers with serial
communication operations (SCOMs) interpreted by the I2C slave. For the
P9 OCC, add a procedure to use the OCC in-kernel API to send a command
to the OCC through the SBE engine.

Signed-off-by: Edward A. James <[email protected]>
---
drivers/hwmon/occ/p8_i2c.c | 185 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/hwmon/occ/p9_sbe.c | 95 ++++++++++++++++++++++-
2 files changed, 278 insertions(+), 2 deletions(-)

diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
index 025471f..8032c0b 100644
--- a/drivers/hwmon/occ/p8_i2c.c
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -9,11 +9,29 @@

#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/fsi-occ.h>
#include <linux/i2c.h>
+#include <linux/jiffies.h>
#include <linux/module.h>
+#include <linux/sched.h>
+#include <asm/unaligned.h>

#include "common.h"

+#define OCC_TIMEOUT_MS 1000
+#define OCC_CMD_IN_PRG_WAIT_MS 50
+
+/* OCB (on-chip control bridge - interface to OCC) registers */
+#define OCB_DATA1 0x6B035
+#define OCB_ADDR 0x6B070
+#define OCB_DATA3 0x6B075
+
+/* OCC SRAM address space */
+#define OCC_SRAM_ADDR_CMD 0xFFFF6000
+#define OCC_SRAM_ADDR_RESP 0xFFFF7000
+
+#define OCC_DATA_ATTN 0x20010000
+
struct p8_i2c_occ {
struct occ occ;
struct i2c_client *client;
@@ -21,9 +39,174 @@ struct p8_i2c_occ {

#define to_p8_i2c_occ(x) container_of((x), struct p8_i2c_occ, occ)

+static int p8_i2c_occ_getscom(struct i2c_client *client, u32 address, u8 *data)
+{
+ ssize_t rc;
+ __be64 buf;
+ struct i2c_msg msgs[2];
+
+ /* p8 i2c slave requires shift */
+ address <<= 1;
+
+ msgs[0].addr = client->addr;
+ msgs[0].flags = client->flags & I2C_M_TEN;
+ msgs[0].len = sizeof(u32);
+ /* address is a scom address; bus-endian */
+ msgs[0].buf = (char *)&address;
+
+ /* data from OCC is big-endian */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
+ msgs[1].len = sizeof(u64);
+ msgs[1].buf = (char *)&buf;
+
+ rc = i2c_transfer(client->adapter, msgs, 2);
+ if (rc < 0)
+ return rc;
+
+ *(u64 *)data = be64_to_cpu(buf);
+
+ return 0;
+}
+
+static int p8_i2c_occ_putscom(struct i2c_client *client, u32 address, u8 *data)
+{
+ u32 buf[3];
+ ssize_t rc;
+
+ /* p8 i2c slave requires shift */
+ address <<= 1;
+
+ /* address is bus-endian; data passed through from user as-is */
+ buf[0] = address;
+ memcpy(&buf[1], &data[4], sizeof(u32));
+ memcpy(&buf[2], data, sizeof(u32));
+
+ rc = i2c_master_send(client, (const char *)buf, sizeof(buf));
+ if (rc < 0)
+ return rc;
+ else if (rc != sizeof(buf))
+ return -EIO;
+
+ return 0;
+}
+
+static int p8_i2c_occ_putscom_u32(struct i2c_client *client, u32 address,
+ u32 data0, u32 data1)
+{
+ u8 buf[8];
+
+ memcpy(buf, &data0, 4);
+ memcpy(buf + 4, &data1, 4);
+
+ return p8_i2c_occ_putscom(client, address, buf);
+}
+
+static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address,
+ u8 *data)
+{
+ __be32 data0, data1;
+
+ memcpy(&data0, data, 4);
+ memcpy(&data1, data + 4, 4);
+
+ return p8_i2c_occ_putscom_u32(client, address, be32_to_cpu(data0),
+ be32_to_cpu(data1));
+}
+
static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd)
{
- return -EOPNOTSUPP;
+ int i, rc;
+ unsigned long start;
+ u16 data_length;
+ const unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS);
+ const long int wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
+ struct p8_i2c_occ *p8_i2c_occ = to_p8_i2c_occ(occ);
+ struct i2c_client *client = p8_i2c_occ->client;
+ struct occ_response *resp = &occ->resp;
+
+ start = jiffies;
+
+ /* set sram address for command */
+ rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR, OCC_SRAM_ADDR_CMD, 0);
+ if (rc)
+ return rc;
+
+ /* write command (expected to already be BE), we need bus-endian... */
+ rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd);
+ if (rc)
+ return rc;
+
+ /* trigger OCC attention */
+ rc = p8_i2c_occ_putscom_u32(client, OCB_DATA1, OCC_DATA_ATTN, 0);
+ if (rc)
+ return rc;
+
+ do {
+ /* set sram address for response */
+ rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR,
+ OCC_SRAM_ADDR_RESP, 0);
+ if (rc)
+ return rc;
+
+ rc = p8_i2c_occ_getscom(client, OCB_DATA3, (u8 *)resp);
+ if (rc)
+ return rc;
+
+ /* wait for OCC */
+ if (resp->return_status == OCC_RESP_CMD_IN_PRG) {
+ rc = -EALREADY;
+
+ if (time_after(jiffies, start + timeout))
+ break;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(wait_time);
+ }
+ } while (rc);
+
+ /* check the OCC response */
+ switch (resp->return_status) {
+ case OCC_RESP_CMD_IN_PRG:
+ rc = -ETIMEDOUT;
+ break;
+ case OCC_RESP_SUCCESS:
+ rc = 0;
+ break;
+ case OCC_RESP_CMD_INVAL:
+ case OCC_RESP_CMD_LEN_INVAL:
+ case OCC_RESP_DATA_INVAL:
+ case OCC_RESP_CHKSUM_ERR:
+ rc = -EINVAL;
+ break;
+ case OCC_RESP_INT_ERR:
+ case OCC_RESP_BAD_STATE:
+ case OCC_RESP_CRIT_EXCEPT:
+ case OCC_RESP_CRIT_INIT:
+ case OCC_RESP_CRIT_WATCHDOG:
+ case OCC_RESP_CRIT_OCB:
+ case OCC_RESP_CRIT_HW:
+ rc = -EREMOTEIO;
+ break;
+ default:
+ rc = -EPROTO;
+ }
+
+ if (rc < 0)
+ return rc;
+
+ data_length = get_unaligned_be16(&resp->data_length);
+ if (data_length > OCC_RESP_DATA_BYTES)
+ return -EMSGSIZE;
+
+ /* fetch the rest of the response data */
+ for (i = 8; i < data_length + 7; i += 8) {
+ rc = p8_i2c_occ_getscom(client, OCB_DATA3, ((u8 *)resp) + i);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
}

static int p8_i2c_occ_probe(struct i2c_client *client,
diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c
index 58c3bb2..be3a469 100644
--- a/drivers/hwmon/occ/p9_sbe.c
+++ b/drivers/hwmon/occ/p9_sbe.c
@@ -9,21 +9,102 @@

#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/fsi-occ.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/spinlock.h>

#include "common.h"

struct p9_sbe_occ {
struct occ occ;
struct device *sbe;
+
+ /*
+ * Pointer to occ device client. We store this so that we can cancel
+ * the client operations in remove() if necessary. We only need one
+ * pointer since we do one OCC operation (open, write, read, close) at
+ * a time (access to p9_sbe_occ_send_cmd is locked in the common code
+ * with occ.lock).
+ */
+ struct occ_client *client;
+
+ /*
+ * This lock controls access to the client pointer and ensures atomic
+ * open, close and NULL assignment. This prevents simultaneous opening
+ * and closing of the client, or closing multiple times.
+ */
+ spinlock_t lock;
};

#define to_p9_sbe_occ(x) container_of((x), struct p9_sbe_occ, occ)

+static void p9_sbe_occ_close_client(struct p9_sbe_occ *occ)
+{
+ unsigned long flags;
+ struct occ_client *tmp_client;
+
+ spin_lock_irqsave(&occ->lock, flags);
+ tmp_client = occ->client;
+ occ->client = NULL;
+ occ_drv_release(tmp_client);
+ spin_unlock_irqrestore(&occ->lock, flags);
+}
+
static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd)
{
- return -EOPNOTSUPP;
+ int rc;
+ unsigned long flags;
+ struct occ_response *resp = &occ->resp;
+ struct p9_sbe_occ *p9_sbe_occ = to_p9_sbe_occ(occ);
+
+ spin_lock_irqsave(&p9_sbe_occ->lock, flags);
+ if (p9_sbe_occ->sbe)
+ p9_sbe_occ->client = occ_drv_open(p9_sbe_occ->sbe, 0);
+ spin_unlock_irqrestore(&p9_sbe_occ->lock, flags);
+
+ if (!p9_sbe_occ->client)
+ return -ENODEV;
+
+ /* skip first byte (sequence number), OCC driver handles it */
+ rc = occ_drv_write(p9_sbe_occ->client, (const char *)&cmd[1], 7);
+ if (rc < 0)
+ goto err;
+
+ rc = occ_drv_read(p9_sbe_occ->client, (char *)resp, sizeof(*resp));
+ if (rc < 0)
+ goto err;
+
+ /* check the OCC response */
+ switch (resp->return_status) {
+ case OCC_RESP_CMD_IN_PRG:
+ rc = -ETIMEDOUT;
+ break;
+ case OCC_RESP_SUCCESS:
+ rc = 0;
+ break;
+ case OCC_RESP_CMD_INVAL:
+ case OCC_RESP_CMD_LEN_INVAL:
+ case OCC_RESP_DATA_INVAL:
+ case OCC_RESP_CHKSUM_ERR:
+ rc = -EINVAL;
+ break;
+ case OCC_RESP_INT_ERR:
+ case OCC_RESP_BAD_STATE:
+ case OCC_RESP_CRIT_EXCEPT:
+ case OCC_RESP_CRIT_INIT:
+ case OCC_RESP_CRIT_WATCHDOG:
+ case OCC_RESP_CRIT_OCB:
+ case OCC_RESP_CRIT_HW:
+ rc = -EREMOTEIO;
+ break;
+ default:
+ rc = -EPROTO;
+ }
+
+err:
+ p9_sbe_occ_close_client(p9_sbe_occ);
+ return rc;
}

static int p9_sbe_occ_probe(struct platform_device *pdev)
@@ -46,6 +127,17 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)
return occ_setup(occ, "p9_occ");
}

+static int p9_sbe_occ_remove(struct platform_device *pdev)
+{
+ struct occ *occ = platform_get_drvdata(pdev);
+ struct p9_sbe_occ *p9_sbe_occ = to_p9_sbe_occ(occ);
+
+ p9_sbe_occ->sbe = NULL;
+ p9_sbe_occ_close_client(p9_sbe_occ);
+
+ return 0;
+}
+
static const struct of_device_id p9_sbe_occ_of_match[] = {
{ .compatible = "ibm,p9-occ-hwmon" },
{ },
@@ -57,6 +149,7 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)
.of_match_table = p9_sbe_occ_of_match,
},
.probe = p9_sbe_occ_probe,
+ .remove = p9_sbe_occ_remove,
};

module_platform_driver(p9_sbe_occ_driver);
--
1.8.3.1


From 1584508092332579645@xxx Sun Nov 19 15:17:51 +0000 2017
X-GM-THRID: 1584505803560516752
X-Gmail-Labels: Inbox,Category Forums,HistoricalUnread

2017-11-20 23:56:01

by Eddie James

[permalink] [raw]
Subject: [PATCH v3 11/12] hwmon (occ): Add error handling

From: "Edward A. James" <[email protected]>

Add logic to detect a number of error scenarios on the OCC. Export any
errors through an additional non-hwmon device attribute. The error
counting and state verification are required by the OCC hardware
specification.

Signed-off-by: Edward A. James <[email protected]>
---
drivers/hwmon/occ/common.c | 55 +++++++++++++++++++++++++++++++++++++++++++++-
drivers/hwmon/occ/common.h | 4 ++++
2 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 53e3592..7a0606d 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -18,6 +18,11 @@

#include "common.h"

+#define OCC_ERROR_COUNT_THRESHOLD 2 /* OCC HW defined */
+
+#define OCC_STATE_SAFE 4
+#define OCC_SAFE_TIMEOUT msecs_to_jiffies(60000) /* 1 min */
+
#define OCC_UPDATE_FREQUENCY msecs_to_jiffies(1000)

#define OCC_TEMP_SENSOR_FAULT 0xFF
@@ -131,10 +136,22 @@ struct extended_sensor {
u8 data[6];
} __packed;

+static ssize_t occ_show_error(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct occ *occ = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE - 1, "%d\n", occ->error);
+}
+
+static DEVICE_ATTR(occ_error, 0444, occ_show_error, NULL);
+
static int occ_poll(struct occ *occ)
{
+ struct occ_poll_response_header *header;
u16 checksum = occ->poll_cmd_data + 1;
u8 cmd[8];
+ int rc;

/* big endian */
cmd[0] = 0; /* sequence number */
@@ -147,7 +164,33 @@ static int occ_poll(struct occ *occ)
cmd[7] = 0;

/* mutex should already be locked if necessary */
- return occ->send_cmd(occ, cmd);
+ rc = occ->send_cmd(occ, cmd);
+ if (rc) {
+ if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
+ occ->error = rc;
+
+ return rc;
+ }
+
+ /* clear error since communication was successful */
+ occ->error_count = 0;
+ occ->error = 0;
+
+ /* check for safe state */
+ header = (struct occ_poll_response_header *)occ->resp.data;
+ if (header->occ_state == OCC_STATE_SAFE) {
+ if (occ->last_safe) {
+ if (time_after(jiffies,
+ occ->last_safe + OCC_SAFE_TIMEOUT))
+ occ->error = -EHOSTDOWN;
+ } else {
+ occ->last_safe = jiffies;
+ }
+ } else {
+ occ->last_safe = 0;
+ }
+
+ return 0;
}

static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
@@ -176,6 +219,15 @@ static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)

mutex_unlock(&occ->lock);

+ if (rc) {
+ if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
+ occ->error = rc;
+ } else {
+ /* successful communication so clear the error */
+ occ->error_count = 0;
+ occ->error = 0;
+ }
+
return rc;
}

@@ -1184,6 +1236,7 @@ static ssize_t occ_show_status(struct device *dev,
&sensor_dev_attr_occ_quick_drop.dev_attr.attr,
&sensor_dev_attr_occ_status.dev_attr.attr,
&sensor_dev_attr_occs_present.dev_attr.attr,
+ &dev_attr_occ_error.attr,
NULL
};

diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index dc9e06d..cef2174 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -107,6 +107,10 @@ struct occ {
struct occ_attribute *attrs;
struct attribute_group group;
const struct attribute_group *groups[2];
+
+ int error;
+ unsigned int error_count; /* number of errors observed */
+ unsigned long last_safe; /* time OCC entered safe state */
};

int occ_setup(struct occ *occ, const char *name);
--
1.8.3.1


From 1584367660514316366@xxx Sat Nov 18 02:05:45 +0000 2017
X-GM-THRID: 1584367660514316366
X-Gmail-Labels: Inbox,Category Forums,HistoricalUnread

2017-11-20 23:55:10

by Eddie James

[permalink] [raw]
Subject: [PATCH v3 12/12] hwmon (occ): Add sysfs notification for errors and throttling

From: "Edward A. James" <[email protected]>

In order to aid application usage of the error, throttling, and
presence count properties, use sysfs_notify to notify users of change on
these attributes.

Signed-off-by: Edward A. James <[email protected]>
---
drivers/hwmon/occ/common.c | 53 ++++++++++++++++++++++++++++++++++++++++++++--
drivers/hwmon/occ/common.h | 5 +++++
2 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 7a0606d..7c97a4c 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -146,6 +146,8 @@ static ssize_t occ_show_error(struct device *dev,

static DEVICE_ATTR(occ_error, 0444, occ_show_error, NULL);

+static void occ_sysfs_notify(struct occ *occ);
+
static int occ_poll(struct occ *occ)
{
struct occ_poll_response_header *header;
@@ -169,7 +171,7 @@ static int occ_poll(struct occ *occ)
if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
occ->error = rc;

- return rc;
+ goto done;
}

/* clear error since communication was successful */
@@ -190,7 +192,9 @@ static int occ_poll(struct occ *occ)
occ->last_safe = 0;
}

- return 0;
+done:
+ occ_sysfs_notify(occ);
+ return rc;
}

static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
@@ -1244,6 +1248,51 @@ static ssize_t occ_show_status(struct device *dev,
.attrs = occ_attributes,
};

+static void occ_sysfs_notify(struct occ *occ)
+{
+ const char *name;
+ struct occ_poll_response_header *header =
+ (struct occ_poll_response_header *)occ->resp.data;
+
+ /* sysfs attributes aren't loaded yet; don't proceed */
+ if (!occ->hwmon)
+ goto done;
+
+ if (header->occs_present != occ->previous_occs_present &&
+ (header->status & OCC_STAT_MASTER)) {
+ name = sensor_dev_attr_occs_present.dev_attr.attr.name;
+ sysfs_notify(&occ->bus_dev->kobj, NULL, name);
+ }
+
+ if ((header->ext_status & OCC_EXT_STAT_DVFS_OT) !=
+ (occ->previous_ext_status & OCC_EXT_STAT_DVFS_OT)) {
+ name = sensor_dev_attr_occ_dvfs_ot.dev_attr.attr.name;
+ sysfs_notify(&occ->bus_dev->kobj, NULL, name);
+ }
+
+ if ((header->ext_status & OCC_EXT_STAT_DVFS_POWER) !=
+ (occ->previous_ext_status & OCC_EXT_STAT_DVFS_POWER)) {
+ name = sensor_dev_attr_occ_dvfs_power.dev_attr.attr.name;
+ sysfs_notify(&occ->bus_dev->kobj, NULL, name);
+ }
+
+ if ((header->ext_status & OCC_EXT_STAT_MEM_THROTTLE) !=
+ (occ->previous_ext_status & OCC_EXT_STAT_MEM_THROTTLE)) {
+ name = sensor_dev_attr_occ_mem_throttle.dev_attr.attr.name;
+ sysfs_notify(&occ->bus_dev->kobj, NULL, name);
+ }
+
+ if (occ->error && occ->error != occ->previous_error) {
+ name = dev_attr_occ_error.attr.name;
+ sysfs_notify(&occ->bus_dev->kobj, NULL, name);
+ }
+
+done:
+ occ->previous_error = occ->error;
+ occ->previous_ext_status = header->ext_status;
+ occ->previous_occs_present = header->occs_present;
+}
+
/* only need to do this once at startup, as OCC won't change sensors on us */
static void occ_parse_poll_response(struct occ *occ)
{
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index cef2174..ffc809f 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -111,6 +111,11 @@ struct occ {
int error;
unsigned int error_count; /* number of errors observed */
unsigned long last_safe; /* time OCC entered safe state */
+
+ /* store previous poll state to compare; notify sysfs on change */
+ int previous_error;
+ u8 previous_ext_status;
+ u8 previous_occs_present;
};

int occ_setup(struct occ *occ, const char *name);
--
1.8.3.1


From 1584157802944265030@xxx Wed Nov 15 18:30:09 +0000 2017
X-GM-THRID: 1584157802944265030
X-Gmail-Labels: Inbox,Category Forums,HistoricalUnread