All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH linux v7 0/6] drivers: hwmon: Add On-Chip Controller driver
@ 2017-02-07 23:10 eajames
  2017-02-07 23:10 ` [PATCH linux v7 1/6] hwmon: Add core On-Chip Controller support for POWER CPUs eajames
                   ` (5 more replies)
  0 siblings, 6 replies; 23+ messages in thread
From: eajames @ 2017-02-07 23:10 UTC (permalink / raw)
  To: linux
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, jdelvare,
	corbet, mark.rutland, robh+dt, wsa, andrew, joel, benh,
	Edward A. James

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

This patchset adds a hwmon driver to support the OCC (On-Chip Controller)
on the IBM POWER8 and POWER9 processors, from a BMC (Baseboard Management
Controller). The OCC is an embedded processor that provides real time
power and thermal monitoring.

The driver provides an interface on a BMC to poll OCC sensor data, set
user power caps, and perform some basic OCC error handling. It interfaces
with userspace through hwmon.

The driver is currently functional only for the OCC on POWER8 chips.
Communicating with the POWER9 OCC requries FSI support.

Edward A. James (6):
  hwmon: Add core On-Chip Controller support for POWER CPUs
  hwmon: occ: Add sysfs interface
  hwmon: occ: Add I2C transport implementation for SCOM operations
  hwmon: occ: Add callbacks for parsing P8 OCC datastructures
  hwmon: occ: Add hwmon implementation for the P8 OCC
  hwmon: occ: Add callbacks for parsing P9 OCC datastructures

 Documentation/devicetree/bindings/hwmon/occ.txt |  13 +
 Documentation/hwmon/occ                         | 114 ++++++
 MAINTAINERS                                     |   7 +
 drivers/hwmon/Kconfig                           |   2 +
 drivers/hwmon/Makefile                          |   1 +
 drivers/hwmon/occ/Kconfig                       |  29 ++
 drivers/hwmon/occ/Makefile                      |   2 +
 drivers/hwmon/occ/occ.c                         | 448 ++++++++++++++++++++++++
 drivers/hwmon/occ/occ.h                         |  81 +++++
 drivers/hwmon/occ/occ_p8.c                      | 248 +++++++++++++
 drivers/hwmon/occ/occ_p8.h                      |  30 ++
 drivers/hwmon/occ/occ_p9.c                      | 309 ++++++++++++++++
 drivers/hwmon/occ/occ_p9.h                      |  30 ++
 drivers/hwmon/occ/occ_scom_i2c.c                |  77 ++++
 drivers/hwmon/occ/occ_scom_i2c.h                |  26 ++
 drivers/hwmon/occ/occ_sysfs.c                   | 251 +++++++++++++
 drivers/hwmon/occ/occ_sysfs.h                   |  30 ++
 drivers/hwmon/occ/p8_occ_i2c.c                  | 104 ++++++
 drivers/hwmon/occ/scom.h                        |  47 +++
 19 files changed, 1849 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/occ.txt
 create mode 100644 Documentation/hwmon/occ
 create mode 100644 drivers/hwmon/occ/Kconfig
 create mode 100644 drivers/hwmon/occ/Makefile
 create mode 100644 drivers/hwmon/occ/occ.c
 create mode 100644 drivers/hwmon/occ/occ.h
 create mode 100644 drivers/hwmon/occ/occ_p8.c
 create mode 100644 drivers/hwmon/occ/occ_p8.h
 create mode 100644 drivers/hwmon/occ/occ_p9.c
 create mode 100644 drivers/hwmon/occ/occ_p9.h
 create mode 100644 drivers/hwmon/occ/occ_scom_i2c.c
 create mode 100644 drivers/hwmon/occ/occ_scom_i2c.h
 create mode 100644 drivers/hwmon/occ/occ_sysfs.c
 create mode 100644 drivers/hwmon/occ/occ_sysfs.h
 create mode 100644 drivers/hwmon/occ/p8_occ_i2c.c
 create mode 100644 drivers/hwmon/occ/scom.h

-- 
1.8.3.1

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

* [PATCH linux v7 1/6] hwmon: Add core On-Chip Controller support for POWER CPUs
  2017-02-07 23:10 [PATCH linux v7 0/6] drivers: hwmon: Add On-Chip Controller driver eajames
@ 2017-02-07 23:10 ` eajames
  2017-02-10  5:31   ` Joel Stanley
  2017-02-07 23:10 ` [PATCH linux v7 2/6] hwmon: occ: Add sysfs interface eajames
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 23+ messages in thread
From: eajames @ 2017-02-07 23:10 UTC (permalink / raw)
  To: linux
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, jdelvare,
	corbet, mark.rutland, robh+dt, wsa, andrew, joel, benh,
	Edward A. James

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

Add core support for polling the OCC for it's sensor data and parsing that
data into sensor-specific information.

Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 Documentation/hwmon/occ    |  40 ++++
 MAINTAINERS                |   7 +
 drivers/hwmon/Kconfig      |   2 +
 drivers/hwmon/Makefile     |   1 +
 drivers/hwmon/occ/Kconfig  |  15 ++
 drivers/hwmon/occ/Makefile |   1 +
 drivers/hwmon/occ/occ.c    | 448 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/hwmon/occ/occ.h    |  81 ++++++++
 drivers/hwmon/occ/scom.h   |  47 +++++
 9 files changed, 642 insertions(+)
 create mode 100644 Documentation/hwmon/occ
 create mode 100644 drivers/hwmon/occ/Kconfig
 create mode 100644 drivers/hwmon/occ/Makefile
 create mode 100644 drivers/hwmon/occ/occ.c
 create mode 100644 drivers/hwmon/occ/occ.h
 create mode 100644 drivers/hwmon/occ/scom.h

diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
new file mode 100644
index 0000000..79d1642
--- /dev/null
+++ b/Documentation/hwmon/occ
@@ -0,0 +1,40 @@
+Kernel driver occ
+=================
+
+Supported chips:
+ * ASPEED AST2400
+ * ASPEED AST2500
+
+Please note that the chip must be connected to a POWER8 or POWER9 processor
+(see the BMC - Host Communications section).
+
+Author: Eddie James <eajames@us.ibm.com>
+
+Description
+-----------
+
+This driver implements support for the OCC (On-Chip Controller) on the IBM
+POWER8 and POWER9 processors, from a BMC (Baseboard Management Controller). The
+OCC is an embedded processor that provides real time power and thermal
+monitoring.
+
+This driver provides an interface on a BMC to poll OCC sensor data, set user
+power caps, and perform some basic OCC error handling.
+
+Currently, all versions of the OCC support four types of sensor data: power,
+temperature, frequency, and "caps," which indicate limits and thresholds used
+internally on the OCC.
+
+BMC - Host Communications
+-------------------------
+
+For the POWER8 application, the BMC can communicate with the P8 over I2C bus.
+However, to access the OCC register space, any data transfer must use a SCOM
+operation. SCOM is a procedure to initiate a data transfer, typically of 8
+bytes. SCOMs consist of writing a 32-bit command register and then
+reading/writing two 32-bit data registers. This driver implements these
+SCOM operations over I2C bus in order to communicate with the OCC.
+
+For the POWER9 application, the BMC can communicate with the P9 over FSI bus
+and SBE engine. Once again, SCOM operations are required. This driver will
+implement SCOM ops over FSI/SBE. This will require the FSI driver.
diff --git a/MAINTAINERS b/MAINTAINERS
index 5f10c28..193a13b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9128,6 +9128,13 @@ T:	git git://linuxtv.org/media_tree.git
 S:	Maintained
 F:	drivers/media/i2c/ov7670.c
 
+ON-CHIP CONTROLLER HWMON DRIVER
+M:	Eddie James <eajames@us.ibm.com>
+L:	linux-hwmon@vger.kernel.org
+S:	Maintained
+F:	Documentation/hwmon/occ
+F:	drivers/hwmon/occ/
+
 ONENAND FLASH DRIVER
 M:	Kyungmin Park <kyungmin.park@samsung.com>
 L:	linux-mtd@lists.infradead.org
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 190d270..e80ca81 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1240,6 +1240,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 d2cb7e8..c7ec5d4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -169,6 +169,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_PPC_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..cdb64a7
--- /dev/null
+++ b/drivers/hwmon/occ/Kconfig
@@ -0,0 +1,15 @@
+#
+# On Chip Controller configuration
+#
+
+menuconfig SENSORS_PPC_OCC
+	bool "PPC On-Chip Controller"
+	help
+	  If you say yes here you get support to monitor Power CPU
+	  sensors via the On-Chip Controller (OCC).
+
+	  Generally this is used by management controllers such as a BMC
+	  on an OpenPower system.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called occ.
diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
new file mode 100644
index 0000000..93cb52f
--- /dev/null
+++ b/drivers/hwmon/occ/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o
diff --git a/drivers/hwmon/occ/occ.c b/drivers/hwmon/occ/occ.c
new file mode 100644
index 0000000..af077f2
--- /dev/null
+++ b/drivers/hwmon/occ/occ.c
@@ -0,0 +1,448 @@
+/*
+ * occ.c - OCC hwmon driver
+ *
+ * This file contains the methods and data structures for the OCC hwmon driver.
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include "occ.h"
+
+#define OCC_DATA_MAX		4096
+#define OCC_DATA_MIN		40
+#define OCC_BMC_TIMEOUT_MS	20000
+
+/* To generate attn to OCC */
+#define ATTN_DATA		0x0006B035
+
+/* For BMC to read/write SRAM */
+#define OCB_ADDRESS		0x0006B070
+#define OCB_DATA		0x0006B075
+#define OCB_STATUS_CONTROL_AND	0x0006B072
+#define OCB_STATUS_CONTROL_OR	0x0006B073
+
+/* To init OCB */
+#define OCB_AND_INIT0		0xFBFFFFFF
+#define OCB_AND_INIT1		0xFFFFFFFF
+#define OCB_OR_INIT0		0x08000000
+#define OCB_OR_INIT1		0x00000000
+
+/* To generate attention on OCC */
+#define ATTN0			0x01010000
+#define ATTN1			0x00000000
+
+/* OCC return status */
+#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
+
+/* time interval to retry on "command in progress" return status */
+#define CMD_IN_PRG_INT_MS	100
+#define CMD_IN_PRG_RETRIES	(OCC_BMC_TIMEOUT_MS / CMD_IN_PRG_INT_MS)
+
+/* OCC command definitions */
+#define OCC_POLL		0
+#define OCC_SET_USER_POWR_CAP	0x22
+
+/* OCC poll command data */
+#define OCC_POLL_STAT_SENSOR	0x10
+
+/* OCC response data offsets */
+#define RESP_RETURN_STATUS		2
+#define RESP_DATA_LENGTH		3
+#define RESP_HEADER_OFFSET		5
+#define SENSOR_STR_OFFSET		37
+#define NUM_SENSOR_BLOCKS_OFFSET	43
+#define SENSOR_BLOCK_OFFSET		45
+
+/* occ_poll_header
+ * structure to match the raw occ poll response data
+ */
+struct occ_poll_header {
+	u8 status;
+	u8 ext_status;
+	u8 occs_present;
+	u8 config;
+	u8 occ_state;
+	u8 mode;
+	u8 ips_status;
+	u8 error_log_id;
+	u32 error_log_addr_start;
+	u16 error_log_length;
+	u8 reserved2;
+	u8 reserved3;
+	u8 occ_code_level[16];
+	u8 sensor_eye_catcher[6];
+	u8 num_sensor_blocks;
+	u8 sensor_data_version;
+} __attribute__((packed, aligned(4)));
+
+struct occ_response {
+	struct occ_poll_header header;
+	struct occ_blocks data;
+};
+
+struct occ {
+	struct device *dev;
+	void *bus;
+	struct occ_bus_ops bus_ops;
+	struct occ_ops ops;
+	struct occ_config config;
+	unsigned long last_updated;
+	struct mutex update_lock;
+	struct occ_response response;
+	bool valid;
+	u8 *raw_data;
+};
+
+static int parse_occ_response(struct occ *driver, u16 num_bytes)
+{
+	int b;
+	int s;
+	unsigned int offset = SENSOR_BLOCK_OFFSET;
+	int sensor_type;
+	u8 num_sensor_blocks;
+	struct sensor_data_block_header *block;
+	void *sensors;
+	struct device *dev = driver->dev;
+	u8 *data = driver->raw_data;
+	struct occ_response *resp = &driver->response;
+
+	/* check if the data is valid */
+	if (strncmp(&data[SENSOR_STR_OFFSET], "SENSOR", 6) != 0) {
+		dev_err(dev, "no SENSOR string in response\n");
+		return -ENODATA;
+	}
+
+	num_sensor_blocks = data[NUM_SENSOR_BLOCKS_OFFSET];
+	if (num_sensor_blocks == 0) {
+		dev_warn(dev, "no sensor blocks available\n");
+		return -ENODATA;
+	}
+
+	memcpy(&resp->header, &data[RESP_HEADER_OFFSET],
+	       sizeof(struct occ_poll_header));
+
+	/* data length starts at actual data */
+	num_bytes += RESP_HEADER_OFFSET;
+
+	/* translate fields > 1 byte */
+	resp->header.error_log_addr_start =
+		be32_to_cpu(resp->header.error_log_addr_start);
+	resp->header.error_log_length =
+		be16_to_cpu(resp->header.error_log_length);
+
+	for (b = 0; b < num_sensor_blocks && b < MAX_OCC_SENSOR_TYPE; b++) {
+		if (offset + sizeof(struct sensor_data_block_header) >
+		    num_bytes) {
+			dev_warn(dev, "exceeded data length\n");
+			return 0;
+		}
+
+		block = (struct sensor_data_block_header *)&data[offset];
+		offset += sizeof(struct sensor_data_block_header);
+
+		if (strncmp(block->sensor_type, "FREQ", 4) == 0)
+			sensor_type = FREQ;
+		else if (strncmp(block->sensor_type, "TEMP", 4) == 0)
+			sensor_type = TEMP;
+		else if (strncmp(block->sensor_type, "POWR", 4) == 0)
+			sensor_type = POWER;
+		else if (strncmp(block->sensor_type, "CAPS", 4) == 0)
+			sensor_type = CAPS;
+		else {
+			dev_warn(dev, "sensor type not supported %.4s\n",
+				block->sensor_type);
+			continue;
+		}
+
+		sensors = &resp->data.blocks[b].sensors;
+		if (!sensors) {
+			/* first poll response */
+			sensors = driver->ops.alloc_sensor(dev, sensor_type,
+							   block->num_sensors);
+			if (!sensors)
+				return -ENOMEM;
+
+			resp->data.blocks[b].sensors = sensors;
+			resp->data.sensor_block_id[sensor_type] = b;
+			resp->data.blocks[b].header = *block;
+		} else if (block->sensor_length !=
+			 resp->data.blocks[b].header.sensor_length) {
+			dev_warn(dev,
+				 "different sensor length than first poll\n");
+			continue;
+		}
+
+		for (s = 0; s < block->num_sensors &&
+		     s < resp->data.blocks[b].header.num_sensors; s++) {
+			if (offset + block->sensor_length > num_bytes) {
+				dev_warn(dev, "exceeded data length\n");
+				return 0;
+			}
+
+			driver->ops.parse_sensor(data, sensors, sensor_type,
+						 offset, s);
+			offset += block->sensor_length;
+		}
+	}
+
+	return 0;
+}
+
+static u8 occ_send_cmd(struct occ *driver, u8 seq, u8 type, u16 length,
+		       const u8 *data, u8 *resp)
+{
+	u32 cmd1, cmd2 = 0;
+	u16 checksum = 0;
+	bool retry = false;
+	int i, rc, tries = 0;
+
+	cmd1 = (seq << 24) | (type << 16) | length;
+	memcpy(&cmd2, data, length);
+	cmd2 <<= ((4 - length) * 8);
+
+	/* checksum: sum of every bytes of cmd1, cmd2 */
+	for (i = 0; i < 4; i++) {
+		checksum += (cmd1 >> (i * 8)) & 0xFF;
+		checksum += (cmd2 >> (i * 8)) & 0xFF;
+	}
+
+	cmd2 |= checksum << ((2 - length) * 8);
+
+	/* Init OCB */
+	rc = driver->bus_ops.putscom(driver->bus, OCB_STATUS_CONTROL_OR,
+				     OCB_OR_INIT0, OCB_OR_INIT1);
+	if (rc)
+		goto err;
+
+	rc = driver->bus_ops.putscom(driver->bus, OCB_STATUS_CONTROL_AND,
+				     OCB_AND_INIT0, OCB_AND_INIT1);
+	if (rc)
+		goto err;
+
+	/* Send command, 2nd half of the 64-bit addr is unused (write 0) */
+	rc = driver->bus_ops.putscom(driver->bus, OCB_ADDRESS,
+				     driver->config.command_addr, 0);
+	if (rc)
+		goto err;
+
+	rc = driver->bus_ops.putscom(driver->bus, OCB_DATA, cmd1, cmd2);
+	if (rc)
+		goto err;
+
+	/* Trigger attention */
+	rc = driver->bus_ops.putscom(driver->bus, ATTN_DATA, ATTN0, ATTN1);
+	if (rc)
+		goto err;
+
+	/* Get response data */
+	rc = driver->bus_ops.putscom(driver->bus, OCB_ADDRESS,
+				     driver->config.response_addr, 0);
+	if (rc)
+		goto err;
+
+	do {
+		if (retry) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(msecs_to_jiffies(CMD_IN_PRG_INT_MS));
+		}
+
+		rc = driver->bus_ops.getscom(driver->bus, OCB_DATA,
+					     (u64 *)resp);
+		if (rc)
+			goto err;
+
+		/* retry if we get "command in progress" return status */
+		retry = resp[RESP_RETURN_STATUS] == RESP_RETURN_CMD_IN_PRG &&
+			tries++ < CMD_IN_PRG_RETRIES;
+	} while (retry);
+
+	/* check the occ response */
+	switch (resp[RESP_RETURN_STATUS]) {
+	case RESP_RETURN_CMD_IN_PRG:
+		rc = -EALREADY;
+		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(driver->dev, "occ bad response:%d\n",
+			 resp[RESP_RETURN_STATUS]);
+
+	return rc;
+
+err:
+	dev_err(driver->dev, "scom op failed rc:%d\n", rc);
+	return rc;
+}
+
+static int occ_get_all(struct occ *driver)
+{
+	int i = 0, rc;
+	u8 *occ_data = driver->raw_data;
+	u16 num_bytes;
+	const u8 poll_cmd_data = OCC_POLL_STAT_SENSOR;
+	struct device *dev = driver->dev;
+
+	memset(occ_data, 0, OCC_DATA_MAX);
+
+	rc = occ_send_cmd(driver, 0, OCC_POLL, 1, &poll_cmd_data, occ_data);
+	if (rc)
+		return rc;
+
+	num_bytes = get_unaligned((u16 *)&occ_data[RESP_DATA_LENGTH]);
+	num_bytes = be16_to_cpu(num_bytes);
+
+	if (num_bytes > OCC_DATA_MAX || num_bytes < OCC_DATA_MIN) {
+		dev_err(dev, "bad OCC data length:%d\n", num_bytes);
+		return -EINVAL;
+	}
+
+	/* read remaining data, 8 byte scoms at a time */
+	for (i = 8; i < num_bytes + 8; i += 8) {
+		rc = driver->bus_ops.getscom(driver->bus, OCB_DATA,
+					     (u64 *)&occ_data[i]);
+		if (rc) {
+			dev_err(dev, "getscom op failed rc:%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* don't need more sanity checks; buffer is alloc'd for max response
+	 * size so we just check for valid data in parse_occ_response
+	 */
+	rc = parse_occ_response(driver, num_bytes);
+
+	return rc;
+}
+
+int occ_update_device(struct occ *driver)
+{
+	int rc = 0;
+
+	mutex_lock(&driver->update_lock);
+
+	if (time_after(jiffies, driver->last_updated + HZ) || !driver->valid) {
+		driver->valid = true;
+
+		rc = occ_get_all(driver);
+		if (rc)
+			driver->valid = false;
+
+		driver->last_updated = jiffies;
+	}
+
+	mutex_unlock(&driver->update_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(occ_update_device);
+
+void *occ_get_sensor(struct occ *driver, int sensor_type)
+{
+	int rc;
+	int type_id;
+
+	/* occ_update_device locks the update lock */
+	rc = occ_update_device(driver);
+	if (rc)
+		return ERR_PTR(rc);
+
+	type_id = driver->response.data.sensor_block_id[sensor_type];
+	if (type_id == -1)
+		return ERR_PTR(-ENODATA);
+
+	return driver->response.data.blocks[type_id].sensors;
+}
+EXPORT_SYMBOL(occ_get_sensor);
+
+int occ_get_sensor_field(struct occ *occ, int sensor_type, int sensor_num,
+			 u32 hwmon, long *val)
+{
+	return occ->ops.get_sensor(occ, sensor_type, sensor_num, hwmon, val);
+}
+EXPORT_SYMBOL(occ_get_sensor_field);
+
+void occ_get_response_blocks(struct occ *occ, struct occ_blocks **blocks)
+{
+	*blocks = &occ->response.data;
+}
+EXPORT_SYMBOL(occ_get_response_blocks);
+
+int occ_set_user_powercap(struct occ *occ, u16 cap)
+{
+	u8 resp[8];
+
+	cap = cpu_to_be16(cap);
+
+	return occ_send_cmd(occ, 0, OCC_SET_USER_POWR_CAP, 2, (const u8 *)&cap,
+			    resp);
+}
+EXPORT_SYMBOL(occ_set_user_powercap);
+
+struct occ *occ_start(struct device *dev, void *bus,
+		      struct occ_bus_ops *bus_ops, const struct occ_ops *ops,
+		      const struct occ_config *config)
+{
+	struct occ *driver = devm_kzalloc(dev, sizeof(struct occ), GFP_KERNEL);
+
+	if (!driver)
+		return ERR_PTR(-ENOMEM);
+
+	driver->dev = dev;
+	driver->bus = bus;
+	driver->bus_ops = *bus_ops;
+	driver->ops = *ops;
+	driver->config = *config;
+	driver->raw_data = devm_kzalloc(dev, OCC_DATA_MAX, GFP_KERNEL);
+	if (!driver->raw_data)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&driver->update_lock);
+
+	return driver;
+}
+EXPORT_SYMBOL(occ_start);
+
+MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
+MODULE_DESCRIPTION("OCC hwmon core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/occ.h b/drivers/hwmon/occ/occ.h
new file mode 100644
index 0000000..2f9e3c8
--- /dev/null
+++ b/drivers/hwmon/occ/occ.h
@@ -0,0 +1,81 @@
+/*
+ * occ.h - hwmon OCC driver
+ *
+ * This file contains data structures and function prototypes for common access
+ * between different bus protocols and host systems.
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __OCC_H__
+#define __OCC_H__
+
+#include "scom.h"
+
+struct device;
+struct occ;
+
+/* sensor_data_block_header
+ * structure to match the raw occ sensor block header
+ */
+struct sensor_data_block_header {
+	u8 sensor_type[4];
+	u8 reserved0;
+	u8 sensor_format;
+	u8 sensor_length;
+	u8 num_sensors;
+} __attribute__((packed, aligned(4)));
+
+struct sensor_data_block {
+	struct sensor_data_block_header header;
+	void *sensors;
+};
+
+enum sensor_type {
+	FREQ = 0,
+	TEMP,
+	POWER,
+	CAPS,
+	MAX_OCC_SENSOR_TYPE
+};
+
+struct occ_ops {
+	void (*parse_sensor)(u8 *data, void *sensor, int sensor_type, int off,
+			     int snum);
+	void *(*alloc_sensor)(struct device *dev, int sensor_type,
+			      int num_sensors);
+	int (*get_sensor)(struct occ *driver, int sensor_type, int sensor_num,
+			  u32 hwmon, long *val);
+};
+
+struct occ_config {
+	u32 command_addr;
+	u32 response_addr;
+};
+
+struct occ_blocks {
+	int sensor_block_id[MAX_OCC_SENSOR_TYPE];
+	struct sensor_data_block blocks[MAX_OCC_SENSOR_TYPE];
+};
+
+struct occ *occ_start(struct device *dev, void *bus,
+		      struct occ_bus_ops *bus_ops, const struct occ_ops *ops,
+		      const struct occ_config *config);
+void *occ_get_sensor(struct occ *occ, int sensor_type);
+int occ_get_sensor_field(struct occ *occ, int sensor_type, int sensor_num,
+			 u32 hwmon, long *val);
+void occ_get_response_blocks(struct occ *occ, struct occ_blocks **blocks);
+int occ_update_device(struct occ *driver);
+int occ_set_user_powercap(struct occ *occ, u16 cap);
+
+#endif /* __OCC_H__ */
diff --git a/drivers/hwmon/occ/scom.h b/drivers/hwmon/occ/scom.h
new file mode 100644
index 0000000..b0691f3
--- /dev/null
+++ b/drivers/hwmon/occ/scom.h
@@ -0,0 +1,47 @@
+/*
+ * scom.h - hwmon OCC driver
+ *
+ * This file contains data structures for scom operations to the OCC
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SCOM_H__
+#define __SCOM_H__
+
+/*
+ * occ_bus_ops - represent the low-level transfer methods to communicate with
+ * the OCC.
+ *
+ * getscom - OCC scom read
+ * @bus: handle to slave device
+ * @address: address
+ * @data: where to store data read from slave; buffer size must be at least
+ * eight bytes.
+ *
+ * Returns 0 on success or a negative errno on error
+ *
+ * putscom - OCC scom write
+ * @bus: handle to slave device
+ * @address: address
+ * @data0: first data word to write
+ * @data1: second data word to write
+ *
+ * Returns 0 on success or a negative errno on error
+ */
+struct occ_bus_ops {
+	int (*getscom)(void *bus, u32 address, u64 *data);
+	int (*putscom)(void *bus, u32 address, u32 data0, u32 data1);
+};
+
+#endif /* __SCOM_H__ */
-- 
1.8.3.1

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

* [PATCH linux v7 2/6] hwmon: occ: Add sysfs interface
  2017-02-07 23:10 [PATCH linux v7 0/6] drivers: hwmon: Add On-Chip Controller driver eajames
  2017-02-07 23:10 ` [PATCH linux v7 1/6] hwmon: Add core On-Chip Controller support for POWER CPUs eajames
@ 2017-02-07 23:10 ` eajames
  2017-02-10  5:31   ` Joel Stanley
  2017-02-07 23:10   ` eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 23+ messages in thread
From: eajames @ 2017-02-07 23:10 UTC (permalink / raw)
  To: linux
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, jdelvare,
	corbet, mark.rutland, robh+dt, wsa, andrew, joel, benh,
	Edward A. James

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

Add a generic mechanism to expose the sensors provided by the OCC in
sysfs.

Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 Documentation/hwmon/occ       |  62 +++++++++++
 drivers/hwmon/occ/Makefile    |   2 +-
 drivers/hwmon/occ/occ_sysfs.c | 251 ++++++++++++++++++++++++++++++++++++++++++
 drivers/hwmon/occ/occ_sysfs.h |  30 +++++
 4 files changed, 344 insertions(+), 1 deletion(-)
 create mode 100644 drivers/hwmon/occ/occ_sysfs.c
 create mode 100644 drivers/hwmon/occ/occ_sysfs.h

diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
index 79d1642..d0bdf06 100644
--- a/Documentation/hwmon/occ
+++ b/Documentation/hwmon/occ
@@ -25,6 +25,68 @@ Currently, all versions of the OCC support four types of sensor data: power,
 temperature, frequency, and "caps," which indicate limits and thresholds used
 internally on the OCC.
 
+sysfs Entries
+-------------
+
+The OCC driver uses the hwmon sysfs framework to provide data to userspace.
+
+The driver exports a number of sysfs files for each type of sensor. The
+sensor-specific files vary depending on the processor type, though many of the
+attributes are common for both the POWER8 and POWER9.
+
+The hwmon interface cannot define every type of sensor that may be used.
+Therefore, the frequency sensor on the OCC uses the "input" type sensor defined
+by the hwmon interface, rather than defining a new type of custom sensor.
+
+Below are detailed the names and meaning of each sensor file for both types of
+processors. All sensors are read-only unless otherwise specified. <x> indicates
+the hwmon index. sensor id indicates the unique internal OCC identifer. Please
+see the POWER OCC specification for details on all these sensor values.
+
+frequency:
+	all processors:
+		in<x>_input - frequency value
+		in<x>_label - sensor id
+temperature:
+	POWER8:
+		temp<x>_input - temperature value
+		temp<x>_label - sensor id
+	POWER9 (in addition to above):
+		temp<x>_type - FRU type
+
+power:
+	POWER8:
+		power<x>_input - power value
+		power<x>_label - sensor id
+		power<x>_average - accumulator
+		power<x>_average_interval - update tag (number of samples in
+			accumulator)
+	POWER9:
+		power<x>_input - power value
+		power<x>_label - sensor id
+		power<x>_average_min - accumulator[0]
+		power<x>_average_max - accumulator[1] (64 bits total)
+		power<x>_average_interval - update tag
+		power<x>_reset_history - (function_id | (apss_channel << 8)
+
+caps:
+	POWER8:
+		power<x>_cap - current powercap
+		power<x>_cap_max - max powercap
+		power<x>_cap_min - min powercap
+		power<x>_max - normal powercap
+		power<x>_alarm - user powercap, r/w
+	POWER9:
+		power<x>_cap_alarm - user powercap source
+
+The driver also provides two sysfs entries through hwmon to better
+control the driver and monitor the master OCC. Though there may be multiple
+OCCs present on the system, these two files are only present for the "master"
+OCC.
+	name - read the name of the driver
+	update_interval - read or write the minimum interval for polling the
+		OCC.
+
 BMC - Host Communications
 -------------------------
 
diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
index 93cb52f..a6881f9 100644
--- a/drivers/hwmon/occ/Makefile
+++ b/drivers/hwmon/occ/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o
+obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o occ_sysfs.o
diff --git a/drivers/hwmon/occ/occ_sysfs.c b/drivers/hwmon/occ/occ_sysfs.c
new file mode 100644
index 0000000..9598f78
--- /dev/null
+++ b/drivers/hwmon/occ/occ_sysfs.c
@@ -0,0 +1,251 @@
+/*
+ * occ_sysfs.c - OCC sysfs interface
+ *
+ * This file contains the methods and data structures for implementing the OCC
+ * hwmon sysfs entries.
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include "occ.h"
+#include "occ_sysfs.h"
+
+#define OCC_HWMON_NAME_LENGTH	32
+
+struct occ_sysfs {
+	struct device *dev;
+	struct occ *occ;
+
+	char hwmon_name[OCC_HWMON_NAME_LENGTH + 1];
+	const u32 *sensor_hwmon_configs;
+	struct hwmon_channel_info **occ_sensors;
+	struct hwmon_chip_info occ_info;
+	u16 user_powercap;
+};
+
+static int occ_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+			  u32 attr, int channel, long *val)
+{
+	int rc;
+	struct occ_sysfs *driver = dev_get_drvdata(dev);
+	struct occ *occ = driver->occ;
+
+	switch (type) {
+	case hwmon_in:
+		rc = occ_get_sensor_field(occ, FREQ, channel, attr, val);
+		break;
+	case hwmon_temp:
+		rc = occ_get_sensor_field(occ, TEMP, channel, attr, val);
+		break;
+	case hwmon_power:
+		rc = occ_get_sensor_field(occ, POWER, channel, attr, val);
+		break;
+	default:
+		rc = -EOPNOTSUPP;
+	}
+
+	return rc;
+}
+
+static int occ_hwmon_read_string(struct device *dev,
+				 enum hwmon_sensor_types type, u32 attr,
+				 int channel, char **str)
+{
+	int rc;
+	unsigned long val = 0;
+
+	if (!((type == hwmon_in && attr == hwmon_in_label) ||
+	    (type == hwmon_temp && attr == hwmon_temp_label) ||
+	    (type == hwmon_power && attr == hwmon_power_label)))
+		return -EOPNOTSUPP;
+
+	/* will fetch the "label", the sensor_id */
+	rc = occ_hwmon_read(dev, type, attr, channel, &val);
+	if (rc < 0)
+		return rc;
+
+	rc = snprintf(*str, PAGE_SIZE - 1, "%lu", val);
+	if (rc > 0)
+		rc = 0;
+
+	return rc;
+}
+
+static int occ_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+			   u32 attr, int channel, long val)
+{
+	int rc;
+	struct occ_sysfs *driver = dev_get_drvdata(dev);
+
+	if (type == hwmon_power && attr == hwmon_power_alarm) {
+		rc = occ_set_user_powercap(driver->occ, val);
+		if (rc) {
+			dev_err(dev, "set user powercap failed:%d\n", rc);
+			return rc;
+		}
+
+		driver->user_powercap = val;
+
+		return rc;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static umode_t occ_is_visible(const void *data, enum hwmon_sensor_types type,
+			      u32 attr, int channel)
+{
+	const struct occ_sysfs *driver = data;
+
+	switch (type) {
+	case hwmon_chip:
+		if (attr == hwmon_chip_update_interval)
+			return 00644;
+		break;
+	case hwmon_in:
+		if (BIT(attr) & driver->sensor_hwmon_configs[0])
+			return 00444;
+		break;
+	case hwmon_temp:
+		if (BIT(attr) & driver->sensor_hwmon_configs[1])
+			return 00444;
+		break;
+	case hwmon_power:
+		/* user power limit */
+		if (attr == hwmon_power_alarm)
+			return 00644;
+		else if ((BIT(attr) & driver->sensor_hwmon_configs[2]) ||
+			 (BIT(attr) & driver->sensor_hwmon_configs[3]))
+			return 00444;
+		break;
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
+static const struct hwmon_ops occ_hwmon_ops = {
+	.is_visible = occ_is_visible,
+	.read = occ_hwmon_read,
+	.read_string = occ_hwmon_read_string,
+	.write = occ_hwmon_write,
+};
+
+static const enum hwmon_sensor_types occ_sensor_types[MAX_OCC_SENSOR_TYPE] = {
+	hwmon_in,
+	hwmon_temp,
+	hwmon_power,
+	hwmon_power
+};
+
+struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ,
+				  const u32 *sensor_hwmon_configs,
+				  const char *name)
+{
+	int rc, i, j, num_sensors, index = 0, id;
+	char *brk;
+	struct occ_blocks *resp = NULL;
+	u32 *sensor_config;
+	struct occ_sysfs *hwmon = devm_kzalloc(dev, sizeof(struct occ_sysfs),
+					       GFP_KERNEL);
+	if (!hwmon)
+		return ERR_PTR(-ENOMEM);
+
+	/* need space for null-termination */
+	hwmon->occ_sensors =
+		devm_kzalloc(dev, sizeof(struct hwmon_channel_info *) *
+			     (MAX_OCC_SENSOR_TYPE + 1), GFP_KERNEL);
+	if (!hwmon->occ_sensors)
+		return ERR_PTR(-ENOMEM);
+
+	hwmon->occ = occ;
+	hwmon->sensor_hwmon_configs = sensor_hwmon_configs;
+	hwmon->occ_info.ops = &occ_hwmon_ops;
+	hwmon->occ_info.info =
+		(const struct hwmon_channel_info **)hwmon->occ_sensors;
+
+	occ_get_response_blocks(occ, &resp);
+
+	for (i = 0; i < MAX_OCC_SENSOR_TYPE; ++i)
+		resp->sensor_block_id[i] = -1;
+
+	/* read sensor data from occ */
+	rc = occ_update_device(occ);
+	if (rc)
+		return ERR_PTR(rc);
+
+	for (i = 0; i < MAX_OCC_SENSOR_TYPE; i++) {
+		id = resp->sensor_block_id[i];
+		if (id < 0)
+			continue;
+
+		num_sensors = resp->blocks[id].header.num_sensors;
+		/* need null-termination */
+		sensor_config = devm_kzalloc(dev,
+					     sizeof(u32) * (num_sensors + 1),
+					     GFP_KERNEL);
+		if (!sensor_config)
+			return ERR_PTR(-ENOMEM);
+
+		for (j = 0; j < num_sensors; j++)
+			sensor_config[j] = sensor_hwmon_configs[i];
+
+		hwmon->occ_sensors[index] =
+			devm_kzalloc(dev, sizeof(struct hwmon_channel_info),
+				     GFP_KERNEL);
+		if (!hwmon->occ_sensors[index])
+			return ERR_PTR(-ENOMEM);
+
+		hwmon->occ_sensors[index]->type = occ_sensor_types[i];
+		hwmon->occ_sensors[index]->config = sensor_config;
+		index++;
+	}
+
+	/* search for bad chars */
+	strncpy(hwmon->hwmon_name, name, OCC_HWMON_NAME_LENGTH);
+	brk = strpbrk(hwmon->hwmon_name, "-* \t\n");
+	while (brk) {
+		*brk = '_';
+		brk = strpbrk(brk,  "-* \t\n");
+	}
+
+	hwmon->dev = devm_hwmon_device_register_with_info(dev,
+							  hwmon->hwmon_name,
+							  hwmon,
+							  &hwmon->occ_info,
+							  NULL);
+	if (IS_ERR(hwmon->dev)) {
+		dev_err(dev, "cannot register hwmon device %s: %ld\n",
+			hwmon->hwmon_name, PTR_ERR(hwmon->dev));
+		return ERR_CAST(hwmon->dev);
+	}
+
+	return hwmon;
+}
+EXPORT_SYMBOL(occ_sysfs_start);
+
+MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
+MODULE_DESCRIPTION("OCC sysfs driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/occ_sysfs.h b/drivers/hwmon/occ/occ_sysfs.h
new file mode 100644
index 0000000..462f9d2
--- /dev/null
+++ b/drivers/hwmon/occ/occ_sysfs.h
@@ -0,0 +1,30 @@
+/*
+ * occ_sysfs.h - OCC sysfs interface
+ *
+ * This file contains the data structures and function prototypes for the OCC
+ * hwmon sysfs entries.
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __OCC_SYSFS_H__
+#define __OCC_SYSFS_H__
+
+struct occ;
+struct device;
+struct occ_sysfs;
+
+struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ,
+				  const u32 *sensor_hwmon_configs,
+				  const char *name);
+#endif /* __OCC_SYSFS_H__ */
-- 
1.8.3.1

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

* [PATCH linux v7 3/6] hwmon: occ: Add I2C transport implementation for SCOM operations
@ 2017-02-07 23:10   ` eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8
  0 siblings, 0 replies; 23+ messages in thread
From: eajames @ 2017-02-07 23:10 UTC (permalink / raw)
  To: linux
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, jdelvare,
	corbet, mark.rutland, robh+dt, wsa, andrew, joel, benh,
	Edward A. James

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

Add functions to send SCOM operations over I2C bus. The BMC can
communicate with the Power8 host processor over I2C, but needs to use SCOM
operations in order to access the OCC register space.

Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 drivers/hwmon/occ/occ_scom_i2c.c | 77 ++++++++++++++++++++++++++++++++++++++++
 drivers/hwmon/occ/occ_scom_i2c.h | 26 ++++++++++++++
 2 files changed, 103 insertions(+)
 create mode 100644 drivers/hwmon/occ/occ_scom_i2c.c
 create mode 100644 drivers/hwmon/occ/occ_scom_i2c.h

diff --git a/drivers/hwmon/occ/occ_scom_i2c.c b/drivers/hwmon/occ/occ_scom_i2c.c
new file mode 100644
index 0000000..74bd6ff
--- /dev/null
+++ b/drivers/hwmon/occ/occ_scom_i2c.c
@@ -0,0 +1,77 @@
+/*
+ * occ_scom_i2c.c - hwmon OCC driver
+ *
+ * This file contains the functions for performing SCOM operations over I2C bus
+ * to access the OCC.
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "occ_scom_i2c.h"
+#include "scom.h"
+
+int occ_i2c_getscom(void *bus, u32 address, u64 *data)
+{
+	ssize_t rc;
+	u64 buf;
+	struct i2c_client *client = bus;
+	struct i2c_msg msgs[2];
+
+	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;
+	msgs[1].flags |= 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;
+
+	*data = be64_to_cpu(buf);
+
+	return 0;
+}
+EXPORT_SYMBOL(occ_i2c_getscom);
+
+int occ_i2c_putscom(void *bus, u32 address, u32 data0, u32 data1)
+{
+	u32 buf[3];
+	ssize_t rc;
+	struct i2c_client *client = bus;
+
+	/* send raw data, user can handle endian */
+	buf[0] = address;
+	buf[1] = data1;
+	buf[2] = data0;
+
+	rc = i2c_master_send(client, (const char *)buf, sizeof(u32) * 3);
+	if (rc < 0)
+		return rc;
+	else if (rc != sizeof(u32) * 3)
+		return -EIO;
+
+	return 0;
+}
+EXPORT_SYMBOL(occ_i2c_putscom);
+
+MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
+MODULE_DESCRIPTION("I2C OCC SCOM transport");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/occ_scom_i2c.h b/drivers/hwmon/occ/occ_scom_i2c.h
new file mode 100644
index 0000000..945739c
--- /dev/null
+++ b/drivers/hwmon/occ/occ_scom_i2c.h
@@ -0,0 +1,26 @@
+/*
+ * occ_scom_i2c.h - hwmon OCC driver
+ *
+ * This file contains function protoypes for peforming SCOM operations over I2C
+ * bus to access the OCC.
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __OCC_SCOM_I2C_H__
+#define __OCC_SCOM_I2C_H__
+
+int occ_i2c_getscom(void *bus, u32 address, u64 *data);
+int occ_i2c_putscom(void *bus, u32 address, u32 data0, u32 data1);
+
+#endif /* __OCC_SCOM_I2C_H__ */
-- 
1.8.3.1

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

* [PATCH linux v7 3/6] hwmon: occ: Add I2C transport implementation for SCOM operations
@ 2017-02-07 23:10   ` eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8
  0 siblings, 0 replies; 23+ messages in thread
From: eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8 @ 2017-02-07 23:10 UTC (permalink / raw)
  To: linux-0h96xk9xTtrk1uMJSBkQmQ
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-hwmon-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, jdelvare-IBi9RG/b67k,
	corbet-T1hC0tSOHrs, mark.rutland-5wv7dgnIgG8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, wsa-z923LK4zBo2bacvFa/9K2g,
	andrew-zrmu5oMJ5Fs, joel-U3u1mxZcP9KHXe+LvDLADg,
	benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Edward A. James

From: "Edward A. James" <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>

Add functions to send SCOM operations over I2C bus. The BMC can
communicate with the Power8 host processor over I2C, but needs to use SCOM
operations in order to access the OCC register space.

Signed-off-by: Edward A. James <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Andrew Jeffery <andrew-zrmu5oMJ5Fs@public.gmane.org>
---
 drivers/hwmon/occ/occ_scom_i2c.c | 77 ++++++++++++++++++++++++++++++++++++++++
 drivers/hwmon/occ/occ_scom_i2c.h | 26 ++++++++++++++
 2 files changed, 103 insertions(+)
 create mode 100644 drivers/hwmon/occ/occ_scom_i2c.c
 create mode 100644 drivers/hwmon/occ/occ_scom_i2c.h

diff --git a/drivers/hwmon/occ/occ_scom_i2c.c b/drivers/hwmon/occ/occ_scom_i2c.c
new file mode 100644
index 0000000..74bd6ff
--- /dev/null
+++ b/drivers/hwmon/occ/occ_scom_i2c.c
@@ -0,0 +1,77 @@
+/*
+ * occ_scom_i2c.c - hwmon OCC driver
+ *
+ * This file contains the functions for performing SCOM operations over I2C bus
+ * to access the OCC.
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "occ_scom_i2c.h"
+#include "scom.h"
+
+int occ_i2c_getscom(void *bus, u32 address, u64 *data)
+{
+	ssize_t rc;
+	u64 buf;
+	struct i2c_client *client = bus;
+	struct i2c_msg msgs[2];
+
+	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;
+	msgs[1].flags |= 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;
+
+	*data = be64_to_cpu(buf);
+
+	return 0;
+}
+EXPORT_SYMBOL(occ_i2c_getscom);
+
+int occ_i2c_putscom(void *bus, u32 address, u32 data0, u32 data1)
+{
+	u32 buf[3];
+	ssize_t rc;
+	struct i2c_client *client = bus;
+
+	/* send raw data, user can handle endian */
+	buf[0] = address;
+	buf[1] = data1;
+	buf[2] = data0;
+
+	rc = i2c_master_send(client, (const char *)buf, sizeof(u32) * 3);
+	if (rc < 0)
+		return rc;
+	else if (rc != sizeof(u32) * 3)
+		return -EIO;
+
+	return 0;
+}
+EXPORT_SYMBOL(occ_i2c_putscom);
+
+MODULE_AUTHOR("Eddie James <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("I2C OCC SCOM transport");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/occ_scom_i2c.h b/drivers/hwmon/occ/occ_scom_i2c.h
new file mode 100644
index 0000000..945739c
--- /dev/null
+++ b/drivers/hwmon/occ/occ_scom_i2c.h
@@ -0,0 +1,26 @@
+/*
+ * occ_scom_i2c.h - hwmon OCC driver
+ *
+ * This file contains function protoypes for peforming SCOM operations over I2C
+ * bus to access the OCC.
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __OCC_SCOM_I2C_H__
+#define __OCC_SCOM_I2C_H__
+
+int occ_i2c_getscom(void *bus, u32 address, u64 *data);
+int occ_i2c_putscom(void *bus, u32 address, u32 data0, u32 data1);
+
+#endif /* __OCC_SCOM_I2C_H__ */
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH linux v7 4/6] hwmon: occ: Add callbacks for parsing P8 OCC datastructures
  2017-02-07 23:10 [PATCH linux v7 0/6] drivers: hwmon: Add On-Chip Controller driver eajames
                   ` (2 preceding siblings ...)
  2017-02-07 23:10   ` eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8
@ 2017-02-07 23:10 ` eajames
  2017-02-10  5:31   ` Joel Stanley
  2017-02-07 23:10 ` [PATCH linux v7 5/6] hwmon: occ: Add hwmon implementation for the P8 OCC eajames
  2017-02-07 23:10 ` [PATCH linux v7 6/6] hwmon: occ: Add callbacks for parsing P9 OCC datastructures eajames
  5 siblings, 1 reply; 23+ messages in thread
From: eajames @ 2017-02-07 23:10 UTC (permalink / raw)
  To: linux
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, jdelvare,
	corbet, mark.rutland, robh+dt, wsa, andrew, joel, benh,
	Edward A. James

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

Add functions to parse the data structures that are specific to the OCC on
the POWER8 processor. These are the sensor data structures, including
temperature, frequency, power, and "caps."

Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 Documentation/hwmon/occ    |   9 ++
 drivers/hwmon/occ/occ_p8.c | 248 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/hwmon/occ/occ_p8.h |  30 ++++++
 3 files changed, 287 insertions(+)
 create mode 100644 drivers/hwmon/occ/occ_p8.c
 create mode 100644 drivers/hwmon/occ/occ_p8.h

diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
index d0bdf06..143951e 100644
--- a/Documentation/hwmon/occ
+++ b/Documentation/hwmon/occ
@@ -25,6 +25,15 @@ Currently, all versions of the OCC support four types of sensor data: power,
 temperature, frequency, and "caps," which indicate limits and thresholds used
 internally on the OCC.
 
+The format for the POWER8 OCC sensor data can be found in the P8 OCC
+specification:
+github.com/open-power/docs/blob/master/occ/OCC_OpenPwr_FW_Interfaces.pdf
+This document provides the details of the OCC sensors: power, frequency,
+temperature, and caps. These sensor formats are specific to the POWER8 OCC. A
+number of data structures, such as command format, response headers, and the
+like, are also defined in this specification, and are common to both POWER8 and
+POWER9 OCCs.
+
 sysfs Entries
 -------------
 
diff --git a/drivers/hwmon/occ/occ_p8.c b/drivers/hwmon/occ/occ_p8.c
new file mode 100644
index 0000000..5c61fc4
--- /dev/null
+++ b/drivers/hwmon/occ/occ_p8.c
@@ -0,0 +1,248 @@
+/*
+ * occ_p8.c - OCC hwmon driver
+ *
+ * This file contains the Power8-specific methods and data structures for
+ * the OCC hwmon driver.
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include "occ.h"
+#include "occ_p8.h"
+
+/* P8 OCC sensor data format */
+struct p8_occ_sensor {
+	u16 sensor_id;
+	u16 value;
+};
+
+struct p8_power_sensor {
+	u16 sensor_id;
+	u32 update_tag;
+	u32 accumulator;
+	u16 value;
+};
+
+struct p8_caps_sensor {
+	u16 curr_powercap;
+	u16 curr_powerreading;
+	u16 norm_powercap;
+	u16 max_powercap;
+	u16 min_powercap;
+	u16 user_powerlimit;
+};
+
+static const u32 p8_sensor_hwmon_configs[MAX_OCC_SENSOR_TYPE] = {
+	HWMON_I_INPUT | HWMON_I_LABEL,	/* freq: value | label */
+	HWMON_T_INPUT | HWMON_T_LABEL,	/* temp: value | label */
+	/* power: value | label | accumulator | update_tag */
+	HWMON_P_INPUT | HWMON_P_LABEL | HWMON_P_AVERAGE |
+		HWMON_P_AVERAGE_INTERVAL,
+	/* caps: curr | max | min | norm | user */
+	HWMON_P_CAP | HWMON_P_CAP_MAX | HWMON_P_CAP_MIN | HWMON_P_MAX |
+		HWMON_P_ALARM,
+};
+
+void p8_parse_sensor(u8 *data, void *sensor, int sensor_type, int off,
+		     int snum)
+{
+	switch (sensor_type) {
+	case FREQ:
+	case TEMP:
+	{
+		struct p8_occ_sensor *os =
+			&(((struct p8_occ_sensor *)sensor)[snum]);
+
+		os->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
+		os->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 2]));
+	}
+		break;
+	case POWER:
+	{
+		struct p8_power_sensor *ps =
+			&(((struct p8_power_sensor *)sensor)[snum]);
+
+		ps->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
+		ps->update_tag =
+			be32_to_cpu(get_unaligned((u32 *)&data[off + 2]));
+		ps->accumulator =
+			be32_to_cpu(get_unaligned((u32 *)&data[off + 6]));
+		ps->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 10]));
+	}
+		break;
+	case CAPS:
+	{
+		struct p8_caps_sensor *cs =
+			&(((struct p8_caps_sensor *)sensor)[snum]);
+
+		cs->curr_powercap =
+			be16_to_cpu(get_unaligned((u16 *)&data[off]));
+		cs->curr_powerreading =
+			be16_to_cpu(get_unaligned((u16 *)&data[off + 2]));
+		cs->norm_powercap =
+			be16_to_cpu(get_unaligned((u16 *)&data[off + 4]));
+		cs->max_powercap =
+			be16_to_cpu(get_unaligned((u16 *)&data[off + 6]));
+		cs->min_powercap =
+			be16_to_cpu(get_unaligned((u16 *)&data[off + 8]));
+		cs->user_powerlimit =
+			be16_to_cpu(get_unaligned((u16 *)&data[off + 10]));
+	}
+		break;
+	};
+}
+
+void *p8_alloc_sensor(struct device *dev, int sensor_type, int num_sensors)
+{
+	switch (sensor_type) {
+	case FREQ:
+	case TEMP:
+		return devm_kzalloc(dev, num_sensors *
+				    sizeof(struct p8_occ_sensor), GFP_KERNEL);
+	case POWER:
+		return devm_kzalloc(dev, num_sensors *
+				    sizeof(struct p8_power_sensor),
+				    GFP_KERNEL);
+	case CAPS:
+		return devm_kzalloc(dev, num_sensors *
+				    sizeof(struct p8_caps_sensor), GFP_KERNEL);
+	default:
+		return NULL;
+	}
+}
+
+int p8_get_sensor(struct occ *driver, int sensor_type, int sensor_num,
+		  u32 hwmon, long *val)
+{
+	int rc = 0;
+	void *sensor;
+
+	if (sensor_type == POWER) {
+		if (hwmon == hwmon_power_cap || hwmon == hwmon_power_cap_max ||
+		    hwmon == hwmon_power_cap_min || hwmon == hwmon_power_max ||
+		    hwmon == hwmon_power_alarm)
+			sensor_type = CAPS;
+	}
+
+	sensor = occ_get_sensor(driver, sensor_type);
+	if (!sensor)
+		return -ENODEV;
+
+	switch (sensor_type) {
+	case FREQ:
+	case TEMP:
+	{
+		struct p8_occ_sensor *os =
+			&(((struct p8_occ_sensor *)sensor)[sensor_num]);
+
+		if (hwmon == hwmon_in_input || hwmon == hwmon_temp_input)
+			*val = os->value;
+		else if (hwmon == hwmon_in_label || hwmon == hwmon_temp_label)
+			*val = os->sensor_id;
+		else
+			rc = -EOPNOTSUPP;
+	}
+		break;
+	case POWER:
+	{
+		struct p8_power_sensor *ps =
+			&(((struct p8_power_sensor *)sensor)[sensor_num]);
+
+		switch (hwmon) {
+		case hwmon_power_input:
+			*val = ps->value;
+			break;
+		case hwmon_power_label:
+			*val = ps->sensor_id;
+			break;
+		case hwmon_power_average:
+			*val = ps->accumulator;
+			break;
+		case hwmon_power_average_interval:
+			*val = ps->update_tag;
+			break;
+		default:
+			rc = -EOPNOTSUPP;
+		}
+	}
+		break;
+	case CAPS:
+	{
+		struct p8_caps_sensor *cs =
+			&(((struct p8_caps_sensor *)sensor)[sensor_num]);
+
+		switch (hwmon) {
+		case hwmon_power_cap:
+			*val = cs->curr_powercap;
+			break;
+		case hwmon_power_cap_max:
+			*val = cs->max_powercap;
+			break;
+		case hwmon_power_cap_min:
+			*val = cs->min_powercap;
+			break;
+		case hwmon_power_max:
+			*val = cs->norm_powercap;
+			break;
+		case hwmon_power_alarm:
+			*val = cs->user_powerlimit;
+			break;
+		default:
+			rc = -EOPNOTSUPP;
+		}
+	}
+		break;
+	default:
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static const struct occ_ops p8_ops = {
+	.parse_sensor = p8_parse_sensor,
+	.alloc_sensor = p8_alloc_sensor,
+	.get_sensor = p8_get_sensor,
+};
+
+static const struct occ_config p8_config = {
+	.command_addr = 0xFFFF6000,
+	.response_addr = 0xFFFF7000,
+};
+
+const u32 *p8_get_sensor_hwmon_configs()
+{
+	return p8_sensor_hwmon_configs;
+}
+EXPORT_SYMBOL(p8_get_sensor_hwmon_configs);
+
+struct occ *p8_occ_start(struct device *dev, void *bus,
+			 struct occ_bus_ops *bus_ops)
+{
+	return occ_start(dev, bus, bus_ops, &p8_ops, &p8_config);
+}
+EXPORT_SYMBOL(p8_occ_start);
+
+MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
+MODULE_DESCRIPTION("P8 OCC sensors");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/occ_p8.h b/drivers/hwmon/occ/occ_p8.h
new file mode 100644
index 0000000..3fe6417
--- /dev/null
+++ b/drivers/hwmon/occ/occ_p8.h
@@ -0,0 +1,30 @@
+/*
+ * occ_p8.h - OCC hwmon driver
+ *
+ * This file contains Power8-specific function prototypes
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __OCC_P8_H__
+#define __OCC_P8_H__
+
+#include "scom.h"
+
+struct device;
+
+const u32 *p8_get_sensor_hwmon_configs(void);
+struct occ *p8_occ_start(struct device *dev, void *bus,
+			 struct occ_bus_ops *bus_ops);
+
+#endif /* __OCC_P8_H__ */
-- 
1.8.3.1

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

* [PATCH linux v7 5/6] hwmon: occ: Add hwmon implementation for the P8 OCC
  2017-02-07 23:10 [PATCH linux v7 0/6] drivers: hwmon: Add On-Chip Controller driver eajames
                   ` (3 preceding siblings ...)
  2017-02-07 23:10 ` [PATCH linux v7 4/6] hwmon: occ: Add callbacks for parsing P8 OCC datastructures eajames
@ 2017-02-07 23:10 ` eajames
  2017-02-10  5:31   ` Joel Stanley
  2017-02-07 23:10 ` [PATCH linux v7 6/6] hwmon: occ: Add callbacks for parsing P9 OCC datastructures eajames
  5 siblings, 1 reply; 23+ messages in thread
From: eajames @ 2017-02-07 23:10 UTC (permalink / raw)
  To: linux
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, jdelvare,
	corbet, mark.rutland, robh+dt, wsa, andrew, joel, benh,
	Edward A. James

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

Add code to tie the hwmon sysfs code and the POWER8 OCC code together, as
well as probe the entire driver from the I2C bus. I2C is the communication
method between the BMC and the P8 OCC.

Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Acked-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/hwmon/occ.txt |  13 +++
 drivers/hwmon/occ/Kconfig                       |  14 ++++
 drivers/hwmon/occ/Makefile                      |   1 +
 drivers/hwmon/occ/p8_occ_i2c.c                  | 104 ++++++++++++++++++++++++
 4 files changed, 132 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/occ.txt
 create mode 100644 drivers/hwmon/occ/p8_occ_i2c.c

diff --git a/Documentation/devicetree/bindings/hwmon/occ.txt b/Documentation/devicetree/bindings/hwmon/occ.txt
new file mode 100644
index 0000000..b0d2b36
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/occ.txt
@@ -0,0 +1,13 @@
+HWMON I2C driver for IBM POWER CPU OCC (On Chip Controller)
+
+Required properties:
+ - compatible: must be "ibm,p8-occ-i2c"
+ - reg: physical address
+
+Example:
+i2c3: i2c-bus@100 {
+	occ@50 {
+		compatible = "ibm,p8-occ-i2c";
+		reg = <0x50>;
+	};
+};
diff --git a/drivers/hwmon/occ/Kconfig b/drivers/hwmon/occ/Kconfig
index cdb64a7..3a5188f 100644
--- a/drivers/hwmon/occ/Kconfig
+++ b/drivers/hwmon/occ/Kconfig
@@ -13,3 +13,17 @@ menuconfig SENSORS_PPC_OCC
 
 	  This driver can also be built as a module. If so, the module
 	  will be called occ.
+
+if SENSORS_PPC_OCC
+
+config SENSORS_PPC_OCC_P8_I2C
+	tristate "POWER8 OCC hwmon support"
+	depends on I2C
+	help
+	 Provide a hwmon sysfs interface for the POWER8 On-Chip Controller,
+	 exposing temperature, frequency and power measurements.
+
+	 This driver can also be built as a module. If so, the module will be
+	 called p8-occ-i2c.
+
+endif
diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
index a6881f9..9294b58 100644
--- a/drivers/hwmon/occ/Makefile
+++ b/drivers/hwmon/occ/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o occ_sysfs.o
+obj-$(CONFIG_SENSORS_PPC_OCC_P8_I2C) += occ_scom_i2c.o occ_p8.o p8_occ_i2c.o
diff --git a/drivers/hwmon/occ/p8_occ_i2c.c b/drivers/hwmon/occ/p8_occ_i2c.c
new file mode 100644
index 0000000..6273040
--- /dev/null
+++ b/drivers/hwmon/occ/p8_occ_i2c.c
@@ -0,0 +1,104 @@
+/*
+ * p8_occ_i2c.c - hwmon OCC driver
+ *
+ * This file contains the i2c layer for accessing the P8 OCC over i2c bus.
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include "occ_p8.h"
+#include "occ_scom_i2c.h"
+#include "occ_sysfs.h"
+#include "scom.h"
+
+#define P8_OCC_I2C_NAME	"p8-occ-i2c"
+
+int p8_i2c_getscom(void *bus, u32 address, u64 *data)
+{
+	/* P8 i2c slave requires address to be shifted by 1 */
+	address = address << 1;
+
+	return occ_i2c_getscom(bus, address, data);
+}
+
+int p8_i2c_putscom(void *bus, u32 address, u32 data0, u32 data1)
+{
+	/* P8 i2c slave requires address to be shifted by 1 */
+	address = address << 1;
+
+	return occ_i2c_putscom(bus, address, data0, data1);
+}
+
+static struct occ_bus_ops p8_bus_ops = {
+	.getscom = p8_i2c_getscom,
+	.putscom = p8_i2c_putscom,
+};
+
+static int p8_occ_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct occ *occ;
+	struct occ_sysfs *hwmon;
+	const u32 *sensor_hwmon_configs = p8_get_sensor_hwmon_configs();
+
+	occ = p8_occ_start(&client->dev, client, &p8_bus_ops);
+	if (IS_ERR(occ))
+		return PTR_ERR(occ);
+
+	hwmon = occ_sysfs_start(&client->dev, occ, sensor_hwmon_configs,
+				P8_OCC_I2C_NAME);
+	if (IS_ERR(hwmon))
+		return PTR_ERR(hwmon);
+
+	i2c_set_clientdata(client, occ);
+
+	return 0;
+}
+
+/* used by old-style board info. */
+static const struct i2c_device_id occ_ids[] = {
+	{ P8_OCC_I2C_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, occ_ids);
+
+/* used by device table */
+static const struct of_device_id occ_of_match[] = {
+	{ .compatible = "ibm,p8-occ-i2c" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, occ_of_match);
+
+static struct i2c_driver p8_occ_driver = {
+	.class = I2C_CLASS_HWMON,
+	.driver = {
+		.name = P8_OCC_I2C_NAME,
+		.of_match_table = occ_of_match,
+	},
+	.probe = p8_occ_probe,
+	.id_table = occ_ids,
+};
+
+module_i2c_driver(p8_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] 23+ messages in thread

* [PATCH linux v7 6/6] hwmon: occ: Add callbacks for parsing P9 OCC datastructures
  2017-02-07 23:10 [PATCH linux v7 0/6] drivers: hwmon: Add On-Chip Controller driver eajames
                   ` (4 preceding siblings ...)
  2017-02-07 23:10 ` [PATCH linux v7 5/6] hwmon: occ: Add hwmon implementation for the P8 OCC eajames
@ 2017-02-07 23:10 ` eajames
  2017-02-10  5:31   ` Joel Stanley
  5 siblings, 1 reply; 23+ messages in thread
From: eajames @ 2017-02-07 23:10 UTC (permalink / raw)
  To: linux
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, jdelvare,
	corbet, mark.rutland, robh+dt, wsa, andrew, joel, benh,
	Edward A. James

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

Add functions to parse the data structures that are specific to the OCC on
the POWER9 processor. These are the sensor data structures, including
temperature, frequency, power, and "caps."

Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 Documentation/hwmon/occ    |   3 +
 drivers/hwmon/occ/occ_p9.c | 309 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/hwmon/occ/occ_p9.h |  30 +++++
 3 files changed, 342 insertions(+)
 create mode 100644 drivers/hwmon/occ/occ_p9.c
 create mode 100644 drivers/hwmon/occ/occ_p9.h

diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
index 143951e..6cea853 100644
--- a/Documentation/hwmon/occ
+++ b/Documentation/hwmon/occ
@@ -34,6 +34,9 @@ number of data structures, such as command format, response headers, and the
 like, are also defined in this specification, and are common to both POWER8 and
 POWER9 OCCs.
 
+There is currently no public P9 OCC specification, and the data structures
+defined in the POWER9 OCC driver are subject to change.
+
 sysfs Entries
 -------------
 
diff --git a/drivers/hwmon/occ/occ_p9.c b/drivers/hwmon/occ/occ_p9.c
new file mode 100644
index 0000000..9c1283c
--- /dev/null
+++ b/drivers/hwmon/occ/occ_p9.c
@@ -0,0 +1,309 @@
+/*
+ * occ_p9.c - OCC hwmon driver
+ *
+ * This file contains the Power9-specific methods and data structures for
+ * the OCC hwmon driver.
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include "occ.h"
+#include "occ_p9.h"
+
+/* P9 OCC sensor data format */
+struct p9_temp_sensor {
+	u32 sensor_id;
+	u8 fru_type;
+	u8 value;
+};
+
+struct p9_freq_sensor {
+	u32 sensor_id;
+	u16 value;
+};
+
+struct p9_power_sensor {
+	u32 sensor_id;
+	u8 function_id;
+	u8 apss_channel;
+	u16 reserved;
+	u32 update_tag;
+	u64 accumulator;
+	u16 value;
+};
+
+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;
+};
+
+static const u32 p9_sensor_hwmon_configs[MAX_OCC_SENSOR_TYPE] = {
+	HWMON_I_INPUT | HWMON_I_LABEL,	/* freq: value | label */
+	/* temp: value | label | fru_type */
+	HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_TYPE,
+	/* power: value | label | accum[0] | accum[1] | update_tag |
+	 *	 (function_id | (apss_channel << 8))
+	 */
+	HWMON_P_INPUT | HWMON_P_LABEL | HWMON_P_AVERAGE_MIN |
+		HWMON_P_AVERAGE_MAX | HWMON_P_AVERAGE_INTERVAL |
+		HWMON_P_RESET_HISTORY,
+	/* caps: curr | max | min | norm | user | source */
+	HWMON_P_CAP | HWMON_P_CAP_MAX | HWMON_P_CAP_MIN | HWMON_P_MAX |
+		HWMON_P_ALARM | HWMON_P_CAP_ALARM,
+};
+
+void p9_parse_sensor(u8 *data, void *sensor, int sensor_type, int off,
+		     int snum)
+{
+	switch (sensor_type) {
+	case FREQ:
+	{
+		struct p9_freq_sensor *fs =
+			&(((struct p9_freq_sensor *)sensor)[snum]);
+
+		fs->sensor_id = be32_to_cpu(get_unaligned((u32 *)&data[off]));
+		fs->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 4]));
+	}
+		break;
+	case TEMP:
+	{
+		struct p9_temp_sensor *ts =
+			&(((struct p9_temp_sensor *)sensor)[snum]);
+
+		ts->sensor_id = be32_to_cpu(get_unaligned((u32 *)&data[off]));
+		ts->fru_type = data[off + 4];
+		ts->value = data[off + 5];
+	}
+		break;
+	case POWER:
+	{
+		struct p9_power_sensor *ps =
+			&(((struct p9_power_sensor *)sensor)[snum]);
+
+		ps->sensor_id = be32_to_cpu(get_unaligned((u32 *)&data[off]));
+		ps->function_id = data[off + 4];
+		ps->apss_channel = data[off + 5];
+		ps->update_tag =
+			be32_to_cpu(get_unaligned((u32 *)&data[off + 8]));
+		ps->accumulator =
+			be64_to_cpu(get_unaligned((u64 *)&data[off + 12]));
+		ps->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 20]));
+	}
+		break;
+	case CAPS:
+	{
+		struct p9_caps_sensor *cs =
+			&(((struct p9_caps_sensor *)sensor)[snum]);
+
+		cs->curr_powercap =
+			be16_to_cpu(get_unaligned((u16 *)&data[off]));
+		cs->curr_powerreading =
+			be16_to_cpu(get_unaligned((u16 *)&data[off + 2]));
+		cs->norm_powercap =
+			be16_to_cpu(get_unaligned((u16 *)&data[off + 4]));
+		cs->max_powercap =
+			be16_to_cpu(get_unaligned((u16 *)&data[off + 6]));
+		cs->min_powercap =
+			be16_to_cpu(get_unaligned((u16 *)&data[off + 8]));
+		cs->user_powerlimit =
+			be16_to_cpu(get_unaligned((u16 *)&data[off + 10]));
+		cs->user_powerlimit_source = data[off + 12];
+	}
+		break;
+	};
+}
+
+void *p9_alloc_sensor(struct device *dev, int sensor_type, int num_sensors)
+{
+	switch (sensor_type) {
+	case FREQ:
+		return devm_kzalloc(dev, num_sensors *
+				    sizeof(struct p9_freq_sensor), GFP_KERNEL);
+	case TEMP:
+		return devm_kzalloc(dev, num_sensors *
+				    sizeof(struct p9_temp_sensor), GFP_KERNEL);
+	case POWER:
+		return devm_kzalloc(dev, num_sensors *
+				    sizeof(struct p9_power_sensor),
+				    GFP_KERNEL);
+	case CAPS:
+		return devm_kzalloc(dev, num_sensors *
+				    sizeof(struct p9_caps_sensor), GFP_KERNEL);
+	default:
+		return NULL;
+	}
+}
+
+int p9_get_sensor(struct occ *driver, int sensor_type, int sensor_num,
+		  u32 hwmon, long *val)
+{
+	int rc = 0;
+	void *sensor;
+
+	if (sensor_type == POWER) {
+		if (hwmon == hwmon_power_cap || hwmon == hwmon_power_cap_max ||
+		    hwmon == hwmon_power_cap_min || hwmon == hwmon_power_max ||
+		    hwmon == hwmon_power_alarm ||
+		    hwmon == hwmon_power_cap_alarm)
+			sensor_type = CAPS;
+	}
+
+	sensor = occ_get_sensor(driver, sensor_type);
+	if (!sensor)
+		return -ENODEV;
+
+	switch (sensor_type) {
+	case FREQ:
+	{
+		struct p9_freq_sensor *fs =
+			&(((struct p9_freq_sensor *)sensor)[sensor_num]);
+
+		switch (hwmon) {
+		case hwmon_in_input:
+			*val = fs->value;
+			break;
+		case hwmon_in_label:
+			*val = fs->sensor_id;
+			break;
+		default:
+			rc = -EOPNOTSUPP;
+		}
+	}
+		break;
+	case TEMP:
+	{
+		struct p9_temp_sensor *ts =
+			&(((struct p9_temp_sensor *)sensor)[sensor_num]);
+
+		switch (hwmon) {
+		case hwmon_temp_input:
+			*val = ts->value;
+			break;
+		case hwmon_temp_type:
+			*val = ts->fru_type;
+			break;
+		case hwmon_temp_label:
+			*val = ts->sensor_id;
+			break;
+		default:
+			rc = -EOPNOTSUPP;
+		}
+	}
+		break;
+	case POWER:
+	{
+		struct p9_power_sensor *ps =
+			&(((struct p9_power_sensor *)sensor)[sensor_num]);
+
+		switch (hwmon) {
+		case hwmon_power_input:
+			*val = ps->value;
+			break;
+		case hwmon_power_label:
+			*val = ps->sensor_id;
+			break;
+		case hwmon_power_average_min:
+			*val = ((u32 *)(&ps->accumulator))[0];
+			break;
+		case hwmon_power_average_max:
+			*val = ((u32 *)(&ps->accumulator))[1];
+			break;
+		case hwmon_power_average_interval:
+			*val = ps->update_tag;
+			break;
+		case hwmon_power_reset_history:
+			*val = ps->function_id | (ps->apss_channel << 8);
+			break;
+		default:
+			rc = -EOPNOTSUPP;
+		}
+	}
+		break;
+	case CAPS:
+	{
+		struct p9_caps_sensor *cs =
+			&(((struct p9_caps_sensor *)sensor)[sensor_num]);
+
+		switch (hwmon) {
+		case hwmon_power_cap:
+			*val = cs->curr_powercap;
+			break;
+		case hwmon_power_cap_max:
+			*val = cs->max_powercap;
+			break;
+		case hwmon_power_cap_min:
+			*val = cs->min_powercap;
+			break;
+		case hwmon_power_max:
+			*val = cs->norm_powercap;
+			break;
+		case hwmon_power_alarm:
+			*val = cs->user_powerlimit;
+			break;
+		case hwmon_power_cap_alarm:
+			*val = cs->user_powerlimit_source;
+			break;
+		default:
+			rc = -EOPNOTSUPP;
+		}
+	}
+		break;
+	default:
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static const struct occ_ops p9_ops = {
+	.parse_sensor = p9_parse_sensor,
+	.alloc_sensor = p9_alloc_sensor,
+	.get_sensor = p9_get_sensor,
+};
+
+static const struct occ_config p9_config = {
+	.command_addr = 0xFFFBE000,
+	.response_addr = 0xFFFBF000,
+};
+
+const u32 *p9_get_sensor_hwmon_configs()
+{
+	return p9_sensor_hwmon_configs;
+}
+EXPORT_SYMBOL(p9_get_sensor_hwmon_configs);
+
+struct occ *p9_occ_start(struct device *dev, void *bus,
+			 struct occ_bus_ops *bus_ops)
+{
+	return occ_start(dev, bus, bus_ops, &p9_ops, &p9_config);
+}
+EXPORT_SYMBOL(p9_occ_start);
+
+MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
+MODULE_DESCRIPTION("P9 OCC sensors");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/occ_p9.h b/drivers/hwmon/occ/occ_p9.h
new file mode 100644
index 0000000..18ca16a
--- /dev/null
+++ b/drivers/hwmon/occ/occ_p9.h
@@ -0,0 +1,30 @@
+/*
+ * occ_p9.h - OCC hwmon driver
+ *
+ * This file contains Power9-specific function prototypes
+ *
+ * Copyright 2016 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __OCC_P9_H__
+#define __OCC_P9_H__
+
+#include "scom.h"
+
+struct device;
+
+const u32 *p9_get_sensor_hwmon_configs(void);
+struct occ *p9_occ_start(struct device *dev, void *bus,
+			 struct occ_bus_ops *bus_ops);
+
+#endif /* __OCC_P9_H__ */
-- 
1.8.3.1

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

* Re: [PATCH linux v7 2/6] hwmon: occ: Add sysfs interface
  2017-02-07 23:10 ` [PATCH linux v7 2/6] hwmon: occ: Add sysfs interface eajames
@ 2017-02-10  5:31   ` Joel Stanley
  0 siblings, 0 replies; 23+ messages in thread
From: Joel Stanley @ 2017-02-10  5:31 UTC (permalink / raw)
  To: eajames
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Andrew Jeffery, Benjamin Herrenschmidt, Edward A. James

On Wed, Feb 8, 2017 at 9:40 AM,  <eajames@linux.vnet.ibm.com> wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Add a generic mechanism to expose the sensors provided by the OCC in
> sysfs.
>
> Signed-off-by: Edward A. James <eajames@us.ibm.com>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
>  Documentation/hwmon/occ       |  62 +++++++++++
>  drivers/hwmon/occ/Makefile    |   2 +-
>  drivers/hwmon/occ/occ_sysfs.c | 251 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/hwmon/occ/occ_sysfs.h |  30 +++++
>  4 files changed, 344 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/hwmon/occ/occ_sysfs.c
>  create mode 100644 drivers/hwmon/occ/occ_sysfs.h
>
> diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
> index 79d1642..d0bdf06 100644
> --- a/Documentation/hwmon/occ
> +++ b/Documentation/hwmon/occ
> @@ -25,6 +25,68 @@ Currently, all versions of the OCC support four types of sensor data: power,
>  temperature, frequency, and "caps," which indicate limits and thresholds used
>  internally on the OCC.
>
> +sysfs Entries
> +-------------
> +
> +The OCC driver uses the hwmon sysfs framework to provide data to userspace.
> +
> +The driver exports a number of sysfs files for each type of sensor. The
> +sensor-specific files vary depending on the processor type, though many of the
> +attributes are common for both the POWER8 and POWER9.
> +
> +The hwmon interface cannot define every type of sensor that may be used.
> +Therefore, the frequency sensor on the OCC uses the "input" type sensor defined
> +by the hwmon interface, rather than defining a new type of custom sensor.
> +
> +Below are detailed the names and meaning of each sensor file for both types of
> +processors. All sensors are read-only unless otherwise specified. <x> indicates
> +the hwmon index. sensor id indicates the unique internal OCC identifer. Please
> +see the POWER OCC specification for details on all these sensor values.
> +
> +frequency:
> +       all processors:
> +               in<x>_input - frequency value
> +               in<x>_label - sensor id
> +temperature:
> +       POWER8:
> +               temp<x>_input - temperature value
> +               temp<x>_label - sensor id
> +       POWER9 (in addition to above):
> +               temp<x>_type - FRU type
> +
> +power:
> +       POWER8:
> +               power<x>_input - power value
> +               power<x>_label - sensor id
> +               power<x>_average - accumulator
> +               power<x>_average_interval - update tag (number of samples in
> +                       accumulator)
> +       POWER9:
> +               power<x>_input - power value
> +               power<x>_label - sensor id
> +               power<x>_average_min - accumulator[0]
> +               power<x>_average_max - accumulator[1] (64 bits total)
> +               power<x>_average_interval - update tag
> +               power<x>_reset_history - (function_id | (apss_channel << 8)
> +
> +caps:
> +       POWER8:
> +               power<x>_cap - current powercap
> +               power<x>_cap_max - max powercap
> +               power<x>_cap_min - min powercap
> +               power<x>_max - normal powercap
> +               power<x>_alarm - user powercap, r/w
> +       POWER9:
> +               power<x>_cap_alarm - user powercap source
> +
> +The driver also provides two sysfs entries through hwmon to better
> +control the driver and monitor the master OCC. Though there may be multiple
> +OCCs present on the system, these two files are only present for the "master"
> +OCC.
> +       name - read the name of the driver
> +       update_interval - read or write the minimum interval for polling the
> +               OCC.
> +
>  BMC - Host Communications
>  -------------------------
>
> diff --git a/drivers/hwmon/occ/Makefile b/drivers/hwmon/occ/Makefile
> index 93cb52f..a6881f9 100644
> --- a/drivers/hwmon/occ/Makefile
> +++ b/drivers/hwmon/occ/Makefile
> @@ -1 +1 @@
> -obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o
> +obj-$(CONFIG_SENSORS_PPC_OCC) += occ.o occ_sysfs.o
> diff --git a/drivers/hwmon/occ/occ_sysfs.c b/drivers/hwmon/occ/occ_sysfs.c
> new file mode 100644
> index 0000000..9598f78
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_sysfs.c
> @@ -0,0 +1,251 @@
> +/*
> + * occ_sysfs.c - OCC sysfs interface
> + *
> + * This file contains the methods and data structures for implementing the OCC
> + * hwmon sysfs entries.
> + *
> + * Copyright 2016 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/init.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include "occ.h"
> +#include "occ_sysfs.h"
> +
> +#define OCC_HWMON_NAME_LENGTH  32
> +
> +struct occ_sysfs {
> +       struct device *dev;
> +       struct occ *occ;
> +
> +       char hwmon_name[OCC_HWMON_NAME_LENGTH + 1];
> +       const u32 *sensor_hwmon_configs;
> +       struct hwmon_channel_info **occ_sensors;
> +       struct hwmon_chip_info occ_info;
> +       u16 user_powercap;
> +};
> +
> +static int occ_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> +                         u32 attr, int channel, long *val)
> +{
> +       int rc;
> +       struct occ_sysfs *driver = dev_get_drvdata(dev);
> +       struct occ *occ = driver->occ;
> +
> +       switch (type) {
> +       case hwmon_in:
> +               rc = occ_get_sensor_field(occ, FREQ, channel, attr, val);
> +               break;
> +       case hwmon_temp:
> +               rc = occ_get_sensor_field(occ, TEMP, channel, attr, val);
> +               break;
> +       case hwmon_power:
> +               rc = occ_get_sensor_field(occ, POWER, channel, attr, val);
> +               break;
> +       default:
> +               rc = -EOPNOTSUPP;
> +       }
> +
> +       return rc;
> +}
> +
> +static int occ_hwmon_read_string(struct device *dev,
> +                                enum hwmon_sensor_types type, u32 attr,
> +                                int channel, char **str)
> +{
> +       int rc;
> +       unsigned long val = 0;
> +
> +       if (!((type == hwmon_in && attr == hwmon_in_label) ||
> +           (type == hwmon_temp && attr == hwmon_temp_label) ||
> +           (type == hwmon_power && attr == hwmon_power_label)))
> +               return -EOPNOTSUPP;
> +
> +       /* will fetch the "label", the sensor_id */
> +       rc = occ_hwmon_read(dev, type, attr, channel, &val);
> +       if (rc < 0)
> +               return rc;
> +
> +       rc = snprintf(*str, PAGE_SIZE - 1, "%lu", val);
> +       if (rc > 0)
> +               rc = 0;
> +
> +       return rc;
> +}
> +
> +static int occ_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
> +                          u32 attr, int channel, long val)
> +{
> +       int rc;
> +       struct occ_sysfs *driver = dev_get_drvdata(dev);
> +
> +       if (type == hwmon_power && attr == hwmon_power_alarm) {
> +               rc = occ_set_user_powercap(driver->occ, val);
> +               if (rc) {
> +                       dev_err(dev, "set user powercap failed:%d\n", rc);
> +                       return rc;
> +               }
> +
> +               driver->user_powercap = val;
> +
> +               return rc;
> +       }
> +
> +       return -EOPNOTSUPP;
> +}
> +
> +static umode_t occ_is_visible(const void *data, enum hwmon_sensor_types type,
> +                             u32 attr, int channel)
> +{
> +       const struct occ_sysfs *driver = data;
> +
> +       switch (type) {
> +       case hwmon_chip:
> +               if (attr == hwmon_chip_update_interval)
> +                       return 00644;

Perhaps 0644?

> +               break;
> +       case hwmon_in:
> +               if (BIT(attr) & driver->sensor_hwmon_configs[0])
> +                       return 00444;
> +               break;
> +       case hwmon_temp:
> +               if (BIT(attr) & driver->sensor_hwmon_configs[1])
> +                       return 00444;
> +               break;
> +       case hwmon_power:
> +               /* user power limit */
> +               if (attr == hwmon_power_alarm)
> +                       return 00644;
> +               else if ((BIT(attr) & driver->sensor_hwmon_configs[2]) ||
> +                        (BIT(attr) & driver->sensor_hwmon_configs[3]))
> +                       return 00444;
> +               break;
> +       default:
> +               return 0;
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct hwmon_ops occ_hwmon_ops = {
> +       .is_visible = occ_is_visible,
> +       .read = occ_hwmon_read,
> +       .read_string = occ_hwmon_read_string,
> +       .write = occ_hwmon_write,
> +};
> +
> +static const enum hwmon_sensor_types occ_sensor_types[MAX_OCC_SENSOR_TYPE] = {
> +       hwmon_in,
> +       hwmon_temp,
> +       hwmon_power,
> +       hwmon_power
> +};
> +
> +struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ,
> +                                 const u32 *sensor_hwmon_configs,
> +                                 const char *name)
> +{
> +       int rc, i, j, num_sensors, index = 0, id;
> +       char *brk;
> +       struct occ_blocks *resp = NULL;
> +       u32 *sensor_config;
> +       struct occ_sysfs *hwmon = devm_kzalloc(dev, sizeof(struct occ_sysfs),
> +                                              GFP_KERNEL);
> +       if (!hwmon)
> +               return ERR_PTR(-ENOMEM);
> +
> +       /* need space for null-termination */
> +       hwmon->occ_sensors =
> +               devm_kzalloc(dev, sizeof(struct hwmon_channel_info *) *
> +                            (MAX_OCC_SENSOR_TYPE + 1), GFP_KERNEL);
> +       if (!hwmon->occ_sensors)
> +               return ERR_PTR(-ENOMEM);
> +
> +       hwmon->occ = occ;
> +       hwmon->sensor_hwmon_configs = sensor_hwmon_configs;
> +       hwmon->occ_info.ops = &occ_hwmon_ops;
> +       hwmon->occ_info.info =
> +               (const struct hwmon_channel_info **)hwmon->occ_sensors;
> +
> +       occ_get_response_blocks(occ, &resp);
> +
> +       for (i = 0; i < MAX_OCC_SENSOR_TYPE; ++i)
> +               resp->sensor_block_id[i] = -1;
> +
> +       /* read sensor data from occ */
> +       rc = occ_update_device(occ);
> +       if (rc)
> +               return ERR_PTR(rc);
> +
> +       for (i = 0; i < MAX_OCC_SENSOR_TYPE; i++) {
> +               id = resp->sensor_block_id[i];
> +               if (id < 0)
> +                       continue;
> +
> +               num_sensors = resp->blocks[id].header.num_sensors;
> +               /* need null-termination */
> +               sensor_config = devm_kzalloc(dev,
> +                                            sizeof(u32) * (num_sensors + 1),
> +                                            GFP_KERNEL);
> +               if (!sensor_config)
> +                       return ERR_PTR(-ENOMEM);
> +
> +               for (j = 0; j < num_sensors; j++)
> +                       sensor_config[j] = sensor_hwmon_configs[i];
> +
> +               hwmon->occ_sensors[index] =
> +                       devm_kzalloc(dev, sizeof(struct hwmon_channel_info),
> +                                    GFP_KERNEL);
> +               if (!hwmon->occ_sensors[index])
> +                       return ERR_PTR(-ENOMEM);
> +
> +               hwmon->occ_sensors[index]->type = occ_sensor_types[i];
> +               hwmon->occ_sensors[index]->config = sensor_config;
> +               index++;
> +       }
> +
> +       /* search for bad chars */
> +       strncpy(hwmon->hwmon_name, name, OCC_HWMON_NAME_LENGTH);
> +       brk = strpbrk(hwmon->hwmon_name, "-* \t\n");
> +       while (brk) {
> +               *brk = '_';
> +               brk = strpbrk(brk,  "-* \t\n");
> +       }
> +
> +       hwmon->dev = devm_hwmon_device_register_with_info(dev,
> +                                                         hwmon->hwmon_name,
> +                                                         hwmon,
> +                                                         &hwmon->occ_info,
> +                                                         NULL);
> +       if (IS_ERR(hwmon->dev)) {
> +               dev_err(dev, "cannot register hwmon device %s: %ld\n",
> +                       hwmon->hwmon_name, PTR_ERR(hwmon->dev));
> +               return ERR_CAST(hwmon->dev);
> +       }
> +
> +       return hwmon;
> +}
> +EXPORT_SYMBOL(occ_sysfs_start);
> +
> +MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
> +MODULE_DESCRIPTION("OCC sysfs driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwmon/occ/occ_sysfs.h b/drivers/hwmon/occ/occ_sysfs.h
> new file mode 100644
> index 0000000..462f9d2
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_sysfs.h
> @@ -0,0 +1,30 @@
> +/*
> + * occ_sysfs.h - OCC sysfs interface
> + *
> + * This file contains the data structures and function prototypes for the OCC
> + * hwmon sysfs entries.
> + *
> + * Copyright 2016 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __OCC_SYSFS_H__
> +#define __OCC_SYSFS_H__
> +
> +struct occ;
> +struct device;
> +struct occ_sysfs;
> +
> +struct occ_sysfs *occ_sysfs_start(struct device *dev, struct occ *occ,
> +                                 const u32 *sensor_hwmon_configs,
> +                                 const char *name);
> +#endif /* __OCC_SYSFS_H__ */
> --
> 1.8.3.1
>

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

* Re: [PATCH linux v7 3/6] hwmon: occ: Add I2C transport implementation for SCOM operations
  2017-02-07 23:10   ` eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8
  (?)
@ 2017-02-10  5:31   ` Joel Stanley
  2017-02-10 21:05     ` Eddie James
  -1 siblings, 1 reply; 23+ messages in thread
From: Joel Stanley @ 2017-02-10  5:31 UTC (permalink / raw)
  To: eajames
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Andrew Jeffery, Benjamin Herrenschmidt, Edward A. James

On Wed, Feb 8, 2017 at 9:40 AM,  <eajames@linux.vnet.ibm.com> wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Add functions to send SCOM operations over I2C bus. The BMC can
> communicate with the Power8 host processor over I2C, but needs to use SCOM
> operations in order to access the OCC register space.

This doesn't need to be separate from the p8_occ_i2c.c file. You can
remove a layer of function calls by merging this in and having these
be your getscom putscom bus_ops callbacks.

> Signed-off-by: Edward A. James <eajames@us.ibm.com>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
>  drivers/hwmon/occ/occ_scom_i2c.c | 77 ++++++++++++++++++++++++++++++++++++++++
>  drivers/hwmon/occ/occ_scom_i2c.h | 26 ++++++++++++++
>  2 files changed, 103 insertions(+)
>  create mode 100644 drivers/hwmon/occ/occ_scom_i2c.c
>  create mode 100644 drivers/hwmon/occ/occ_scom_i2c.h
>
> diff --git a/drivers/hwmon/occ/occ_scom_i2c.c b/drivers/hwmon/occ/occ_scom_i2c.c
> new file mode 100644
> index 0000000..74bd6ff
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_scom_i2c.c
> @@ -0,0 +1,77 @@

> +
> +int occ_i2c_getscom(void *bus, u32 address, u64 *data)
> +{
> +       ssize_t rc;
> +       u64 buf;

If you add endianness annotations sparse can check your types are
consistent. The warning looks like this:

make C=2 drivers/hwmon/occ/occ_scom_i2c.o
drivers/hwmon/occ/occ_scom_i2c.c:48:17: warning: cast to restricted __be64

Which tells you it expects the type you pass to be64_to_cpu to be __be64.


> +       struct i2c_client *client = bus;
> +       struct i2c_msg msgs[2];
> +
> +       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;
> +       msgs[1].flags |= I2C_M_RD;

I first thought you had made a mistake here. Instead you could do:

       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;
> +

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

* Re: [PATCH linux v7 4/6] hwmon: occ: Add callbacks for parsing P8 OCC datastructures
  2017-02-07 23:10 ` [PATCH linux v7 4/6] hwmon: occ: Add callbacks for parsing P8 OCC datastructures eajames
@ 2017-02-10  5:31   ` Joel Stanley
  2017-02-13  1:17       ` Andrew Jeffery
  0 siblings, 1 reply; 23+ messages in thread
From: Joel Stanley @ 2017-02-10  5:31 UTC (permalink / raw)
  To: eajames
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Andrew Jeffery, Benjamin Herrenschmidt, Edward A. James

On Wed, Feb 8, 2017 at 9:40 AM,  <eajames@linux.vnet.ibm.com> wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Add functions to parse the data structures that are specific to the OCC on
> the POWER8 processor. These are the sensor data structures, including
> temperature, frequency, power, and "caps."
>
> Signed-off-by: Edward A. James <eajames@us.ibm.com>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
>  Documentation/hwmon/occ    |   9 ++
>  drivers/hwmon/occ/occ_p8.c | 248 +++++++++++++++++++++++++++++++++++++++++++++
>  drivers/hwmon/occ/occ_p8.h |  30 ++++++
>  3 files changed, 287 insertions(+)
>  create mode 100644 drivers/hwmon/occ/occ_p8.c
>  create mode 100644 drivers/hwmon/occ/occ_p8.h

>
> diff --git a/drivers/hwmon/occ/occ_p8.c b/drivers/hwmon/occ/occ_p8.c
> new file mode 100644
> index 0000000..5c61fc4
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_p8.c

> +void p8_parse_sensor(u8 *data, void *sensor, int sensor_type, int off,
> +                    int snum)
> +{
> +       switch (sensor_type) {
> +       case FREQ:
> +       case TEMP:
> +       {
> +               struct p8_occ_sensor *os =
> +                       &(((struct p8_occ_sensor *)sensor)[snum]);
> +
> +               os->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
> +               os->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 2]));
> +       }
> +               break;
> +       case POWER:
> +       {
> +               struct p8_power_sensor *ps =
> +                       &(((struct p8_power_sensor *)sensor)[snum]);
> +
> +               ps->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
> +               ps->update_tag =
> +                       be32_to_cpu(get_unaligned((u32 *)&data[off + 2]));

This might be more readable if you wrote a
cast_get_unaliged_be32_to_cpu() macro.

> +               ps->accumulator =
> +                       be32_to_cpu(get_unaligned((u32 *)&data[off + 6]));
> +               ps->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 10]));
> +       }
> +               break;
> +       case CAPS:
> +       {

> +const u32 *p8_get_sensor_hwmon_configs()
> +{
> +       return p8_sensor_hwmon_configs;
> +}
> +EXPORT_SYMBOL(p8_get_sensor_hwmon_configs);
> +
> +struct occ *p8_occ_start(struct device *dev, void *bus,
> +                        struct occ_bus_ops *bus_ops)
> +{
> +       return occ_start(dev, bus, bus_ops, &p8_ops, &p8_config);
> +}
> +EXPORT_SYMBOL(p8_occ_start);

We don't need to export these symbols; they're not used outside of the
OCC module. The same goes for all of the exports you've made in this
series.

I suggest we re-architect the drivers so we build all of the objects
and link them into one module for each platform, instead of having an
occ module and occ-p8/occ-p9 modules and i2c modules that all depend
on each other. The Makefile could look like this:

obj-$(CONFIG_SENSORS_PPC_OCC_P8_I2C) += hwmon_occ_p8.o
obj-$(CONFIG_SENSORS_PPC_OCC_P9) += hwmon_occ_p9.o

hwmon_occ_p8-$(CONFIG_SENSORS_PPC_OCC_P8_I2C) += occ_scom_i2c.o
occ_p8.o p8_occ_i2c.o occ_sysfs.o occ.o
hwmon_occ_p9-$(CONFIG_SENSORS_PPC_OCC_P9) += occ_p9.o occ_sysfs.o occ.o

And the Kbuild like this:

menuconfig SENSORS_PPC_OCC
        bool "PPC On-Chip Controller"

if SENSORS_PPC_OCC

config SENSORS_PPC_OCC_P8_I2C
        bool "POWER8 OCC hwmon support"
        depends on I2C

config SENSORS_PPC_OCC_P9
        bool "POWER9 OCC hwmon support"

endif

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

* Re: [PATCH linux v7 1/6] hwmon: Add core On-Chip Controller support for POWER CPUs
  2017-02-07 23:10 ` [PATCH linux v7 1/6] hwmon: Add core On-Chip Controller support for POWER CPUs eajames
@ 2017-02-10  5:31   ` Joel Stanley
  2017-02-10 21:02     ` Eddie James
  2017-02-14 15:36     ` Eddie James
  0 siblings, 2 replies; 23+ messages in thread
From: Joel Stanley @ 2017-02-10  5:31 UTC (permalink / raw)
  To: eajames
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Andrew Jeffery, Benjamin Herrenschmidt, Edward A. James

On Wed, Feb 8, 2017 at 9:40 AM,  <eajames@linux.vnet.ibm.com> wrote:

> diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
> new file mode 100644
> index 0000000..79d1642
> --- /dev/null
> +++ b/Documentation/hwmon/occ

The kernel is using  reStructuredText these days. You should consider
following suit:

 https://www.kernel.org/doc/html/latest/doc-guide/sphinx.html#writing-documentation


> @@ -0,0 +1,40 @@
> +Kernel driver occ
> +=================
> +
> +Supported chips:
> + * ASPEED AST2400
> + * ASPEED AST2500

Not really - this will run on any chip that's connected to a P8 or P9.


> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5f10c28..193a13b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9128,6 +9128,13 @@ T:       git git://linuxtv.org/media_tree.git
>  S:     Maintained
>  F:     drivers/media/i2c/ov7670.c
>
> +ON-CHIP CONTROLLER HWMON DRIVER

Mention P8 and P9?

> +M:     Eddie James <eajames@us.ibm.com>
> +L:     linux-hwmon@vger.kernel.org

Have you subscribed to this list? Would you prefer the mail to come to
the openbmc list?

> +S:     Maintained
> +F:     Documentation/hwmon/occ
> +F:     drivers/hwmon/occ/
> +
>  ONENAND FLASH DRIVER
>  M:     Kyungmin Park <kyungmin.park@samsung.com>
>  L:     linux-mtd@lists.infradead.org
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 190d270..e80ca81 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1240,6 +1240,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 d2cb7e8..c7ec5d4 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -169,6 +169,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_PPC_OCC)  += occ/
>  obj-$(CONFIG_PMBUS)            += pmbus/
>
>  ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG


> diff --git a/drivers/hwmon/occ/occ.c b/drivers/hwmon/occ/occ.c
> new file mode 100644
> index 0000000..af077f2
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ.c

> +               sensors = &resp->data.blocks[b].sensors;
> +               if (!sensors) {
> +                       /* first poll response */
> +                       sensors = driver->ops.alloc_sensor(dev, sensor_type,
> +                                                          block->num_sensors);
> +                       if (!sensors)
> +                               return -ENOMEM;
> +
> +                       resp->data.blocks[b].sensors = sensors;
> +                       resp->data.sensor_block_id[sensor_type] = b;
> +                       resp->data.blocks[b].header = *block;
> +               } else if (block->sensor_length !=
> +                        resp->data.blocks[b].header.sensor_length) {
> +                       dev_warn(dev,
> +                                "different sensor length than first poll\n");

The driver has changed behaviour from your previous version; now it
discards data if the sensors change.

Under what circumstances does the sensor data change?

> +                       continue;
> +               }
> +
> +               for (s = 0; s < block->num_sensors &&
> +                    s < resp->data.blocks[b].header.num_sensors; s++) {
> +                       if (offset + block->sensor_length > num_bytes) {
> +                               dev_warn(dev, "exceeded data length\n");
> +                               return 0;
> +                       }
> +
> +                       driver->ops.parse_sensor(data, sensors, sensor_type,
> +                                                offset, s);
> +                       offset += block->sensor_length;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static u8 occ_send_cmd(struct occ *driver, u8 seq, u8 type, u16 length,
> +                      const u8 *data, u8 *resp)
> +{
> +       u32 cmd1, cmd2 = 0;
> +       u16 checksum = 0;
> +       bool retry = false;
> +       int i, rc, tries = 0;
> +
> +       cmd1 = (seq << 24) | (type << 16) | length;
> +       memcpy(&cmd2, data, length);
> +       cmd2 <<= ((4 - length) * 8);
> +
> +       /* checksum: sum of every bytes of cmd1, cmd2 */
> +       for (i = 0; i < 4; i++) {
> +               checksum += (cmd1 >> (i * 8)) & 0xFF;
> +               checksum += (cmd2 >> (i * 8)) & 0xFF;
> +       }
> +
> +       cmd2 |= checksum << ((2 - length) * 8);
> +
> +       /* Init OCB */

You should mention what OCB means in your documentation.

> +       rc = driver->bus_ops.putscom(driver->bus, OCB_STATUS_CONTROL_OR,
> +                                    OCB_OR_INIT0, OCB_OR_INIT1);
> +       if (rc)
> +               goto err;
> +

> +int occ_set_user_powercap(struct occ *occ, u16 cap)
> +{
> +       u8 resp[8];
> +
> +       cap = cpu_to_be16(cap);
> +
> +       return occ_send_cmd(occ, 0, OCC_SET_USER_POWR_CAP, 2, (const u8 *)&cap,
> +                           resp);
> +}
> +EXPORT_SYMBOL(occ_set_user_powercap);
> +
> +struct occ *occ_start(struct device *dev, void *bus,

>From what I can tell this doesn't start anything. Call it occ_init()
or something.

> +                     struct occ_bus_ops *bus_ops, const struct occ_ops *ops,
> +                     const struct occ_config *config)

Create a structure with all of these details in it. Some of them don't
need to be broken out into their own, for instance:

struct occ *occ_start(struct device *dev, const struct occ_init_context *init)
{

   driver->dev = dev;
   driver->bus = init->bus;
   driver->bus_read = init->bus_read;
   driver->bus_write = init->bus_write;
   driver->config = init->config;
   driver->cmd_addr = init->cmd_addr;

etc.



> +{
> +       struct occ *driver = devm_kzalloc(dev, sizeof(struct occ), GFP_KERNEL);
> +
> +       if (!driver)
> +               return ERR_PTR(-ENOMEM);
> +
> +       driver->dev = dev;
> +       driver->bus = bus;
> +       driver->bus_ops = *bus_ops;
> +       driver->ops = *ops;
> +       driver->config = *config;
> +       driver->raw_data = devm_kzalloc(dev, OCC_DATA_MAX, GFP_KERNEL);
> +       if (!driver->raw_data)
> +               return ERR_PTR(-ENOMEM);
> +
> +       mutex_init(&driver->update_lock);
> +
> +       return driver;
> +}
> +EXPORT_SYMBOL(occ_start);
> +
> +MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
> +MODULE_DESCRIPTION("OCC hwmon core driver");
> +MODULE_LICENSE("GPL");

> diff --git a/drivers/hwmon/occ/scom.h b/drivers/hwmon/occ/scom.h
> new file mode 100644
> index 0000000..b0691f3
> --- /dev/null
> +++ b/drivers/hwmon/occ/scom.h
> @@ -0,0 +1,47 @@
> +/*
> + * scom.h - hwmon OCC driver
> + *
> + * This file contains data structures for scom operations to the OCC

Are these really SCOM operations?

I think they're better described read and write callbacks, as the
operation is may be talking i2c or FSI or in the future some other
kind of access.

They do take scom addresses as parameters, so I can see the argument
for calling them getscom/putscom.


> + *
> + * Copyright 2016 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __SCOM_H__
> +#define __SCOM_H__
> +
> +/*
> + * occ_bus_ops - represent the low-level transfer methods to communicate with
> + * the OCC.
> + *
> + * getscom - OCC scom read
> + * @bus: handle to slave device
> + * @address: address
> + * @data: where to store data read from slave; buffer size must be at least
> + * eight bytes.

Are there situations where it's more than 8 bytes?

Would it be safer to add a length argument so the read call so we
don't put more data in the buffer than the caller expects?


> + *
> + * Returns 0 on success or a negative errno on error
> + *
> + * putscom - OCC scom write
> + * @bus: handle to slave device
> + * @address: address
> + * @data0: first data word to write
> + * @data1: second data word to write
> + *
> + * Returns 0 on success or a negative errno on error
> + */
> +struct occ_bus_ops {
> +       int (*getscom)(void *bus, u32 address, u64 *data);
> +       int (*putscom)(void *bus, u32 address, u32 data0, u32 data1);
> +};
> +
> +#endif /* __SCOM_H__ */

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

* Re: [PATCH linux v7 5/6] hwmon: occ: Add hwmon implementation for the P8 OCC
  2017-02-07 23:10 ` [PATCH linux v7 5/6] hwmon: occ: Add hwmon implementation for the P8 OCC eajames
@ 2017-02-10  5:31   ` Joel Stanley
  0 siblings, 0 replies; 23+ messages in thread
From: Joel Stanley @ 2017-02-10  5:31 UTC (permalink / raw)
  To: eajames
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Andrew Jeffery, Benjamin Herrenschmidt, Edward A. James

On Wed, Feb 8, 2017 at 9:40 AM,  <eajames@linux.vnet.ibm.com> wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Add code to tie the hwmon sysfs code and the POWER8 OCC code together, as
> well as probe the entire driver from the I2C bus. I2C is the communication
> method between the BMC and the P8 OCC.
>
> Signed-off-by: Edward A. James <eajames@us.ibm.com>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> Acked-by: Rob Herring <robh@kernel.org>
> ---
>  Documentation/devicetree/bindings/hwmon/occ.txt |  13 +++
>  drivers/hwmon/occ/Kconfig                       |  14 ++++
>  drivers/hwmon/occ/Makefile                      |   1 +
>  drivers/hwmon/occ/p8_occ_i2c.c                  | 104 ++++++++++++++++++++++++

For consistency, how about we call this occ_p8_i2c.c? The other files
have the machine name second.


>  4 files changed, 132 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/hwmon/occ.txt
>  create mode 100644 drivers/hwmon/occ/p8_occ_i2c.c
>

> diff --git a/drivers/hwmon/occ/Kconfig b/drivers/hwmon/occ/Kconfig
> index cdb64a7..3a5188f 100644
> --- a/drivers/hwmon/occ/Kconfig
> +++ b/drivers/hwmon/occ/Kconfig
> @@ -13,3 +13,17 @@ menuconfig SENSORS_PPC_OCC
>
>           This driver can also be built as a module. If so, the module
>           will be called occ.
> +
> +if SENSORS_PPC_OCC
> +
> +config SENSORS_PPC_OCC_P8_I2C
> +       tristate "POWER8 OCC hwmon support"
> +       depends on I2C
> +       help
> +        Provide a hwmon sysfs interface for the POWER8 On-Chip Controller,
> +        exposing temperature, frequency and power measurements.
> +
> +        This driver can also be built as a module. If so, the module will be
> +        called p8-occ-i2c.

Mention that this driver is for the BMC (or just "service processor"),
and is not useful in the Power8 kernel.

I'm trying to think of a better prefix than PPC. PPC means much more
than Power8 and Power9, which is what we mean. It's also confusing as
we don't run this on any PowerPC machine - it's for an ARM chip.

> +
> +int p8_i2c_getscom(void *bus, u32 address, u64 *data)
> +{
> +       /* P8 i2c slave requires address to be shifted by 1 */
> +       address = address << 1;

I think these are scom addresses? Please indicate that so we don't get
them confused with i2c.

Or are they scom operations?

> +
> +       return occ_i2c_getscom(bus, address, data);
> +}
> +
> +int p8_i2c_putscom(void *bus, u32 address, u32 data0, u32 data1)

The functions can be static.

> +{
> +       /* P8 i2c slave requires address to be shifted by 1 */
> +       address = address << 1;
> +
> +       return occ_i2c_putscom(bus, address, data0, data1);
> +}

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

* Re: [PATCH linux v7 6/6] hwmon: occ: Add callbacks for parsing P9 OCC datastructures
  2017-02-07 23:10 ` [PATCH linux v7 6/6] hwmon: occ: Add callbacks for parsing P9 OCC datastructures eajames
@ 2017-02-10  5:31   ` Joel Stanley
  2017-02-13  1:29       ` Andrew Jeffery
  0 siblings, 1 reply; 23+ messages in thread
From: Joel Stanley @ 2017-02-10  5:31 UTC (permalink / raw)
  To: eajames
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Andrew Jeffery, Benjamin Herrenschmidt, Edward A. James

On Wed, Feb 8, 2017 at 9:40 AM,  <eajames@linux.vnet.ibm.com> wrote:
> From: "Edward A. James" <eajames@us.ibm.com>
>
> Add functions to parse the data structures that are specific to the OCC on
> the POWER9 processor. These are the sensor data structures, including
> temperature, frequency, power, and "caps."
>
> Signed-off-by: Edward A. James <eajames@us.ibm.com>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
>  Documentation/hwmon/occ    |   3 +
>  drivers/hwmon/occ/occ_p9.c | 309 +++++++++++++++++++++++++++++++++++++++++++++
>  drivers/hwmon/occ/occ_p9.h |  30 +++++
>  3 files changed, 342 insertions(+)
>  create mode 100644 drivers/hwmon/occ/occ_p9.c
>  create mode 100644 drivers/hwmon/occ/occ_p9.h

> diff --git a/drivers/hwmon/occ/occ_p9.c b/drivers/hwmon/occ/occ_p9.c
> new file mode 100644
> index 0000000..9c1283c
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_p9.c
> @@ -0,0 +1,309 @@
> +/*
> + * occ_p9.c - OCC hwmon driver
> + *
> + * This file contains the Power9-specific methods and data structures for
> + * the OCC hwmon driver.
> + *
> + * Copyright 2016 IBM Corp.

It's 2017.

> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.

We generally just include the first paragraph. Same goes for all of the files.

> +
> +static const u32 p9_sensor_hwmon_configs[MAX_OCC_SENSOR_TYPE] = {
> +       HWMON_I_INPUT | HWMON_I_LABEL,  /* freq: value | label */
> +       /* temp: value | label | fru_type */
> +       HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_TYPE,
> +       /* power: value | label | accum[0] | accum[1] | update_tag |
> +        *       (function_id | (apss_channel << 8))
> +        */
> +       HWMON_P_INPUT | HWMON_P_LABEL | HWMON_P_AVERAGE_MIN |
> +               HWMON_P_AVERAGE_MAX | HWMON_P_AVERAGE_INTERVAL |
> +               HWMON_P_RESET_HISTORY,
> +       /* caps: curr | max | min | norm | user | source */
> +       HWMON_P_CAP | HWMON_P_CAP_MAX | HWMON_P_CAP_MIN | HWMON_P_MAX |
> +               HWMON_P_ALARM | HWMON_P_CAP_ALARM,

I find this really hard to read. Perhaps something like this:


#define FREQ_CONFIG        (HWMON_I_INPUT | HWMON_I_LABEL)
#deifne TEMP_CONFIG        (HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_TYPE)
#define POWER_CONFIG    ( HWMON_P_INPUT | HWMON_P_LABEL |
HWMON_P_AVERAGE_MIN | \
                                                 HWMON_P_AVERAGE_MAX |
HWMON_P_AVERAGE_INTERVAL | \
                                                 HWMON_P_RESET_HISTORY)

etc. Do the same in the p8 driver.


> diff --git a/drivers/hwmon/occ/occ_p9.h b/drivers/hwmon/occ/occ_p9.h
> new file mode 100644
> index 0000000..18ca16a
> --- /dev/null
> +++ b/drivers/hwmon/occ/occ_p9.h

> +
> +#ifndef __OCC_P9_H__
> +#define __OCC_P9_H__
> +
> +#include "scom.h"
> +
> +struct device;

Include the header for struct device instead.

Did you consider the one header file for all of your shared functions?
I don't think there's much value in having a whole heap of small ones.

> +
> +const u32 *p9_get_sensor_hwmon_configs(void);
> +struct occ *p9_occ_start(struct device *dev, void *bus,
> +                        struct occ_bus_ops *bus_ops);
> +
> +#endif /* __OCC_P9_H__ */
> --
> 1.8.3.1
>

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

* Re: [PATCH linux v7 1/6] hwmon: Add core On-Chip Controller support for POWER CPUs
  2017-02-10  5:31   ` Joel Stanley
@ 2017-02-10 21:02     ` Eddie James
  2017-02-14 15:36     ` Eddie James
  1 sibling, 0 replies; 23+ messages in thread
From: Eddie James @ 2017-02-10 21:02 UTC (permalink / raw)
  To: Joel Stanley
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Andrew Jeffery, Benjamin Herrenschmidt, Edward A. James



On 02/09/2017 11:31 PM, Joel Stanley wrote:
> On Wed, Feb 8, 2017 at 9:40 AM,  <eajames@linux.vnet.ibm.com> wrote:
>
>> diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
>> new file mode 100644
>> index 0000000..79d1642
>> --- /dev/null
>> +++ b/Documentation/hwmon/occ
> The kernel is using  reStructuredText these days. You should consider
> following suit:
>
>   https://www.kernel.org/doc/html/latest/doc-guide/sphinx.html#writing-documentation
>
>
>> @@ -0,0 +1,40 @@
>> +Kernel driver occ
>> +=================
>> +
>> +Supported chips:
>> + * ASPEED AST2400
>> + * ASPEED AST2500
> Not really - this will run on any chip that's connected to a P8 or P9.
>
>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 5f10c28..193a13b 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -9128,6 +9128,13 @@ T:       git git://linuxtv.org/media_tree.git
>>   S:     Maintained
>>   F:     drivers/media/i2c/ov7670.c
>>
>> +ON-CHIP CONTROLLER HWMON DRIVER
> Mention P8 and P9?
>
>> +M:     Eddie James <eajames@us.ibm.com>
>> +L:     linux-hwmon@vger.kernel.org
> Have you subscribed to this list? Would you prefer the mail to come to
> the openbmc list?
>
>> +S:     Maintained
>> +F:     Documentation/hwmon/occ
>> +F:     drivers/hwmon/occ/
>> +
>>   ONENAND FLASH DRIVER
>>   M:     Kyungmin Park <kyungmin.park@samsung.com>
>>   L:     linux-mtd@lists.infradead.org
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 190d270..e80ca81 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -1240,6 +1240,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 d2cb7e8..c7ec5d4 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -169,6 +169,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_PPC_OCC)  += occ/
>>   obj-$(CONFIG_PMBUS)            += pmbus/
>>
>>   ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
>
>> diff --git a/drivers/hwmon/occ/occ.c b/drivers/hwmon/occ/occ.c
>> new file mode 100644
>> index 0000000..af077f2
>> --- /dev/null
>> +++ b/drivers/hwmon/occ/occ.c
>> +               sensors = &resp->data.blocks[b].sensors;
>> +               if (!sensors) {
>> +                       /* first poll response */
>> +                       sensors = driver->ops.alloc_sensor(dev, sensor_type,
>> +                                                          block->num_sensors);
>> +                       if (!sensors)
>> +                               return -ENOMEM;
>> +
>> +                       resp->data.blocks[b].sensors = sensors;
>> +                       resp->data.sensor_block_id[sensor_type] = b;
>> +                       resp->data.blocks[b].header = *block;
>> +               } else if (block->sensor_length !=
>> +                        resp->data.blocks[b].header.sensor_length) {
>> +                       dev_warn(dev,
>> +                                "different sensor length than first poll\n");
> The driver has changed behaviour from your previous version; now it
> discards data if the sensors change.
>
> Under what circumstances does the sensor data change?

Yes. The sensor data shouldn't change, as far as I know. I think 
something would be wrong if it did.

>
>> +                       continue;
>> +               }
>> +
>> +               for (s = 0; s < block->num_sensors &&
>> +                    s < resp->data.blocks[b].header.num_sensors; s++) {
>> +                       if (offset + block->sensor_length > num_bytes) {
>> +                               dev_warn(dev, "exceeded data length\n");
>> +                               return 0;
>> +                       }
>> +
>> +                       driver->ops.parse_sensor(data, sensors, sensor_type,
>> +                                                offset, s);
>> +                       offset += block->sensor_length;
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static u8 occ_send_cmd(struct occ *driver, u8 seq, u8 type, u16 length,
>> +                      const u8 *data, u8 *resp)
>> +{
>> +       u32 cmd1, cmd2 = 0;
>> +       u16 checksum = 0;
>> +       bool retry = false;
>> +       int i, rc, tries = 0;
>> +
>> +       cmd1 = (seq << 24) | (type << 16) | length;
>> +       memcpy(&cmd2, data, length);
>> +       cmd2 <<= ((4 - length) * 8);
>> +
>> +       /* checksum: sum of every bytes of cmd1, cmd2 */
>> +       for (i = 0; i < 4; i++) {
>> +               checksum += (cmd1 >> (i * 8)) & 0xFF;
>> +               checksum += (cmd2 >> (i * 8)) & 0xFF;
>> +       }
>> +
>> +       cmd2 |= checksum << ((2 - length) * 8);
>> +
>> +       /* Init OCB */
> You should mention what OCB means in your documentation.
>
>> +       rc = driver->bus_ops.putscom(driver->bus, OCB_STATUS_CONTROL_OR,
>> +                                    OCB_OR_INIT0, OCB_OR_INIT1);
>> +       if (rc)
>> +               goto err;
>> +
>> +int occ_set_user_powercap(struct occ *occ, u16 cap)
>> +{
>> +       u8 resp[8];
>> +
>> +       cap = cpu_to_be16(cap);
>> +
>> +       return occ_send_cmd(occ, 0, OCC_SET_USER_POWR_CAP, 2, (const u8 *)&cap,
>> +                           resp);
>> +}
>> +EXPORT_SYMBOL(occ_set_user_powercap);
>> +
>> +struct occ *occ_start(struct device *dev, void *bus,
>  From what I can tell this doesn't start anything. Call it occ_init()
> or something.
>
>> +                     struct occ_bus_ops *bus_ops, const struct occ_ops *ops,
>> +                     const struct occ_config *config)
> Create a structure with all of these details in it. Some of them don't
> need to be broken out into their own, for instance:
>
> struct occ *occ_start(struct device *dev, const struct occ_init_context *init)
> {
>
>     driver->dev = dev;
>     driver->bus = init->bus;
>     driver->bus_read = init->bus_read;
>     driver->bus_write = init->bus_write;
>     driver->config = init->config;
>     driver->cmd_addr = init->cmd_addr;
>
> etc.
>
>
>
>> +{
>> +       struct occ *driver = devm_kzalloc(dev, sizeof(struct occ), GFP_KERNEL);
>> +
>> +       if (!driver)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       driver->dev = dev;
>> +       driver->bus = bus;
>> +       driver->bus_ops = *bus_ops;
>> +       driver->ops = *ops;
>> +       driver->config = *config;
>> +       driver->raw_data = devm_kzalloc(dev, OCC_DATA_MAX, GFP_KERNEL);
>> +       if (!driver->raw_data)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       mutex_init(&driver->update_lock);
>> +
>> +       return driver;
>> +}
>> +EXPORT_SYMBOL(occ_start);
>> +
>> +MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
>> +MODULE_DESCRIPTION("OCC hwmon core driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/hwmon/occ/scom.h b/drivers/hwmon/occ/scom.h
>> new file mode 100644
>> index 0000000..b0691f3
>> --- /dev/null
>> +++ b/drivers/hwmon/occ/scom.h
>> @@ -0,0 +1,47 @@
>> +/*
>> + * scom.h - hwmon OCC driver
>> + *
>> + * This file contains data structures for scom operations to the OCC
> Are these really SCOM operations?
>
> I think they're better described read and write callbacks, as the
> operation is may be talking i2c or FSI or in the future some other
> kind of access.
>
> They do take scom addresses as parameters, so I can see the argument
> for calling them getscom/putscom.

These are scom operations. The only way to talk to the OCC is with a 
scom operation as I understand it. The transport mechanism can change 
(i2c or fsi/sbe), but it's still a "scom."

>
>
>> + *
>> + * Copyright 2016 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.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __SCOM_H__
>> +#define __SCOM_H__
>> +
>> +/*
>> + * occ_bus_ops - represent the low-level transfer methods to communicate with
>> + * the OCC.
>> + *
>> + * getscom - OCC scom read
>> + * @bus: handle to slave device
>> + * @address: address
>> + * @data: where to store data read from slave; buffer size must be at least
>> + * eight bytes.
> Are there situations where it's more than 8 bytes?
>
> Would it be safer to add a length argument so the read call so we
> don't put more data in the buffer than the caller expects?

I guess the documentation isn't that clear, but it's always an 8 byte 
operation. I said "at least" 8 bytes because the calling code calls this 
function repeatedly with one large buffer at different offsets. I'll add 
a comment that it's always 8 bytes.

>
>
>> + *
>> + * Returns 0 on success or a negative errno on error
>> + *
>> + * putscom - OCC scom write
>> + * @bus: handle to slave device
>> + * @address: address
>> + * @data0: first data word to write
>> + * @data1: second data word to write
>> + *
>> + * Returns 0 on success or a negative errno on error
>> + */
>> +struct occ_bus_ops {
>> +       int (*getscom)(void *bus, u32 address, u64 *data);
>> +       int (*putscom)(void *bus, u32 address, u32 data0, u32 data1);
>> +};
>> +
>> +#endif /* __SCOM_H__ */

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

* Re: [PATCH linux v7 3/6] hwmon: occ: Add I2C transport implementation for SCOM operations
  2017-02-10  5:31   ` Joel Stanley
@ 2017-02-10 21:05     ` Eddie James
  2017-02-13  1:12       ` Andrew Jeffery
  0 siblings, 1 reply; 23+ messages in thread
From: Eddie James @ 2017-02-10 21:05 UTC (permalink / raw)
  To: Joel Stanley
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Andrew Jeffery, Benjamin Herrenschmidt, Edward A. James



On 02/09/2017 11:31 PM, Joel Stanley wrote:
> On Wed, Feb 8, 2017 at 9:40 AM,<eajames@linux.vnet.ibm.com>  wrote:
>> From: "Edward A. James"<eajames@us.ibm.com>
>>
>> Add functions to send SCOM operations over I2C bus. The BMC can
>> communicate with the Power8 host processor over I2C, but needs to use SCOM
>> operations in order to access the OCC register space.
> This doesn't need to be separate from the p8_occ_i2c.c file. You can
> remove a layer of function calls by merging this in and having these
> be your getscom putscom bus_ops callbacks.

The purpose of having this separate was so that we could do the scom 
address shift for p8 separately.

>> Signed-off-by: Edward A. James<eajames@us.ibm.com>
>> Signed-off-by: Andrew Jeffery<andrew@aj.id.au>
>> ---
>>   drivers/hwmon/occ/occ_scom_i2c.c | 77 ++++++++++++++++++++++++++++++++++++++++
>>   drivers/hwmon/occ/occ_scom_i2c.h | 26 ++++++++++++++
>>   2 files changed, 103 insertions(+)
>>   create mode 100644 drivers/hwmon/occ/occ_scom_i2c.c
>>   create mode 100644 drivers/hwmon/occ/occ_scom_i2c.h
>>
>> diff --git a/drivers/hwmon/occ/occ_scom_i2c.c b/drivers/hwmon/occ/occ_scom_i2c.c
>> new file mode 100644
>> index 0000000..74bd6ff
>> --- /dev/null
>> +++ b/drivers/hwmon/occ/occ_scom_i2c.c
>> @@ -0,0 +1,77 @@
>> +
>> +int occ_i2c_getscom(void *bus, u32 address, u64 *data)
>> +{
>> +       ssize_t rc;
>> +       u64 buf;
> If you add endianness annotations sparse can check your types are
> consistent. The warning looks like this:
>
> make C=2 drivers/hwmon/occ/occ_scom_i2c.o
> drivers/hwmon/occ/occ_scom_i2c.c:48:17: warning: cast to restricted __be64
>
> Which tells you it expects the type you pass to be64_to_cpu to be __be64.
>
>
>> +       struct i2c_client *client = bus;
>> +       struct i2c_msg msgs[2];
>> +
>> +       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;
>> +       msgs[1].flags |= I2C_M_RD;
> I first thought you had made a mistake here. Instead you could do:
>
>         msgs[1].flags = client->flags & I2C_M_TEN | I2C_M_RD;

Sure. Was just copying i2c_master_recv.

>> +       msgs[1].len = sizeof(u64);
>> +       msgs[1].buf = (char *)&buf;
>> +
>> +       rc = i2c_transfer(client->adapter, msgs, 2);
>> +       if (rc < 0)
>> +               return rc;
>> +

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

* Re: [PATCH linux v7 3/6] hwmon: occ: Add I2C transport implementation for SCOM operations
  2017-02-10 21:05     ` Eddie James
@ 2017-02-13  1:12       ` Andrew Jeffery
  0 siblings, 0 replies; 23+ messages in thread
From: Andrew Jeffery @ 2017-02-13  1:12 UTC (permalink / raw)
  To: Eddie James, Joel Stanley
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Benjamin Herrenschmidt, Edward A. James

[-- Attachment #1: Type: text/plain, Size: 1422 bytes --]

On Fri, 2017-02-10 at 15:05 -0600, Eddie James wrote:
> On 02/09/2017 11:31 PM, Joel Stanley wrote:
> > > On Wed, Feb 8, 2017 at 9:40 AM,<eajames@linux.vnet.ibm.com>  wrote:
> > >> From: "Edward A. James"<eajames@us.ibm.com>
> >>
> >> Add functions to send SCOM operations over I2C bus. The BMC can
> >> communicate with the Power8 host processor over I2C, but needs to use SCOM
> >> operations in order to access the OCC register space.
> > This doesn't need to be separate from the p8_occ_i2c.c file. You can
> > remove a layer of function calls by merging this in and having these
> > be your getscom putscom bus_ops callbacks.
> 
> The purpose of having this separate was so that we could do the scom 
> address shift for p8 separately.

Separation was my suggestion. It makes the I2C transport implementation
independent of any P8 details, and having it separate* is no more
abstract than the core performing SCOMs through the FSI interface when
that's available. I feel like it's muddying the waters conceptually to
bury P8 details in the implementation of a SCOM transport layer.

However, in our less abstract world the P8 will be the only system that
uses the I2C transport, so while I don't think merging the functions is
a good idea from an abstraction perspective it won't have a big impact.

Andrew

* Maybe the I2C SCOM transport even deserves to live outside
drivers/hwmon/occ?

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* Re: [PATCH linux v7 4/6] hwmon: occ: Add callbacks for parsing P8 OCC datastructures
  2017-02-10  5:31   ` Joel Stanley
@ 2017-02-13  1:17       ` Andrew Jeffery
  0 siblings, 0 replies; 23+ messages in thread
From: Andrew Jeffery @ 2017-02-13  1:17 UTC (permalink / raw)
  To: Joel Stanley, eajames
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Benjamin Herrenschmidt, Edward A. James

[-- Attachment #1: Type: text/plain, Size: 4389 bytes --]

On Fri, 2017-02-10 at 16:01 +1030, Joel Stanley wrote:
> > On Wed, Feb 8, 2017 at 9:40 AM,  <eajames@linux.vnet.ibm.com> wrote:
> > > > From: "Edward A. James" <eajames@us.ibm.com>
> > 
> > Add functions to parse the data structures that are specific to the OCC on
> > the POWER8 processor. These are the sensor data structures, including
> > temperature, frequency, power, and "caps."
> > 
> > > > Signed-off-by: Edward A. James <eajames@us.ibm.com>
> > > > Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> > ---
> >  Documentation/hwmon/occ    |   9 ++
> >  drivers/hwmon/occ/occ_p8.c | 248 +++++++++++++++++++++++++++++++++++++++++++++
> >  drivers/hwmon/occ/occ_p8.h |  30 ++++++
> >  3 files changed, 287 insertions(+)
> >  create mode 100644 drivers/hwmon/occ/occ_p8.c
> >  create mode 100644 drivers/hwmon/occ/occ_p8.h
> > 
> > diff --git a/drivers/hwmon/occ/occ_p8.c b/drivers/hwmon/occ/occ_p8.c
> > new file mode 100644
> > index 0000000..5c61fc4
> > --- /dev/null
> > +++ b/drivers/hwmon/occ/occ_p8.c
> > +void p8_parse_sensor(u8 *data, void *sensor, int sensor_type, int off,
> > +                    int snum)
> > +{
> > +       switch (sensor_type) {
> > +       case FREQ:
> > +       case TEMP:
> > +       {
> > +               struct p8_occ_sensor *os =
> > +                       &(((struct p8_occ_sensor *)sensor)[snum]);
> > +
> > +               os->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
> > +               os->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 2]));
> > +       }
> > +               break;
> > +       case POWER:
> > +       {
> > +               struct p8_power_sensor *ps =
> > +                       &(((struct p8_power_sensor *)sensor)[snum]);
> > +
> > +               ps->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
> > +               ps->update_tag =
> > +                       be32_to_cpu(get_unaligned((u32 *)&data[off + 2]));
> 
> This might be more readable if you wrote a
> cast_get_unaliged_be32_to_cpu() macro.
> 
> > +               ps->accumulator =
> > +                       be32_to_cpu(get_unaligned((u32 *)&data[off + 6]));
> > +               ps->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 10]));
> > +       }
> > +               break;
> > +       case CAPS:
> > +       {
> > +const u32 *p8_get_sensor_hwmon_configs()
> > +{
> > +       return p8_sensor_hwmon_configs;
> > +}
> > +EXPORT_SYMBOL(p8_get_sensor_hwmon_configs);
> > +
> > +struct occ *p8_occ_start(struct device *dev, void *bus,
> > +                        struct occ_bus_ops *bus_ops)
> > +{
> > +       return occ_start(dev, bus, bus_ops, &p8_ops, &p8_config);
> > +}
> > +EXPORT_SYMBOL(p8_occ_start);
> 
> We don't need to export these symbols; they're not used outside of the
> OCC module. The same goes for all of the exports you've made in this
> series.

Sorry, this was my doing in an attempt to get everything to build as
modules rather than just built-in. I should have studied
Documentation/kbuild/modules.txt a bit more.

> 
> I suggest we re-architect the drivers so we build all of the objects
> and link them into one module for each platform, instead of having an
> occ module and occ-p8/occ-p9 modules and i2c modules that all depend
> on each other. The Makefile could look like this:
> 
> obj-$(CONFIG_SENSORS_PPC_OCC_P8_I2C) += hwmon_occ_p8.o
> obj-$(CONFIG_SENSORS_PPC_OCC_P9) += hwmon_occ_p9.o
> 
> hwmon_occ_p8-$(CONFIG_SENSORS_PPC_OCC_P8_I2C) += occ_scom_i2c.o
> occ_p8.o p8_occ_i2c.o occ_sysfs.o occ.o
> hwmon_occ_p9-$(CONFIG_SENSORS_PPC_OCC_P9) += occ_p9.o occ_sysfs.o occ.o
> 
> And the Kbuild like this:
> 
> menuconfig SENSORS_PPC_OCC
>         bool "PPC On-Chip Controller"
> 
> if SENSORS_PPC_OCC
> 
> config SENSORS_PPC_OCC_P8_I2C
>         bool "POWER8 OCC hwmon support"
>         depends on I2C
> 
> config SENSORS_PPC_OCC_P9
>         bool "POWER9 OCC hwmon support"
> 
> endif

Given we can drop the exports that's a much more sensible idea.

Andrew

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* Re: [PATCH linux v7 4/6] hwmon: occ: Add callbacks for parsing P8 OCC datastructures
@ 2017-02-13  1:17       ` Andrew Jeffery
  0 siblings, 0 replies; 23+ messages in thread
From: Andrew Jeffery @ 2017-02-13  1:17 UTC (permalink / raw)
  To: Joel Stanley, eajames
  Cc: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-hwmon-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, jdelvare-IBi9RG/b67k,
	corbet-T1hC0tSOHrs, Mark Rutland, Rob Herring, Wolfram Sang,
	Benjamin Herrenschmidt, Edward A. James

[-- Attachment #1: Type: text/plain, Size: 4389 bytes --]

On Fri, 2017-02-10 at 16:01 +1030, Joel Stanley wrote:
> > On Wed, Feb 8, 2017 at 9:40 AM,  <eajames@linux.vnet.ibm.com> wrote:
> > > > From: "Edward A. James" <eajames@us.ibm.com>
> > 
> > Add functions to parse the data structures that are specific to the OCC on
> > the POWER8 processor. These are the sensor data structures, including
> > temperature, frequency, power, and "caps."
> > 
> > > > Signed-off-by: Edward A. James <eajames@us.ibm.com>
> > > > Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> > ---
> >  Documentation/hwmon/occ    |   9 ++
> >  drivers/hwmon/occ/occ_p8.c | 248 +++++++++++++++++++++++++++++++++++++++++++++
> >  drivers/hwmon/occ/occ_p8.h |  30 ++++++
> >  3 files changed, 287 insertions(+)
> >  create mode 100644 drivers/hwmon/occ/occ_p8.c
> >  create mode 100644 drivers/hwmon/occ/occ_p8.h
> > 
> > diff --git a/drivers/hwmon/occ/occ_p8.c b/drivers/hwmon/occ/occ_p8.c
> > new file mode 100644
> > index 0000000..5c61fc4
> > --- /dev/null
> > +++ b/drivers/hwmon/occ/occ_p8.c
> > +void p8_parse_sensor(u8 *data, void *sensor, int sensor_type, int off,
> > +                    int snum)
> > +{
> > +       switch (sensor_type) {
> > +       case FREQ:
> > +       case TEMP:
> > +       {
> > +               struct p8_occ_sensor *os =
> > +                       &(((struct p8_occ_sensor *)sensor)[snum]);
> > +
> > +               os->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
> > +               os->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 2]));
> > +       }
> > +               break;
> > +       case POWER:
> > +       {
> > +               struct p8_power_sensor *ps =
> > +                       &(((struct p8_power_sensor *)sensor)[snum]);
> > +
> > +               ps->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
> > +               ps->update_tag =
> > +                       be32_to_cpu(get_unaligned((u32 *)&data[off + 2]));
> 
> This might be more readable if you wrote a
> cast_get_unaliged_be32_to_cpu() macro.
> 
> > +               ps->accumulator =
> > +                       be32_to_cpu(get_unaligned((u32 *)&data[off + 6]));
> > +               ps->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 10]));
> > +       }
> > +               break;
> > +       case CAPS:
> > +       {
> > +const u32 *p8_get_sensor_hwmon_configs()
> > +{
> > +       return p8_sensor_hwmon_configs;
> > +}
> > +EXPORT_SYMBOL(p8_get_sensor_hwmon_configs);
> > +
> > +struct occ *p8_occ_start(struct device *dev, void *bus,
> > +                        struct occ_bus_ops *bus_ops)
> > +{
> > +       return occ_start(dev, bus, bus_ops, &p8_ops, &p8_config);
> > +}
> > +EXPORT_SYMBOL(p8_occ_start);
> 
> We don't need to export these symbols; they're not used outside of the
> OCC module. The same goes for all of the exports you've made in this
> series.

Sorry, this was my doing in an attempt to get everything to build as
modules rather than just built-in. I should have studied
Documentation/kbuild/modules.txt a bit more.

> 
> I suggest we re-architect the drivers so we build all of the objects
> and link them into one module for each platform, instead of having an
> occ module and occ-p8/occ-p9 modules and i2c modules that all depend
> on each other. The Makefile could look like this:
> 
> obj-$(CONFIG_SENSORS_PPC_OCC_P8_I2C) += hwmon_occ_p8.o
> obj-$(CONFIG_SENSORS_PPC_OCC_P9) += hwmon_occ_p9.o
> 
> hwmon_occ_p8-$(CONFIG_SENSORS_PPC_OCC_P8_I2C) += occ_scom_i2c.o
> occ_p8.o p8_occ_i2c.o occ_sysfs.o occ.o
> hwmon_occ_p9-$(CONFIG_SENSORS_PPC_OCC_P9) += occ_p9.o occ_sysfs.o occ.o
> 
> And the Kbuild like this:
> 
> menuconfig SENSORS_PPC_OCC
>         bool "PPC On-Chip Controller"
> 
> if SENSORS_PPC_OCC
> 
> config SENSORS_PPC_OCC_P8_I2C
>         bool "POWER8 OCC hwmon support"
>         depends on I2C
> 
> config SENSORS_PPC_OCC_P9
>         bool "POWER9 OCC hwmon support"
> 
> endif

Given we can drop the exports that's a much more sensible idea.

Andrew

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* Re: [PATCH linux v7 6/6] hwmon: occ: Add callbacks for parsing P9 OCC datastructures
@ 2017-02-13  1:29       ` Andrew Jeffery
  0 siblings, 0 replies; 23+ messages in thread
From: Andrew Jeffery @ 2017-02-13  1:29 UTC (permalink / raw)
  To: Joel Stanley, eajames
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Benjamin Herrenschmidt, Edward A. James

[-- Attachment #1: Type: text/plain, Size: 724 bytes --]

On Fri, 2017-02-10 at 16:01 +1030, Joel Stanley wrote:
> > +
> > +#ifndef __OCC_P9_H__
> > +#define __OCC_P9_H__
> > +
> > +#include "scom.h"
> > +
> > +struct device;
> 
> Include the header for struct device instead.
> 
> Did you consider the one header file for all of your shared functions?
> I don't think there's much value in having a whole heap of small ones.

My bias is against monolithic headers. While it would be no
linux/sched.h[1] so the impact won't be great, I prefer keeping headers
to only describing the abstract data type at hand. A collection of
small, relevant headers makes it easier for me to understand the
abstraction boundaries.

Andrew

[1] https://lwn.net/Articles/713712/

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* Re: [PATCH linux v7 6/6] hwmon: occ: Add callbacks for parsing P9 OCC datastructures
@ 2017-02-13  1:29       ` Andrew Jeffery
  0 siblings, 0 replies; 23+ messages in thread
From: Andrew Jeffery @ 2017-02-13  1:29 UTC (permalink / raw)
  To: Joel Stanley, eajames
  Cc: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-hwmon-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, jdelvare-IBi9RG/b67k,
	corbet-T1hC0tSOHrs, Mark Rutland, Rob Herring, Wolfram Sang,
	Benjamin Herrenschmidt, Edward A. James

[-- Attachment #1: Type: text/plain, Size: 724 bytes --]

On Fri, 2017-02-10 at 16:01 +1030, Joel Stanley wrote:
> > +
> > +#ifndef __OCC_P9_H__
> > +#define __OCC_P9_H__
> > +
> > +#include "scom.h"
> > +
> > +struct device;
> 
> Include the header for struct device instead.
> 
> Did you consider the one header file for all of your shared functions?
> I don't think there's much value in having a whole heap of small ones.

My bias is against monolithic headers. While it would be no
linux/sched.h[1] so the impact won't be great, I prefer keeping headers
to only describing the abstract data type at hand. A collection of
small, relevant headers makes it easier for me to understand the
abstraction boundaries.

Andrew

[1] https://lwn.net/Articles/713712/

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* Re: [PATCH linux v7 4/6] hwmon: occ: Add callbacks for parsing P8 OCC datastructures
  2017-02-13  1:17       ` Andrew Jeffery
  (?)
@ 2017-02-13 17:01       ` Guenter Roeck
  -1 siblings, 0 replies; 23+ messages in thread
From: Guenter Roeck @ 2017-02-13 17:01 UTC (permalink / raw)
  To: Andrew Jeffery
  Cc: Joel Stanley, eajames, devicetree, linux-kernel, linux-hwmon,
	linux-doc, jdelvare, corbet, Mark Rutland, Rob Herring,
	Wolfram Sang, Benjamin Herrenschmidt, Edward A. James

On Mon, Feb 13, 2017 at 11:47:22AM +1030, Andrew Jeffery wrote:
> On Fri, 2017-02-10 at 16:01 +1030, Joel Stanley wrote:
> > > On Wed, Feb 8, 2017 at 9:40 AM,  <eajames@linux.vnet.ibm.com> wrote:
> > > > > From: "Edward A. James" <eajames@us.ibm.com>
> > > 
> > > Add functions to parse the data structures that are specific to the OCC on
> > > the POWER8 processor. These are the sensor data structures, including
> > > temperature, frequency, power, and "caps."
> > > 
> > > > > Signed-off-by: Edward A. James <eajames@us.ibm.com>
> > > > > Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> > > ---
> > >  Documentation/hwmon/occ    |   9 ++
> > >  drivers/hwmon/occ/occ_p8.c | 248 +++++++++++++++++++++++++++++++++++++++++++++
> > >  drivers/hwmon/occ/occ_p8.h |  30 ++++++
> > >  3 files changed, 287 insertions(+)
> > >  create mode 100644 drivers/hwmon/occ/occ_p8.c
> > >  create mode 100644 drivers/hwmon/occ/occ_p8.h
> > > 
> > > diff --git a/drivers/hwmon/occ/occ_p8.c b/drivers/hwmon/occ/occ_p8.c
> > > new file mode 100644
> > > index 0000000..5c61fc4
> > > --- /dev/null
> > > +++ b/drivers/hwmon/occ/occ_p8.c
> > > +void p8_parse_sensor(u8 *data, void *sensor, int sensor_type, int off,
> > > +                    int snum)
> > > +{
> > > +       switch (sensor_type) {
> > > +       case FREQ:
> > > +       case TEMP:
> > > +       {
> > > +               struct p8_occ_sensor *os =
> > > +                       &(((struct p8_occ_sensor *)sensor)[snum]);
> > > +
> > > +               os->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
> > > +               os->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 2]));
> > > +       }
> > > +               break;
> > > +       case POWER:
> > > +       {
> > > +               struct p8_power_sensor *ps =
> > > +                       &(((struct p8_power_sensor *)sensor)[snum]);
> > > +
> > > +               ps->sensor_id = be16_to_cpu(get_unaligned((u16 *)&data[off]));
> > > +               ps->update_tag =
> > > +                       be32_to_cpu(get_unaligned((u32 *)&data[off + 2]));
> > 
> > This might be more readable if you wrote a
> > cast_get_unaliged_be32_to_cpu() macro.
> > 
> > > +               ps->accumulator =
> > > +                       be32_to_cpu(get_unaligned((u32 *)&data[off + 6]));
> > > +               ps->value = be16_to_cpu(get_unaligned((u16 *)&data[off + 10]));
> > > +       }
> > > +               break;
> > > +       case CAPS:
> > > +       {
> > > +const u32 *p8_get_sensor_hwmon_configs()
> > > +{
> > > +       return p8_sensor_hwmon_configs;
> > > +}
> > > +EXPORT_SYMBOL(p8_get_sensor_hwmon_configs);
> > > +
> > > +struct occ *p8_occ_start(struct device *dev, void *bus,
> > > +                        struct occ_bus_ops *bus_ops)
> > > +{
> > > +       return occ_start(dev, bus, bus_ops, &p8_ops, &p8_config);
> > > +}
> > > +EXPORT_SYMBOL(p8_occ_start);
> > 
> > We don't need to export these symbols; they're not used outside of the
> > OCC module. The same goes for all of the exports you've made in this
> > series.
> 
> Sorry, this was my doing in an attempt to get everything to build as
> modules rather than just built-in. I should have studied
> Documentation/kbuild/modules.txt a bit more.
> 
> > 
> > I suggest we re-architect the drivers so we build all of the objects
> > and link them into one module for each platform, instead of having an
> > occ module and occ-p8/occ-p9 modules and i2c modules that all depend
> > on each other. The Makefile could look like this:
> > 
> > obj-$(CONFIG_SENSORS_PPC_OCC_P8_I2C) += hwmon_occ_p8.o
> > obj-$(CONFIG_SENSORS_PPC_OCC_P9) += hwmon_occ_p9.o
> > 
> > hwmon_occ_p8-$(CONFIG_SENSORS_PPC_OCC_P8_I2C) += occ_scom_i2c.o
> > occ_p8.o p8_occ_i2c.o occ_sysfs.o occ.o
> > hwmon_occ_p9-$(CONFIG_SENSORS_PPC_OCC_P9) += occ_p9.o occ_sysfs.o occ.o
> > 
Please note that the above will still result in separate modules,
one per object file. Is this really what you want ? I don't even
know what happens if an object file is specified twice, so
you may also have to make sure that CONFIG_SENSORS_PPC_OCC_P8_I2C
and CONFIG_SENSORS_PPC_OCC_P9 are either-or configurations.
Again, I am not really sure if that is what you want.

> > And the Kbuild like this:
> > 
> > menuconfig SENSORS_PPC_OCC
> >         bool "PPC On-Chip Controller"
> > 
> > if SENSORS_PPC_OCC
> > 
> > config SENSORS_PPC_OCC_P8_I2C
> >         bool "POWER8 OCC hwmon support"
> >         depends on I2C
> > 
> > config SENSORS_PPC_OCC_P9
> >         bool "POWER9 OCC hwmon support"
> > 
If both are bool, ie there won't be any module support,
I don't really see the point of specifying occ_sysfs.o and
occ.o twice above.

> > endif
> 
> Given we can drop the exports that's a much more sensible idea.
> 
Please make sure that both "allmodconfig" and "allyesconfig"
still build after this change.

Thanks,
Guenter

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

* Re: [PATCH linux v7 1/6] hwmon: Add core On-Chip Controller support for POWER CPUs
  2017-02-10  5:31   ` Joel Stanley
  2017-02-10 21:02     ` Eddie James
@ 2017-02-14 15:36     ` Eddie James
  1 sibling, 0 replies; 23+ messages in thread
From: Eddie James @ 2017-02-14 15:36 UTC (permalink / raw)
  To: Joel Stanley
  Cc: Guenter Roeck, devicetree, linux-kernel, linux-hwmon, linux-doc,
	jdelvare, corbet, Mark Rutland, Rob Herring, Wolfram Sang,
	Andrew Jeffery, Benjamin Herrenschmidt, Edward A. James



On 02/09/2017 11:31 PM, Joel Stanley wrote:
> On Wed, Feb 8, 2017 at 9:40 AM,  <eajames@linux.vnet.ibm.com> wrote:
>
>> diff --git a/Documentation/hwmon/occ b/Documentation/hwmon/occ
>> new file mode 100644
>> index 0000000..79d1642
>> --- /dev/null
>> +++ b/Documentation/hwmon/occ
> The kernel is using  reStructuredText these days. You should consider
> following suit:
>
>   https://www.kernel.org/doc/html/latest/doc-guide/sphinx.html#writing-documentation
>
>
>> @@ -0,0 +1,40 @@
>> +Kernel driver occ
>> +=================
>> +
>> +Supported chips:
>> + * ASPEED AST2400
>> + * ASPEED AST2500
> Not really - this will run on any chip that's connected to a P8 or P9.
>
>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 5f10c28..193a13b 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -9128,6 +9128,13 @@ T:       git git://linuxtv.org/media_tree.git
>>   S:     Maintained
>>   F:     drivers/media/i2c/ov7670.c
>>
>> +ON-CHIP CONTROLLER HWMON DRIVER
> Mention P8 and P9?
>
>> +M:     Eddie James <eajames@us.ibm.com>
>> +L:     linux-hwmon@vger.kernel.org
> Have you subscribed to this list? Would you prefer the mail to come to
> the openbmc list?
>
>> +S:     Maintained
>> +F:     Documentation/hwmon/occ
>> +F:     drivers/hwmon/occ/
>> +
>>   ONENAND FLASH DRIVER
>>   M:     Kyungmin Park <kyungmin.park@samsung.com>
>>   L:     linux-mtd@lists.infradead.org
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 190d270..e80ca81 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -1240,6 +1240,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 d2cb7e8..c7ec5d4 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -169,6 +169,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_PPC_OCC)  += occ/
>>   obj-$(CONFIG_PMBUS)            += pmbus/
>>
>>   ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
>
>> diff --git a/drivers/hwmon/occ/occ.c b/drivers/hwmon/occ/occ.c
>> new file mode 100644
>> index 0000000..af077f2
>> --- /dev/null
>> +++ b/drivers/hwmon/occ/occ.c
>> +               sensors = &resp->data.blocks[b].sensors;
>> +               if (!sensors) {
>> +                       /* first poll response */
>> +                       sensors = driver->ops.alloc_sensor(dev, sensor_type,
>> +                                                          block->num_sensors);
>> +                       if (!sensors)
>> +                               return -ENOMEM;
>> +
>> +                       resp->data.blocks[b].sensors = sensors;
>> +                       resp->data.sensor_block_id[sensor_type] = b;
>> +                       resp->data.blocks[b].header = *block;
>> +               } else if (block->sensor_length !=
>> +                        resp->data.blocks[b].header.sensor_length) {
>> +                       dev_warn(dev,
>> +                                "different sensor length than first poll\n");
> The driver has changed behaviour from your previous version; now it
> discards data if the sensors change.
>
> Under what circumstances does the sensor data change?
>
>> +                       continue;
>> +               }
>> +
>> +               for (s = 0; s < block->num_sensors &&
>> +                    s < resp->data.blocks[b].header.num_sensors; s++) {
>> +                       if (offset + block->sensor_length > num_bytes) {
>> +                               dev_warn(dev, "exceeded data length\n");
>> +                               return 0;
>> +                       }
>> +
>> +                       driver->ops.parse_sensor(data, sensors, sensor_type,
>> +                                                offset, s);
>> +                       offset += block->sensor_length;
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static u8 occ_send_cmd(struct occ *driver, u8 seq, u8 type, u16 length,
>> +                      const u8 *data, u8 *resp)
>> +{
>> +       u32 cmd1, cmd2 = 0;
>> +       u16 checksum = 0;
>> +       bool retry = false;
>> +       int i, rc, tries = 0;
>> +
>> +       cmd1 = (seq << 24) | (type << 16) | length;
>> +       memcpy(&cmd2, data, length);
>> +       cmd2 <<= ((4 - length) * 8);
>> +
>> +       /* checksum: sum of every bytes of cmd1, cmd2 */
>> +       for (i = 0; i < 4; i++) {
>> +               checksum += (cmd1 >> (i * 8)) & 0xFF;
>> +               checksum += (cmd2 >> (i * 8)) & 0xFF;
>> +       }
>> +
>> +       cmd2 |= checksum << ((2 - length) * 8);
>> +
>> +       /* Init OCB */
> You should mention what OCB means in your documentation.
>
>> +       rc = driver->bus_ops.putscom(driver->bus, OCB_STATUS_CONTROL_OR,
>> +                                    OCB_OR_INIT0, OCB_OR_INIT1);
>> +       if (rc)
>> +               goto err;
>> +
>> +int occ_set_user_powercap(struct occ *occ, u16 cap)
>> +{
>> +       u8 resp[8];
>> +
>> +       cap = cpu_to_be16(cap);
>> +
>> +       return occ_send_cmd(occ, 0, OCC_SET_USER_POWR_CAP, 2, (const u8 *)&cap,
>> +                           resp);
>> +}
>> +EXPORT_SYMBOL(occ_set_user_powercap);
>> +
>> +struct occ *occ_start(struct device *dev, void *bus,
>  From what I can tell this doesn't start anything. Call it occ_init()
> or something.
>
>> +                     struct occ_bus_ops *bus_ops, const struct occ_ops *ops,
>> +                     const struct occ_config *config)
> Create a structure with all of these details in it. Some of them don't
> need to be broken out into their own, for instance:
>
> struct occ *occ_start(struct device *dev, const struct occ_init_context *init)
> {
>
>     driver->dev = dev;
>     driver->bus = init->bus;
>     driver->bus_read = init->bus_read;
>     driver->bus_write = init->bus_write;
>     driver->config = init->config;
>     driver->cmd_addr = init->cmd_addr;
>
> etc.

Tried this but it doesn't make that much sense, as half the args come 
from occ_p8.c and the other half from p8_occ_i2c.c. Can't make the 
structure const then, and it seems strange to init the structure in two 
places...

>
>
>
>> +{
>> +       struct occ *driver = devm_kzalloc(dev, sizeof(struct occ), GFP_KERNEL);
>> +
>> +       if (!driver)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       driver->dev = dev;
>> +       driver->bus = bus;
>> +       driver->bus_ops = *bus_ops;
>> +       driver->ops = *ops;
>> +       driver->config = *config;
>> +       driver->raw_data = devm_kzalloc(dev, OCC_DATA_MAX, GFP_KERNEL);
>> +       if (!driver->raw_data)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       mutex_init(&driver->update_lock);
>> +
>> +       return driver;
>> +}
>> +EXPORT_SYMBOL(occ_start);
>> +
>> +MODULE_AUTHOR("Eddie James <eajames@us.ibm.com>");
>> +MODULE_DESCRIPTION("OCC hwmon core driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/hwmon/occ/scom.h b/drivers/hwmon/occ/scom.h
>> new file mode 100644
>> index 0000000..b0691f3
>> --- /dev/null
>> +++ b/drivers/hwmon/occ/scom.h
>> @@ -0,0 +1,47 @@
>> +/*
>> + * scom.h - hwmon OCC driver
>> + *
>> + * This file contains data structures for scom operations to the OCC
> Are these really SCOM operations?
>
> I think they're better described read and write callbacks, as the
> operation is may be talking i2c or FSI or in the future some other
> kind of access.
>
> They do take scom addresses as parameters, so I can see the argument
> for calling them getscom/putscom.
>
>
>> + *
>> + * Copyright 2016 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.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef __SCOM_H__
>> +#define __SCOM_H__
>> +
>> +/*
>> + * occ_bus_ops - represent the low-level transfer methods to communicate with
>> + * the OCC.
>> + *
>> + * getscom - OCC scom read
>> + * @bus: handle to slave device
>> + * @address: address
>> + * @data: where to store data read from slave; buffer size must be at least
>> + * eight bytes.
> Are there situations where it's more than 8 bytes?
>
> Would it be safer to add a length argument so the read call so we
> don't put more data in the buffer than the caller expects?
>
>
>> + *
>> + * Returns 0 on success or a negative errno on error
>> + *
>> + * putscom - OCC scom write
>> + * @bus: handle to slave device
>> + * @address: address
>> + * @data0: first data word to write
>> + * @data1: second data word to write
>> + *
>> + * Returns 0 on success or a negative errno on error
>> + */
>> +struct occ_bus_ops {
>> +       int (*getscom)(void *bus, u32 address, u64 *data);
>> +       int (*putscom)(void *bus, u32 address, u32 data0, u32 data1);
>> +};
>> +
>> +#endif /* __SCOM_H__ */

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

end of thread, other threads:[~2017-02-14 15:36 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-07 23:10 [PATCH linux v7 0/6] drivers: hwmon: Add On-Chip Controller driver eajames
2017-02-07 23:10 ` [PATCH linux v7 1/6] hwmon: Add core On-Chip Controller support for POWER CPUs eajames
2017-02-10  5:31   ` Joel Stanley
2017-02-10 21:02     ` Eddie James
2017-02-14 15:36     ` Eddie James
2017-02-07 23:10 ` [PATCH linux v7 2/6] hwmon: occ: Add sysfs interface eajames
2017-02-10  5:31   ` Joel Stanley
2017-02-07 23:10 ` [PATCH linux v7 3/6] hwmon: occ: Add I2C transport implementation for SCOM operations eajames
2017-02-07 23:10   ` eajames-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8
2017-02-10  5:31   ` Joel Stanley
2017-02-10 21:05     ` Eddie James
2017-02-13  1:12       ` Andrew Jeffery
2017-02-07 23:10 ` [PATCH linux v7 4/6] hwmon: occ: Add callbacks for parsing P8 OCC datastructures eajames
2017-02-10  5:31   ` Joel Stanley
2017-02-13  1:17     ` Andrew Jeffery
2017-02-13  1:17       ` Andrew Jeffery
2017-02-13 17:01       ` Guenter Roeck
2017-02-07 23:10 ` [PATCH linux v7 5/6] hwmon: occ: Add hwmon implementation for the P8 OCC eajames
2017-02-10  5:31   ` Joel Stanley
2017-02-07 23:10 ` [PATCH linux v7 6/6] hwmon: occ: Add callbacks for parsing P9 OCC datastructures eajames
2017-02-10  5:31   ` Joel Stanley
2017-02-13  1:29     ` Andrew Jeffery
2017-02-13  1:29       ` Andrew Jeffery

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.