summaryrefslogtreecommitdiff
path: root/drivers/hwmon/occ/common.c
diff options
context:
space:
mode:
authorEddie James <eajames@linux.vnet.ibm.com>2018-11-09 00:05:29 +0300
committerGuenter Roeck <linux@roeck-us.net>2018-12-17 02:13:10 +0300
commitdf04ced684d48f6ec5729ccd034702952160c6b3 (patch)
treec036e0a54bcbaf44c0d1a973646b3070f9623e69 /drivers/hwmon/occ/common.c
parent54076cb3b5ff21f6474f5fd254a0b018b24771df (diff)
downloadlinux-df04ced684d48f6ec5729ccd034702952160c6b3.tar.xz
hwmon (occ): Add sysfs attributes for additional OCC data
The OCC provides a variety of additional information about the state of the host processor, such as throttling, error conditions, and the number of OCCs detected in the system. This information is essential to service processor applications such as fan control and host management. Therefore, export this data in the form of sysfs attributes attached to the platform device (to which the hwmon device is also attached). Signed-off-by: Eddie James <eajames@linux.ibm.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers/hwmon/occ/common.c')
-rw-r--r--drivers/hwmon/occ/common.c45
1 files changed, 42 insertions, 3 deletions
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index c6c816135f67..423903f87955 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -14,6 +14,11 @@
#define EXTN_FLAG_SENSOR_ID BIT(7)
+#define OCC_ERROR_COUNT_THRESHOLD 2 /* required by OCC spec */
+
+#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
@@ -115,8 +120,10 @@ struct extended_sensor {
static int occ_poll(struct occ *occ)
{
+ int rc;
u16 checksum = occ->poll_cmd_data + 1;
u8 cmd[8];
+ struct occ_poll_response_header *header;
/* big endian */
cmd[0] = 0; /* sequence number */
@@ -129,7 +136,35 @@ 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;
+
+ goto done;
+ }
+
+ /* 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;
+ }
+
+done:
+ occ_sysfs_poll_done(occ);
+ return rc;
}
static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
@@ -161,7 +196,7 @@ static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
return rc;
}
-static int occ_update_response(struct occ *occ)
+int occ_update_response(struct occ *occ)
{
int rc = mutex_lock_interruptible(&occ->lock);
@@ -1055,5 +1090,9 @@ int occ_setup(struct occ *occ, const char *name)
return rc;
}
- return 0;
+ rc = occ_setup_sysfs(occ);
+ if (rc)
+ dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
+
+ return rc;
}