Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1698029 > unrolled thread
| Started by | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| First post | 2017-07-27 16:40 +0200 |
| Last post | 2017-07-27 16:40 +0200 |
| Articles | 7 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH v2 00/10] drivers/hwmon: Add On-Chip Controller (OCC) hwmon driver Eddie James <eajames@linux.vnet.ibm.com> - 2017-07-27 16:40 +0200
[PATCH v2 01/10] drivers/hwmon: Add On-Chip Controller (OCC) hwmon driver Eddie James <eajames@linux.vnet.ibm.com> - 2017-07-27 16:40 +0200
[PATCH v2 02/10] drivers/hwmon/occ: Add command transport method for P8 and P9 Eddie James <eajames@linux.vnet.ibm.com> - 2017-07-27 16:40 +0200
[PATCH v2 07/10] drivers/hwmon/occ: Add error handling Eddie James <eajames@linux.vnet.ibm.com> - 2017-07-27 16:40 +0200
[PATCH v2 03/10] drivers/hwmon/occ: Parse OCC poll response Eddie James <eajames@linux.vnet.ibm.com> - 2017-07-27 16:40 +0200
[PATCH v2 09/10] Documentation: ABI: Add occ-hwmon driver sysfs documentation Eddie James <eajames@linux.vnet.ibm.com> - 2017-07-27 16:40 +0200
[PATCH v2 10/10] dt-bindings: i2c: Add P8 OCC hwmon driver documentation Eddie James <eajames@linux.vnet.ibm.com> - 2017-07-27 16:40 +0200
| From | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-07-27 16:40 +0200 |
| Subject | [PATCH v2 00/10] drivers/hwmon: Add On-Chip Controller (OCC) hwmon driver |
| Message-ID | <u7S7f-7g8-3@gated-at.bofh.it> |
From: "Edward A. James" <eajames@us.ibm.com> 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/7/5/633). 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 (10): drivers/hwmon: Add On-Chip Controller (OCC) hwmon driver drivers/hwmon/occ: Add command transport method for P8 and P9 drivers/hwmon/occ: Parse OCC poll response drivers/hwmon/occ: Add sensor types and versions drivers/hwmon/occ: Add sensor attributes and register hwmon device drivers/hwmon/occ: Add non-hwmon attributes drivers/hwmon/occ: Add error handling Documentation: hwmon: Add OCC documentation Documentation: ABI: Add occ-hwmon driver sysfs documentation dt-bindings: i2c: Add P8 OCC hwmon driver documentation Documentation/ABI/testing/sysfs-driver-occ-hwmon | 77 ++ .../devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt | 25 + Documentation/hwmon/occ | 74 ++ drivers/hwmon/Kconfig | 2 + drivers/hwmon/Makefile | 1 + drivers/hwmon/occ/Kconfig | 28 + drivers/hwmon/occ/Makefile | 11 + drivers/hwmon/occ/common.c | 1289 ++++++++++++++++++++ drivers/hwmon/occ/common.h | 119 ++ drivers/hwmon/occ/p8_i2c.c | 261 ++++ drivers/hwmon/occ/p9_sbe.c | 124 ++ 11 files changed, 2011 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-occ-hwmon 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
[toc] | [next] | [standalone]
| From | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-07-27 16:40 +0200 |
| Subject | [PATCH v2 01/10] drivers/hwmon: Add On-Chip Controller (OCC) hwmon driver |
| Message-ID | <u7S7f-7g8-15@gated-at.bofh.it> |
| In reply to | #1698029 |
From: "Edward A. James" <eajames@us.ibm.com>
The OCC is a device embedded on a POWER processor that collects and
aggregates sensor data from the processor and system. The OCC can
provide the raw sensor data as well as perform thermal and power
management on the system.
This driver provides a hwmon interface to the OCC from a service
processor (e.g. a BMC). The driver supports both POWER8 and POWER9 OCCs.
Communications with the POWER8 OCC are established over standard I2C
bus. The driver communicates with the POWER9 OCC through the FSI-based
OCC driver, which handles the lower-level communication details.
This patch lays out the structure of the OCC hwmon driver. There are two
platform drivers, one each for P8 and P9 OCCs. These are probed through
the I2C tree and the FSI-based OCC driver, respectively. The patch also
defines the first common structures and methods between the two OCC
versions.
Signed-off-by: Edward A. James <eajames@us.ibm.com>
---
drivers/hwmon/Kconfig | 2 ++
drivers/hwmon/Makefile | 1 +
drivers/hwmon/occ/Kconfig | 28 +++++++++++++++++++
drivers/hwmon/occ/Makefile | 11 ++++++++
drivers/hwmon/occ/common.c | 44 +++++++++++++++++++++++++++++
drivers/hwmon/occ/common.h | 40 +++++++++++++++++++++++++++
drivers/hwmon/occ/p8_i2c.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/hwmon/occ/p9_sbe.c | 66 ++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 261 insertions(+)
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
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5ef2814..08add7b 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1250,6 +1250,8 @@ config SENSORS_NSA320
This driver can also be built as a module. If so, the module
will be called nsa320-hwmon.
+source drivers/hwmon/occ/Kconfig
+
config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index d4641a9..0b342d0 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -170,6 +170,7 @@ obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
+obj-$(CONFIG_SENSORS_OCC) += occ/
obj-$(CONFIG_PMBUS) += pmbus/
ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
diff --git a/drivers/hwmon/occ/Kconfig b/drivers/hwmon/occ/Kconfig
new file mode 100644
index 0000000..0501280
--- /dev/null
+++ b/drivers/hwmon/occ/Kconfig
@@ -0,0 +1,28 @@
+#
+# On-Chip Controller configuration
+#
+
+config SENSORS_OCC
+ tristate "POWER On-Chip Controller"
+ ---help---
+ This option enables support for monitoring a variety of system sensors
+ provided by the On-Chip Controller (OCC) on a POWER processor.
+
+ This driver can also be built as a module. If so, the module will be
+ called occ-hwmon.
+
+config SENSORS_OCC_P8_I2C
+ bool "POWER8 OCC through I2C"
+ depends on I2C && SENSORS_OCC
+ ---help---
+ This option enables support for monitoring sensors provided by the OCC
+ on a POWER8 processor. Communications with the OCC are established
+ through I2C bus.
+
+config SENSORS_OCC_P9_SBE
+ bool "POWER9 OCC through SBE"
+ depends on OCCFIFO && SENSORS_OCC
+ ---help---
+ This option enables support for monitoring sensors provided by the OCC
+ on a POWER9 processor. Communications with the OCC are established
+ through SBE engine on an FSI bus.
diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
new file mode 100644
index 0000000..ab5c3e9
--- /dev/null
+++ b/drivers/hwmon/occ/Makefile
@@ -0,0 +1,11 @@
+occ-hwmon-objs := common.o
+
+ifeq ($(CONFIG_SENSORS_OCC_P9_SBE), y)
+occ-hwmon-objs += p9_sbe.o
+endif
+
+ifeq ($(CONFIG_SENSORS_OCC_P8_I2C), y)
+occ-hwmon-objs += p8_i2c.o
+endif
+
+obj-$(CONFIG_SENSORS_OCC) += occ-hwmon.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
new file mode 100644
index 0000000..92f06ae
--- /dev/null
+++ b/drivers/hwmon/occ/common.c
@@ -0,0 +1,44 @@
+/*
+ * 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/device.h>
+
+#include "common.h"
+
+static int occ_poll(struct occ *occ)
+{
+ u16 checksum = occ->poll_cmd_data + 1;
+ u8 cmd[8];
+
+ /* big endian */
+ cmd[0] = 0; /* sequence number */
+ cmd[1] = 0; /* cmd type */
+ cmd[2] = 0; /* data length msb */
+ cmd[3] = 1; /* data length lsb */
+ cmd[4] = occ->poll_cmd_data; /* data */
+ cmd[5] = checksum >> 8; /* checksum msb */
+ cmd[6] = checksum & 0xFF; /* checksum lsb */
+ cmd[7] = 0;
+
+ return occ->send_cmd(occ, cmd);
+}
+
+int occ_setup(struct occ *occ, const char *name)
+{
+ int rc;
+
+ rc = occ_poll(occ);
+ if (rc < 0) {
+ dev_err(occ->bus_dev, "failed to get OCC poll response: %d\n",
+ rc);
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
new file mode 100644
index 0000000..705dfe3
--- /dev/null
+++ b/drivers/hwmon/occ/common.h
@@ -0,0 +1,40 @@
+/*
+ * 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_COMMON_H
+#define OCC_COMMON_H
+
+struct device;
+
+#define OCC_RESP_DATA_BYTES 4089
+
+/* Same response format for all OCC versions.
+ * Allocate the largest possible response.
+ */
+struct occ_response {
+ u8 seq_no;
+ u8 cmd_type;
+ u8 return_status;
+ __be16 data_length;
+ u8 data[OCC_RESP_DATA_BYTES];
+ __be16 checksum;
+} __packed;
+
+struct occ {
+ struct device *bus_dev;
+
+ struct occ_response resp;
+
+ u8 poll_cmd_data; /* to perform OCC poll command */
+ int (*send_cmd)(struct occ *occ, u8 *cmd);
+};
+
+int occ_setup(struct occ *occ, const char *name);
+
+#endif /* OCC_COMMON_H */
diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
new file mode 100644
index 0000000..025471f
--- /dev/null
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -0,0 +1,69 @@
+/*
+ * 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/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#include "common.h"
+
+struct p8_i2c_occ {
+ struct occ occ;
+ struct i2c_client *client;
+};
+
+#define to_p8_i2c_occ(x) container_of((x), struct p8_i2c_occ, occ)
+
+static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd)
+{
+ return -EOPNOTSUPP;
+}
+
+static int p8_i2c_occ_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct occ *occ;
+ struct p8_i2c_occ *p8_i2c_occ = devm_kzalloc(&client->dev,
+ sizeof(*p8_i2c_occ),
+ GFP_KERNEL);
+ if (!p8_i2c_occ)
+ return -ENOMEM;
+
+ p8_i2c_occ->client = client;
+ occ = &p8_i2c_occ->occ;
+ occ->bus_dev = &client->dev;
+ dev_set_drvdata(&client->dev, occ);
+
+ occ->poll_cmd_data = 0x10; /* P8 OCC poll data */
+ occ->send_cmd = p8_i2c_occ_send_cmd;
+
+ return occ_setup(occ, "p8_occ");
+}
+
+static const struct of_device_id p8_i2c_occ_of_match[] = {
+ { .compatible = "ibm,p8-occ-hwmon" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, p8_i2c_occ_of_match);
+
+static struct i2c_driver p8_i2c_occ_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "occ-hwmon",
+ .of_match_table = p8_i2c_occ_of_match,
+ },
+ .probe = p8_i2c_occ_probe,
+};
+
+module_i2c_driver(p8_i2c_occ_driver);
+
+MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
+MODULE_DESCRIPTION("BMC P8 OCC hwmon driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c
new file mode 100644
index 0000000..58c3bb2
--- /dev/null
+++ b/drivers/hwmon/occ/p9_sbe.c
@@ -0,0 +1,66 @@
+/*
+ * 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/device.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "common.h"
+
+struct p9_sbe_occ {
+ struct occ occ;
+ struct device *sbe;
+};
+
+#define to_p9_sbe_occ(x) container_of((x), struct p9_sbe_occ, occ)
+
+static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd)
+{
+ return -EOPNOTSUPP;
+}
+
+static int p9_sbe_occ_probe(struct platform_device *pdev)
+{
+ struct occ *occ;
+ struct p9_sbe_occ *p9_sbe_occ = devm_kzalloc(&pdev->dev,
+ sizeof(*p9_sbe_occ),
+ GFP_KERNEL);
+ if (!p9_sbe_occ)
+ return -ENOMEM;
+
+ p9_sbe_occ->sbe = pdev->dev.parent;
+ occ = &p9_sbe_occ->occ;
+ occ->bus_dev = &pdev->dev;
+ platform_set_drvdata(pdev, occ);
+
+ occ->poll_cmd_data = 0x20; /* P9 OCC poll data */
+ occ->send_cmd = p9_sbe_occ_send_cmd;
+
+ return occ_setup(occ, "p9_occ");
+}
+
+static const struct of_device_id p9_sbe_occ_of_match[] = {
+ { .compatible = "ibm,p9-occ-hwmon" },
+ { },
+};
+
+static struct platform_driver p9_sbe_occ_driver = {
+ .driver = {
+ .name = "occ-hwmon",
+ .of_match_table = p9_sbe_occ_of_match,
+ },
+ .probe = p9_sbe_occ_probe,
+};
+
+module_platform_driver(p9_sbe_occ_driver);
+
+MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
+MODULE_DESCRIPTION("BMC P9 OCC hwmon driver");
+MODULE_LICENSE("GPL");
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-07-27 16:40 +0200 |
| Subject | [PATCH v2 02/10] drivers/hwmon/occ: Add command transport method for P8 and P9 |
| Message-ID | <u7S7f-7g8-23@gated-at.bofh.it> |
| In reply to | #1698029 |
From: "Edward A. James" <eajames@us.ibm.com>
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 <eajames@us.ibm.com>
---
drivers/hwmon/occ/p8_i2c.c | 184 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/hwmon/occ/p9_sbe.c | 50 +++++++++++-
2 files changed, 232 insertions(+), 2 deletions(-)
diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
index 025471f..745fb52 100644
--- a/drivers/hwmon/occ/p8_i2c.c
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -11,9 +11,26 @@
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/occ.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 +38,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..667d190 100644
--- a/drivers/hwmon/occ/p9_sbe.c
+++ b/drivers/hwmon/occ/p9_sbe.c
@@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/module.h>
+#include <linux/occ.h>
#include <linux/platform_device.h>
#include "common.h"
@@ -23,7 +24,54 @@ struct p9_sbe_occ {
static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd)
{
- return -EOPNOTSUPP;
+ int rc;
+ struct occ_client *client;
+ struct occ_response *resp = &occ->resp;
+ struct p9_sbe_occ *p9_sbe_occ = to_p9_sbe_occ(occ);
+
+ client = occ_drv_open(p9_sbe_occ->sbe, 0);
+ if (!client)
+ return -ENODEV;
+
+ /* skip first byte (sequence number), OCC driver handles it */
+ rc = occ_drv_write(client, (const char *)&cmd[1], 7);
+ if (rc < 0)
+ goto err;
+
+ rc = occ_drv_read(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:
+ occ_drv_release(client);
+ return rc;
}
static int p9_sbe_occ_probe(struct platform_device *pdev)
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-07-27 16:40 +0200 |
| Subject | [PATCH v2 07/10] drivers/hwmon/occ: Add error handling |
| Message-ID | <u7S7g-7g8-27@gated-at.bofh.it> |
| In reply to | #1698029 |
From: "Edward A. James" <eajames@us.ibm.com>
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 <eajames@us.ibm.com>
---
drivers/hwmon/occ/common.c | 82 ++++++++++++++++++++++++++++++++++++++++++++--
drivers/hwmon/occ/common.h | 4 +++
2 files changed, 84 insertions(+), 2 deletions(-)
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 2459e12..970b31f 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -17,6 +17,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)
/* OCC status bits */
@@ -126,10 +131,33 @@ struct extended_sensor {
u8 data[6];
} __packed;
+static atomic_t occs_present = ATOMIC_INIT(0);
+
+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);
+
+/* Notify user if we have an error and a change in error state. */
+static void occ_notify_error(struct occ *occ, int old_error)
+{
+ /* check hwmon pointer to verify error attribute has been added */
+ if (occ->error != old_error && occ->error && occ->hwmon)
+ sysfs_notify(&occ->bus_dev->kobj, NULL,
+ dev_attr_occ_error.attr.name);
+}
+
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, old_error = occ->error;
/* big endian */
cmd[0] = 0; /* sequence number */
@@ -142,12 +170,47 @@ 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;
+
+ header = (struct occ_poll_response_header *)occ->resp.data;
+ /* check for safe state */
+ 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;
+ }
+
+ if (header->status & OCC_STAT_MASTER) {
+ /* check if we're missing any OCCs */
+ if (hweight8(header->occs_present) !=
+ atomic_read(&occs_present))
+ occ->error = -ENXIO;
+ }
+
+done:
+ occ_notify_error(occ, old_error);
+ return rc;
}
static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
{
- int rc;
+ int rc, old_error = occ->error;
u8 cmd[8];
u16 checksum = 0x24;
__be16 user_power_cap_be = cpu_to_be16(user_power_cap);
@@ -171,6 +234,16 @@ 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;
+ }
+
+ occ_notify_error(occ, old_error);
return rc;
}
@@ -1110,6 +1183,7 @@ static ssize_t occ_show_status(struct device *dev,
&sensor_dev_attr_occ_mem_throttle.dev_attr.attr,
&sensor_dev_attr_occ_quick_drop.dev_attr.attr,
&sensor_dev_attr_occ_status.dev_attr.attr,
+ &dev_attr_occ_error.attr,
NULL
};
@@ -1197,6 +1271,8 @@ int occ_setup(struct occ *occ, const char *name)
return rc;
}
+ atomic_inc(&occs_present);
+
rc = sysfs_create_group(&occ->bus_dev->kobj, &occ_attr_group);
if (rc)
dev_warn(occ->bus_dev, "failed to create status attrs: %d\n",
@@ -1208,4 +1284,6 @@ int occ_setup(struct occ *occ, const char *name)
void occ_shutdown(struct occ *occ)
{
sysfs_remove_group(&occ->bus_dev->kobj, &occ_attr_group);
+
+ atomic_dec(&occs_present);
}
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
[toc] | [prev] | [next] | [standalone]
| From | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-07-27 16:40 +0200 |
| Subject | [PATCH v2 03/10] drivers/hwmon/occ: Parse OCC poll response |
| Message-ID | <u7S7g-7g8-39@gated-at.bofh.it> |
| In reply to | #1698029 |
From: "Edward A. James" <eajames@us.ibm.com>
Add method to parse the response from the OCC poll command. This only
needs to be done during probe(), since the OCC shouldn't change the
number or format of sensors while it's running. The parsed response
allows quick access to sensor data, as well as information on the
number and version of sensors, which we need to instantiate hwmon
attributes.
Signed-off-by: Edward A. James <eajames@us.ibm.com>
---
drivers/hwmon/occ/common.c | 50 ++++++++++++++++++++++++++++++++++++++++++
drivers/hwmon/occ/common.h | 54 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 104 insertions(+)
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 92f06ae..c55aec0 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -8,6 +8,7 @@
*/
#include <linux/device.h>
+#include <linux/kernel.h>
#include "common.h"
@@ -29,6 +30,53 @@ static int occ_poll(struct occ *occ)
return occ->send_cmd(occ, cmd);
}
+/* 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)
+{
+ unsigned int i, offset = 0, size = 0;
+ struct occ_sensor *sensor;
+ struct occ_sensors *sensors = &occ->sensors;
+ struct occ_response *resp = &occ->resp;
+ struct occ_poll_response *poll =
+ (struct occ_poll_response *)&resp->data[0];
+ struct occ_poll_response_header *header = &poll->header;
+ struct occ_sensor_data_block *block = &poll->block;
+
+ for (i = 0; i < header->num_sensor_data_blocks; ++i) {
+ block = (struct occ_sensor_data_block *)((u8 *)block + offset);
+ offset = (block->header.num_sensors *
+ block->header.sensor_length) + sizeof(block->header);
+ size += offset;
+
+ /* validate all the length/size fields */
+ if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
+ dev_warn(occ->bus_dev, "exceeded response buffer\n");
+ return;
+ }
+
+ /* match sensor block type */
+ if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0)
+ sensor = &sensors->temp;
+ else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0)
+ sensor = &sensors->freq;
+ else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0)
+ sensor = &sensors->power;
+ else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0)
+ sensor = &sensors->caps;
+ else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0)
+ sensor = &sensors->extended;
+ else {
+ dev_warn(occ->bus_dev, "sensor not supported %.4s\n",
+ block->header.eye_catcher);
+ continue;
+ }
+
+ sensor->num_sensors = block->header.num_sensors;
+ sensor->version = block->header.sensor_format;
+ sensor->data = &block->data;
+ }
+}
+
int occ_setup(struct occ *occ, const char *name)
{
int rc;
@@ -40,5 +88,7 @@ int occ_setup(struct occ *occ, const char *name)
return rc;
}
+ occ_parse_poll_response(occ);
+
return 0;
}
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index 705dfe3..f52d45a 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -26,10 +26,64 @@ struct occ_response {
__be16 checksum;
} __packed;
+struct occ_sensor_data_block_header {
+ u8 eye_catcher[4];
+ u8 reserved;
+ u8 sensor_format;
+ u8 sensor_length;
+ u8 num_sensors;
+} __packed;
+
+struct occ_sensor_data_block {
+ struct occ_sensor_data_block_header header;
+ u32 data;
+} __packed;
+
+struct occ_poll_response_header {
+ u8 status;
+ u8 ext_status;
+ u8 occs_present;
+ u8 config_data;
+ u8 occ_state;
+ u8 mode;
+ u8 ips_status;
+ u8 error_log_id;
+ __be32 error_log_start_address;
+ __be16 error_log_length;
+ u16 reserved;
+ u8 occ_code_level[16];
+ u8 eye_catcher[6];
+ u8 num_sensor_data_blocks;
+ u8 sensor_data_block_header_version;
+} __packed;
+
+struct occ_poll_response {
+ struct occ_poll_response_header header;
+ struct occ_sensor_data_block block;
+} __packed;
+
+struct occ_sensor {
+ u8 num_sensors;
+ u8 version;
+ void *data; /* pointer to sensor data start within response */
+};
+
+/* OCC only provides one sensor data block of each type, but any number of
+ * sensors within that block.
+ */
+struct occ_sensors {
+ struct occ_sensor temp;
+ struct occ_sensor freq;
+ struct occ_sensor power;
+ struct occ_sensor caps;
+ struct occ_sensor extended;
+};
+
struct occ {
struct device *bus_dev;
struct occ_response resp;
+ struct occ_sensors sensors;
u8 poll_cmd_data; /* to perform OCC poll command */
int (*send_cmd)(struct occ *occ, u8 *cmd);
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-07-27 16:40 +0200 |
| Subject | [PATCH v2 09/10] Documentation: ABI: Add occ-hwmon driver sysfs documentation |
| Message-ID | <u7S7g-7g8-41@gated-at.bofh.it> |
| In reply to | #1698029 |
From: "Edward A. James" <eajames@us.ibm.com> Detail the sysfs attributes provided by the occ-hwmon driver. Signed-off-by: Edward A. James <eajames@us.ibm.com> --- Documentation/ABI/testing/sysfs-driver-occ-hwmon | 77 ++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-occ-hwmon diff --git a/Documentation/ABI/testing/sysfs-driver-occ-hwmon b/Documentation/ABI/testing/sysfs-driver-occ-hwmon new file mode 100644 index 0000000..5563128 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-occ-hwmon @@ -0,0 +1,77 @@ +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_active +Date: July 2017 +KernelVersion: 4.14 +Contact: eajames@us.ibm.com +Description: + A read-only attribute that indicates (with a "1" or a "0", + respectively) whether or not this OCC is in the "active" state. + +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_dvfs_ot +Date: July 2017 +KernelVersion: 4.14 +Contact: eajames@us.ibm.com +Description: + A read-only attribute that indicates (with a "1" or a "0", + respectively) whether or not this OCC has limited the processor + frequency due to over-temperature. + +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_dvfs_power +Date: July 2017 +KernelVersion: 4.14 +Contact: eajames@us.ibm.com +Description: + A read-only attribute that indicates (with a "1" or a "0", + respectively) whether or not this OCC has limited the processor + frequency due to power usage. + +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_error +Date: July 2017 +KernelVersion: 4.14 +Contact: eajames@us.ibm.com +Description: + A read-only attribute that indicates any error condition + observed by the OCC or detected by the driver. Reading the + attribute will return an integer. A negative integer indicates + either an error response from the OCC or bus error or other + error condition detected by the driver. A "0" indicates no + error. + +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_master +Date: July 2017 +KernelVersion: 4.14 +Contact: eajames@us.ibm.com +Description: + A read-only attribute that indicates (with a "1" or a "0", + respectively) whether or not this OCC is the "master" OCC. + +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_mem_throttle +Date: July 2017 +KernelVersion: 4.14 +Contact: eajames@us.ibm.com +Description: + A read-only attribute that indicates (with a "1" or a "0", + respectively) whether or not the OCC has throttled memory due + to over-temperature. + +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_quick_drop +Date: July 2017 +KernelVersion: 4.14 +Contact: eajames@us.ibm.com +Description: + A read-only attribute that indicates (with a "1" or a "0", + respectively) whether or not this OCC has asserted the "quick + power drop" signal. + +What: /sys/bus/platform/drivers/occ-hwmon/<dev>/occ_status +Date: July 2017 +KernelVersion: 4.14 +Contact: eajames@us.ibm.com +Description: + A read-only attribute that indicates the current OCC state. The + value of the attribute will be one of the following states: + 0: Reserved + 1: Standby + 2: Observation + 3: Active + 4: Safe + 5: Characterization -- 1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Eddie James <eajames@linux.vnet.ibm.com> |
|---|---|
| Date | 2017-07-27 16:40 +0200 |
| Subject | [PATCH v2 10/10] dt-bindings: i2c: Add P8 OCC hwmon driver documentation |
| Message-ID | <u7S7g-7g8-43@gated-at.bofh.it> |
| In reply to | #1698029 |
From: "Edward A. James" <eajames@us.ibm.com>
Document the bindings for I2C-based OCC hwmon driver.
Signed-off-by: Edward A. James <eajames@us.ibm.com>
---
.../devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt | 25 ++++++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
diff --git a/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt b/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
new file mode 100644
index 0000000..8c765d0
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt
@@ -0,0 +1,25 @@
+Device-tree bindings for I2C-based On-Chip Controller hwmon driver
+------------------------------------------------------------------
+
+Required properties:
+ - compatible = "ibm,p8-occ-hwmon";
+ - reg = <I2C address>; : I2C bus address
+
+Examples:
+
+ i2c-bus@100 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clock-frequency = <100000>;
+ < more properties >
+
+ occ-hwmon@1 {
+ compatible = "ibm,p8-occ-hwmon";
+ reg = <0x50>;
+ };
+
+ occ-hwmon@2 {
+ compatible = "ibm,p8-occ-hwmon";
+ reg = <0x51>;
+ };
+ };
--
1.8.3.1
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web