All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8
@ 2017-06-09 18:01 Eddie James
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 1/5] drivers: fsi: occ: Fix 1 byte response and rc for O_NONBLOCK Eddie James
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Eddie James @ 2017-06-09 18:01 UTC (permalink / raw)
  To: openbmc; +Cc: joel, Edward A. James

From: "Edward A. James" <eajames@us.ibm.com>

This series refactors the OCC hwmon driver to make a number of changes and bug
fixes. It also includes bug fixes for the FSI OCC driver. This includes 3
patches that were previously sent individually on the openbmc mailing list.

Edward A. James (5):
  drivers: fsi: occ: Fix 1 byte response and rc for O_NONBLOCK
  drivers: hwmon: occ: Add new sensor versions and non-hwmon attributes
  drivers: hwmon: occ: Add P8 OCC access
  drivers: occ: hwmon and fsi probing fix
  aspeed: update power9 dts and defconfigs for OCC

 arch/arm/boot/dts/ibm-power9-cfam.dtsi |   9 +
 arch/arm/configs/aspeed_g4_defconfig   |   2 +
 arch/arm/configs/aspeed_g5_defconfig   |   1 +
 drivers/fsi/fsi-sbefifo.c              |   4 +-
 drivers/fsi/occ.c                      |  22 +-
 drivers/hwmon/Makefile                 |   2 +-
 drivers/hwmon/occ/Kconfig              |  19 +-
 drivers/hwmon/occ/Makefile             |  12 +-
 drivers/hwmon/occ/common.c             | 991 ++++++++++++++++++++++++++++++++-
 drivers/hwmon/occ/common.h             |  17 +
 drivers/hwmon/occ/p8_i2c.c             | 270 +++++++++
 drivers/hwmon/occ/p9.c                 | 350 ------------
 drivers/hwmon/occ/p9.h                 |  17 -
 drivers/hwmon/occ/p9_sbe.c             |  60 +-
 14 files changed, 1354 insertions(+), 422 deletions(-)
 create mode 100644 drivers/hwmon/occ/p8_i2c.c
 delete mode 100644 drivers/hwmon/occ/p9.c
 delete mode 100644 drivers/hwmon/occ/p9.h

-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH linux dev-4.10 v2 1/5] drivers: fsi: occ: Fix 1 byte response and rc for O_NONBLOCK
  2017-06-09 18:01 [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8 Eddie James
@ 2017-06-09 18:01 ` Eddie James
  2017-06-16  5:11   ` Joel Stanley
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 2/5] drivers: hwmon: occ: Add new sensor versions and non-hwmon attributes Eddie James
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 9+ messages in thread
From: Eddie James @ 2017-06-09 18:01 UTC (permalink / raw)
  To: openbmc; +Cc: joel, Edward A. James

From: "Edward A. James" <eajames@us.ibm.com>

Couple of bug fixes.

Signed-off-by: Edward A. James <eajames@us.ibm.com>
---
 drivers/fsi/occ.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/fsi/occ.c b/drivers/fsi/occ.c
index fc014b6..a1508f2 100644
--- a/drivers/fsi/occ.c
+++ b/drivers/fsi/occ.c
@@ -188,7 +188,7 @@ static ssize_t occ_read_common(struct occ_client *client, char __user *ubuf,
 
 	if (!test_bit(XFR_COMPLETE, &xfr->flags)) {
 		if (test_bit(CLIENT_NONBLOCKING, &client->flags)) {
-			rc = -ERESTARTSYS;
+			rc = -EAGAIN;
 			goto done;
 		}
 
@@ -593,11 +593,13 @@ static void occ_worker(struct work_struct *work)
 		goto done;
 	}
 
-	/* already got 3 bytes resp, also need 2 bytes checksum */
-	rc = occ_getsram(sbefifo, 0xFFFBF008, &xfr->buf[8],
-			 resp_data_length - 1);
-	if (rc)
-		goto done;
+	if (resp_data_length > 1) {
+		/* already got 3 bytes resp, also need 2 bytes checksum */
+		rc = occ_getsram(sbefifo, 0xFFFBF008, &xfr->buf[8],
+				 resp_data_length - 1);
+		if (rc)
+			goto done;
+	}
 
 	xfr->resp_data_length = resp_data_length + 7;
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH linux dev-4.10 v2 2/5] drivers: hwmon: occ: Add new sensor versions and non-hwmon attributes
  2017-06-09 18:01 [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8 Eddie James
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 1/5] drivers: fsi: occ: Fix 1 byte response and rc for O_NONBLOCK Eddie James
@ 2017-06-09 18:01 ` Eddie James
  2017-06-16  5:36   ` Joel Stanley
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 3/5] drivers: hwmon: occ: Add P8 OCC access Eddie James
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 9+ messages in thread
From: Eddie James @ 2017-06-09 18:01 UTC (permalink / raw)
  To: openbmc; +Cc: joel, Edward A. James

From: "Edward A. James" <eajames@us.ibm.com>

Switch to versioned sensors which can be used for both p8 and p9. This
adds more common code and removes the need for the processor-specific
functions (other than the OCC access code).

Also add non-hwmon attributes for OCC status.

Signed-off-by: Edward A. James <eajames@us.ibm.com>
---
 drivers/hwmon/occ/Makefile |   2 +-
 drivers/hwmon/occ/common.c | 991 ++++++++++++++++++++++++++++++++++++++++++++-
 drivers/hwmon/occ/common.h |  17 +
 drivers/hwmon/occ/p9.c     | 350 ----------------
 drivers/hwmon/occ/p9.h     |  17 -
 drivers/hwmon/occ/p9_sbe.c |  30 +-
 6 files changed, 1020 insertions(+), 387 deletions(-)
 delete mode 100644 drivers/hwmon/occ/p9.c
 delete mode 100644 drivers/hwmon/occ/p9.h

diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
index 3b0ddc6..6245c25 100644
--- a/drivers/hwmon/occ/Makefile
+++ b/drivers/hwmon/occ/Makefile
@@ -1 +1 @@
-obj-y += p9.o p9_sbe.o common.o
+obj-y += p9_sbe.o common.o
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 1f17def..bee64cd 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -7,8 +7,114 @@
  * (at your option) any later version.
  */
 
+#include <asm/unaligned.h>
 #include "common.h"
 
+#define OCC_NUM_STATUS_ATTRS		6
+
+#define OCC_STAT_ACTIVE			0x01
+#define OCC_EXT_STAT_DVFS_OT		0x80
+#define OCC_EXT_STAT_DVFS_POWER		0x40
+#define OCC_EXT_STAT_MEM_THROTTLE	0x20
+#define OCC_EXT_STAT_QUICK_DROP		0x10
+
+struct temp_sensor_1 {
+	u16 sensor_id;
+	u16 value;
+} __packed;
+
+struct temp_sensor_2 {
+	u32 sensor_id;
+	u8 fru_type;
+	u8 value;
+} __packed;
+
+struct freq_sensor_1 {
+	u16 sensor_id;
+	u16 value;
+} __packed;
+
+struct freq_sensor_2 {
+	u32 sensor_id;
+	u16 value;
+} __packed;
+
+struct power_sensor_1 {
+	u16 sensor_id;
+	u32 update_tag;
+	u32 accumulator;
+	u16 value;
+} __packed;
+
+struct power_sensor_2 {
+	u32 sensor_id;
+	u8 function_id;
+	u8 apss_channel;
+	u16 reserved;
+	u32 update_tag;
+	u64 accumulator;
+	u16 value;
+} __packed;
+
+struct power_sensor_data {
+	u16 value;
+	u32 update_tag;
+	u64 accumulator;
+} __packed;
+
+struct power_sensor_data_and_time {
+	u16 update_time;
+	u16 value;
+	u32 update_tag;
+	u64 accumulator;
+} __packed;
+
+struct power_sensor_a0 {
+	u32 sensor_id;
+	struct power_sensor_data_and_time system;
+	u32 reserved;
+	struct power_sensor_data_and_time proc;
+	struct power_sensor_data vdd;
+	struct power_sensor_data vdn;
+} __packed;
+
+struct caps_sensor_1 {
+	u16 curr_powercap;
+	u16 curr_powerreading;
+	u16 norm_powercap;
+	u16 max_powercap;
+	u16 min_powercap;
+	u16 user_powerlimit;
+} __packed;
+
+struct caps_sensor_2 {
+	u16 curr_powercap;
+	u16 curr_powerreading;
+	u16 norm_powercap;
+	u16 max_powercap;
+	u16 min_powercap;
+	u16 user_powerlimit;
+	u8 user_powerlimit_source;
+} __packed;
+
+struct caps_sensor_3 {
+	u16 curr_powercap;
+	u16 curr_powerreading;
+	u16 norm_powercap;
+	u16 max_powercap;
+	u16 hard_min_powercap;
+	u16 soft_min_powercap;
+	u16 user_powerlimit;
+	u8 user_powerlimit_source;
+} __packed;
+
+struct extended_sensor {
+	u8 name[4];
+	u8 flags;
+	u8 reserved;
+	u8 data[6];
+} __packed;
+
 void occ_parse_poll_response(struct occ *occ)
 {
 	unsigned int i, offset = 0, size = 0;
@@ -40,6 +146,8 @@ void occ_parse_poll_response(struct occ *occ)
 			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);
@@ -47,6 +155,7 @@ void occ_parse_poll_response(struct occ *occ)
 		}
 
 		sensor->num_sensors = block->header.num_sensors;
+		sensor->version = block->header.sensor_format;
 		sensor->data = &block->data;
 	}
 }
@@ -73,8 +182,9 @@ int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
 	int rc;
 	u8 cmd[8];
 	u16 checksum = 0x24;
+	__be16 user_power_cap_be;
 
-	user_power_cap = cpu_to_be16(user_power_cap);
+	user_power_cap_be = cpu_to_be16(user_power_cap);
 
 	cmd[0] = 0;
 	cmd[1] = 0x22;
@@ -108,3 +218,882 @@ int occ_update_response(struct occ *occ)
 	mutex_unlock(&occ->lock);
 	return rc;
 }
+
+static ssize_t occ_show_status(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	int rc;
+	int val;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_poll_response_header *header;
+	struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	header = (struct occ_poll_response_header *)occ->resp.data;
+
+	switch (sattr->index) {
+	case 0:
+		val = header->status & OCC_STAT_ACTIVE;
+		break;
+	case 1:
+		val = header->ext_status & OCC_EXT_STAT_DVFS_OT;
+		break;
+	case 2:
+		val = header->ext_status & OCC_EXT_STAT_DVFS_POWER;
+		break;
+	case 3:
+		val = header->ext_status & OCC_EXT_STAT_MEM_THROTTLE;
+		break;
+	case 4:
+		val = header->ext_status & OCC_EXT_STAT_QUICK_DROP;
+		break;
+	case 5:
+		val = header->occ_state;
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
+}
+
+static ssize_t occ_show_temp_1(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	int rc;
+	u16 val = 0;
+	struct temp_sensor_1 *temp;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_sensors *sensors = &occ->sensors;
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	temp = ((struct temp_sensor_1 *)sensors->temp.data) + sattr->index;
+
+	switch (sattr->nr) {
+	case 0:
+		val = get_unaligned_be16(&temp->sensor_id);
+		break;
+	case 1:
+		val = get_unaligned_be16(&temp->value) * 1000;
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+}
+
+static ssize_t occ_show_temp_2(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	int rc;
+	u32 val = 0;
+	struct temp_sensor_2 *temp;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_sensors *sensors = &occ->sensors;
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	temp = ((struct temp_sensor_2 *)sensors->temp.data) + sattr->index;
+
+	switch (sattr->nr) {
+	case 0:
+		val = get_unaligned_be32(&temp->sensor_id);
+		break;
+	case 1:
+		val = temp->value * 1000;
+		break;
+	case 2:
+		val = temp->fru_type;
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+}
+
+static ssize_t occ_show_freq_1(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	int rc;
+	u16 val = 0;
+	struct freq_sensor_1 *freq;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_sensors *sensors = &occ->sensors;
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	freq = ((struct freq_sensor_1 *)sensors->freq.data) + sattr->index;
+
+	switch (sattr->nr) {
+	case 0:
+		val = get_unaligned_be16(&freq->sensor_id);
+		break;
+	case 1:
+		val = get_unaligned_be16(&freq->value);
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+}
+
+static ssize_t occ_show_freq_2(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	int rc;
+	u32 val = 0;
+	struct freq_sensor_2 *freq;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_sensors *sensors = &occ->sensors;
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	freq = ((struct freq_sensor_2 *)sensors->freq.data) + sattr->index;
+
+	switch (sattr->nr) {
+	case 0:
+		val = get_unaligned_be32(&freq->sensor_id);
+		break;
+	case 1:
+		val = get_unaligned_be16(&freq->value);
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+}
+
+static ssize_t occ_show_power_1(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int rc;
+	u32 val = 0;
+	struct power_sensor_1 *power;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_sensors *sensors = &occ->sensors;
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	power = ((struct power_sensor_1 *)sensors->power.data) + sattr->index;
+
+	switch (sattr->nr) {
+	case 0:
+		val = get_unaligned_be16(&power->sensor_id);
+		break;
+	case 1:
+		val = get_unaligned_be32(&power->update_tag);
+		break;
+	case 2:
+		val = get_unaligned_be32(&power->accumulator);
+		break;
+	case 3:
+		val = get_unaligned_be16(&power->value);
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+}
+
+static ssize_t occ_show_power_2(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int rc;
+	u64 val = 0;
+	struct power_sensor_2 *power;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_sensors *sensors = &occ->sensors;
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	power = ((struct power_sensor_2 *)sensors->power.data) + sattr->index;
+
+	switch (sattr->nr) {
+	case 0:
+		val = get_unaligned_be32(&power->sensor_id);
+		break;
+	case 1:
+		val = get_unaligned_be32(&power->update_tag);
+		break;
+	case 2:
+		val = get_unaligned_be64(&power->accumulator);
+		break;
+	case 3:
+		val = get_unaligned_be16(&power->value);
+		break;
+	case 4:
+		val = power->function_id;
+		break;
+	case 5:
+		val = power->apss_channel;
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
+}
+
+static ssize_t occ_show_power_a0(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	int rc;
+	u64 val = 0;
+	struct power_sensor_a0 *power;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_sensors *sensors = &occ->sensors;
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	power = ((struct power_sensor_a0 *)sensors->power.data) + sattr->index;
+
+	switch (sattr->nr) {
+	case 0:
+		val = get_unaligned_be32(&power->sensor_id);
+		break;
+	case 1:
+		val = get_unaligned_be16(&power->system.update_time);
+		break;
+	case 2:
+		val = get_unaligned_be16(&power->system.value);
+		break;
+	case 3:
+		val = get_unaligned_be32(&power->system.update_tag);
+		break;
+	case 4:
+		val = get_unaligned_be64(&power->system.accumulator);
+		break;
+	case 5:
+		val = get_unaligned_be16(&power->proc.update_time);
+		break;
+	case 6:
+		val = get_unaligned_be16(&power->proc.value);
+		break;
+	case 7:
+		val = get_unaligned_be32(&power->proc.update_tag);
+		break;
+	case 8:
+		val = get_unaligned_be64(&power->proc.accumulator);
+		break;
+	case 9:
+		val = get_unaligned_be16(&power->vdd.value);
+		break;
+	case 10:
+		val = get_unaligned_be32(&power->vdd.update_tag);
+		break;
+	case 11:
+		val = get_unaligned_be64(&power->vdd.accumulator);
+		break;
+	case 12:
+		val = get_unaligned_be16(&power->vdn.value);
+		break;
+	case 13:
+		val = get_unaligned_be32(&power->vdn.update_tag);
+		break;
+	case 14:
+		val = get_unaligned_be64(&power->vdn.accumulator);
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
+}
+
+
+static ssize_t occ_show_caps_1(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	int rc;
+	u16 val = 0;
+	struct caps_sensor_1 *caps;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_sensors *sensors = &occ->sensors;
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	caps = ((struct caps_sensor_1 *)sensors->caps.data) + sattr->index;
+
+	switch (sattr->nr) {
+	case 0:
+		val = get_unaligned_be16(&caps->curr_powercap);
+		break;
+	case 1:
+		val = get_unaligned_be16(&caps->curr_powerreading);
+		break;
+	case 2:
+		val = get_unaligned_be16(&caps->norm_powercap);
+		break;
+	case 3:
+		val = get_unaligned_be16(&caps->max_powercap);
+		break;
+	case 4:
+		val = get_unaligned_be16(&caps->min_powercap);
+		break;
+	case 5:
+		val = get_unaligned_be16(&caps->user_powerlimit);
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+}
+
+static ssize_t occ_show_caps_2(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	int rc;
+	u16 val = 0;
+	struct caps_sensor_2 *caps;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_sensors *sensors = &occ->sensors;
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	caps = ((struct caps_sensor_2 *)sensors->caps.data) + sattr->index;
+
+	switch (sattr->nr) {
+	case 0:
+		val = get_unaligned_be16(&caps->curr_powercap);
+		break;
+	case 1:
+		val = get_unaligned_be16(&caps->curr_powerreading);
+		break;
+	case 2:
+		val = get_unaligned_be16(&caps->norm_powercap);
+		break;
+	case 3:
+		val = get_unaligned_be16(&caps->max_powercap);
+		break;
+	case 4:
+		val = get_unaligned_be16(&caps->min_powercap);
+		break;
+	case 5:
+		val = get_unaligned_be16(&caps->user_powerlimit);
+		break;
+	case 6:
+		val = caps->user_powerlimit_source;
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+}
+
+static ssize_t occ_show_caps_3(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	int rc;
+	u16 val = 0;
+	struct caps_sensor_3 *caps;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_sensors *sensors = &occ->sensors;
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	caps = ((struct caps_sensor_3 *)sensors->caps.data) + sattr->index;
+
+	switch (sattr->nr) {
+	case 0:
+		val = get_unaligned_be16(&caps->curr_powercap);
+		break;
+	case 1:
+		val = get_unaligned_be16(&caps->curr_powerreading);
+		break;
+	case 2:
+		val = get_unaligned_be16(&caps->norm_powercap);
+		break;
+	case 3:
+		val = get_unaligned_be16(&caps->max_powercap);
+		break;
+	case 4:
+		val = get_unaligned_be16(&caps->hard_min_powercap);
+		break;
+	case 5:
+		val = get_unaligned_be16(&caps->user_powerlimit);
+		break;
+	case 6:
+		val = caps->user_powerlimit_source;
+		break;
+	case 7:
+		val = get_unaligned_be16(&caps->soft_min_powercap);
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+}
+
+
+static ssize_t occ_store_caps_user(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	int rc;
+	u16 user_power_cap;
+	struct occ *occ = dev_get_drvdata(dev);
+
+	rc = kstrtou16(buf, 0, &user_power_cap);
+	if (rc)
+		return rc;
+
+	rc = occ_set_user_power_cap(occ, user_power_cap);
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+static ssize_t occ_show_extended(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	int rc;
+	struct extended_sensor *extn;
+	struct occ *occ = dev_get_drvdata(dev);
+	struct occ_sensors *sensors = &occ->sensors;
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+	rc = occ_update_response(occ);
+	if (rc)
+		return rc;
+
+	extn = ((struct extended_sensor *)sensors->extended.data) +
+		sattr->index;
+
+	switch (sattr->nr) {
+	case 0:
+		rc = snprintf(buf, PAGE_SIZE - 1, "%02x%02x%02x%02x\n",
+			      extn->name[0], extn->name[1], extn->name[2],
+			      extn->name[3]);
+		break;
+	case 1:
+		rc = snprintf(buf, PAGE_SIZE - 1, "%02x\n", extn->flags);
+		break;
+	case 2:
+		rc = snprintf(buf, PAGE_SIZE - 1, "%02x%02x%02x%02x%02x%02x\n",
+			      extn->data[0], extn->data[1], extn->data[2],
+			      extn->data[3], extn->data[4], extn->data[5]);
+		break;
+	}
+
+	return rc;
+}
+
+int occ_setup_sensor_attrs(struct occ *occ)
+{
+	unsigned int i, s;
+	struct device *dev = occ->bus_dev;
+	struct occ_sensors *sensors = &occ->sensors;
+	struct occ_attribute *attr;
+	ssize_t (*show_temp)(struct device *, struct device_attribute *,
+			     char *) = occ_show_temp_1;
+	ssize_t (*show_freq)(struct device *, struct device_attribute *,
+			     char *) = occ_show_freq_1;
+	ssize_t (*show_power)(struct device *, struct device_attribute *,
+			      char *) = occ_show_power_1;
+	ssize_t (*show_caps)(struct device *, struct device_attribute *,
+			     char *) = occ_show_caps_1;
+
+	occ->num_attrs = 0;
+
+	switch (sensors->temp.version) {
+	case 1:
+		occ->num_attrs += (sensors->temp.num_sensors * 2);
+		break;
+	case 2:
+		occ->num_attrs += (sensors->temp.num_sensors * 3);
+		show_temp = occ_show_temp_2;
+		break;
+	default:
+		sensors->temp.num_sensors = 0;
+	}
+
+	switch (sensors->freq.version) {
+	case 2:
+		show_freq = occ_show_freq_2;
+		/* fall through */
+	case 1:
+		occ->num_attrs += (sensors->freq.num_sensors * 2);
+		break;
+	default:
+		sensors->freq.num_sensors = 0;
+	}
+
+	switch (sensors->power.version) {
+	case 1:
+		occ->num_attrs += (sensors->power.num_sensors * 4);
+		break;
+	case 2:
+		occ->num_attrs += (sensors->power.num_sensors * 6);
+		show_power = occ_show_power_2;
+		break;
+	case 0xA0:
+		occ->num_attrs += (sensors->power.num_sensors * 15);
+		show_power = occ_show_power_a0;
+		break;
+	default:
+		sensors->power.num_sensors = 0;
+	}
+
+	switch (sensors->caps.version) {
+	case 1:
+		occ->num_attrs += (sensors->caps.num_sensors * 6);
+		break;
+	case 2:
+		occ->num_attrs += (sensors->caps.num_sensors * 7);
+		show_caps = occ_show_caps_2;
+		break;
+	case 3:
+		occ->num_attrs += (sensors->caps.num_sensors * 8);
+		show_caps = occ_show_caps_3;
+		break;
+	default:
+		sensors->caps.num_sensors = 0;
+	}
+
+	switch (sensors->extended.version) {
+	case 1:
+		occ->num_attrs += sensors->extended.num_sensors;
+		break;
+	default:
+		sensors->extended.num_sensors = 0;
+	}
+
+	occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * occ->num_attrs,
+				  GFP_KERNEL);
+	if (!occ->attrs)
+		return -ENOMEM;
+
+	occ->group.attrs = devm_kzalloc(dev, sizeof(*occ->group.attrs) *
+					occ->num_attrs + 1, GFP_KERNEL);
+	if (!occ->group.attrs)
+		return -ENOMEM;
+
+	attr = occ->attrs;
+
+	for (i = 0; i < sensors->temp.num_sensors; ++i) {
+		s = i + 1;
+
+		snprintf(attr->name, sizeof(attr->name), "temp%d_label", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL,
+					     0, i);
+		attr++;
+
+		snprintf(attr->name, sizeof(attr->name), "temp%d_input", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL,
+					     1, i);
+		attr++;
+
+		if (sensors->temp.version > 1) {
+			snprintf(attr->name, sizeof(attr->name),
+				 "temp%d_fru_type", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_temp, NULL, 2, i);
+			attr++;
+		}
+	}
+
+	for (i = 0; i < sensors->freq.num_sensors; ++i) {
+		s = i + 1;
+
+		snprintf(attr->name, sizeof(attr->name), "freq%d_label", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL,
+					     0, i);
+		attr++;
+
+		snprintf(attr->name, sizeof(attr->name), "freq%d_input", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL,
+					     1, i);
+		attr++;
+	}
+
+	if (sensors->power.version == 0xA0) {
+		for (i = 0; i < sensors->power.num_sensors; ++i) {
+			s = i + 1;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_label", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 0, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_system_update_time", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 1, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_system_value", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 2, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_system_update_tag", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 3, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_system_accumulator", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 4, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_proc_update_time", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 5, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_proc_value", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 6, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_proc_update_tag", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 7, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_proc_accumulator", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 8, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_vdd_value", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 9, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_vdd_update_tag", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 10, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_vdd_accumulator", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 11, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_vdn_value", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 12, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_vdn_update_tag", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 13, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_vdn_accumulator", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 14, i);
+			attr++;
+		}
+	} else {
+		for (i = 0; i < sensors->power.num_sensors; ++i) {
+			s = i + 1;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_label", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 0, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_update_tag", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 1, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_accumulator", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 2, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "power%d_input", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_power, NULL, 3, i);
+			attr++;
+
+			if (sensors->power.version > 1) {
+				snprintf(attr->name, sizeof(attr->name),
+					 "power%d_function_id", s);
+				attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+							     show_power, NULL,
+							     4, i);
+				attr++;
+
+				snprintf(attr->name, sizeof(attr->name),
+					 "power%d_apss_channel", s);
+				attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+							     show_power, NULL,
+							     5, i);
+				attr++;
+			}
+		}
+	}
+
+	for (i = 0; i < sensors->caps.num_sensors; ++i) {
+		s = i + 1;
+
+		snprintf(attr->name, sizeof(attr->name), "caps%d_current", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
+					     0, i);
+		attr++;
+
+		snprintf(attr->name, sizeof(attr->name), "caps%d_reading", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
+					     1, i);
+		attr++;
+
+		snprintf(attr->name, sizeof(attr->name), "caps%d_norm", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
+					     2, i);
+		attr++;
+
+		snprintf(attr->name, sizeof(attr->name), "caps%d_max", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
+					     3, i);
+		attr++;
+
+		if (sensors->caps.version > 2) {
+			snprintf(attr->name, sizeof(attr->name),
+				 "caps%d_min_hard", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_caps, NULL, 4, i);
+			attr++;
+
+			snprintf(attr->name, sizeof(attr->name),
+				 "caps%d_min_soft", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_caps, NULL, 7, i);
+			attr++;
+		} else {
+			snprintf(attr->name, sizeof(attr->name), "caps%d_min",
+				 s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_caps, NULL, 4, i);
+			attr++;
+		}
+
+		snprintf(attr->name, sizeof(attr->name), "caps%d_user", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0644, show_caps,
+					     occ_store_caps_user, 5, i);
+		attr++;
+
+		if (sensors->caps.version > 1) {
+			snprintf(attr->name, sizeof(attr->name),
+				 "caps%d_user_source", s);
+			attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+						     show_caps, NULL, 6, i);
+			attr++;
+		}
+	}
+
+	for (i = 0; i < sensors->extended.num_sensors; ++i) {
+		s = i + 1;
+
+		snprintf(attr->name, sizeof(attr->name), "extn%d_label", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+					     occ_show_extended, NULL, 0, i);
+		attr++;
+
+		snprintf(attr->name, sizeof(attr->name), "extn%d_flags", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+					     occ_show_extended, NULL, 1, i);
+		attr++;
+
+		snprintf(attr->name, sizeof(attr->name), "extn%d_value", s);
+		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
+					     occ_show_extended, NULL, 2, i);
+		attr++;
+	}
+
+	/* put the sensors in the group */
+	for (i = 0; i < occ->num_attrs; ++i)
+		occ->group.attrs[i] = &occ->attrs[i].sensor.dev_attr.attr;
+
+	return 0;
+}
+
+int occ_create_status_attrs(struct occ *occ)
+{
+	int rc, i;
+	struct device *dev = occ->bus_dev;
+
+	occ->status_attrs = devm_kzalloc(dev, sizeof(*occ->status_attrs) *
+					 OCC_NUM_STATUS_ATTRS, GFP_KERNEL);
+	if (!occ->status_attrs)
+		return -ENOMEM;
+
+	occ->status_attrs[0] =
+		(struct sensor_device_attribute)SENSOR_ATTR(occ_active, 0444,
+							    occ_show_status,
+							    NULL, 0);
+	occ->status_attrs[1] =
+		(struct sensor_device_attribute)SENSOR_ATTR(occ_dvfs_ot, 0444,
+							    occ_show_status,
+							    NULL, 1);
+	occ->status_attrs[2] =
+		(struct sensor_device_attribute)SENSOR_ATTR(occ_dvfs_power,
+							    0444,
+							    occ_show_status,
+							    NULL, 2);
+	occ->status_attrs[3] =
+		(struct sensor_device_attribute)SENSOR_ATTR(occ_mem_throttle,
+							    0444,
+							    occ_show_status,
+							    NULL, 3);
+	occ->status_attrs[4] =
+		(struct sensor_device_attribute)SENSOR_ATTR(occ_quick_drop,
+							    0444,
+							    occ_show_status,
+							    NULL, 4);
+	occ->status_attrs[5] =
+		(struct sensor_device_attribute)SENSOR_ATTR(occ_status, 0444,
+							    occ_show_status,
+							    NULL, 5);
+
+	for (i = 0; i < OCC_NUM_STATUS_ATTRS; ++i) {
+		rc = device_create_file(dev, &occ->status_attrs[i].dev_attr);
+		if (rc)
+			dev_warn(dev, "error %d creating status attr %d\n", rc,
+				 i);
+	}
+
+	return 0;
+}
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index a3d733a..ad21538 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -16,6 +16,18 @@
 #define OCC_UPDATE_FREQUENCY		msecs_to_jiffies(1000)
 #define OCC_RESP_DATA_BYTES		4089
 
+#define OCC_TIMEOUT_MS			5000
+#define OCC_CMD_IN_PRG_MS		100
+
+#define RESP_RETURN_CMD_IN_PRG		0xFF
+#define RESP_RETURN_SUCCESS		0
+#define RESP_RETURN_CMD_INVAL		0x11
+#define RESP_RETURN_CMD_LEN		0x12
+#define RESP_RETURN_DATA_INVAL		0x13
+#define RESP_RETURN_CHKSUM		0x14
+#define RESP_RETURN_OCC_ERR		0x15
+#define RESP_RETURN_STATE		0x16
+
 struct occ_response {
 	u8 seq_no;
 	u8 cmd_type;
@@ -63,6 +75,7 @@ struct occ_poll_response {
 
 struct occ_sensor {
 	u8 num_sensors;
+	u8 version;
 	void *data;
 };
 
@@ -71,6 +84,7 @@ struct occ_sensors {
 	struct occ_sensor freq;
 	struct occ_sensor power;
 	struct occ_sensor caps;
+	struct occ_sensor extended;
 };
 
 struct occ_attribute {
@@ -92,6 +106,7 @@ struct occ {
 	struct occ_attribute *attrs;
 	struct attribute_group group;
 	const struct attribute_group *groups[2];
+	struct sensor_device_attribute *status_attrs;
 
 	u8 poll_cmd_data;
 	int (*send_cmd)(struct occ *occ, u8 *cmd);
@@ -120,5 +135,7 @@ struct occ {
 int occ_poll(struct occ *occ);
 int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap);
 int occ_update_response(struct occ *occ);
+int occ_setup_sensor_attrs(struct occ *occ);
+int occ_create_status_attrs(struct occ *occ);
 
 #endif /* __OCC_COMMON_H__ */
diff --git a/drivers/hwmon/occ/p9.c b/drivers/hwmon/occ/p9.c
deleted file mode 100644
index 832d6ff..0000000
--- a/drivers/hwmon/occ/p9.c
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * 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 "common.h"
-
-struct p9_temp_sensor {
-	u32 sensor_id;
-	u8 fru_type;
-	u8 value;
-} __packed;
-
-struct p9_freq_sensor {
-	u32 sensor_id;
-	u16 value;
-} __packed;
-
-struct p9_power_sensor {
-	u32 sensor_id;
-	u8 function_id;
-	u8 apss_channel;
-	u16 reserved;
-	u32 update_tag;
-	u64 accumulator;
-	u16 value;
-} __packed;
-
-struct p9_caps_sensor {
-	u16 curr_powercap;
-	u16 curr_powerreading;
-	u16 norm_powercap;
-	u16 max_powercap;
-	u16 min_powercap;
-	u16 user_powerlimit;
-	u8 user_powerlimit_source;
-} __packed;
-
-static ssize_t p9_occ_show_temp(struct device *dev,
-				struct device_attribute *attr,
-				char *buf)
-{
-	int rc;
-	u32 val = 0;
-	struct p9_temp_sensor *temp;
-	struct occ *occ = dev_get_drvdata(dev);
-	struct occ_sensors *sensors = &occ->sensors;
-	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
-
-	rc = occ_update_response(occ);
-	if (rc)
-		return rc;
-
-	temp = ((struct p9_temp_sensor *)sensors->temp.data) + sattr->index;
-
-	switch (sattr->nr) {
-	case 0:
-		val = be32_to_cpu(get_unaligned(&temp->sensor_id));
-		break;
-	case 1:
-		val = temp->fru_type;
-		break;
-	case 2:
-		/* millidegree */
-		val = temp->value * 1000;
-		break;
-	}
-
-	return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
-}
-
-static ssize_t p9_occ_show_freq(struct device *dev,
-				struct device_attribute *attr,
-				char *buf)
-{
-	int rc;
-	u32 val = 0;
-	struct p9_freq_sensor *freq;
-	struct occ *occ = dev_get_drvdata(dev);
-	struct occ_sensors *sensors = &occ->sensors;
-	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
-
-	rc = occ_update_response(occ);
-	if (rc)
-		return rc;
-
-	freq = ((struct p9_freq_sensor *)sensors->freq.data) + sattr->index;
-
-	switch (sattr->nr) {
-	case 0:
-		val = be32_to_cpu(get_unaligned(&freq->sensor_id));
-		break;
-	case 1:
-		val = be16_to_cpu(get_unaligned(&freq->value));
-		break;
-	}
-
-	return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
-}
-
-static ssize_t p9_occ_show_power(struct device *dev,
-				 struct device_attribute *attr,
-				 char *buf)
-{
-	int rc;
-	u64 val = 0;
-	struct p9_power_sensor *power;
-	struct occ *occ = dev_get_drvdata(dev);
-	struct occ_sensors *sensors = &occ->sensors;
-	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
-
-	rc = occ_update_response(occ);
-	if (rc)
-		return rc;
-
-	power = ((struct p9_power_sensor *)sensors->power.data) + sattr->index;
-
-	switch (sattr->nr) {
-	case 0:
-		val = be32_to_cpu(get_unaligned(&power->sensor_id));
-		break;
-	case 1:
-		val = power->function_id;
-		break;
-	case 2:
-		val = power->apss_channel;
-		break;
-	case 3:
-		val = be32_to_cpu(get_unaligned(&power->update_tag));
-		break;
-	case 4:
-		val = be64_to_cpu(get_unaligned(&power->accumulator));
-		break;
-	case 5:
-		val = be16_to_cpu(get_unaligned(&power->value));
-		break;
-	}
-
-	return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
-}
-
-static ssize_t p9_occ_show_caps(struct device *dev,
-				struct device_attribute *attr,
-				char *buf)
-{
-	int rc;
-	u16 val = 0;
-	struct p9_caps_sensor *caps;
-	struct occ *occ = dev_get_drvdata(dev);
-	struct occ_sensors *sensors = &occ->sensors;
-	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
-
-	rc = occ_update_response(occ);
-	if (rc)
-		return rc;
-
-	caps = ((struct p9_caps_sensor *)sensors->caps.data) + sattr->index;
-
-	switch (sattr->nr) {
-	case 0:
-		val = be16_to_cpu(get_unaligned(&caps->curr_powercap));
-		break;
-	case 1:
-		val = be16_to_cpu(get_unaligned(&caps->curr_powerreading));
-		break;
-	case 2:
-		val = be16_to_cpu(get_unaligned(&caps->norm_powercap));
-		break;
-	case 3:
-		val = be16_to_cpu(get_unaligned(&caps->max_powercap));
-		break;
-	case 4:
-		val = be16_to_cpu(get_unaligned(&caps->min_powercap));
-		break;
-	case 5:
-		val = be16_to_cpu(get_unaligned(&caps->user_powerlimit));
-		break;
-	case 6:
-		val = caps->user_powerlimit_source;
-		break;
-	}
-
-	return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
-}
-
-static ssize_t p9_occ_store_caps_user(struct device *dev,
-				      struct device_attribute *attr,
-				      const char *buf, size_t count)
-{
-	int rc;
-	u16 user_power_cap;
-	struct occ *occ = dev_get_drvdata(dev);
-
-	rc = kstrtou16(buf, 0, &user_power_cap);
-	if (rc)
-		return rc;
-
-	rc = occ_set_user_power_cap(occ, user_power_cap);
-	if (rc)
-		return rc;
-
-	return count;
-}
-
-int p9_occ_setup_sensor_attrs(struct occ *occ)
-{
-	unsigned int i, s;
-	struct device *dev = occ->bus_dev;
-	struct occ_sensors *sensors = &occ->sensors;
-	struct occ_attribute *attr;
-
-	occ->num_attrs = (sensors->temp.num_sensors * 3);
-	occ->num_attrs += (sensors->freq.num_sensors * 2);
-	occ->num_attrs += (sensors->power.num_sensors * 6);
-	occ->num_attrs += (sensors->caps.num_sensors * 7);
-
-	occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * occ->num_attrs,
-				  GFP_KERNEL);
-	if (!occ->attrs)
-		return -ENOMEM;
-
-	occ->group.attrs = devm_kzalloc(dev, sizeof(*occ->group.attrs) *
-					occ->num_attrs + 1, GFP_KERNEL);
-	if (!occ->group.attrs)
-		return -ENOMEM;
-
-	attr = occ->attrs;
-	for (i = 0; i < sensors->temp.num_sensors; ++i) {
-		s = i + 1;
-
-		snprintf(attr->name, sizeof(attr->name), "temp%d_label", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_temp, NULL, 0, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "temp%d_fru_type", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_temp, NULL, 1, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "temp%d_input", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_temp, NULL, 2, i);
-		attr++;
-	}
-
-	for (i = 0; i < sensors->freq.num_sensors; ++i) {
-		s = i + 1;
-
-		snprintf(attr->name, sizeof(attr->name), "freq%d_label", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_freq, NULL, 0, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "freq%d_input", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_freq, NULL, 1, i);
-		attr++;
-	}
-
-	for (i = 0; i < sensors->power.num_sensors; ++i) {
-		s = i + 1;
-
-		snprintf(attr->name, sizeof(attr->name), "power%d_label", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_power, NULL, 0, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "power%d_function_id",
-			 s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_power, NULL, 1, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name),
-			 "power%d_apss_channel", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_power, NULL, 2, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "power%d_update_tag",
-			 s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_power, NULL, 3, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "power%d_accumulator",
-			 s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_power, NULL, 4, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "power%d_input", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_power, NULL, 5, i);
-		attr++;
-	}
-
-	for (i = 0; i < sensors->caps.num_sensors; ++i) {
-		s = i + 1;
-
-		snprintf(attr->name, sizeof(attr->name), "caps%d_curr", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_caps, NULL, 0, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "caps%d_reading", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_caps, NULL, 1, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "caps%d_norm", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_caps, NULL, 2, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "caps%d_max", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_caps, NULL, 3, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "caps%d_min", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_caps, NULL, 4, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "caps%d_user", s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0644,
-					     p9_occ_show_caps,
-					     p9_occ_store_caps_user, 5, i);
-		attr++;
-
-		snprintf(attr->name, sizeof(attr->name), "caps%d_user_source",
-			 s);
-		attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
-					     p9_occ_show_caps, NULL, 6, i);
-		attr++;
-	}
-
-	/* put the sensors in the group */
-	for (i = 0; i < occ->num_attrs; ++i)
-		occ->group.attrs[i] = &occ->attrs[i].sensor.dev_attr.attr;
-
-	return 0;
-}
diff --git a/drivers/hwmon/occ/p9.h b/drivers/hwmon/occ/p9.h
deleted file mode 100644
index 12d1bc5..0000000
--- a/drivers/hwmon/occ/p9.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * 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_P9_H__
-#define __OCC_P9_H__
-
-struct occ;
-
-int p9_occ_setup_sensor_attrs(struct occ *occ);
-
-#endif /* __OCC_P9_H__ */
diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c
index 88f71b1..fe435ef 100644
--- a/drivers/hwmon/occ/p9_sbe.c
+++ b/drivers/hwmon/occ/p9_sbe.c
@@ -15,21 +15,8 @@
 #include <linux/occ.h>
 #include <linux/sched.h>
 #include <linux/workqueue.h>
-#include "p9.h"
 
-#define P9_SBE_OCC_SETUP_DELAY		2500
-
-#define OCC_TIMEOUT_MS			5000
-#define OCC_CMD_IN_PRG_MS		100
-
-#define RESP_RETURN_CMD_IN_PRG		0xFF
-#define RESP_RETURN_SUCCESS		0
-#define RESP_RETURN_CMD_INVAL		0x11
-#define RESP_RETURN_CMD_LEN		0x12
-#define RESP_RETURN_DATA_INVAL		0x13
-#define RESP_RETURN_CHKSUM		0x14
-#define RESP_RETURN_OCC_ERR		0x15
-#define RESP_RETURN_STATE		0x16
+#define P9_SBE_OCC_SETUP_DELAY		5000
 
 struct p9_sbe_occ {
 	struct occ occ;
@@ -66,7 +53,8 @@ static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd)
 
 	switch (resp->return_status) {
 	case RESP_RETURN_CMD_IN_PRG:
-		if (time_after(jiffies, start + msecs_to_jiffies(OCC_TIMEOUT_MS)))
+		if (time_after(jiffies,
+			       start + msecs_to_jiffies(OCC_TIMEOUT_MS)))
 			rc = -EALREADY;
 		else {
 			set_current_state(TASK_INTERRUPTIBLE);
@@ -123,7 +111,7 @@ static void p9_sbe_occ_setup(struct work_struct *work)
 
 	occ_parse_poll_response(occ);
 
-	rc = p9_occ_setup_sensor_attrs(occ);
+	rc = occ_setup_sensor_attrs(occ);
 	if (rc) {
 		dev_err(occ->bus_dev, "failed to setup p9 attrs: %d\n", rc);
 		return;
@@ -137,6 +125,10 @@ static void p9_sbe_occ_setup(struct work_struct *work)
 			PTR_ERR(occ->hwmon));
 		return;
 	}
+
+	rc = occ_create_status_attrs(occ);
+	if (rc)
+		dev_err(occ->bus_dev, "failed to setup p9 status attrs: %d\n", rc);
 }
 
 static int p9_sbe_occ_probe(struct platform_device *pdev)
@@ -157,7 +149,8 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)
 	occ->send_cmd = p9_sbe_occ_send_cmd;
 	mutex_init(&occ->lock);
 	INIT_DELAYED_WORK(&p9_sbe_occ->setup, p9_sbe_occ_setup);
-	platform_set_drvdata(pdev, p9_sbe_occ);
+
+	platform_set_drvdata(pdev, occ);
 
 	schedule_delayed_work(&p9_sbe_occ->setup,
 			      msecs_to_jiffies(P9_SBE_OCC_SETUP_DELAY));
@@ -167,7 +160,8 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)
 
 static int p9_sbe_occ_remove(struct platform_device *pdev)
 {
-	struct p9_sbe_occ *p9_sbe_occ = platform_get_drvdata(pdev);
+	struct occ *occ = platform_get_drvdata(pdev);
+	struct p9_sbe_occ *p9_sbe_occ = to_p9_sbe_occ(occ);
 
 	cancel_delayed_work_sync(&p9_sbe_occ->setup);
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH linux dev-4.10 v2 3/5] drivers: hwmon: occ: Add P8 OCC access
  2017-06-09 18:01 [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8 Eddie James
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 1/5] drivers: fsi: occ: Fix 1 byte response and rc for O_NONBLOCK Eddie James
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 2/5] drivers: hwmon: occ: Add new sensor versions and non-hwmon attributes Eddie James
@ 2017-06-09 18:01 ` Eddie James
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 4/5] drivers: occ: hwmon and fsi probing fix Eddie James
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Eddie James @ 2017-06-09 18:01 UTC (permalink / raw)
  To: openbmc; +Cc: joel, Edward A. James

From: "Edward A. James" <eajames@us.ibm.com>

Access the P8 OCC over I2C bus.

Signed-off-by: Edward A. James <eajames@us.ibm.com>
---
 drivers/hwmon/Makefile     |   2 +-
 drivers/hwmon/occ/Kconfig  |  19 +++-
 drivers/hwmon/occ/Makefile |  12 +-
 drivers/hwmon/occ/p8_i2c.c | 270 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 300 insertions(+), 3 deletions(-)
 create mode 100644 drivers/hwmon/occ/p8_i2c.c

diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 73d79bc..4b7c15f 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -47,7 +47,6 @@ obj-$(CONFIG_SENSORS_APPLESMC)	+= applesmc.o
 obj-$(CONFIG_SENSORS_ARM_SCPI)	+= scpi-hwmon.o
 obj-$(CONFIG_SENSORS_ASC7621)	+= asc7621.o
 obj-$(CONFIG_SENSORS_ASPEED)	+= aspeed-pwm-tacho.o
-obj-$(CONFIG_SENSORS_OCC_P9_SBE) += occ/
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_CORETEMP)	+= coretemp.o
 obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o
@@ -172,6 +171,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
index 8196b50..4024dc0 100644
--- a/drivers/hwmon/occ/Kconfig
+++ b/drivers/hwmon/occ/Kconfig
@@ -2,9 +2,26 @@
 # On-Chip Controller configuration
 #
 
+config SENSORS_OCC
+	tristate "POWER On-Chip Controller"
+	help
+	  If you say yes here you get support for monitoring the IBM POWER
+	  processor sensors via the On-Chip Controller (OCC).
+
+	  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 via I2C"
+	depends on I2C && SENSORS_OCC
+	help
+	  If you say yes here you get support for monitoring the POWER8
+	  processor sensors via the OCC, from a service processor, over I2C
+	  bus.
+
 config SENSORS_OCC_P9_SBE
 	bool "POWER9 OCC via SBE"
-	depends on OCCFIFO
+	depends on OCCFIFO && SENSORS_OCC
 	help
 	  If you say yes here you get support for monitoring the POWER9
 	  processor sensors via the OCC, from a service processor, over SBE
diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
index 6245c25..ab5c3e9 100644
--- a/drivers/hwmon/occ/Makefile
+++ b/drivers/hwmon/occ/Makefile
@@ -1 +1,11 @@
-obj-y += p9_sbe.o common.o
+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/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
new file mode 100644
index 0000000..a2f10ea
--- /dev/null
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -0,0 +1,270 @@
+/*
+ * 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 "common.h"
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.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_getscom(struct i2c_client *client, u32 address, u8 *data)
+{
+	ssize_t rc;
+	__be64 buf_be;
+	u64 buf;
+	struct i2c_msg msgs[2];
+
+	address <<= 1;
+
+	msgs[0].addr = client->addr;
+	msgs[0].flags = client->flags & I2C_M_TEN;
+	msgs[0].len = sizeof(u32);
+	msgs[0].buf = (char *)&address;
+
+
+	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_be;
+
+	rc = i2c_transfer(client->adapter, msgs, 2);
+	if (rc < 0)
+		return rc;
+
+	buf = be64_to_cpu(buf_be);
+	memcpy(data, &buf, sizeof(u64));
+
+	return 0;
+}
+
+static int p8_i2c_occ_putscom(struct i2c_client *client, u32 address, u8 *data)
+{
+	u32 buf[3];
+	ssize_t rc;
+
+	address <<= 1;
+
+	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)
+{
+	unsigned int i;
+	u8 buf[8];
+
+	for (i = 0; i < 4; ++i) {
+		buf[i] = data[3 - i];
+		buf[i + 4] = data[7 - i];
+	}
+
+	return p8_i2c_occ_putscom(client, address, buf);
+}
+
+static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd)
+{
+	int i, rc;
+	unsigned long start;
+	u16 data_length;
+	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, 0x6B070, 0xFFFF6000, 0);
+	if (rc)
+		goto err;
+
+	/* write command (already be), i2c expects le */
+	rc = p8_i2c_occ_putscom_be(client, 0x6B075, cmd);
+	if (rc)
+		goto err;
+
+	/* trigger OCC attention */
+	rc = p8_i2c_occ_putscom_u32(client, 0x6B035, 0x20010000, 0);
+	if (rc)
+		goto err;
+
+	/* set sram address for response */
+	rc = p8_i2c_occ_putscom_u32(client, 0x6B070, 0xFFFF7000, 0);
+	if (rc)
+		goto err;
+
+retry:
+	rc = p8_i2c_occ_getscom(client, 0x6B075, (u8 *)resp);
+	if (rc)
+		goto err;
+
+	/* check the occ response */
+	switch (resp->return_status) {
+	case RESP_RETURN_CMD_IN_PRG:
+		if (time_after(jiffies,
+			       start + msecs_to_jiffies(OCC_TIMEOUT_MS)))
+			rc = -EALREADY;
+		else {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(msecs_to_jiffies(OCC_CMD_IN_PRG_MS));
+
+			goto retry;
+		}
+		break;
+	case RESP_RETURN_SUCCESS:
+		rc = 0;
+		break;
+	case RESP_RETURN_CMD_INVAL:
+	case RESP_RETURN_CMD_LEN:
+	case RESP_RETURN_DATA_INVAL:
+	case RESP_RETURN_CHKSUM:
+		rc = -EINVAL;
+		break;
+	case RESP_RETURN_OCC_ERR:
+		rc = -EREMOTE;
+		break;
+	default:
+		rc = -EFAULT;
+	}
+
+	if (rc < 0) {
+		dev_warn(&client->dev, "occ bad response:%d\n",
+			 resp->return_status);
+		return rc;
+	}
+
+	data_length = get_unaligned_be16(&resp->data_length_be);
+	if (data_length > OCC_RESP_DATA_BYTES) {
+		dev_warn(&client->dev, "occ bad data length:%d\n",
+			 data_length);
+		return -EDOM;
+	}
+
+	for (i = 8; i < data_length + 7; i += 8) {
+		rc = p8_i2c_occ_getscom(client, 0x6B075, ((u8 *)resp) + i);
+		if (rc)
+			goto err;
+	}
+
+	return data_length + 7;
+
+err:
+	dev_err(&client->dev, "i2c scom op failed rc:%d\n", rc);
+	return rc;
+}
+
+static int p8_i2c_occ_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	int rc;
+	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;
+	occ->groups[0] = &occ->group;
+	occ->poll_cmd_data = 0x10;
+	occ->send_cmd = p8_i2c_occ_send_cmd;
+	mutex_init(&occ->lock);
+
+	dev_set_drvdata(&client->dev, occ);
+
+	/* no need to lock yet */
+	rc = occ_poll(occ);
+	if (rc < 0) {
+		dev_err(occ->bus_dev, "failed to get OCC poll response: %d\n",
+			rc);
+		return rc;
+	}
+
+	occ_parse_poll_response(occ);
+
+	rc = occ_setup_sensor_attrs(occ);
+	if (rc) {
+		dev_err(occ->bus_dev, "failed to setup p8 attrs: %d\n", rc);
+		return rc;
+	}
+
+	occ->hwmon = devm_hwmon_device_register_with_groups(occ->bus_dev,
+							    "p8_occ", occ,
+							    occ->groups);
+	if (IS_ERR(occ->hwmon)) {
+		rc = PTR_ERR(occ->hwmon);
+		dev_err(occ->bus_dev, "failed to register hwmon device: %d\n",
+			rc);
+		return rc;
+	}
+
+	rc = occ_create_status_attrs(occ);
+	if (rc) {
+		dev_err(occ->bus_dev, "failed to setup p8 status attrs: %d\n",
+			rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+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 const unsigned short p8_i2c_occ_addr[] = { 0x50, 0x51, I2C_CLIENT_END };
+
+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,
+	.address_list = p8_i2c_occ_addr,
+};
+
+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");
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH linux dev-4.10 v2 4/5] drivers: occ: hwmon and fsi probing fix
  2017-06-09 18:01 [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8 Eddie James
                   ` (2 preceding siblings ...)
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 3/5] drivers: hwmon: occ: Add P8 OCC access Eddie James
@ 2017-06-09 18:01 ` Eddie James
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 5/5] aspeed: update power9 dts and defconfigs for OCC Eddie James
  2017-06-16  5:40 ` [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8 Joel Stanley
  5 siblings, 0 replies; 9+ messages in thread
From: Eddie James @ 2017-06-09 18:01 UTC (permalink / raw)
  To: openbmc; +Cc: joel, Edward A. James

From: "Edward A. James" <eajames@us.ibm.com>

Probe the FSI-based OCC hwmon driver immediately instead of delaying.
The probe will fail during FSI scan intentionally, since the OCC is not
active when the system is first powering on. The driver can then be
manually bound when the OCC becomes active.

Signed-off-by: Edward A. James <eajames@us.ibm.com>
---
 drivers/fsi/fsi-sbefifo.c  |  4 ++--
 drivers/fsi/occ.c          |  8 +++++++-
 drivers/hwmon/occ/p9_sbe.c | 38 ++++++++++----------------------------
 3 files changed, 19 insertions(+), 31 deletions(-)

diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
index fbc8dc5..bcf284c 100644
--- a/drivers/fsi/fsi-sbefifo.c
+++ b/drivers/fsi/fsi-sbefifo.c
@@ -839,8 +839,6 @@ static int sbefifo_probe(struct device *dev)
 	setup_timer(&sbefifo->poll_timer, sbefifo_poll_timer,
 			(unsigned long)sbefifo);
 
-	list_add(&sbefifo->link, &sbefifo_fifos);
-
 	if (dev->of_node) {
 		/* create platform devs for dts child nodes (occ, etc) */
 		for_each_child_of_node(dev->of_node, np) {
@@ -852,6 +850,8 @@ static int sbefifo_probe(struct device *dev)
 					 "failed to create child node dev\n");
 		}
 	}
+
+	list_add(&sbefifo->link, &sbefifo_fifos);
 	
 	return misc_register(&sbefifo->mdev);
 }
diff --git a/drivers/fsi/occ.c b/drivers/fsi/occ.c
index a1508f2..f43ae51 100644
--- a/drivers/fsi/occ.c
+++ b/drivers/fsi/occ.c
@@ -635,6 +635,9 @@ struct occ_client *occ_drv_open(struct device *dev, unsigned long flags)
 {
 	struct occ *occ = dev_get_drvdata(dev);
 
+	if (!occ)
+		return NULL;
+
 	return occ_open_common(occ, flags);
 }
 EXPORT_SYMBOL_GPL(occ_drv_open);
@@ -688,7 +691,8 @@ static int occ_probe(struct platform_device *pdev)
 	mutex_init(&occ->occ_lock);
 	INIT_WORK(&occ->work, occ_worker);
 
-	platform_set_drvdata(pdev, occ);
+	/* ensure NULL before we probe children, so they don't hang FSI */
+	platform_set_drvdata(pdev, NULL);
 
 	if (dev->of_node) {
 		rc = of_property_read_u32(dev->of_node, "reg", &reg);
@@ -715,6 +719,8 @@ static int occ_probe(struct platform_device *pdev)
 	} else
 		occ->idx = ida_simple_get(&occ_ida, 1, INT_MAX, GFP_KERNEL);
 
+	platform_set_drvdata(pdev, occ);
+
 	snprintf(occ->name, sizeof(occ->name), "occ%d", occ->idx);
 	occ->mdev.fops = &occ_fops;
 	occ->mdev.minor = MISC_DYNAMIC_MINOR;
diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c
index fe435ef..c70858d 100644
--- a/drivers/hwmon/occ/p9_sbe.c
+++ b/drivers/hwmon/occ/p9_sbe.c
@@ -16,11 +16,8 @@
 #include <linux/sched.h>
 #include <linux/workqueue.h>
 
-#define P9_SBE_OCC_SETUP_DELAY		5000
-
 struct p9_sbe_occ {
 	struct occ occ;
-	struct delayed_work setup;
 	struct device *sbe;
 };
 
@@ -93,12 +90,9 @@ static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd)
 	return rc;
 }
 
-static void p9_sbe_occ_setup(struct work_struct *work)
+static int p9_sbe_occ_setup(struct p9_sbe_occ *p9_sbe_occ)
 {
 	int rc;
-	struct delayed_work *dwork = to_delayed_work(work);
-	struct p9_sbe_occ *p9_sbe_occ = container_of(dwork, struct p9_sbe_occ,
-						     setup);
 	struct occ *occ = &p9_sbe_occ->occ;
 
 	/* no need to lock */
@@ -106,7 +100,7 @@ static void p9_sbe_occ_setup(struct work_struct *work)
 	if (rc < 0) {
 		dev_err(occ->bus_dev, "failed to get OCC poll response: %d\n",
 			rc);
-		return;
+		return rc;
 	}
 
 	occ_parse_poll_response(occ);
@@ -114,21 +108,24 @@ static void p9_sbe_occ_setup(struct work_struct *work)
 	rc = occ_setup_sensor_attrs(occ);
 	if (rc) {
 		dev_err(occ->bus_dev, "failed to setup p9 attrs: %d\n", rc);
-		return;
+		return rc;
 	}
 
 	occ->hwmon = devm_hwmon_device_register_with_groups(occ->bus_dev,
 							    "p9_occ", occ,
 							    occ->groups);
 	if (IS_ERR(occ->hwmon)) {
-		dev_err(occ->bus_dev, "failed to register hwmon device: %ld\n",
-			PTR_ERR(occ->hwmon));
-		return;
+		rc = PTR_ERR(occ->hwmon);
+		dev_err(occ->bus_dev, "failed to register hwmon device: %d\n",
+			rc);
+		return rc;
 	}
 
 	rc = occ_create_status_attrs(occ);
 	if (rc)
 		dev_err(occ->bus_dev, "failed to setup p9 status attrs: %d\n", rc);
+
+	return rc;
 }
 
 static int p9_sbe_occ_probe(struct platform_device *pdev)
@@ -148,24 +145,10 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)
 	occ->poll_cmd_data = 0x20;
 	occ->send_cmd = p9_sbe_occ_send_cmd;
 	mutex_init(&occ->lock);
-	INIT_DELAYED_WORK(&p9_sbe_occ->setup, p9_sbe_occ_setup);
 
 	platform_set_drvdata(pdev, occ);
 
-	schedule_delayed_work(&p9_sbe_occ->setup,
-			      msecs_to_jiffies(P9_SBE_OCC_SETUP_DELAY));
-
-	return 0;
-}
-
-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);
-
-	cancel_delayed_work_sync(&p9_sbe_occ->setup);
-
-	return 0;
+	return p9_sbe_occ_setup(p9_sbe_occ);
 }
 
 static const struct of_device_id p9_sbe_occ_of_match[] = {
@@ -179,7 +162,6 @@ static int p9_sbe_occ_remove(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

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH linux dev-4.10 v2 5/5] aspeed: update power9 dts and defconfigs for OCC
  2017-06-09 18:01 [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8 Eddie James
                   ` (3 preceding siblings ...)
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 4/5] drivers: occ: hwmon and fsi probing fix Eddie James
@ 2017-06-09 18:01 ` Eddie James
  2017-06-16  5:40 ` [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8 Joel Stanley
  5 siblings, 0 replies; 9+ messages in thread
From: Eddie James @ 2017-06-09 18:01 UTC (permalink / raw)
  To: openbmc; +Cc: joel, Edward A. James

From: "Edward A. James" <eajames@us.ibm.com>

Signed-off-by: Edward A. James <eajames@us.ibm.com>
---
 arch/arm/boot/dts/ibm-power9-cfam.dtsi | 9 +++++++++
 arch/arm/configs/aspeed_g4_defconfig   | 2 ++
 arch/arm/configs/aspeed_g5_defconfig   | 1 +
 3 files changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/ibm-power9-cfam.dtsi b/arch/arm/boot/dts/ibm-power9-cfam.dtsi
index 5beb978..f29c6e3 100644
--- a/arch/arm/boot/dts/ibm-power9-cfam.dtsi
+++ b/arch/arm/boot/dts/ibm-power9-cfam.dtsi
@@ -13,6 +13,10 @@
 			occ@1 {
 				compatible = "ibm,p9-occ";
 				reg = <1>;
+
+				occ-hwmon@1 {
+					compatible = "ibm,p9-occ-hwmon";
+				};
 			};
 		};
 
@@ -37,6 +41,11 @@
 						compatible =
 							"ibm,p9-occ";
 						reg = <2>;
+
+						occ-hwmon@2 {
+							compatible =
+							    "ibm,p9-occ-hwmon";
+						};
 					};
 				};
 			};
diff --git a/arch/arm/configs/aspeed_g4_defconfig b/arch/arm/configs/aspeed_g4_defconfig
index 1dbea59..4766919 100644
--- a/arch/arm/configs/aspeed_g4_defconfig
+++ b/arch/arm/configs/aspeed_g4_defconfig
@@ -141,6 +141,8 @@ CONFIG_SENSORS_IIO_HWMON=y
 CONFIG_SENSORS_MAX31785=y
 CONFIG_SENSORS_LM75=y
 CONFIG_SENSORS_NCT7904=y
+CONFIG_SENSORS_OCC=y
+CONFIG_SENSORS_OCC_P8_I2C=y
 CONFIG_PMBUS=y
 # CONFIG_SENSORS_PMBUS is not set
 CONFIG_SENSORS_ADM1275=y
diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig
index 64e3aed..a8a34bc 100644
--- a/arch/arm/configs/aspeed_g5_defconfig
+++ b/arch/arm/configs/aspeed_g5_defconfig
@@ -140,6 +140,7 @@ CONFIG_SENSORS_IIO_HWMON=y
 CONFIG_SENSORS_MAX31785=y
 CONFIG_SENSORS_LM75=y
 CONFIG_SENSORS_NCT7904=y
+CONFIG_SENSORS_OCC=y
 CONFIG_SENSORS_OCC_P9_SBE=y
 CONFIG_PMBUS=y
 CONFIG_SENSORS_ADM1275=y
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH linux dev-4.10 v2 1/5] drivers: fsi: occ: Fix 1 byte response and rc for O_NONBLOCK
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 1/5] drivers: fsi: occ: Fix 1 byte response and rc for O_NONBLOCK Eddie James
@ 2017-06-16  5:11   ` Joel Stanley
  0 siblings, 0 replies; 9+ messages in thread
From: Joel Stanley @ 2017-06-16  5:11 UTC (permalink / raw)
  To: Eddie James; +Cc: OpenBMC Maillist, Edward A. James

On Sat, Jun 10, 2017 at 3:31 AM, Eddie James <eajames@linux.vnet.ibm.com> wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Couple of bug fixes.
>
> Signed-off-by: Edward A. James <eajames@us.ibm.com>

Applied to dev-4.10.

> ---
>  drivers/fsi/occ.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/fsi/occ.c b/drivers/fsi/occ.c
> index fc014b6..a1508f2 100644
> --- a/drivers/fsi/occ.c
> +++ b/drivers/fsi/occ.c
> @@ -188,7 +188,7 @@ static ssize_t occ_read_common(struct occ_client *client, char __user *ubuf,
>
>         if (!test_bit(XFR_COMPLETE, &xfr->flags)) {
>                 if (test_bit(CLIENT_NONBLOCKING, &client->flags)) {
> -                       rc = -ERESTARTSYS;
> +                       rc = -EAGAIN;
>                         goto done;
>                 }
>
> @@ -593,11 +593,13 @@ static void occ_worker(struct work_struct *work)
>                 goto done;
>         }
>
> -       /* already got 3 bytes resp, also need 2 bytes checksum */
> -       rc = occ_getsram(sbefifo, 0xFFFBF008, &xfr->buf[8],
> -                        resp_data_length - 1);
> -       if (rc)
> -               goto done;
> +       if (resp_data_length > 1) {
> +               /* already got 3 bytes resp, also need 2 bytes checksum */
> +               rc = occ_getsram(sbefifo, 0xFFFBF008, &xfr->buf[8],
> +                                resp_data_length - 1);
> +               if (rc)
> +                       goto done;
> +       }
>
>         xfr->resp_data_length = resp_data_length + 7;
>
> --
> 1.8.3.1
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH linux dev-4.10 v2 2/5] drivers: hwmon: occ: Add new sensor versions and non-hwmon attributes
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 2/5] drivers: hwmon: occ: Add new sensor versions and non-hwmon attributes Eddie James
@ 2017-06-16  5:36   ` Joel Stanley
  0 siblings, 0 replies; 9+ messages in thread
From: Joel Stanley @ 2017-06-16  5:36 UTC (permalink / raw)
  To: Eddie James; +Cc: OpenBMC Maillist, Edward A. James

On Sat, Jun 10, 2017 at 3:31 AM, Eddie James <eajames@linux.vnet.ibm.com> wrote:

> +static ssize_t occ_show_status(struct device *dev,
> +                              struct device_attribute *attr, char *buf)
> +{
> +       int rc;
> +       int val;
> +       struct occ *occ = dev_get_drvdata(dev);
> +       struct occ_poll_response_header *header;
> +       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
> +
> +       rc = occ_update_response(occ);
> +       if (rc)
> +               return rc;
> +
> +       header = (struct occ_poll_response_header *)occ->resp.data;
> +
> +       switch (sattr->index) {
> +       case 0:
> +               val = header->status & OCC_STAT_ACTIVE;
> +               break;
> +       case 1:
> +               val = header->ext_status & OCC_EXT_STAT_DVFS_OT;
> +               break;
> +       case 2:
> +               val = header->ext_status & OCC_EXT_STAT_DVFS_POWER;
> +               break;
> +       case 3:
> +               val = header->ext_status & OCC_EXT_STAT_MEM_THROTTLE;
> +               break;
> +       case 4:
> +               val = header->ext_status & OCC_EXT_STAT_QUICK_DROP;
> +               break;
> +       case 5:
> +               val = header->occ_state;
> +               break;
> +       }
> +
> +       return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);

drivers/hwmon/occ/common.c: In function ‘occ_show_status’:
drivers/hwmon/occ/common.c:258:9: warning: ‘val’ may be used
uninitialized in this function [-Wmaybe-uninitialized]
  return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


Please fix this.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8
  2017-06-09 18:01 [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8 Eddie James
                   ` (4 preceding siblings ...)
  2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 5/5] aspeed: update power9 dts and defconfigs for OCC Eddie James
@ 2017-06-16  5:40 ` Joel Stanley
  5 siblings, 0 replies; 9+ messages in thread
From: Joel Stanley @ 2017-06-16  5:40 UTC (permalink / raw)
  To: Eddie James; +Cc: OpenBMC Maillist, Edward A. James

2017-06-10 3:31 GMT+09:30 Eddie James <eajames@linux.vnet.ibm.com>:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> This series refactors the OCC hwmon driver to make a number of changes and bug
> fixes. It also includes bug fixes for the FSI OCC driver. This includes 3
> patches that were previously sent individually on the openbmc mailing list.
>
> Edward A. James (5):
>   drivers: fsi: occ: Fix 1 byte response and rc for O_NONBLOCK
>   drivers: hwmon: occ: Add new sensor versions and non-hwmon attributes
>   drivers: hwmon: occ: Add P8 OCC access
>   drivers: occ: hwmon and fsi probing fix
>   aspeed: update power9 dts and defconfigs for OCC

I've applied this series. They look like there is some more work to be
done, so please find some reviewers ASAP.

Cheers,

Joel

>
>  arch/arm/boot/dts/ibm-power9-cfam.dtsi |   9 +
>  arch/arm/configs/aspeed_g4_defconfig   |   2 +
>  arch/arm/configs/aspeed_g5_defconfig   |   1 +
>  drivers/fsi/fsi-sbefifo.c              |   4 +-
>  drivers/fsi/occ.c                      |  22 +-
>  drivers/hwmon/Makefile                 |   2 +-
>  drivers/hwmon/occ/Kconfig              |  19 +-
>  drivers/hwmon/occ/Makefile             |  12 +-
>  drivers/hwmon/occ/common.c             | 991 ++++++++++++++++++++++++++++++++-
>  drivers/hwmon/occ/common.h             |  17 +
>  drivers/hwmon/occ/p8_i2c.c             | 270 +++++++++
>  drivers/hwmon/occ/p9.c                 | 350 ------------
>  drivers/hwmon/occ/p9.h                 |  17 -
>  drivers/hwmon/occ/p9_sbe.c             |  60 +-
>  14 files changed, 1354 insertions(+), 422 deletions(-)
>  create mode 100644 drivers/hwmon/occ/p8_i2c.c
>  delete mode 100644 drivers/hwmon/occ/p9.c
>  delete mode 100644 drivers/hwmon/occ/p9.h
>
> --
> 1.8.3.1
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2017-06-16  5:41 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-09 18:01 [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8 Eddie James
2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 1/5] drivers: fsi: occ: Fix 1 byte response and rc for O_NONBLOCK Eddie James
2017-06-16  5:11   ` Joel Stanley
2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 2/5] drivers: hwmon: occ: Add new sensor versions and non-hwmon attributes Eddie James
2017-06-16  5:36   ` Joel Stanley
2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 3/5] drivers: hwmon: occ: Add P8 OCC access Eddie James
2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 4/5] drivers: occ: hwmon and fsi probing fix Eddie James
2017-06-09 18:01 ` [PATCH linux dev-4.10 v2 5/5] aspeed: update power9 dts and defconfigs for OCC Eddie James
2017-06-16  5:40 ` [PATCH linux dev-4.10 v2 0/5] drivers: OCC: Update sensors, fix probing, add P8 Joel Stanley

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.