* [PATCH v8 01/12] dt-bindings: Add a document of PECI subsystem
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
2018-09-27 15:10 ` Rob Herring
2018-09-18 21:51 ` [PATCH v8 02/12] Documentation: ioctl: Add ioctl numbers for " Jae Hyun Yoo
` (10 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo
This commit adds a document of generic PECI bus, adapter and client
driver.
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Joel Stanley <joel@jms.id.au>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
---
.../devicetree/bindings/peci/peci.txt | 43 +++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 Documentation/devicetree/bindings/peci/peci.txt
diff --git a/Documentation/devicetree/bindings/peci/peci.txt b/Documentation/devicetree/bindings/peci/peci.txt
new file mode 100644
index 000000000000..864b7ab4e9fa
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci.txt
@@ -0,0 +1,43 @@
+Generic device tree configuration for PECI adapters
+===================================================
+
+Required properties:
+- #address-cells : Should be <1>. Read more about client addresses below.
+- #size-cells : Should be <0>. Read more about client addresses below.
+
+The cells properties above define that an address of CPU clients of a PECI bus
+are described by a single value.
+
+Example:
+ peci0: peci-bus@0 {
+ compatible = "vendor,soc-peci";
+ reg = <0x0 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+Generic device tree configuration for PECI clients
+==================================================
+
+Required properties:
+- compatible : Should contain name of PECI client.
+- reg : Should contain address of a client CPU. Address range of CPU
+ clients starts from 0x30 based on PECI specification.
+
+Example:
+ peci-bus@0 {
+ compatible = "vendor,soc-peci";
+ reg = <0x0 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ peci-client@30 {
+ compatible = "intel,peci-client";
+ reg = <0x30>;
+ };
+
+ peci-client@31 {
+ compatible = "intel,peci-client";
+ reg = <0x31>;
+ };
+ };
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v8 01/12] dt-bindings: Add a document of PECI subsystem
2018-09-18 21:51 ` [PATCH v8 01/12] dt-bindings: Add a document of PECI subsystem Jae Hyun Yoo
@ 2018-09-27 15:10 ` Rob Herring
2018-09-27 16:27 ` Jae Hyun Yoo
0 siblings, 1 reply; 23+ messages in thread
From: Rob Herring @ 2018-09-27 15:10 UTC (permalink / raw)
To: Jae Hyun Yoo
Cc: Lee Jones, Thomas Gleixner, Juergen Gross, Cyrille Pitchen,
linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo
On Tue, 18 Sep 2018 14:51:13 -0700, Jae Hyun Yoo wrote:
> This commit adds a document of generic PECI bus, adapter and client
> driver.
>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: Joel Stanley <joel@jms.id.au>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
> Reviewed-by: James Feist <james.feist@linux.intel.com>
> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
> ---
> .../devicetree/bindings/peci/peci.txt | 43 +++++++++++++++++++
> 1 file changed, 43 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/peci/peci.txt
>
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v8 01/12] dt-bindings: Add a document of PECI subsystem
2018-09-27 15:10 ` Rob Herring
@ 2018-09-27 16:27 ` Jae Hyun Yoo
0 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-27 16:27 UTC (permalink / raw)
To: Rob Herring
Cc: Lee Jones, Thomas Gleixner, Juergen Gross, Cyrille Pitchen,
linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc
On 9/27/2018 8:10 AM, Rob Herring wrote:
> On Tue, 18 Sep 2018 14:51:13 -0700, Jae Hyun Yoo wrote:
>> This commit adds a document of generic PECI bus, adapter and client
>> driver.
>>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
>> Reviewed-by: James Feist <james.feist@linux.intel.com>
>> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
>> ---
>> .../devicetree/bindings/peci/peci.txt | 43 +++++++++++++++++++
>> 1 file changed, 43 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/peci/peci.txt
>>
>
> Reviewed-by: Rob Herring <robh@kernel.org>
>
Thanks for the review, Rob!
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v8 02/12] Documentation: ioctl: Add ioctl numbers for PECI subsystem
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 01/12] dt-bindings: Add a document of PECI subsystem Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 03/12] peci: Add support for PECI bus driver core Jae Hyun Yoo
` (9 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo, James Feist,
Jason M Biils, Vernon Mauery
This commit updates ioctl-number.txt to reflect ioctl numbers used
by the PECI subsystem.
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Darrick J. Wong <darrick.wong@oracle.com>
Cc: Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
Cc: Eric Sandeen <sandeen@redhat.com>
Cc: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
Cc: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: James Feist <james.feist@linux.intel.com>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
Documentation/ioctl/ioctl-number.txt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 13a7c999c04a..d9577593647e 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -325,6 +325,8 @@ Code Seq#(hex) Include File Comments
0xB4 00-0F linux/gpio.h <mailto:linux-gpio@vger.kernel.org>
0xB5 00-0F uapi/linux/rpmsg.h <mailto:linux-remoteproc@vger.kernel.org>
0xB6 all linux/fpga-dfl.h
+0xB7 00-0F uapi/linux/peci-ioctl.h PECI subsystem
+ <mailto:jae.hyun.yoo@linux.intel.com>
0xC0 00-0F linux/usb/iowarrior.h
0xCA 00-0F uapi/misc/cxl.h
0xCA 10-2F uapi/misc/ocxl.h
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v8 03/12] peci: Add support for PECI bus driver core
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 01/12] dt-bindings: Add a document of PECI subsystem Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 02/12] Documentation: ioctl: Add ioctl numbers for " Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 04/12] dt-bindings: Add a document of PECI adapter driver for ASPEED AST24xx/25xx SoCs Jae Hyun Yoo
` (8 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo, Gavin Schenk,
Viresh Kumar, Cyrille Pitchen, Alan Cox, Andrew Lunn,
Andy Shevchenko, Benjamin Herrenschmidt, Fengguang Wu,
Jason M Biils, Julia Cartwright
This commit adds driver implementation for PECI bus core into linux
driver framework.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Philippe Ombredanne <pombredanne@nexb.com>
Cc: Gavin Schenk <g.schenk@eckelmann.de>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Sagar Dharia <sdharia@codeaurora.org>
Cc: David Kershner <david.kershner@unisys.com>
Cc: Johan Hovold <johan@kernel.org>
Cc: Uwe Kleine-Konig <u.kleine-koenig@pengutronix.de>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Cyrille Pitchen <cyrille.pitchen@free-electrons.com>
Cc: Juergen Gross <jgross@suse.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Andy Shevchenko <andriy.shevchenko@intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Fengguang Wu <fengguang.wu@intel.com>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Julia Cartwright <juliac@eso.teric.us>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
---
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/peci/Kconfig | 12 +
drivers/peci/Makefile | 6 +
drivers/peci/peci-core.c | 1440 +++++++++++++++++++++++++++++++
include/linux/peci.h | 104 +++
include/uapi/linux/peci-ioctl.h | 265 ++++++
7 files changed, 1830 insertions(+)
create mode 100644 drivers/peci/Kconfig
create mode 100644 drivers/peci/Makefile
create mode 100644 drivers/peci/peci-core.c
create mode 100644 include/linux/peci.h
create mode 100644 include/uapi/linux/peci-ioctl.h
diff --git a/drivers/Kconfig b/drivers/Kconfig
index ab4d43923c4d..c633db2b41fb 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -219,4 +219,6 @@ source "drivers/siox/Kconfig"
source "drivers/slimbus/Kconfig"
+source "drivers/peci/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 578f469f72fb..63c9b425e6e1 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -186,3 +186,4 @@ obj-$(CONFIG_MULTIPLEXER) += mux/
obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/
obj-$(CONFIG_SIOX) += siox/
obj-$(CONFIG_GNSS) += gnss/
+obj-$(CONFIG_PECI) += peci/
diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig
new file mode 100644
index 000000000000..4ccacb22a356
--- /dev/null
+++ b/drivers/peci/Kconfig
@@ -0,0 +1,12 @@
+#
+# Platform Environment Control Interface (PECI) subsystem configuration
+#
+
+config PECI
+ bool "PECI support"
+ select RT_MUTEXES
+ select CRC8
+ help
+ The Platform Environment Control Interface (PECI) is a one-wire bus
+ interface that provides a communication channel from Intel processors
+ and chipset components to external monitoring or control devices.
diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile
new file mode 100644
index 000000000000..9e8615e0d3ff
--- /dev/null
+++ b/drivers/peci/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the PECI core and bus drivers.
+#
+
+# Core functionality
+obj-$(CONFIG_PECI) += peci-core.o
diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c
new file mode 100644
index 000000000000..7a99e823c14b
--- /dev/null
+++ b/drivers/peci/peci-core.c
@@ -0,0 +1,1440 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/peci.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+/* Mask for getting minor revision number from DIB */
+#define MINOR_REV_NUM_MASK GENMASK(11, 8)
+
+/* CRC8 table for Assure Write Frame Check */
+#define PECI_CRC8_POLYNOMIAL 0x07
+DECLARE_CRC8_TABLE(peci_crc8_table);
+
+static struct device_type peci_adapter_type;
+static struct device_type peci_client_type;
+
+/* Max number of peci cdev */
+#define PECI_CDEV_MAX 16
+
+static dev_t peci_devt;
+static bool is_registered;
+
+static DEFINE_MUTEX(core_lock);
+static DEFINE_IDR(peci_adapter_idr);
+
+static struct peci_adapter *peci_get_adapter(int nr)
+{
+ struct peci_adapter *adapter;
+
+ mutex_lock(&core_lock);
+ adapter = idr_find(&peci_adapter_idr, nr);
+ if (!adapter)
+ goto out_unlock;
+
+ if (try_module_get(adapter->owner))
+ get_device(&adapter->dev);
+ else
+ adapter = NULL;
+
+out_unlock:
+ mutex_unlock(&core_lock);
+ return adapter;
+}
+
+static void peci_put_adapter(struct peci_adapter *adapter)
+{
+ if (!adapter)
+ return;
+
+ put_device(&adapter->dev);
+ module_put(adapter->owner);
+}
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", dev->type == &peci_client_type ?
+ to_peci_client(dev)->name : to_peci_adapter(dev)->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static void peci_client_dev_release(struct device *dev)
+{
+ struct peci_client *client = to_peci_client(dev);
+
+ dev_dbg(dev, "%s: %s\n", __func__, client->name);
+ peci_put_adapter(client->adapter);
+ kfree(client);
+}
+
+static struct attribute *peci_device_attrs[] = {
+ &dev_attr_name.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(peci_device);
+
+static struct device_type peci_client_type = {
+ .groups = peci_device_groups,
+ .release = peci_client_dev_release,
+};
+
+struct peci_client *peci_verify_client(struct device *dev)
+{
+ return (dev->type == &peci_client_type)
+ ? to_peci_client(dev)
+ : NULL;
+}
+EXPORT_SYMBOL_GPL(peci_verify_client);
+
+static u8 peci_aw_fcs(u8 *data, int len)
+{
+ return crc8(peci_crc8_table, data, (size_t)len, 0);
+}
+
+static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg,
+ bool do_retry, bool has_aw_fcs)
+{
+ ktime_t start, end;
+ s64 elapsed_ms;
+ int rc = 0;
+
+ /**
+ * For some commands, the PECI originator may need to retry a command if
+ * the processor PECI client responds with a 0x8x completion code. In
+ * each instance, the processor PECI client may have started the
+ * operation but not completed it yet. When the 'retry' bit is set, the
+ * PECI client will ignore a new request if it exactly matches a
+ * previous valid request.
+ */
+
+ if (do_retry)
+ start = ktime_get();
+
+ do {
+ rc = adapter->xfer(adapter, msg);
+
+ if (!do_retry || rc)
+ break;
+
+ if (msg->rx_buf[0] == DEV_PECI_CC_SUCCESS)
+ break;
+
+ /* Retry is needed when completion code is 0x8x */
+ if ((msg->rx_buf[0] & DEV_PECI_CC_RETRY_CHECK_MASK) !=
+ DEV_PECI_CC_NEED_RETRY) {
+ rc = -EIO;
+ break;
+ }
+
+ /* Set the retry bit to indicate a retry attempt */
+ msg->tx_buf[1] |= DEV_PECI_RETRY_BIT;
+
+ /* Recalculate the AW FCS if it has one */
+ if (has_aw_fcs)
+ msg->tx_buf[msg->tx_len - 1] = 0x80 ^
+ peci_aw_fcs((u8 *)msg,
+ 2 + msg->tx_len);
+
+ /**
+ * Retry for at least 250ms before returning an error.
+ * Retry interval guideline:
+ * No minimum < Retry Interval < No maximum
+ * (recommend 10ms)
+ */
+ end = ktime_get();
+ elapsed_ms = ktime_to_ms(ktime_sub(end, start));
+ if (elapsed_ms >= DEV_PECI_RETRY_TIME_MS) {
+ dev_dbg(&adapter->dev, "Timeout retrying xfer!\n");
+ rc = -ETIMEDOUT;
+ break;
+ }
+
+ usleep_range((DEV_PECI_RETRY_INTERVAL_USEC >> 2) + 1,
+ DEV_PECI_RETRY_INTERVAL_USEC);
+ } while (true);
+
+ if (rc)
+ dev_dbg(&adapter->dev, "xfer error, rc: %d\n", rc);
+
+ return rc;
+}
+
+static int peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg)
+{
+ return __peci_xfer(adapter, msg, false, false);
+}
+
+static int peci_xfer_with_retries(struct peci_adapter *adapter,
+ struct peci_xfer_msg *msg,
+ bool has_aw_fcs)
+{
+ return __peci_xfer(adapter, msg, true, has_aw_fcs);
+}
+
+static int peci_scan_cmd_mask(struct peci_adapter *adapter)
+{
+ struct peci_xfer_msg msg;
+ int rc = 0;
+ u32 dib;
+
+ /* Update command mask just once */
+ if (adapter->cmd_mask & BIT(PECI_CMD_XFER))
+ return 0;
+
+ msg.addr = PECI_BASE_ADDR;
+ msg.tx_len = GET_DIB_WR_LEN;
+ msg.rx_len = GET_DIB_RD_LEN;
+ msg.tx_buf[0] = GET_DIB_PECI_CMD;
+
+ rc = peci_xfer(adapter, &msg);
+ if (rc)
+ return rc;
+
+ dib = le32_to_cpup((__le32 *)msg.rx_buf);
+
+ /* Check special case for Get DIB command */
+ if (dib == 0x00) {
+ dev_dbg(&adapter->dev, "DIB read as 0x00\n");
+ return -EIO;
+ }
+
+ /**
+ * Setting up the supporting commands based on minor revision number.
+ * See PECI Spec Table 3-1.
+ */
+ switch (FIELD_GET(MINOR_REV_NUM_MASK, dib)) {
+ case 6:
+ adapter->cmd_mask |= BIT(PECI_CMD_WR_IA_MSR);
+ /* fallthrough */
+ case 5:
+ adapter->cmd_mask |= BIT(PECI_CMD_WR_PCI_CFG);
+ /* fallthrough */
+ case 4:
+ adapter->cmd_mask |= BIT(PECI_CMD_RD_PCI_CFG);
+ /* fallthrough */
+ case 3:
+ adapter->cmd_mask |= BIT(PECI_CMD_RD_PCI_CFG_LOCAL);
+ adapter->cmd_mask |= BIT(PECI_CMD_WR_PCI_CFG_LOCAL);
+ /* fallthrough */
+ case 2:
+ adapter->cmd_mask |= BIT(PECI_CMD_RD_IA_MSR);
+ /* fallthrough */
+ case 1:
+ adapter->cmd_mask |= BIT(PECI_CMD_RD_PKG_CFG);
+ adapter->cmd_mask |= BIT(PECI_CMD_WR_PKG_CFG);
+ }
+
+ adapter->cmd_mask |= BIT(PECI_CMD_XFER);
+ adapter->cmd_mask |= BIT(PECI_CMD_GET_TEMP);
+ adapter->cmd_mask |= BIT(PECI_CMD_GET_DIB);
+ adapter->cmd_mask |= BIT(PECI_CMD_PING);
+
+ return rc;
+}
+
+static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd)
+{
+ if (!(adapter->cmd_mask & BIT(PECI_CMD_PING)) &&
+ peci_scan_cmd_mask(adapter) < 0) {
+ dev_dbg(&adapter->dev, "Failed to scan command mask\n");
+ return -EIO;
+ }
+
+ if (!(adapter->cmd_mask & BIT(cmd))) {
+ dev_dbg(&adapter->dev, "Command %d is not supported\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int peci_ioctl_xfer(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_xfer_msg *msg = vmsg;
+
+ return peci_xfer(adapter, msg);
+}
+
+static int peci_ioctl_ping(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_ping_msg *umsg = vmsg;
+ struct peci_xfer_msg msg;
+
+ msg.addr = umsg->addr;
+ msg.tx_len = 0;
+ msg.rx_len = 0;
+
+ return peci_xfer(adapter, &msg);
+}
+
+static int peci_ioctl_get_dib(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_get_dib_msg *umsg = vmsg;
+ struct peci_xfer_msg msg;
+ int rc;
+
+ msg.addr = umsg->addr;
+ msg.tx_len = GET_DIB_WR_LEN;
+ msg.rx_len = GET_DIB_RD_LEN;
+ msg.tx_buf[0] = GET_DIB_PECI_CMD;
+
+ rc = peci_xfer(adapter, &msg);
+ if (rc)
+ return rc;
+
+ umsg->dib = le32_to_cpup((__le32 *)msg.rx_buf);
+
+ return 0;
+}
+
+static int peci_ioctl_get_temp(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_get_temp_msg *umsg = vmsg;
+ struct peci_xfer_msg msg;
+ int rc;
+
+ msg.addr = umsg->addr;
+ msg.tx_len = GET_TEMP_WR_LEN;
+ msg.rx_len = GET_TEMP_RD_LEN;
+ msg.tx_buf[0] = GET_TEMP_PECI_CMD;
+
+ rc = peci_xfer(adapter, &msg);
+ if (rc)
+ return rc;
+
+ umsg->temp_raw = le16_to_cpup((__le16 *)msg.rx_buf);
+
+ return 0;
+}
+
+static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_rd_pkg_cfg_msg *umsg = vmsg;
+ struct peci_xfer_msg msg;
+ int rc = 0;
+
+ /* Per the PECI spec, the read length must be a byte, word, or dword */
+ if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) {
+ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+ umsg->rx_len);
+ return -EINVAL;
+ }
+
+ msg.addr = umsg->addr;
+ msg.tx_len = RDPKGCFG_WRITE_LEN;
+ /* read lengths of 1 and 2 result in an error, so only use 4 for now */
+ msg.rx_len = RDPKGCFG_READ_LEN_BASE + umsg->rx_len;
+ msg.tx_buf[0] = RDPKGCFG_PECI_CMD;
+ msg.tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg.tx_buf[2] = umsg->index; /* RdPkgConfig index */
+ msg.tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */
+ msg.tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
+
+ rc = peci_xfer_with_retries(adapter, &msg, false);
+ if (!rc)
+ memcpy(umsg->pkg_config, &msg.rx_buf[1], umsg->rx_len);
+
+ return rc;
+}
+
+static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_wr_pkg_cfg_msg *umsg = vmsg;
+ struct peci_xfer_msg msg;
+ int rc = 0, i;
+
+ /* Per the PECI spec, the write length must be a dword */
+ if (umsg->tx_len != 4) {
+ dev_dbg(&adapter->dev, "Invalid write length, tx_len: %d\n",
+ umsg->tx_len);
+ return -EINVAL;
+ }
+
+ msg.addr = umsg->addr;
+ msg.tx_len = WRPKGCFG_WRITE_LEN_BASE + umsg->tx_len;
+ /* read lengths of 1 and 2 result in an error, so only use 4 for now */
+ msg.rx_len = WRPKGCFG_READ_LEN;
+ msg.tx_buf[0] = WRPKGCFG_PECI_CMD;
+ msg.tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg.tx_buf[2] = umsg->index; /* RdPkgConfig index */
+ msg.tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */
+ msg.tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */
+ for (i = 0; i < umsg->tx_len; i++)
+ msg.tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
+
+ /* Add an Assure Write Frame Check Sequence byte */
+ msg.tx_buf[5 + i] = 0x80 ^
+ peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len);
+
+ rc = peci_xfer_with_retries(adapter, &msg, true);
+
+ return rc;
+}
+
+static int peci_ioctl_rd_ia_msr(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_rd_ia_msr_msg *umsg = vmsg;
+ struct peci_xfer_msg msg;
+ int rc = 0;
+
+ msg.addr = umsg->addr;
+ msg.tx_len = RDIAMSR_WRITE_LEN;
+ msg.rx_len = RDIAMSR_READ_LEN;
+ msg.tx_buf[0] = RDIAMSR_PECI_CMD;
+ msg.tx_buf[1] = 0x00;
+ msg.tx_buf[2] = umsg->thread_id;
+ msg.tx_buf[3] = (u8)umsg->address;
+ msg.tx_buf[4] = (u8)(umsg->address >> 8);
+
+ rc = peci_xfer_with_retries(adapter, &msg, false);
+ if (!rc)
+ memcpy(&umsg->value, &msg.rx_buf[1], sizeof(uint64_t));
+
+ return rc;
+}
+
+static int peci_ioctl_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_rd_pci_cfg_msg *umsg = vmsg;
+ struct peci_xfer_msg msg;
+ u32 address;
+ int rc = 0;
+
+ address = umsg->reg; /* [11:0] - Register */
+ address |= (u32)umsg->function << 12; /* [14:12] - Function */
+ address |= (u32)umsg->device << 15; /* [19:15] - Device */
+ address |= (u32)umsg->bus << 20; /* [27:20] - Bus */
+ /* [31:28] - Reserved */
+ msg.addr = umsg->addr;
+ msg.tx_len = RDPCICFG_WRITE_LEN;
+ msg.rx_len = RDPCICFG_READ_LEN;
+ msg.tx_buf[0] = RDPCICFG_PECI_CMD;
+ msg.tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg.tx_buf[2] = (u8)address; /* LSB - PCI Config Address */
+ msg.tx_buf[3] = (u8)(address >> 8); /* PCI Config Address */
+ msg.tx_buf[4] = (u8)(address >> 16); /* PCI Config Address */
+ msg.tx_buf[5] = (u8)(address >> 24); /* MSB - PCI Config Address */
+
+ rc = peci_xfer_with_retries(adapter, &msg, false);
+ if (!rc)
+ memcpy(umsg->pci_config, &msg.rx_buf[1], 4);
+
+ return rc;
+}
+
+static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_rd_pci_cfg_local_msg *umsg = vmsg;
+ struct peci_xfer_msg msg;
+ u32 address;
+ int rc = 0;
+
+ /* Per the PECI spec, the read length must be a byte, word, or dword */
+ if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) {
+ dev_dbg(&adapter->dev, "Invalid read length, rx_len: %d\n",
+ umsg->rx_len);
+ return -EINVAL;
+ }
+
+ address = umsg->reg; /* [11:0] - Register */
+ address |= (u32)umsg->function << 12; /* [14:12] - Function */
+ address |= (u32)umsg->device << 15; /* [19:15] - Device */
+ address |= (u32)umsg->bus << 20; /* [23:20] - Bus */
+
+ msg.addr = umsg->addr;
+ msg.tx_len = RDPCICFGLOCAL_WRITE_LEN;
+ msg.rx_len = RDPCICFGLOCAL_READ_LEN_BASE + umsg->rx_len;
+ msg.tx_buf[0] = RDPCICFGLOCAL_PECI_CMD;
+ msg.tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg.tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */
+ msg.tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */
+ msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
+
+ rc = peci_xfer_with_retries(adapter, &msg, false);
+ if (!rc)
+ memcpy(umsg->pci_config, &msg.rx_buf[1], umsg->rx_len);
+
+ return rc;
+}
+
+static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg)
+{
+ struct peci_wr_pci_cfg_local_msg *umsg = vmsg;
+ struct peci_xfer_msg msg;
+ int rc = 0, i;
+ u32 address;
+
+ /* Per the PECI spec, the write length must be a byte, word, or dword */
+ if (umsg->tx_len != 1 && umsg->tx_len != 2 && umsg->tx_len != 4) {
+ dev_dbg(&adapter->dev, "Invalid write length, tx_len: %d\n",
+ umsg->tx_len);
+ return -EINVAL;
+ }
+
+ address = umsg->reg; /* [11:0] - Register */
+ address |= (u32)umsg->function << 12; /* [14:12] - Function */
+ address |= (u32)umsg->device << 15; /* [19:15] - Device */
+ address |= (u32)umsg->bus << 20; /* [23:20] - Bus */
+
+ msg.addr = umsg->addr;
+ msg.tx_len = WRPCICFGLOCAL_WRITE_LEN_BASE + umsg->tx_len;
+ msg.rx_len = WRPCICFGLOCAL_READ_LEN;
+ msg.tx_buf[0] = WRPCICFGLOCAL_PECI_CMD;
+ msg.tx_buf[1] = 0x00; /* request byte for Host ID | Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg.tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */
+ msg.tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */
+ msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
+ for (i = 0; i < umsg->tx_len; i++)
+ msg.tx_buf[5 + i] = (u8)(umsg->value >> (i << 3));
+
+ /* Add an Assure Write Frame Check Sequence byte */
+ msg.tx_buf[5 + i] = 0x80 ^
+ peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len);
+
+ rc = peci_xfer_with_retries(adapter, &msg, true);
+
+ return rc;
+}
+
+typedef int (*peci_ioctl_fn_type)(struct peci_adapter *, void *);
+
+static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = {
+ peci_ioctl_xfer,
+ peci_ioctl_ping,
+ peci_ioctl_get_dib,
+ peci_ioctl_get_temp,
+ peci_ioctl_rd_pkg_cfg,
+ peci_ioctl_wr_pkg_cfg,
+ peci_ioctl_rd_ia_msr,
+ NULL, /* Reserved */
+ peci_ioctl_rd_pci_cfg,
+ NULL, /* Reserved */
+ peci_ioctl_rd_pci_cfg_local,
+ peci_ioctl_wr_pci_cfg_local,
+};
+
+int peci_command(struct peci_adapter *adapter, enum peci_cmd cmd, void *vmsg)
+{
+ int rc = 0;
+
+ if (cmd >= PECI_CMD_MAX || cmd < PECI_CMD_XFER)
+ return -EINVAL;
+
+ dev_dbg(&adapter->dev, "%s, cmd=0x%02x\n", __func__, cmd);
+
+ if (!peci_ioctl_fn[cmd])
+ return -EINVAL;
+
+ rt_mutex_lock(&adapter->bus_lock);
+
+ rc = peci_cmd_support(adapter, cmd);
+ if (!rc)
+ rc = peci_ioctl_fn[cmd](adapter, vmsg);
+
+ rt_mutex_unlock(&adapter->bus_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(peci_command);
+
+static long peci_ioctl(struct file *file, unsigned int iocmd, unsigned long arg)
+{
+ struct peci_adapter *adapter = file->private_data;
+ void __user *argp = (void __user *)arg;
+ unsigned int msg_len;
+ enum peci_cmd cmd;
+ int rc = 0;
+ u8 *msg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ dev_dbg(&adapter->dev, "ioctl, cmd=0x%x, arg=0x%lx\n", iocmd, arg);
+
+ switch (iocmd) {
+ case PECI_IOC_XFER:
+ case PECI_IOC_PING:
+ case PECI_IOC_GET_DIB:
+ case PECI_IOC_GET_TEMP:
+ case PECI_IOC_RD_PKG_CFG:
+ case PECI_IOC_WR_PKG_CFG:
+ case PECI_IOC_RD_IA_MSR:
+ case PECI_IOC_RD_PCI_CFG:
+ case PECI_IOC_RD_PCI_CFG_LOCAL:
+ case PECI_IOC_WR_PCI_CFG_LOCAL:
+ cmd = _IOC_NR(iocmd);
+ msg_len = _IOC_SIZE(iocmd);
+ break;
+
+ default:
+ dev_dbg(&adapter->dev, "Invalid ioctl cmd : 0x%x\n", iocmd);
+ return -ENOTTY;
+ }
+
+ if (!access_ok(VERIFY_WRITE, argp, msg_len))
+ return -EFAULT;
+
+ msg = memdup_user(argp, msg_len);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ rc = peci_command(adapter, cmd, msg);
+
+ if (!rc && copy_to_user(argp, msg, msg_len))
+ rc = -EFAULT;
+
+ kfree(msg);
+ return (long)rc;
+}
+
+static int peci_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor = iminor(inode);
+ struct peci_adapter *adapter;
+
+ adapter = peci_get_adapter(minor);
+ if (!adapter)
+ return -ENODEV;
+
+ file->private_data = adapter;
+
+ return 0;
+}
+
+static int peci_release(struct inode *inode, struct file *file)
+{
+ struct peci_adapter *adapter = file->private_data;
+
+ peci_put_adapter(adapter);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static const struct file_operations peci_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = peci_ioctl,
+ .open = peci_open,
+ .release = peci_release,
+};
+
+static int peci_detect(struct peci_adapter *adapter, u8 addr)
+{
+ struct peci_ping_msg msg;
+
+ msg.addr = addr;
+
+ return peci_command(adapter, PECI_CMD_PING, &msg);
+}
+
+static const struct of_device_id *
+peci_of_match_device(const struct of_device_id *matches,
+ struct peci_client *client)
+{
+#if IS_ENABLED(CONFIG_OF)
+ if (!(client && matches))
+ return NULL;
+
+ return of_match_device(matches, &client->dev);
+#else
+ return NULL;
+#endif
+}
+
+static const struct peci_device_id *
+peci_match_id(const struct peci_device_id *id, struct peci_client *client)
+{
+ if (!(id && client))
+ return NULL;
+
+ while (id->name[0]) {
+ if (!strncmp(client->name, id->name, PECI_NAME_SIZE))
+ return id;
+ id++;
+ }
+
+ return NULL;
+}
+
+static int peci_device_match(struct device *dev, struct device_driver *drv)
+{
+ struct peci_client *client = peci_verify_client(dev);
+ struct peci_driver *driver;
+
+ /* Attempt an OF style match */
+ if (peci_of_match_device(drv->of_match_table, client))
+ return 1;
+
+ driver = to_peci_driver(drv);
+
+ /* Finally an ID match */
+ if (peci_match_id(driver->id_table, client))
+ return 1;
+
+ return 0;
+}
+
+static int peci_device_probe(struct device *dev)
+{
+ struct peci_client *client = peci_verify_client(dev);
+ struct peci_driver *driver;
+ int status = -EINVAL;
+
+ if (!client)
+ return 0;
+
+ driver = to_peci_driver(dev->driver);
+
+ if (!driver->id_table &&
+ !peci_of_match_device(dev->driver->of_match_table, client))
+ return -ENODEV;
+
+ dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+
+ status = dev_pm_domain_attach(&client->dev, true);
+ if (status == -EPROBE_DEFER)
+ return status;
+
+ if (driver->probe)
+ status = driver->probe(client);
+ else
+ status = -EINVAL;
+
+ if (status)
+ goto err_detach_pm_domain;
+
+ return 0;
+
+err_detach_pm_domain:
+ dev_pm_domain_detach(&client->dev, true);
+ return status;
+}
+
+static int peci_device_remove(struct device *dev)
+{
+ struct peci_client *client = peci_verify_client(dev);
+ struct peci_driver *driver;
+ int status = 0;
+
+ if (!client || !dev->driver)
+ return 0;
+
+ driver = to_peci_driver(dev->driver);
+ if (driver->remove) {
+ dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+ status = driver->remove(client);
+ }
+
+ dev_pm_domain_detach(&client->dev, true);
+
+ return status;
+}
+
+static void peci_device_shutdown(struct device *dev)
+{
+ struct peci_client *client = peci_verify_client(dev);
+ struct peci_driver *driver;
+
+ if (!client || !dev->driver)
+ return;
+
+ dev_dbg(dev, "%s: name:%s\n", __func__, client->name);
+
+ driver = to_peci_driver(dev->driver);
+ if (driver->shutdown)
+ driver->shutdown(client);
+}
+
+static struct bus_type peci_bus_type = {
+ .name = "peci",
+ .match = peci_device_match,
+ .probe = peci_device_probe,
+ .remove = peci_device_remove,
+ .shutdown = peci_device_shutdown,
+};
+
+static int peci_check_addr_validity(u8 addr)
+{
+ if (addr < PECI_BASE_ADDR && addr > PECI_BASE_ADDR + PECI_OFFSET_MAX)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int peci_check_client_busy(struct device *dev, void *client_new_p)
+{
+ struct peci_client *client = peci_verify_client(dev);
+ struct peci_client *client_new = client_new_p;
+
+ if (client && client->addr == client_new->addr)
+ return -EBUSY;
+
+ return 0;
+}
+
+int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id)
+{
+ struct peci_rd_pkg_cfg_msg msg;
+ int rc;
+
+ msg.addr = addr;
+ msg.index = MBX_INDEX_CPU_ID;
+ msg.param = PKG_ID_CPU_ID;
+ msg.rx_len = 4;
+
+ rc = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg);
+ if (!rc)
+ *cpu_id = le32_to_cpup((__le32 *)msg.pkg_config);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(peci_get_cpu_id);
+
+static struct peci_client *peci_new_device(struct peci_adapter *adapter,
+ struct peci_board_info const *info)
+{
+ struct peci_client *client;
+ int rc;
+
+ /* Increase reference count for the adapter assigned */
+ if (!peci_get_adapter(adapter->nr))
+ return NULL;
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ goto err_put_adapter;
+
+ client->adapter = adapter;
+ client->addr = info->addr;
+ strlcpy(client->name, info->type, sizeof(client->name));
+
+ rc = peci_check_addr_validity(client->addr);
+ if (rc) {
+ dev_err(&adapter->dev, "Invalid PECI CPU address 0x%02hx\n",
+ client->addr);
+ goto err_free_client_silent;
+ }
+
+ /* Check online status of client */
+ rc = peci_detect(adapter, client->addr);
+ if (rc)
+ goto err_free_client;
+
+ rc = device_for_each_child(&adapter->dev, client,
+ peci_check_client_busy);
+ if (rc)
+ goto err_free_client;
+
+ client->dev.parent = &client->adapter->dev;
+ client->dev.bus = &peci_bus_type;
+ client->dev.type = &peci_client_type;
+ client->dev.of_node = info->of_node;
+ dev_set_name(&client->dev, "%d-%02x", adapter->nr, client->addr);
+
+ rc = device_register(&client->dev);
+ if (rc)
+ goto err_free_client;
+
+ dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
+ client->name, dev_name(&client->dev));
+
+ return client;
+
+err_free_client:
+ dev_err(&adapter->dev,
+ "Failed to register peci client %s at 0x%02x (%d)\n",
+ client->name, client->addr, rc);
+err_free_client_silent:
+ kfree(client);
+err_put_adapter:
+ peci_put_adapter(adapter);
+ return NULL;
+}
+
+static void peci_unregister_device(struct peci_client *client)
+{
+ if (!client)
+ return;
+
+ if (client->dev.of_node)
+ of_node_clear_flag(client->dev.of_node, OF_POPULATED);
+
+ device_unregister(&client->dev);
+}
+
+static int peci_unregister_client(struct device *dev, void *dummy)
+{
+ struct peci_client *client = peci_verify_client(dev);
+
+ peci_unregister_device(client);
+
+ return 0;
+}
+
+static void peci_adapter_dev_release(struct device *dev)
+{
+ struct peci_adapter *adapter = to_peci_adapter(dev);
+
+ dev_dbg(dev, "%s: %s\n", __func__, adapter->name);
+ mutex_destroy(&adapter->userspace_clients_lock);
+ rt_mutex_destroy(&adapter->bus_lock);
+ kfree(adapter);
+}
+
+static ssize_t peci_sysfs_new_device(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct peci_adapter *adapter = to_peci_adapter(dev);
+ struct peci_board_info info = {};
+ struct peci_client *client;
+ char *blank, end;
+ int rc;
+
+ /* Parse device type */
+ blank = strchr(buf, ' ');
+ if (!blank) {
+ dev_err(dev, "%s: Missing parameters\n", "new_device");
+ return -EINVAL;
+ }
+ if (blank - buf > PECI_NAME_SIZE - 1) {
+ dev_err(dev, "%s: Invalid device type\n", "new_device");
+ return -EINVAL;
+ }
+ memcpy(info.type, buf, blank - buf);
+
+ /* Parse remaining parameters, reject extra parameters */
+ rc = sscanf(++blank, "%hi%c", &info.addr, &end);
+ if (rc < 1) {
+ dev_err(dev, "%s: Can't parse client address\n", "new_device");
+ return -EINVAL;
+ }
+ if (rc > 1 && end != '\n') {
+ dev_err(dev, "%s: Extra parameters\n", "new_device");
+ return -EINVAL;
+ }
+
+ client = peci_new_device(adapter, &info);
+ if (!client)
+ return -EINVAL;
+
+ /* Keep track of the added device */
+ mutex_lock(&adapter->userspace_clients_lock);
+ list_add_tail(&client->detected, &adapter->userspace_clients);
+ mutex_unlock(&adapter->userspace_clients_lock);
+ dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
+ info.type, info.addr);
+
+ return count;
+}
+static DEVICE_ATTR(new_device, 0200, NULL, peci_sysfs_new_device);
+
+static ssize_t peci_sysfs_delete_device(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct peci_adapter *adapter = to_peci_adapter(dev);
+ struct peci_client *client, *next;
+ struct peci_board_info info = {};
+ struct peci_driver *driver;
+ char *blank, end;
+ int rc;
+
+ /* Parse device type */
+ blank = strchr(buf, ' ');
+ if (!blank) {
+ dev_err(dev, "%s: Missing parameters\n", "delete_device");
+ return -EINVAL;
+ }
+ if (blank - buf > PECI_NAME_SIZE - 1) {
+ dev_err(dev, "%s: Invalid device type\n", "delete_device");
+ return -EINVAL;
+ }
+ memcpy(info.type, buf, blank - buf);
+
+ /* Parse remaining parameters, reject extra parameters */
+ rc = sscanf(++blank, "%hi%c", &info.addr, &end);
+ if (rc < 1) {
+ dev_err(dev, "%s: Can't parse client address\n",
+ "delete_device");
+ return -EINVAL;
+ }
+ if (rc > 1 && end != '\n') {
+ dev_err(dev, "%s: Extra parameters\n", "delete_device");
+ return -EINVAL;
+ }
+
+ /* Make sure the device was added through sysfs */
+ rc = -ENOENT;
+ mutex_lock(&adapter->userspace_clients_lock);
+ list_for_each_entry_safe(client, next, &adapter->userspace_clients,
+ detected) {
+ driver = to_peci_driver(client->dev.driver);
+
+ if (client->addr == info.addr &&
+ !strncmp(client->name, info.type, PECI_NAME_SIZE)) {
+ dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
+ "delete_device", client->name, client->addr);
+ list_del(&client->detected);
+ peci_unregister_device(client);
+ rc = count;
+ break;
+ }
+ }
+ mutex_unlock(&adapter->userspace_clients_lock);
+
+ if (rc < 0)
+ dev_err(dev, "%s: Can't find device in list\n",
+ "delete_device");
+
+ return rc;
+}
+static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL,
+ peci_sysfs_delete_device);
+
+static struct attribute *peci_adapter_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_new_device.attr,
+ &dev_attr_delete_device.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(peci_adapter);
+
+static struct device_type peci_adapter_type = {
+ .groups = peci_adapter_groups,
+ .release = peci_adapter_dev_release,
+};
+
+struct peci_adapter *peci_verify_adapter(struct device *dev)
+{
+ return (dev->type == &peci_adapter_type)
+ ? to_peci_adapter(dev)
+ : NULL;
+}
+EXPORT_SYMBOL_GPL(peci_verify_adapter);
+
+#if IS_ENABLED(CONFIG_OF)
+static struct peci_client *peci_of_register_device(struct peci_adapter *adapter,
+ struct device_node *node)
+{
+ struct peci_board_info info = {};
+ struct peci_client *result;
+ const __be32 *addr_be;
+ int len;
+
+ dev_dbg(&adapter->dev, "register %pOF\n", node);
+
+ if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
+ dev_err(&adapter->dev, "modalias failure on %pOF\n", node);
+ return ERR_PTR(-EINVAL);
+ }
+
+ addr_be = of_get_property(node, "reg", &len);
+ if (!addr_be || len < sizeof(*addr_be)) {
+ dev_err(&adapter->dev, "invalid reg on %pOF\n", node);
+ return ERR_PTR(-EINVAL);
+ }
+
+ info.addr = be32_to_cpup(addr_be);
+ info.of_node = of_node_get(node);
+
+ result = peci_new_device(adapter, &info);
+ if (!result)
+ result = ERR_PTR(-EINVAL);
+
+ of_node_put(node);
+ return result;
+}
+
+static void peci_of_register_devices(struct peci_adapter *adapter)
+{
+ struct device_node *bus, *node;
+ struct peci_client *client;
+
+ /* Only register child devices if the adapter has a node pointer set */
+ if (!adapter->dev.of_node)
+ return;
+
+ bus = of_get_child_by_name(adapter->dev.of_node, "peci-bus");
+ if (!bus)
+ bus = of_node_get(adapter->dev.of_node);
+
+ for_each_available_child_of_node(bus, node) {
+ if (of_node_test_and_set_flag(node, OF_POPULATED))
+ continue;
+
+ client = peci_of_register_device(adapter, node);
+ if (IS_ERR(client)) {
+ dev_warn(&adapter->dev,
+ "Failed to create PECI device for %pOF\n",
+ node);
+ of_node_clear_flag(node, OF_POPULATED);
+ }
+ }
+
+ of_node_put(bus);
+}
+#else
+static void peci_of_register_devices(struct peci_adapter *adapter) { }
+#endif /* CONFIG_OF */
+
+#if IS_ENABLED(CONFIG_OF_DYNAMIC)
+static int peci_of_match_node(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+/* must call put_device() when done with returned peci_client device */
+static struct peci_client *peci_of_find_device(struct device_node *node)
+{
+ struct peci_client *client;
+ struct device *dev;
+
+ dev = bus_find_device(&peci_bus_type, NULL, node, peci_of_match_node);
+ if (!dev)
+ return NULL;
+
+ client = peci_verify_client(dev);
+ if (!client)
+ put_device(dev);
+
+ return client;
+}
+
+/* must call put_device() when done with returned peci_adapter device */
+static struct peci_adapter *peci_of_find_adapter(struct device_node *node)
+{
+ struct peci_adapter *adapter;
+ struct device *dev;
+
+ dev = bus_find_device(&peci_bus_type, NULL, node, peci_of_match_node);
+ if (!dev)
+ return NULL;
+
+ adapter = peci_verify_adapter(dev);
+ if (!adapter)
+ put_device(dev);
+
+ return adapter;
+}
+
+static int peci_of_notify(struct notifier_block *nb,
+ unsigned long action,
+ void *arg)
+{
+ struct of_reconfig_data *rd = arg;
+ struct peci_adapter *adapter;
+ struct peci_client *client;
+
+ switch (of_reconfig_get_state_change(action, rd)) {
+ case OF_RECONFIG_CHANGE_ADD:
+ adapter = peci_of_find_adapter(rd->dn->parent);
+ if (!adapter)
+ return NOTIFY_OK; /* not for us */
+
+ if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) {
+ put_device(&adapter->dev);
+ return NOTIFY_OK;
+ }
+
+ client = peci_of_register_device(adapter, rd->dn);
+ put_device(&adapter->dev);
+
+ if (IS_ERR(client)) {
+ dev_err(&adapter->dev,
+ "failed to create client for '%pOF'\n", rd->dn);
+ of_node_clear_flag(rd->dn, OF_POPULATED);
+ return notifier_from_errno(PTR_ERR(client));
+ }
+ break;
+ case OF_RECONFIG_CHANGE_REMOVE:
+ /* already depopulated? */
+ if (!of_node_check_flag(rd->dn, OF_POPULATED))
+ return NOTIFY_OK;
+
+ /* find our device by node */
+ client = peci_of_find_device(rd->dn);
+ if (!client)
+ return NOTIFY_OK; /* no? not meant for us */
+
+ /* unregister takes one ref away */
+ peci_unregister_device(client);
+
+ /* and put the reference of the find */
+ put_device(&client->dev);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block peci_of_notifier = {
+ .notifier_call = peci_of_notify,
+};
+#else
+extern struct notifier_block peci_of_notifier;
+#endif /* CONFIG_OF_DYNAMIC */
+
+struct peci_adapter *peci_alloc_adapter(struct device *dev, unsigned int size)
+{
+ struct peci_adapter *adapter;
+
+ if (!dev)
+ return NULL;
+
+ adapter = kzalloc(size + sizeof(*adapter), GFP_KERNEL);
+ if (!adapter)
+ return NULL;
+
+ device_initialize(&adapter->dev);
+ adapter->dev.parent = dev;
+ adapter->dev.bus = &peci_bus_type;
+ adapter->dev.type = &peci_adapter_type;
+ peci_set_adapdata(adapter, &adapter[1]);
+
+ return adapter;
+}
+EXPORT_SYMBOL_GPL(peci_alloc_adapter);
+
+static int peci_register_adapter(struct peci_adapter *adapter)
+{
+ int rc = -EINVAL;
+
+ /* Can't register until after driver model init */
+ if (WARN_ON(!is_registered))
+ goto err_free_idr;
+
+ if (WARN(!adapter->name[0], "peci adapter has no name"))
+ goto err_free_idr;
+
+ if (WARN(!adapter->xfer, "peci adapter has no xfer function\n"))
+ goto err_free_idr;
+
+ rt_mutex_init(&adapter->bus_lock);
+ mutex_init(&adapter->userspace_clients_lock);
+ INIT_LIST_HEAD(&adapter->userspace_clients);
+
+ dev_set_name(&adapter->dev, "peci%d", adapter->nr);
+
+ /* cdev */
+ cdev_init(&adapter->cdev, &peci_fops);
+ adapter->cdev.owner = THIS_MODULE;
+ adapter->dev.devt = MKDEV(MAJOR(peci_devt), adapter->nr);
+ rc = cdev_add(&adapter->cdev, adapter->dev.devt, 1);
+ if (rc) {
+ pr_err("adapter '%s': can't add cdev (%d)\n",
+ adapter->name, rc);
+ goto err_free_idr;
+ }
+ rc = device_add(&adapter->dev);
+ if (rc) {
+ pr_err("adapter '%s': can't add device (%d)\n",
+ adapter->name, rc);
+ goto err_del_cdev;
+ }
+
+ dev_dbg(&adapter->dev, "adapter [%s] registered\n", adapter->name);
+
+ pm_runtime_no_callbacks(&adapter->dev);
+ pm_suspend_ignore_children(&adapter->dev, true);
+ pm_runtime_enable(&adapter->dev);
+
+ /* create pre-declared device nodes */
+ peci_of_register_devices(adapter);
+
+ return 0;
+
+err_del_cdev:
+ cdev_del(&adapter->cdev);
+err_free_idr:
+ mutex_lock(&core_lock);
+ idr_remove(&peci_adapter_idr, adapter->nr);
+ mutex_unlock(&core_lock);
+ return rc;
+}
+
+static int peci_add_numbered_adapter(struct peci_adapter *adapter)
+{
+ int id;
+
+ mutex_lock(&core_lock);
+ id = idr_alloc(&peci_adapter_idr, adapter,
+ adapter->nr, adapter->nr + 1, GFP_KERNEL);
+ mutex_unlock(&core_lock);
+ if (WARN(id < 0, "couldn't get idr"))
+ return id == -ENOSPC ? -EBUSY : id;
+
+ return peci_register_adapter(adapter);
+}
+
+int peci_add_adapter(struct peci_adapter *adapter)
+{
+ struct device *dev = &adapter->dev;
+ int id;
+
+ if (dev->of_node) {
+ id = of_alias_get_id(dev->of_node, "peci");
+ if (id >= 0) {
+ adapter->nr = id;
+ return peci_add_numbered_adapter(adapter);
+ }
+ }
+
+ mutex_lock(&core_lock);
+ id = idr_alloc(&peci_adapter_idr, adapter, 0, 0, GFP_KERNEL);
+ mutex_unlock(&core_lock);
+ if (WARN(id < 0, "couldn't get idr"))
+ return id;
+
+ adapter->nr = id;
+
+ return peci_register_adapter(adapter);
+}
+EXPORT_SYMBOL_GPL(peci_add_adapter);
+
+void peci_del_adapter(struct peci_adapter *adapter)
+{
+ struct peci_client *client, *next;
+ struct peci_adapter *found;
+ int nr;
+
+ /* First make sure that this adapter was ever added */
+ mutex_lock(&core_lock);
+ found = idr_find(&peci_adapter_idr, adapter->nr);
+ mutex_unlock(&core_lock);
+
+ if (found != adapter)
+ return;
+
+ /* Remove devices instantiated from sysfs */
+ mutex_lock(&adapter->userspace_clients_lock);
+ list_for_each_entry_safe(client, next, &adapter->userspace_clients,
+ detected) {
+ dev_dbg(&adapter->dev, "Removing %s at 0x%x\n", client->name,
+ client->addr);
+ list_del(&client->detected);
+ peci_unregister_device(client);
+ }
+ mutex_unlock(&adapter->userspace_clients_lock);
+
+ /**
+ * Detach any active clients. This can't fail, thus we do not
+ * check the returned value.
+ */
+ device_for_each_child(&adapter->dev, NULL, peci_unregister_client);
+
+ /* device name is gone after device_unregister */
+ dev_dbg(&adapter->dev, "adapter [%s] unregistered\n", adapter->name);
+
+ /* free cdev */
+ cdev_del(&adapter->cdev);
+
+ pm_runtime_disable(&adapter->dev);
+
+ nr = adapter->nr;
+
+ device_unregister(&adapter->dev);
+
+ /* free bus id */
+ mutex_lock(&core_lock);
+ idr_remove(&peci_adapter_idr, nr);
+ mutex_unlock(&core_lock);
+}
+EXPORT_SYMBOL_GPL(peci_del_adapter);
+
+/**
+ * A peci_driver is used with one or more peci_client (device) nodes to access
+ * peci clients, on a bus instance associated with some peci_adapter.
+ */
+int peci_register_driver(struct module *owner, struct peci_driver *driver)
+{
+ int rc;
+
+ /* Can't register until after driver model init */
+ if (WARN_ON(!is_registered))
+ return -EAGAIN;
+
+ /* add the driver to the list of peci drivers in the driver core */
+ driver->driver.owner = owner;
+ driver->driver.bus = &peci_bus_type;
+
+ /**
+ * When registration returns, the driver core
+ * will have called probe() for all matching-but-unbound devices.
+ */
+ rc = driver_register(&driver->driver);
+ if (rc)
+ return rc;
+
+ pr_debug("driver [%s] registered\n", driver->driver.name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(peci_register_driver);
+
+void peci_del_driver(struct peci_driver *driver)
+{
+ driver_unregister(&driver->driver);
+ pr_debug("driver [%s] unregistered\n", driver->driver.name);
+}
+EXPORT_SYMBOL_GPL(peci_del_driver);
+
+static int __init peci_init(void)
+{
+ int ret;
+
+ ret = bus_register(&peci_bus_type);
+ if (ret < 0) {
+ pr_err("peci: Failed to register PECI bus type!\n");
+ return ret;
+ }
+
+ ret = alloc_chrdev_region(&peci_devt, 0, PECI_CDEV_MAX, "peci");
+ if (ret < 0) {
+ pr_err("peci: Failed to allocate chr dev region!\n");
+ bus_unregister(&peci_bus_type);
+ return ret;
+ }
+
+ crc8_populate_msb(peci_crc8_table, PECI_CRC8_POLYNOMIAL);
+
+ if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+ WARN_ON(of_reconfig_notifier_register(&peci_of_notifier));
+
+ is_registered = true;
+
+ return 0;
+}
+
+static void __exit peci_exit(void)
+{
+ if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+ WARN_ON(of_reconfig_notifier_unregister(&peci_of_notifier));
+
+ unregister_chrdev_region(peci_devt, PECI_CDEV_MAX);
+ bus_unregister(&peci_bus_type);
+}
+
+postcore_initcall(peci_init);
+module_exit(peci_exit);
+
+MODULE_AUTHOR("Jason M Biils <jason.m.bills@linux.intel.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI bus core module");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/peci.h b/include/linux/peci.h
new file mode 100644
index 000000000000..425c9cdab6dc
--- /dev/null
+++ b/include/linux/peci.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Intel Corporation */
+
+#ifndef __LINUX_PECI_H
+#define __LINUX_PECI_H
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/peci-ioctl.h>
+#include <linux/rtmutex.h>
+
+#define PECI_NAME_SIZE 32
+
+struct peci_board_info {
+ char type[PECI_NAME_SIZE];
+ unsigned short addr; /* CPU client address */
+ struct device_node *of_node;
+};
+
+struct peci_adapter {
+ struct module *owner;
+ struct rt_mutex bus_lock;
+ struct device dev;
+ struct cdev cdev;
+ int nr;
+ char name[PECI_NAME_SIZE];
+ struct mutex userspace_clients_lock; /* clients list mutex */
+ struct list_head userspace_clients;
+ int (*xfer)(struct peci_adapter *adapter,
+ struct peci_xfer_msg *msg);
+ uint cmd_mask;
+};
+
+static inline struct peci_adapter *to_peci_adapter(void *d)
+{
+ return container_of(d, struct peci_adapter, dev);
+}
+
+static inline void *peci_get_adapdata(const struct peci_adapter *adapter)
+{
+ return dev_get_drvdata(&adapter->dev);
+}
+
+static inline void peci_set_adapdata(struct peci_adapter *adapter, void *data)
+{
+ dev_set_drvdata(&adapter->dev, data);
+}
+
+struct peci_client {
+ struct device dev; /* the device structure */
+ struct peci_adapter *adapter; /* the adapter we sit on */
+ u8 addr; /* CPU client address */
+ char name[PECI_NAME_SIZE];
+ struct list_head detected;
+};
+
+static inline struct peci_client *to_peci_client(void *d)
+{
+ return container_of(d, struct peci_client, dev);
+}
+
+struct peci_device_id {
+ char name[PECI_NAME_SIZE];
+ unsigned long driver_data; /* Data private to the driver */
+};
+
+struct peci_driver {
+ int (*probe)(struct peci_client *client);
+ int (*remove)(struct peci_client *client);
+ void (*shutdown)(struct peci_client *client);
+ struct device_driver driver;
+ const struct peci_device_id *id_table;
+};
+
+static inline struct peci_driver *to_peci_driver(void *d)
+{
+ return container_of(d, struct peci_driver, driver);
+}
+
+/**
+ * module_peci_driver() - Helper macro for registering a modular PECI driver
+ * @__peci_driver: peci_driver struct
+ *
+ * Helper macro for PECI drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_peci_driver(__peci_driver) \
+ module_driver(__peci_driver, peci_add_driver, peci_del_driver)
+
+/* use a define to avoid include chaining to get THIS_MODULE */
+#define peci_add_driver(driver) peci_register_driver(THIS_MODULE, driver)
+
+int peci_register_driver(struct module *owner, struct peci_driver *drv);
+void peci_del_driver(struct peci_driver *driver);
+struct peci_client *peci_verify_client(struct device *dev);
+struct peci_adapter *peci_alloc_adapter(struct device *dev, unsigned int size);
+int peci_add_adapter(struct peci_adapter *adapter);
+void peci_del_adapter(struct peci_adapter *adapter);
+struct peci_adapter *peci_verify_adapter(struct device *dev);
+int peci_command(struct peci_adapter *adpater, enum peci_cmd cmd, void *vmsg);
+int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id);
+
+#endif /* __LINUX_PECI_H */
diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h
new file mode 100644
index 000000000000..b30d797c3b6e
--- /dev/null
+++ b/include/uapi/linux/peci-ioctl.h
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Intel Corporation */
+
+#ifndef __PECI_IOCTL_H
+#define __PECI_IOCTL_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* Base Address of 48d */
+#define PECI_BASE_ADDR 0x30 /* The PECI client's default address of 0x30 */
+#define PECI_OFFSET_MAX 8 /* Max numver of CPU clients */
+
+/* PCI Access */
+#define MAX_PCI_READ_LEN 24 /* Number of bytes of the PCI Space read */
+
+#define PCI_BUS0_CPU0 0x00
+#define PCI_BUS0_CPU1 0x80
+#define PCI_CPUBUSNO_BUS 0x00
+#define PCI_CPUBUSNO_DEV 0x08
+#define PCI_CPUBUSNO_FUNC 0x02
+#define PCI_CPUBUSNO 0xcc
+#define PCI_CPUBUSNO_1 0xd0
+#define PCI_CPUBUSNO_VALID 0xd4
+
+/* Package Identifier Read Parameter Value */
+#define PKG_ID_CPU_ID 0x0000 /* CPUID Info */
+#define PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */
+#define PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */
+#define PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */
+#define PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */
+#define PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */
+
+/* RdPkgConfig Index */
+#define MBX_INDEX_CPU_ID 0 /* Package Identifier Read */
+#define MBX_INDEX_VR_DEBUG 1 /* VR Debug */
+#define MBX_INDEX_PKG_TEMP_READ 2 /* Package Temperature Read */
+#define MBX_INDEX_ENERGY_COUNTER 3 /* Energy counter */
+#define MBX_INDEX_ENERGY_STATUS 4 /* DDR Energy Status */
+#define MBX_INDEX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */
+#define MBX_INDEX_EPI 6 /* Efficient Performance Indication */
+#define MBX_INDEX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */
+#define MBX_INDEX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */
+#define MBX_INDEX_DTS_MARGIN 10 /* DTS thermal margin */
+#define MBX_INDEX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */
+#define MBX_INDEX_CFG_TDP_CONTROL 12 /* TDP Config Control */
+#define MBX_INDEX_CFG_TDP_LEVELS 13 /* TDP Config Levels */
+#define MBX_INDEX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */
+#define MBX_INDEX_CFG_ICCMAX 15 /* Configurable ICCMAX */
+#define MBX_INDEX_TEMP_TARGET 16 /* Temperature Target Read */
+#define MBX_INDEX_CURR_CFG_LIMIT 17 /* Current Config Limit */
+#define MBX_INDEX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */
+#define MBX_INDEX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */
+#define MBX_INDEX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */
+#define MBX_INDEX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */
+#define MBX_INDEX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */
+#define MBX_INDEX_TDP 28 /* Thermal design power minimum */
+#define MBX_INDEX_TDP_HIGH 29 /* Thermal design power maximum */
+#define MBX_INDEX_TDP_UNITS 30 /* Units for power/energy registers */
+#define MBX_INDEX_RUN_TIME 31 /* Accumulated Run Time */
+#define MBX_INDEX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */
+#define MBX_INDEX_TURBO_RATIO 33 /* Turbo Activation Ratio */
+#define MBX_INDEX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */
+#define MBX_INDEX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */
+#define MBX_INDEX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */
+#define MBX_INDEX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */
+#define MBX_INDEX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */
+#define MBX_INDEX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */
+#define MBX_INDEX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */
+#define MBX_INDEX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */
+#define MBX_INDEX_DDR_THERM_STATUS 46 /* DDR Thermal Status */
+#define MBX_INDEX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */
+#define MBX_INDEX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */
+#define MBX_INDEX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */
+#define MBX_INDEX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */
+#define MBX_INDEX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */
+#define MBX_INDEX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */
+
+/* WrPkgConfig Index */
+#define MBX_INDEX_DIMM_AMBIENT 19
+#define MBX_INDEX_DIMM_TEMP 24
+
+/* Device Specific Completion Code (CC) Definition */
+#define DEV_PECI_CC_SUCCESS 0x40
+#define DEV_PECI_CC_TIMEOUT 0x80
+#define DEV_PECI_CC_OUT_OF_RESOURCE 0x81
+#define DEV_PECI_CC_UNAVAIL_RESOURCE 0x82
+#define DEV_PECI_CC_INVALID_REQ 0x90
+
+/* Completion Code mask to check retry needs */
+#define DEV_PECI_CC_RETRY_CHECK_MASK 0xf0
+#define DEV_PECI_CC_NEED_RETRY 0x80
+
+/* Skylake EDS says to retry for 250ms */
+#define DEV_PECI_RETRY_TIME_MS 250
+#define DEV_PECI_RETRY_INTERVAL_USEC 10000
+#define DEV_PECI_RETRY_BIT 0x01
+
+#define GET_TEMP_WR_LEN 1
+#define GET_TEMP_RD_LEN 2
+#define GET_TEMP_PECI_CMD 0x01
+
+#define GET_DIB_WR_LEN 1
+#define GET_DIB_RD_LEN 8
+#define GET_DIB_PECI_CMD 0xf7
+
+#define RDPKGCFG_WRITE_LEN 5
+#define RDPKGCFG_READ_LEN_BASE 1
+#define RDPKGCFG_PECI_CMD 0xa1
+
+#define WRPKGCFG_WRITE_LEN_BASE 6
+#define WRPKGCFG_READ_LEN 1
+#define WRPKGCFG_PECI_CMD 0xa5
+
+#define RDIAMSR_WRITE_LEN 5
+#define RDIAMSR_READ_LEN 9
+#define RDIAMSR_PECI_CMD 0xb1
+
+#define WRIAMSR_PECI_CMD 0xb5
+
+#define RDPCICFG_WRITE_LEN 6
+#define RDPCICFG_READ_LEN 5
+#define RDPCICFG_PECI_CMD 0x61
+
+#define WRPCICFG_PECI_CMD 0x65
+
+#define RDPCICFGLOCAL_WRITE_LEN 5
+#define RDPCICFGLOCAL_READ_LEN_BASE 1
+#define RDPCICFGLOCAL_PECI_CMD 0xe1
+
+#define WRPCICFGLOCAL_WRITE_LEN_BASE 6
+#define WRPCICFGLOCAL_READ_LEN 1
+#define WRPCICFGLOCAL_PECI_CMD 0xe5
+
+#define PECI_BUFFER_SIZE 32
+
+enum peci_cmd {
+ PECI_CMD_XFER = 0,
+ PECI_CMD_PING,
+ PECI_CMD_GET_DIB,
+ PECI_CMD_GET_TEMP,
+ PECI_CMD_RD_PKG_CFG,
+ PECI_CMD_WR_PKG_CFG,
+ PECI_CMD_RD_IA_MSR,
+ PECI_CMD_WR_IA_MSR,
+ PECI_CMD_RD_PCI_CFG,
+ PECI_CMD_WR_PCI_CFG,
+ PECI_CMD_RD_PCI_CFG_LOCAL,
+ PECI_CMD_WR_PCI_CFG_LOCAL,
+ PECI_CMD_MAX
+};
+
+struct peci_xfer_msg {
+ __u8 addr;
+ __u8 tx_len;
+ __u8 rx_len;
+ __u8 tx_buf[PECI_BUFFER_SIZE];
+ __u8 rx_buf[PECI_BUFFER_SIZE];
+} __attribute__((__packed__));
+
+struct peci_ping_msg {
+ __u8 addr;
+} __attribute__((__packed__));
+
+struct peci_get_dib_msg {
+ __u8 addr;
+ __u32 dib;
+} __attribute__((__packed__));
+
+struct peci_get_temp_msg {
+ __u8 addr;
+ __s16 temp_raw;
+} __attribute__((__packed__));
+
+struct peci_rd_pkg_cfg_msg {
+ __u8 addr;
+ __u8 index;
+ __u16 param;
+ __u8 rx_len;
+ __u8 pkg_config[4];
+} __attribute__((__packed__));
+
+struct peci_wr_pkg_cfg_msg {
+ __u8 addr;
+ __u8 index;
+ __u16 param;
+ __u8 tx_len;
+ __u32 value;
+} __attribute__((__packed__));
+
+struct peci_rd_ia_msr_msg {
+ __u8 addr;
+ __u8 thread_id;
+ __u16 address;
+ __u64 value;
+} __attribute__((__packed__));
+
+struct peci_rd_pci_cfg_msg {
+ __u8 addr;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ __u8 pci_config[4];
+} __attribute__((__packed__));
+
+struct peci_rd_pci_cfg_local_msg {
+ __u8 addr;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ __u8 rx_len;
+ __u8 pci_config[4];
+} __attribute__((__packed__));
+
+struct peci_wr_pci_cfg_local_msg {
+ __u8 addr;
+ __u8 bus;
+ __u8 device;
+ __u8 function;
+ __u16 reg;
+ __u8 tx_len;
+ __u32 value;
+} __attribute__((__packed__));
+
+#define PECI_IOC_BASE 0xb7
+
+#define PECI_IOC_XFER \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_XFER, struct peci_xfer_msg)
+
+#define PECI_IOC_PING \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_PING, struct peci_ping_msg)
+
+#define PECI_IOC_GET_DIB \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_GET_DIB, struct peci_get_dib_msg)
+
+#define PECI_IOC_GET_TEMP \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_GET_TEMP, struct peci_get_temp_msg)
+
+#define PECI_IOC_RD_PKG_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PKG_CFG, struct peci_rd_pkg_cfg_msg)
+
+#define PECI_IOC_WR_PKG_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PKG_CFG, struct peci_wr_pkg_cfg_msg)
+
+#define PECI_IOC_RD_IA_MSR \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_IA_MSR, struct peci_rd_ia_msr_msg)
+
+#define PECI_IOC_RD_PCI_CFG \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG, struct peci_rd_pci_cfg_msg)
+
+#define PECI_IOC_RD_PCI_CFG_LOCAL \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG_LOCAL, \
+ struct peci_rd_pci_cfg_local_msg)
+
+#define PECI_IOC_WR_PCI_CFG_LOCAL \
+ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG_LOCAL, \
+ struct peci_wr_pci_cfg_local_msg)
+
+#endif /* __PECI_IOCTL_H */
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v8 04/12] dt-bindings: Add a document of PECI adapter driver for ASPEED AST24xx/25xx SoCs
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
` (2 preceding siblings ...)
2018-09-18 21:51 ` [PATCH v8 03/12] peci: Add support for PECI bus driver core Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 05/12] ARM: dts: aspeed: peci: Add PECI node Jae Hyun Yoo
` (7 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo,
Benjamin Herrenschmidt, Jason M Biils, Milton Miller II,
Pavel Machek, Robin Murphy, Ryan Chen
This commit adds a dt-bindings document of PECI adapter driver for ASPEED
AST24xx/25xx SoCs.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Milton Miller II <miltonm@us.ibm.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
.../devicetree/bindings/peci/peci-aspeed.txt | 55 +++++++++++++++++++
1 file changed, 55 insertions(+)
create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.txt
diff --git a/Documentation/devicetree/bindings/peci/peci-aspeed.txt b/Documentation/devicetree/bindings/peci/peci-aspeed.txt
new file mode 100644
index 000000000000..cdca73a3b7d8
--- /dev/null
+++ b/Documentation/devicetree/bindings/peci/peci-aspeed.txt
@@ -0,0 +1,55 @@
+Device tree configuration for PECI buses on the AST24XX and AST25XX SoCs.
+
+Required properties:
+- compatible : Should be one of:
+ "aspeed,ast2400-peci"
+ "aspeed,ast2500-peci"
+- reg : Should contain PECI controller registers location and
+ length.
+- #address-cells : Should be <1> required to define a client address.
+- #size-cells : Should be <0> required to define a client address.
+- interrupts : Should contain PECI controller interrupt.
+- clocks : Should contain clock source for PECI controller. Should
+ reference the external oscillator clock in the second
+ cell.
+- resets : Should contain phandle to reset controller with the reset
+ number in the second cell.
+- clock-frequency : Should contain the operation frequency of PECI controller
+ in units of Hz.
+ 187500 ~ 24000000
+
+Optional properties:
+- msg-timing : Message timing negotiation period. This value will
+ determine the period of message timing negotiation to be
+ issued by PECI controller. The unit of the programmed
+ value is four times of PECI clock period.
+ 0 ~ 255 (default: 1)
+- addr-timing : Address timing negotiation period. This value will
+ determine the period of address timing negotiation to be
+ issued by PECI controller. The unit of the programmed
+ value is four times of PECI clock period.
+ 0 ~ 255 (default: 1)
+- rd-sampling-point : Read sampling point selection. The whole period of a bit
+ time will be divided into 16 time frames. This value will
+ determine the time frame in which the controller will
+ sample PECI signal for data read back. Usually in the
+ middle of a bit time is the best.
+ 0 ~ 15 (default: 8)
+- cmd-timeout-ms : Command timeout in units of ms.
+ 1 ~ 60000 (default: 1000)
+
+Example:
+ peci0: peci-bus@0 {
+ compatible = "aspeed,ast2500-peci";
+ reg = <0x0 0x60>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <15>;
+ clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+ resets = <&syscon ASPEED_RESET_PECI>;
+ clock-frequency = <24000000>;
+ msg-timing = <1>;
+ addr-timing = <1>;
+ rd-sampling-point = <8>;
+ cmd-timeout-ms = <1000>;
+ };
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v8 05/12] ARM: dts: aspeed: peci: Add PECI node
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
` (3 preceding siblings ...)
2018-09-18 21:51 ` [PATCH v8 04/12] dt-bindings: Add a document of PECI adapter driver for ASPEED AST24xx/25xx SoCs Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 06/12] peci: Add a PECI adapter driver for Aspeed AST24xx/AST25xx Jae Hyun Yoo
` (6 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo, Jason M Biils,
Ryan Chen
This commit adds PECI bus/adapter node of AST24xx/AST25xx into
aspeed-g4 and aspeed-g5.
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
---
arch/arm/boot/dts/aspeed-g4.dtsi | 26 ++++++++++++++++++++++++++
arch/arm/boot/dts/aspeed-g5.dtsi | 26 ++++++++++++++++++++++++++
2 files changed, 52 insertions(+)
diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
index b23a983f95a5..ba31b2f0e0d8 100644
--- a/arch/arm/boot/dts/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed-g4.dtsi
@@ -29,6 +29,7 @@
serial3 = &uart4;
serial4 = &uart5;
serial5 = &vuart;
+ peci0 = &peci0;
};
cpus {
@@ -317,6 +318,13 @@
};
};
+ peci: bus@1e78b000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e78b000 0x60>;
+ };
+
uart2: serial@1e78d000 {
compatible = "ns16550a";
reg = <0x1e78d000 0x20>;
@@ -360,6 +368,24 @@
};
};
+&peci {
+ peci0: peci-bus@0 {
+ compatible = "aspeed,ast2400-peci";
+ reg = <0x0 0x60>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <15>;
+ clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+ resets = <&syscon ASPEED_RESET_PECI>;
+ clock-frequency = <24000000>;
+ msg-timing = <1>;
+ addr-timing = <1>;
+ rd-sampling-point = <8>;
+ cmd-timeout-ms = <1000>;
+ status = "disabled";
+ };
+};
+
&i2c {
i2c_ic: interrupt-controller@0 {
#interrupt-cells = <1>;
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index 87fdc146ff52..c93ab96131e6 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -29,6 +29,7 @@
serial3 = &uart4;
serial4 = &uart5;
serial5 = &vuart;
+ peci0 = &peci0;
};
cpus {
@@ -377,6 +378,13 @@
};
};
+ peci: bus@1e78b000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e78b000 0x60>;
+ };
+
uart2: serial@1e78d000 {
compatible = "ns16550a";
reg = <0x1e78d000 0x20>;
@@ -420,6 +428,24 @@
};
};
+&peci {
+ peci0: peci-bus@0 {
+ compatible = "aspeed,ast2500-peci";
+ reg = <0x0 0x60>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <15>;
+ clocks = <&syscon ASPEED_CLK_GATE_REFCLK>;
+ resets = <&syscon ASPEED_RESET_PECI>;
+ clock-frequency = <24000000>;
+ msg-timing = <1>;
+ addr-timing = <1>;
+ rd-sampling-point = <8>;
+ cmd-timeout-ms = <1000>;
+ status = "disabled";
+ };
+};
+
&i2c {
i2c_ic: interrupt-controller@0 {
#interrupt-cells = <1>;
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v8 06/12] peci: Add a PECI adapter driver for Aspeed AST24xx/AST25xx
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
` (4 preceding siblings ...)
2018-09-18 21:51 ` [PATCH v8 05/12] ARM: dts: aspeed: peci: Add PECI node Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 07/12] dt-bindings: mfd: Add a document for PECI client MFD Jae Hyun Yoo
` (5 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo, Andy Shevchenko,
Robin Murphy, Ryan Chen
This commit adds PECI adapter driver implementation for Aspeed
AST24xx/AST25xx SoCs.
Cc: Joel Stanley <joel@jms.id.au>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Andy Shevchenko <andriy.shevchenko@intel.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
---
drivers/peci/Kconfig | 27 ++
drivers/peci/Makefile | 3 +
drivers/peci/peci-aspeed.c | 498 +++++++++++++++++++++++++++++++++++++
3 files changed, 528 insertions(+)
create mode 100644 drivers/peci/peci-aspeed.c
diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig
index 4ccacb22a356..9e9845ebcff4 100644
--- a/drivers/peci/Kconfig
+++ b/drivers/peci/Kconfig
@@ -10,3 +10,30 @@ config PECI
The Platform Environment Control Interface (PECI) is a one-wire bus
interface that provides a communication channel from Intel processors
and chipset components to external monitoring or control devices.
+
+ If you want PECI support, you should say Y here and also to the
+ specific driver for your bus adapter(s) below.
+
+if PECI
+
+#
+# PECI hardware bus configuration
+#
+
+menu "PECI Hardware Bus support"
+
+config PECI_ASPEED
+ tristate "ASPEED PECI support"
+ select REGMAP_MMIO
+ depends on OF
+ depends on ARCH_ASPEED || COMPILE_TEST
+ help
+ Say Y here if you want support for the Platform Environment Control
+ Interface (PECI) bus adapter driver on the ASPEED SoCs.
+
+ This support is also available as a module. If so, the module
+ will be called peci-aspeed.
+
+endmenu
+
+endif # PECI
diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile
index 9e8615e0d3ff..886285e69765 100644
--- a/drivers/peci/Makefile
+++ b/drivers/peci/Makefile
@@ -4,3 +4,6 @@
# Core functionality
obj-$(CONFIG_PECI) += peci-core.o
+
+# Hardware specific bus drivers
+obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o
diff --git a/drivers/peci/peci-aspeed.c b/drivers/peci/peci-aspeed.c
new file mode 100644
index 000000000000..8070ec18d484
--- /dev/null
+++ b/drivers/peci/peci-aspeed.c
@@ -0,0 +1,498 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2012-2017 ASPEED Technology Inc.
+// Copyright (c) 2018 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+/* ASPEED PECI Registers */
+#define ASPEED_PECI_CTRL 0x00
+#define ASPEED_PECI_TIMING 0x04
+#define ASPEED_PECI_CMD 0x08
+#define ASPEED_PECI_CMD_CTRL 0x0c
+#define ASPEED_PECI_EXP_FCS 0x10
+#define ASPEED_PECI_CAP_FCS 0x14
+#define ASPEED_PECI_INT_CTRL 0x18
+#define ASPEED_PECI_INT_STS 0x1c
+#define ASPEED_PECI_W_DATA0 0x20
+#define ASPEED_PECI_W_DATA1 0x24
+#define ASPEED_PECI_W_DATA2 0x28
+#define ASPEED_PECI_W_DATA3 0x2c
+#define ASPEED_PECI_R_DATA0 0x30
+#define ASPEED_PECI_R_DATA1 0x34
+#define ASPEED_PECI_R_DATA2 0x38
+#define ASPEED_PECI_R_DATA3 0x3c
+#define ASPEED_PECI_W_DATA4 0x40
+#define ASPEED_PECI_W_DATA5 0x44
+#define ASPEED_PECI_W_DATA6 0x48
+#define ASPEED_PECI_W_DATA7 0x4c
+#define ASPEED_PECI_R_DATA4 0x50
+#define ASPEED_PECI_R_DATA5 0x54
+#define ASPEED_PECI_R_DATA6 0x58
+#define ASPEED_PECI_R_DATA7 0x5c
+
+/* ASPEED_PECI_CTRL - 0x00 : Control Register */
+#define PECI_CTRL_SAMPLING_MASK GENMASK(19, 16)
+#define PECI_CTRL_READ_MODE_MASK GENMASK(13, 12)
+#define PECI_CTRL_READ_MODE_COUNT BIT(12)
+#define PECI_CTRL_READ_MODE_DBG BIT(13)
+#define PECI_CTRL_CLK_SOURCE_MASK BIT(11)
+#define PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8)
+#define PECI_CTRL_INVERT_OUT BIT(7)
+#define PECI_CTRL_INVERT_IN BIT(6)
+#define PECI_CTRL_BUS_CONTENT_EN BIT(5)
+#define PECI_CTRL_PECI_EN BIT(4)
+#define PECI_CTRL_PECI_CLK_EN BIT(0)
+
+/* ASPEED_PECI_TIMING - 0x04 : Timing Negotiation Register */
+#define PECI_TIMING_MESSAGE_MASK GENMASK(15, 8)
+#define PECI_TIMING_ADDRESS_MASK GENMASK(7, 0)
+
+/* ASPEED_PECI_CMD - 0x08 : Command Register */
+#define PECI_CMD_PIN_MON BIT(31)
+#define PECI_CMD_STS_MASK GENMASK(27, 24)
+#define PECI_CMD_IDLE_MASK (PECI_CMD_STS_MASK | PECI_CMD_PIN_MON)
+#define PECI_CMD_FIRE BIT(0)
+
+/* ASPEED_PECI_LEN - 0x0C : Read/Write Length Register */
+#define PECI_AW_FCS_EN BIT(31)
+#define PECI_READ_LEN_MASK GENMASK(23, 16)
+#define PECI_WRITE_LEN_MASK GENMASK(15, 8)
+#define PECI_TAGET_ADDR_MASK GENMASK(7, 0)
+
+/* ASPEED_PECI_EXP_FCS - 0x10 : Expected FCS Data Register */
+#define PECI_EXPECT_READ_FCS_MASK GENMASK(23, 16)
+#define PECI_EXPECT_AW_FCS_AUTO_MASK GENMASK(15, 8)
+#define PECI_EXPECT_WRITE_FCS_MASK GENMASK(7, 0)
+
+/* ASPEED_PECI_CAP_FCS - 0x14 : Captured FCS Data Register */
+#define PECI_CAPTURE_READ_FCS_MASK GENMASK(23, 16)
+#define PECI_CAPTURE_WRITE_FCS_MASK GENMASK(7, 0)
+
+/* ASPEED_PECI_INT_CTRL/STS - 0x18/0x1c : Interrupt Register */
+#define PECI_INT_TIMING_RESULT_MASK GENMASK(31, 30)
+#define PECI_INT_TIMEOUT BIT(4)
+#define PECI_INT_CONNECT BIT(3)
+#define PECI_INT_W_FCS_BAD BIT(2)
+#define PECI_INT_W_FCS_ABORT BIT(1)
+#define PECI_INT_CMD_DONE BIT(0)
+
+#define PECI_INT_MASK (PECI_INT_TIMEOUT | PECI_INT_CONNECT | \
+ PECI_INT_W_FCS_BAD | PECI_INT_W_FCS_ABORT | \
+ PECI_INT_CMD_DONE)
+
+#define PECI_IDLE_CHECK_TIMEOUT_USEC 50000
+#define PECI_IDLE_CHECK_INTERVAL_USEC 10000
+
+#define PECI_RD_SAMPLING_POINT_DEFAULT 8
+#define PECI_RD_SAMPLING_POINT_MAX 15
+#define PECI_CLK_DIV_DEFAULT 0
+#define PECI_CLK_DIV_MAX 7
+#define PECI_MSG_TIMING_DEFAULT 1
+#define PECI_MSG_TIMING_MAX 255
+#define PECI_ADDR_TIMING_DEFAULT 1
+#define PECI_ADDR_TIMING_MAX 255
+#define PECI_CMD_TIMEOUT_MS_DEFAULT 1000
+#define PECI_CMD_TIMEOUT_MS_MAX 60000
+
+struct aspeed_peci {
+ struct peci_adapter *adapter;
+ struct device *dev;
+ struct regmap *regmap;
+ struct reset_control *rst;
+ int irq;
+ spinlock_t lock; /* to sync completion status handling */
+ struct completion xfer_complete;
+ u32 status;
+ u32 cmd_timeout_ms;
+};
+
+static int aspeed_peci_xfer_native(struct aspeed_peci *priv,
+ struct peci_xfer_msg *msg)
+{
+ long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+ u32 peci_head, peci_state, rx_data, cmd_sts;
+ unsigned long flags;
+ int i, rc;
+ uint reg;
+
+ /* Check command sts and bus idle state */
+ rc = regmap_read_poll_timeout(priv->regmap, ASPEED_PECI_CMD, cmd_sts,
+ !(cmd_sts & PECI_CMD_IDLE_MASK),
+ PECI_IDLE_CHECK_INTERVAL_USEC,
+ PECI_IDLE_CHECK_TIMEOUT_USEC);
+ if (rc)
+ return rc; /* -ETIMEDOUT */
+
+ spin_lock_irqsave(&priv->lock, flags);
+ reinit_completion(&priv->xfer_complete);
+
+ peci_head = FIELD_PREP(PECI_TAGET_ADDR_MASK, msg->addr) |
+ FIELD_PREP(PECI_WRITE_LEN_MASK, msg->tx_len) |
+ FIELD_PREP(PECI_READ_LEN_MASK, msg->rx_len);
+
+ regmap_write(priv->regmap, ASPEED_PECI_CMD_CTRL, peci_head);
+
+ for (i = 0; i < msg->tx_len; i += 4) {
+ reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 :
+ ASPEED_PECI_W_DATA4 + i % 16;
+ regmap_write(priv->regmap, reg,
+ le32_to_cpup((__le32 *)&msg->tx_buf[i]));
+ }
+
+ dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head);
+ print_hex_dump_debug("TX : ", DUMP_PREFIX_NONE, 16, 1,
+ msg->tx_buf, msg->tx_len, true);
+
+ priv->status = 0;
+ regmap_write(priv->regmap, ASPEED_PECI_CMD, PECI_CMD_FIRE);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ err = wait_for_completion_interruptible_timeout(&priv->xfer_complete,
+ timeout);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->status);
+ regmap_read(priv->regmap, ASPEED_PECI_CMD, &peci_state);
+ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+ FIELD_GET(PECI_CMD_STS_MASK, peci_state));
+
+ regmap_write(priv->regmap, ASPEED_PECI_CMD, 0);
+
+ if (err <= 0 || priv->status != PECI_INT_CMD_DONE) {
+ if (err < 0) { /* -ERESTARTSYS */
+ rc = (int)err;
+ goto err_irqrestore;
+ } else if (err == 0) {
+ dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+ rc = -ETIMEDOUT;
+ goto err_irqrestore;
+ }
+
+ dev_dbg(priv->dev, "No valid response!\n");
+ rc = -EIO;
+ goto err_irqrestore;
+ }
+
+ /**
+ * Note that rx_len and rx_buf size can be an odd number.
+ * Byte handling is more efficient.
+ */
+ for (i = 0; i < msg->rx_len; i++) {
+ u8 byte_offset = i % 4;
+
+ if (byte_offset == 0) {
+ reg = i < 16 ? ASPEED_PECI_R_DATA0 + i % 16 :
+ ASPEED_PECI_R_DATA4 + i % 16;
+ regmap_read(priv->regmap, reg, &rx_data);
+ }
+
+ msg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3));
+ }
+
+ print_hex_dump_debug("RX : ", DUMP_PREFIX_NONE, 16, 1,
+ msg->rx_buf, msg->rx_len, true);
+
+ regmap_read(priv->regmap, ASPEED_PECI_CMD, &peci_state);
+ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+ FIELD_GET(PECI_CMD_STS_MASK, peci_state));
+ dev_dbg(priv->dev, "------------------------\n");
+
+err_irqrestore:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return rc;
+}
+
+static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg)
+{
+ struct aspeed_peci *priv = arg;
+ u32 status_ack = 0;
+ u32 status;
+
+ spin_lock(&priv->lock);
+ regmap_read(priv->regmap, ASPEED_PECI_INT_STS, &status);
+ priv->status |= (status & PECI_INT_MASK);
+
+ /**
+ * In most cases, interrupt bits will be set one by one but also note
+ * that multiple interrupt bits could be set at the same time.
+ */
+ if (status & PECI_INT_TIMEOUT) {
+ dev_dbg(priv->dev, "PECI_INT_TIMEOUT\n");
+ status_ack |= PECI_INT_TIMEOUT;
+ }
+
+ if (status & PECI_INT_CONNECT) {
+ dev_dbg(priv->dev, "PECI_INT_CONNECT\n");
+ status_ack |= PECI_INT_CONNECT;
+ }
+
+ if (status & PECI_INT_W_FCS_BAD) {
+ dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n");
+ status_ack |= PECI_INT_W_FCS_BAD;
+ }
+
+ if (status & PECI_INT_W_FCS_ABORT) {
+ dev_dbg(priv->dev, "PECI_INT_W_FCS_ABORT\n");
+ status_ack |= PECI_INT_W_FCS_ABORT;
+ }
+
+ /**
+ * All commands should be ended up with a PECI_INT_CMD_DONE bit set
+ * even in an error case.
+ */
+ if (status & PECI_INT_CMD_DONE) {
+ dev_dbg(priv->dev, "PECI_INT_CMD_DONE\n");
+ status_ack |= PECI_INT_CMD_DONE;
+ complete(&priv->xfer_complete);
+ }
+
+ regmap_write(priv->regmap, ASPEED_PECI_INT_STS, status_ack);
+ spin_unlock(&priv->lock);
+ return IRQ_HANDLED;
+}
+
+static int aspeed_peci_init_ctrl(struct aspeed_peci *priv)
+{
+ u32 msg_timing, addr_timing, rd_sampling_point;
+ u32 clk_freq, clk_divisor, clk_div_val = 0;
+ struct clk *clkin;
+ int ret;
+
+ clkin = devm_clk_get(priv->dev, NULL);
+ if (IS_ERR(clkin)) {
+ dev_err(priv->dev, "Failed to get clk source.\n");
+ return PTR_ERR(clkin);
+ }
+
+ ret = of_property_read_u32(priv->dev->of_node, "clock-frequency",
+ &clk_freq);
+ if (ret) {
+ dev_err(priv->dev,
+ "Could not read clock-frequency property.\n");
+ return ret;
+ }
+
+ clk_divisor = clk_get_rate(clkin) / clk_freq;
+ devm_clk_put(priv->dev, clkin);
+
+ while ((clk_divisor >> 1) && (clk_div_val < PECI_CLK_DIV_MAX))
+ clk_div_val++;
+
+ ret = of_property_read_u32(priv->dev->of_node, "msg-timing",
+ &msg_timing);
+ if (ret || msg_timing > PECI_MSG_TIMING_MAX) {
+ if (!ret)
+ dev_warn(priv->dev,
+ "Invalid msg-timing : %u, Use default : %u\n",
+ msg_timing, PECI_MSG_TIMING_DEFAULT);
+ msg_timing = PECI_MSG_TIMING_DEFAULT;
+ }
+
+ ret = of_property_read_u32(priv->dev->of_node, "addr-timing",
+ &addr_timing);
+ if (ret || addr_timing > PECI_ADDR_TIMING_MAX) {
+ if (!ret)
+ dev_warn(priv->dev,
+ "Invalid addr-timing : %u, Use default : %u\n",
+ addr_timing, PECI_ADDR_TIMING_DEFAULT);
+ addr_timing = PECI_ADDR_TIMING_DEFAULT;
+ }
+
+ ret = of_property_read_u32(priv->dev->of_node, "rd-sampling-point",
+ &rd_sampling_point);
+ if (ret || rd_sampling_point > PECI_RD_SAMPLING_POINT_MAX) {
+ if (!ret)
+ dev_warn(priv->dev,
+ "Invalid rd-sampling-point : %u. Use default : %u\n",
+ rd_sampling_point,
+ PECI_RD_SAMPLING_POINT_DEFAULT);
+ rd_sampling_point = PECI_RD_SAMPLING_POINT_DEFAULT;
+ }
+
+ ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms",
+ &priv->cmd_timeout_ms);
+ if (ret || priv->cmd_timeout_ms > PECI_CMD_TIMEOUT_MS_MAX ||
+ priv->cmd_timeout_ms == 0) {
+ if (!ret)
+ dev_warn(priv->dev,
+ "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+ priv->cmd_timeout_ms,
+ PECI_CMD_TIMEOUT_MS_DEFAULT);
+ priv->cmd_timeout_ms = PECI_CMD_TIMEOUT_MS_DEFAULT;
+ }
+
+ regmap_write(priv->regmap, ASPEED_PECI_CTRL,
+ FIELD_PREP(PECI_CTRL_CLK_DIV_MASK, PECI_CLK_DIV_DEFAULT) |
+ PECI_CTRL_PECI_CLK_EN);
+
+ /**
+ * Timing negotiation period setting.
+ * The unit of the programmed value is 4 times of PECI clock period.
+ */
+ regmap_write(priv->regmap, ASPEED_PECI_TIMING,
+ FIELD_PREP(PECI_TIMING_MESSAGE_MASK, msg_timing) |
+ FIELD_PREP(PECI_TIMING_ADDRESS_MASK, addr_timing));
+
+ /* Clear interrupts */
+ regmap_write(priv->regmap, ASPEED_PECI_INT_STS, PECI_INT_MASK);
+
+ /* Enable interrupts */
+ regmap_write(priv->regmap, ASPEED_PECI_INT_CTRL, PECI_INT_MASK);
+
+ /* Read sampling point and clock speed setting */
+ regmap_write(priv->regmap, ASPEED_PECI_CTRL,
+ FIELD_PREP(PECI_CTRL_SAMPLING_MASK, rd_sampling_point) |
+ FIELD_PREP(PECI_CTRL_CLK_DIV_MASK, clk_div_val) |
+ PECI_CTRL_PECI_EN | PECI_CTRL_PECI_CLK_EN);
+
+ return 0;
+}
+
+static const struct regmap_config aspeed_peci_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = ASPEED_PECI_R_DATA7,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+ .fast_io = true,
+};
+
+static int aspeed_peci_xfer(struct peci_adapter *adapter,
+ struct peci_xfer_msg *msg)
+{
+ struct aspeed_peci *priv = peci_get_adapdata(adapter);
+
+ return aspeed_peci_xfer_native(priv, msg);
+}
+
+static int aspeed_peci_probe(struct platform_device *pdev)
+{
+ struct peci_adapter *adapter;
+ struct aspeed_peci *priv;
+ struct resource *res;
+ void __iomem *base;
+ u32 cmd_sts;
+ int ret;
+
+ adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv));
+ if (!adapter)
+ return -ENOMEM;
+
+ priv = peci_get_adapdata(adapter);
+ priv->adapter = adapter;
+ priv->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, priv);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err_put_adapter_dev;
+ }
+
+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &aspeed_peci_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ ret = PTR_ERR(priv->regmap);
+ goto err_put_adapter_dev;
+ }
+
+ /**
+ * We check that the regmap works on this very first access,
+ * but as this is an MMIO-backed regmap, subsequent regmap
+ * access is not going to fail and we skip error checks from
+ * this point.
+ */
+ ret = regmap_read(priv->regmap, ASPEED_PECI_CMD, &cmd_sts);
+ if (ret) {
+ ret = -EIO;
+ goto err_put_adapter_dev;
+ }
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (!priv->irq) {
+ ret = -ENODEV;
+ goto err_put_adapter_dev;
+ }
+
+ ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler,
+ 0, "peci-aspeed-irq", priv);
+ if (ret)
+ goto err_put_adapter_dev;
+
+ init_completion(&priv->xfer_complete);
+ spin_lock_init(&priv->lock);
+
+ priv->adapter->owner = THIS_MODULE;
+ priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev));
+ strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name));
+ priv->adapter->xfer = aspeed_peci_xfer;
+
+ priv->rst = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->rst)) {
+ dev_err(&pdev->dev,
+ "missing or invalid reset controller entry");
+ ret = PTR_ERR(priv->rst);
+ goto err_put_adapter_dev;
+ }
+ reset_control_deassert(priv->rst);
+
+ ret = aspeed_peci_init_ctrl(priv);
+ if (ret)
+ goto err_put_adapter_dev;
+
+ ret = peci_add_adapter(priv->adapter);
+ if (ret)
+ goto err_put_adapter_dev;
+
+ dev_info(&pdev->dev, "peci bus %d registered, irq %d\n",
+ priv->adapter->nr, priv->irq);
+
+ return 0;
+
+err_put_adapter_dev:
+ put_device(&adapter->dev);
+ return ret;
+}
+
+static int aspeed_peci_remove(struct platform_device *pdev)
+{
+ struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev);
+
+ reset_control_assert(priv->rst);
+ peci_del_adapter(priv->adapter);
+ of_node_put(priv->adapter->dev.of_node);
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_peci_of_table[] = {
+ { .compatible = "aspeed,ast2400-peci", },
+ { .compatible = "aspeed,ast2500-peci", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aspeed_peci_of_table);
+
+static struct platform_driver aspeed_peci_driver = {
+ .probe = aspeed_peci_probe,
+ .remove = aspeed_peci_remove,
+ .driver = {
+ .name = "peci-aspeed",
+ .of_match_table = of_match_ptr(aspeed_peci_of_table),
+ },
+};
+module_platform_driver(aspeed_peci_driver);
+
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("ASPEED PECI driver");
+MODULE_LICENSE("GPL v2");
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v8 07/12] dt-bindings: mfd: Add a document for PECI client MFD
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
` (5 preceding siblings ...)
2018-09-18 21:51 ` [PATCH v8 06/12] peci: Add a PECI adapter driver for Aspeed AST24xx/AST25xx Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
2018-09-27 15:15 ` Rob Herring
2018-10-24 7:25 ` Lee Jones
2018-09-18 21:51 ` [PATCH v8 08/12] mfd: intel-peci-client: Add PECI client MFD driver Jae Hyun Yoo
` (4 subsequent siblings)
11 siblings, 2 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo, James Feist,
Jason M Biils, Vernon Mauery
This commit adds a dt-bindings document for PECI client MFD.
Cc: Lee Jones <lee.jones@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: James Feist <james.feist@linux.intel.com>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
.../bindings/mfd/intel-peci-client.txt | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/intel-peci-client.txt
diff --git a/Documentation/devicetree/bindings/mfd/intel-peci-client.txt b/Documentation/devicetree/bindings/mfd/intel-peci-client.txt
new file mode 100644
index 000000000000..cb341e363add
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/intel-peci-client.txt
@@ -0,0 +1,34 @@
+* Intel PECI client bindings
+
+PECI (Platform Environment Control Interface) is a one-wire bus interface that
+provides a communication channel from PECI clients in Intel processors and
+chipset components to external monitoring or control devices. PECI is designed
+to support the following sideband functions:
+
+- Processor and DRAM thermal management
+- Platform Manageability
+- Processor Interface Tuning and Diagnostics
+- Failure Analysis
+
+Required properties:
+- compatible : Should be "intel,peci-client".
+- reg : Should contain address of a client CPU. Address range of CPU
+ clients starts from 0x30 based on PECI specification.
+
+Example:
+ peci-bus@0 {
+ compatible = "vendor,soc-peci";
+ reg = <0x0 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ peci-client@30 {
+ compatible = "intel,peci-client";
+ reg = <0x30>;
+ };
+
+ peci-client@31 {
+ compatible = "intel,peci-client";
+ reg = <0x31>;
+ };
+ };
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v8 07/12] dt-bindings: mfd: Add a document for PECI client MFD
2018-09-18 21:51 ` [PATCH v8 07/12] dt-bindings: mfd: Add a document for PECI client MFD Jae Hyun Yoo
@ 2018-09-27 15:15 ` Rob Herring
2018-09-27 16:28 ` Jae Hyun Yoo
2018-10-24 7:25 ` Lee Jones
1 sibling, 1 reply; 23+ messages in thread
From: Rob Herring @ 2018-09-27 15:15 UTC (permalink / raw)
To: Jae Hyun Yoo
Cc: Lee Jones, Thomas Gleixner, Juergen Gross, Cyrille Pitchen,
linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo, James Feist,
Jason M Biils, Vernon Mauery
On Tue, 18 Sep 2018 14:51:19 -0700, Jae Hyun Yoo wrote:
> This commit adds a dt-bindings document for PECI client MFD.
>
> Cc: Lee Jones <lee.jones@linaro.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: James Feist <james.feist@linux.intel.com>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
> .../bindings/mfd/intel-peci-client.txt | 34 +++++++++++++++++++
> 1 file changed, 34 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/intel-peci-client.txt
>
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v8 07/12] dt-bindings: mfd: Add a document for PECI client MFD
2018-09-27 15:15 ` Rob Herring
@ 2018-09-27 16:28 ` Jae Hyun Yoo
0 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-27 16:28 UTC (permalink / raw)
To: Rob Herring
Cc: Lee Jones, Thomas Gleixner, Juergen Gross, Cyrille Pitchen,
linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, James Feist, Jason M Biils,
Vernon Mauery
On 9/27/2018 8:15 AM, Rob Herring wrote:
> On Tue, 18 Sep 2018 14:51:19 -0700, Jae Hyun Yoo wrote:
>> This commit adds a dt-bindings document for PECI client MFD.
>>
>> Cc: Lee Jones <lee.jones@linaro.org>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Andrew Jeffery <andrew@aj.id.au>
>> Cc: James Feist <james.feist@linux.intel.com>
>> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
>> Cc: Joel Stanley <joel@jms.id.au>
>> Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>> ---
>> .../bindings/mfd/intel-peci-client.txt | 34 +++++++++++++++++++
>> 1 file changed, 34 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/mfd/intel-peci-client.txt
>>
>
> Reviewed-by: Rob Herring <robh@kernel.org>
>
Thanks for the review, Rob!
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v8 07/12] dt-bindings: mfd: Add a document for PECI client MFD
2018-09-18 21:51 ` [PATCH v8 07/12] dt-bindings: mfd: Add a document for PECI client MFD Jae Hyun Yoo
2018-09-27 15:15 ` Rob Herring
@ 2018-10-24 7:25 ` Lee Jones
2018-10-24 16:39 ` Jae Hyun Yoo
1 sibling, 1 reply; 23+ messages in thread
From: Lee Jones @ 2018-10-24 7:25 UTC (permalink / raw)
To: Jae Hyun Yoo
Cc: Rob Herring, Jean Delvare, Guenter Roeck, Mark Rutland,
Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen, linux-hwmon,
devicetree, linux-kernel, linux-arm-kernel, linux-aspeed,
linux-doc, openbmc, James Feist, Jason M Biils, Vernon Mauery
On Tue, 18 Sep 2018, Jae Hyun Yoo wrote:
> This commit adds a dt-bindings document for PECI client MFD.
>
> Cc: Lee Jones <lee.jones@linaro.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: James Feist <james.feist@linux.intel.com>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
> .../bindings/mfd/intel-peci-client.txt | 34 +++++++++++++++++++
> 1 file changed, 34 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/intel-peci-client.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/intel-peci-client.txt b/Documentation/devicetree/bindings/mfd/intel-peci-client.txt
> new file mode 100644
> index 000000000000..cb341e363add
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/intel-peci-client.txt
> @@ -0,0 +1,34 @@
> +* Intel PECI client bindings
> +
> +PECI (Platform Environment Control Interface) is a one-wire bus interface that
> +provides a communication channel from PECI clients in Intel processors and
> +chipset components to external monitoring or control devices. PECI is designed
> +to support the following sideband functions:
> +
> +- Processor and DRAM thermal management
> +- Platform Manageability
> +- Processor Interface Tuning and Diagnostics
> +- Failure Analysis
> +
> +Required properties:
> +- compatible : Should be "intel,peci-client".
> +- reg : Should contain address of a client CPU. Address range of CPU
> + clients starts from 0x30 based on PECI specification.
Nit: "start"
Would be better worded:
"According to the PECI specification client addresses start from 0x30."
> +Example:
> + peci-bus@0 {
> + compatible = "vendor,soc-peci";
> + reg = <0x0 0x1000>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + peci-client@30 {
> + compatible = "intel,peci-client";
> + reg = <0x30>;
> + };
> +
> + peci-client@31 {
> + compatible = "intel,peci-client";
> + reg = <0x31>;
> + };
> + };
--
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v8 07/12] dt-bindings: mfd: Add a document for PECI client MFD
2018-10-24 7:25 ` Lee Jones
@ 2018-10-24 16:39 ` Jae Hyun Yoo
0 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-10-24 16:39 UTC (permalink / raw)
To: Lee Jones
Cc: Mark Rutland, Randy Dunlap, linux-aspeed, devicetree,
Vernon Mauery, Frederic Barrat, Mauro Carvalho Chehab,
Jason M Biils, Eric Sandeen, Lorenzo Pieralisi, Jonathan Corbet,
openbmc, linux-doc, Kishon Vijay Abraham I, Bryant G . Ly,
Uwe Kleine-Konig, David Kershner, Guenter Roeck, Wu Hao,
linux-hwmon, Jean Delvare, Arnd Bergmann, Philippe Ombredanne,
Johan Hovold, Tomohiro Kusumi, Rob Herring, Thomas Gleixner,
Sagar Dharia, linux-arm-kernel, Juergen Gross, Cyrille Pitchen,
Andrew Jeffery, Greg Kroah-Hartman, Darrick J . Wong,
Stephen Boyd, linux-kernel, Vinod Koul, James Feist,
Gustavo Pimentel, Andrew Morton, David S . Miller
Hi Lee,
On 10/24/2018 12:25 AM, Lee Jones wrote:
> On Tue, 18 Sep 2018, Jae Hyun Yoo wrote:
>
>> +Required properties:
>> +- compatible : Should be "intel,peci-client".
>> +- reg : Should contain address of a client CPU. Address range of CPU
>> + clients starts from 0x30 based on PECI specification.
>
> Nit: "start"
>
> Would be better worded:
>
> "According to the PECI specification client addresses start from 0x30."
>
Yes, that would be better. Will fix it like you suggested.
Thanks for the review!
Jae
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v8 08/12] mfd: intel-peci-client: Add PECI client MFD driver
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
` (6 preceding siblings ...)
2018-09-18 21:51 ` [PATCH v8 07/12] dt-bindings: mfd: Add a document for PECI client MFD Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
2018-10-24 10:59 ` Lee Jones
2018-09-18 21:51 ` [PATCH v8 09/12] Documentation: hwmon: Add documents for PECI hwmon client drivers Jae Hyun Yoo
` (3 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo, James Feist,
Jason M Biils, Vernon Mauery
This commit adds PECI client MFD driver.
Cc: Lee Jones <lee.jones@linaro.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: James Feist <james.feist@linux.intel.com>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
drivers/mfd/Kconfig | 14 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/intel-peci-client.c | 181 ++++++++++++++++++++++++++
include/linux/mfd/intel-peci-client.h | 81 ++++++++++++
4 files changed, 277 insertions(+)
create mode 100644 drivers/mfd/intel-peci-client.c
create mode 100644 include/linux/mfd/intel-peci-client.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 11841f4b7b2b..e68ae5d820ba 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -595,6 +595,20 @@ config MFD_INTEL_MSIC
Passage) chip. This chip embeds audio, battery, GPIO, etc.
devices used in Intel Medfield platforms.
+config MFD_INTEL_PECI_CLIENT
+ bool "Intel PECI client"
+ depends on (PECI || COMPILE_TEST)
+ select MFD_CORE
+ help
+ If you say yes to this option, support will be included for the
+ multi-functional Intel PECI (Platform Environment Control Interface)
+ client. PECI is a one-wire bus interface that provides a communication
+ channel from PECI clients in Intel processors and chipset components
+ to external monitoring or control devices.
+
+ Additional drivers must be enabled in order to use the functionality
+ of the device.
+
config MFD_IPAQ_MICRO
bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
depends on SA1100_H3100 || SA1100_H3600
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5856a9489cbd..ed05583dc30e 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -203,6 +203,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o
obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o
obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
+obj-$(CONFIG_MFD_INTEL_PECI_CLIENT) += intel-peci-client.o
obj-$(CONFIG_MFD_PALMAS) += palmas.o
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
new file mode 100644
index 000000000000..507e4ac66dfa
--- /dev/null
+++ b/drivers/mfd/intel-peci-client.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Intel Corporation
+
+#include <linux/bitfield.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/peci.h>
+#include <linux/of_device.h>
+
+enum cpu_gens {
+ CPU_GEN_HSX = 0, /* Haswell Xeon */
+ CPU_GEN_BRX, /* Broadwell Xeon */
+ CPU_GEN_SKX, /* Skylake Xeon */
+};
+
+static struct mfd_cell peci_functions[] = {
+ {
+ .name = "peci-cputemp",
+ },
+ {
+ .name = "peci-dimmtemp",
+ },
+ /* TODO: Add additional PECI sideband functions into here */
+};
+
+static const struct cpu_gen_info cpu_gen_info_table[] = {
+ [CPU_GEN_HSX] = {
+ .family = 6, /* Family code */
+ .model = INTEL_FAM6_HASWELL_X,
+ .core_max = CORE_MAX_ON_HSX,
+ .chan_rank_max = CHAN_RANK_MAX_ON_HSX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_HSX },
+ [CPU_GEN_BRX] = {
+ .family = 6, /* Family code */
+ .model = INTEL_FAM6_BROADWELL_X,
+ .core_max = CORE_MAX_ON_BDX,
+ .chan_rank_max = CHAN_RANK_MAX_ON_BDX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_BDX },
+ [CPU_GEN_SKX] = {
+ .family = 6, /* Family code */
+ .model = INTEL_FAM6_SKYLAKE_X,
+ .core_max = CORE_MAX_ON_SKX,
+ .chan_rank_max = CHAN_RANK_MAX_ON_SKX,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_SKX },
+};
+
+static int peci_client_get_cpu_gen_info(struct peci_mfd *priv)
+{
+ u32 cpu_id;
+ int i, rc;
+
+ rc = peci_get_cpu_id(priv->adapter, priv->addr, &cpu_id);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
+ if (FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id) +
+ FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id) ==
+ cpu_gen_info_table[i].family &&
+ FIELD_GET(CPU_ID_MODEL_MASK, cpu_id) ==
+ FIELD_GET(LOWER_NIBBLE_MASK,
+ cpu_gen_info_table[i].model) &&
+ FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id) ==
+ FIELD_GET(UPPER_NIBBLE_MASK,
+ cpu_gen_info_table[i].model)) {
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(cpu_gen_info_table))
+ return -ENODEV;
+
+ priv->gen_info = &cpu_gen_info_table[i];
+
+ return 0;
+}
+
+bool peci_temp_need_update(struct temp_data *temp)
+{
+ if (temp->valid &&
+ time_before(jiffies, temp->last_updated + UPDATE_INTERVAL))
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(peci_temp_need_update);
+
+void peci_temp_mark_updated(struct temp_data *temp)
+{
+ temp->valid = 1;
+ temp->last_updated = jiffies;
+}
+EXPORT_SYMBOL_GPL(peci_temp_mark_updated);
+
+int peci_client_command(struct peci_mfd *priv, enum peci_cmd cmd, void *vmsg)
+{
+ return peci_command(priv->adapter, cmd, vmsg);
+}
+EXPORT_SYMBOL_GPL(peci_client_command);
+
+int peci_client_rd_pkg_cfg_cmd(struct peci_mfd *priv, u8 mbx_idx,
+ u16 param, u8 *data)
+{
+ struct peci_rd_pkg_cfg_msg msg;
+ int rc;
+
+ msg.addr = priv->addr;
+ msg.index = mbx_idx;
+ msg.param = param;
+ msg.rx_len = 4;
+
+ rc = peci_command(priv->adapter, PECI_CMD_RD_PKG_CFG, &msg);
+ if (!rc)
+ memcpy(data, msg.pkg_config, 4);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(peci_client_rd_pkg_cfg_cmd);
+
+static int peci_client_probe(struct peci_client *client)
+{
+ struct device *dev = &client->dev;
+ struct peci_mfd *priv;
+ int rc;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->client = client;
+ priv->dev = dev;
+ priv->adapter = client->adapter;
+ priv->addr = client->addr;
+ priv->cpu_no = client->addr - PECI_BASE_ADDR;
+
+ snprintf(priv->name, PECI_NAME_SIZE, "peci_client.cpu%d", priv->cpu_no);
+
+ rc = peci_client_get_cpu_gen_info(priv);
+ if (rc)
+ return rc;
+
+ rc = devm_mfd_add_devices(priv->dev, priv->cpu_no, peci_functions,
+ ARRAY_SIZE(peci_functions), NULL, 0, NULL);
+ if (rc < 0) {
+ dev_err(priv->dev, "devm_mfd_add_devices failed: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id peci_client_of_table[] = {
+ { .compatible = "intel,peci-client" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, peci_client_of_table);
+#endif
+
+static const struct peci_device_id peci_client_ids[] = {
+ { .name = "peci-client", .driver_data = 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(peci, peci_client_ids);
+
+static struct peci_driver peci_client_driver = {
+ .probe = peci_client_probe,
+ .id_table = peci_client_ids,
+ .driver = {
+ .name = "peci-client",
+ .of_match_table =
+ of_match_ptr(peci_client_of_table),
+ },
+};
+module_peci_driver(peci_client_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI client MFD driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
new file mode 100644
index 000000000000..7ec272cddceb
--- /dev/null
+++ b/include/linux/mfd/intel-peci-client.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Intel Corporation */
+
+#ifndef __LINUX_MFD_PECI_CLIENT_H
+#define __LINUX_MFD_PECI_CLIENT_H
+
+#include <linux/peci.h>
+
+#if IS_ENABLED(CONFIG_X86)
+#include <asm/intel-family.h>
+#else
+/**
+ * Architectures other than x86 cannot include the header file so define these
+ * at here. These are needed for detecting type of client x86 CPUs behind a PECI
+ * connection.
+ */
+#define INTEL_FAM6_HASWELL_X 0x3F
+#define INTEL_FAM6_BROADWELL_X 0x4F
+#define INTEL_FAM6_SKYLAKE_X 0x55
+#endif
+
+#define LOWER_NIBBLE_MASK GENMASK(3, 0)
+#define UPPER_NIBBLE_MASK GENMASK(7, 4)
+
+#define CPU_ID_MODEL_MASK GENMASK(7, 4)
+#define CPU_ID_FAMILY_MASK GENMASK(11, 8)
+#define CPU_ID_EXT_MODEL_MASK GENMASK(19, 16)
+#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
+
+#define CORE_MAX_ON_HSX 18 /* Max number of cores on Haswell */
+#define CHAN_RANK_MAX_ON_HSX 8 /* Max number of channel ranks on Haswell */
+#define DIMM_IDX_MAX_ON_HSX 3 /* Max DIMM index per channel on Haswell */
+
+#define CORE_MAX_ON_BDX 24 /* Max number of cores on Broadwell */
+#define CHAN_RANK_MAX_ON_BDX 4 /* Max number of channel ranks on Broadwell */
+#define DIMM_IDX_MAX_ON_BDX 3 /* Max DIMM index per channel on Broadwell */
+
+#define CORE_MAX_ON_SKX 28 /* Max number of cores on Skylake */
+#define CHAN_RANK_MAX_ON_SKX 6 /* Max number of channel ranks on Skylake */
+#define DIMM_IDX_MAX_ON_SKX 2 /* Max DIMM index per channel on Skylake */
+
+#define CORE_NUMS_MAX CORE_MAX_ON_SKX
+#define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX
+#define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX
+#define DIMM_NUMS_MAX (CHAN_RANK_MAX * DIMM_IDX_MAX)
+
+#define TEMP_TYPE_PECI 6 /* Sensor type 6: Intel PECI */
+
+#define UPDATE_INTERVAL HZ
+
+struct temp_data {
+ uint valid;
+ s32 value;
+ ulong last_updated;
+};
+
+struct cpu_gen_info {
+ u16 family;
+ u8 model;
+ uint core_max;
+ uint chan_rank_max;
+ uint dimm_idx_max;
+};
+
+struct peci_mfd {
+ struct peci_client *client;
+ struct device *dev;
+ struct peci_adapter *adapter;
+ char name[PECI_NAME_SIZE];
+ u8 addr;
+ uint cpu_no;
+ const struct cpu_gen_info *gen_info;
+};
+
+bool peci_temp_need_update(struct temp_data *temp);
+void peci_temp_mark_updated(struct temp_data *temp);
+int peci_client_command(struct peci_mfd *mfd, enum peci_cmd cmd, void *vmsg);
+int peci_client_rd_pkg_cfg_cmd(struct peci_mfd *mfd, u8 mbx_idx,
+ u16 param, u8 *data);
+
+#endif /* __LINUX_MFD_PECI_CLIENT_H */
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v8 08/12] mfd: intel-peci-client: Add PECI client MFD driver
2018-09-18 21:51 ` [PATCH v8 08/12] mfd: intel-peci-client: Add PECI client MFD driver Jae Hyun Yoo
@ 2018-10-24 10:59 ` Lee Jones
2018-10-24 21:10 ` Jae Hyun Yoo
0 siblings, 1 reply; 23+ messages in thread
From: Lee Jones @ 2018-10-24 10:59 UTC (permalink / raw)
To: Jae Hyun Yoo
Cc: Rob Herring, Jean Delvare, Guenter Roeck, Mark Rutland,
Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen, linux-hwmon,
devicetree, linux-kernel, linux-arm-kernel, linux-aspeed,
linux-doc, openbmc, James Feist, Jason M Biils, Vernon Mauery
On Tue, 18 Sep 2018, Jae Hyun Yoo wrote:
> This commit adds PECI client MFD driver.
>
> Cc: Lee Jones <lee.jones@linaro.org>
> Cc: Randy Dunlap <rdunlap@infradead.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Andrew Jeffery <andrew@aj.id.au>
> Cc: James Feist <james.feist@linux.intel.com>
> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
> Cc: Joel Stanley <joel@jms.id.au>
> Cc: Vernon Mauery <vernon.mauery@linux.intel.com>
> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
> ---
> drivers/mfd/Kconfig | 14 ++
> drivers/mfd/Makefile | 1 +
> drivers/mfd/intel-peci-client.c | 181 ++++++++++++++++++++++++++
> include/linux/mfd/intel-peci-client.h | 81 ++++++++++++
> 4 files changed, 277 insertions(+)
> create mode 100644 drivers/mfd/intel-peci-client.c
> create mode 100644 include/linux/mfd/intel-peci-client.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 11841f4b7b2b..e68ae5d820ba 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -595,6 +595,20 @@ config MFD_INTEL_MSIC
> Passage) chip. This chip embeds audio, battery, GPIO, etc.
> devices used in Intel Medfield platforms.
>
> +config MFD_INTEL_PECI_CLIENT
> + bool "Intel PECI client"
> + depends on (PECI || COMPILE_TEST)
> + select MFD_CORE
> + help
> + If you say yes to this option, support will be included for the
> + multi-functional Intel PECI (Platform Environment Control Interface)
Remove 'multi-functional' from this sentence.
> + client. PECI is a one-wire bus interface that provides a communication
> + channel from PECI clients in Intel processors and chipset components
> + to external monitoring or control devices.
> +
> + Additional drivers must be enabled in order to use the functionality
> + of the device.
> +
> config MFD_IPAQ_MICRO
> bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
> depends on SA1100_H3100 || SA1100_H3600
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 5856a9489cbd..ed05583dc30e 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -203,6 +203,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o
> obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o
> obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
> obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
> +obj-$(CONFIG_MFD_INTEL_PECI_CLIENT) += intel-peci-client.o
> obj-$(CONFIG_MFD_PALMAS) += palmas.o
> obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
> obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
> diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
> new file mode 100644
> index 000000000000..507e4ac66dfa
> --- /dev/null
> +++ b/drivers/mfd/intel-peci-client.c
> @@ -0,0 +1,181 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2018 Intel Corporation
> +
> +#include <linux/bitfield.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/intel-peci-client.h>
> +#include <linux/module.h>
> +#include <linux/peci.h>
> +#include <linux/of_device.h>
> +
> +enum cpu_gens {
> + CPU_GEN_HSX = 0, /* Haswell Xeon */
> + CPU_GEN_BRX, /* Broadwell Xeon */
> + CPU_GEN_SKX, /* Skylake Xeon */
> +};
> +
> +static struct mfd_cell peci_functions[] = {
> + {
> + .name = "peci-cputemp",
> + },
> + {
> + .name = "peci-dimmtemp",
> + },
{ .name = "peci-cputemp", },
{ .name = "peci-dimmtemp", },
Do these have 2 different drivers? Where are you putting them?
> + /* TODO: Add additional PECI sideband functions into here */
When will this be done?
> +};
> +
> +static const struct cpu_gen_info cpu_gen_info_table[] = {
> + [CPU_GEN_HSX] = {
> + .family = 6, /* Family code */
> + .model = INTEL_FAM6_HASWELL_X,
> + .core_max = CORE_MAX_ON_HSX,
> + .chan_rank_max = CHAN_RANK_MAX_ON_HSX,
> + .dimm_idx_max = DIMM_IDX_MAX_ON_HSX },
> + [CPU_GEN_BRX] = {
> + .family = 6, /* Family code */
> + .model = INTEL_FAM6_BROADWELL_X,
> + .core_max = CORE_MAX_ON_BDX,
> + .chan_rank_max = CHAN_RANK_MAX_ON_BDX,
> + .dimm_idx_max = DIMM_IDX_MAX_ON_BDX },
> + [CPU_GEN_SKX] = {
> + .family = 6, /* Family code */
> + .model = INTEL_FAM6_SKYLAKE_X,
> + .core_max = CORE_MAX_ON_SKX,
> + .chan_rank_max = CHAN_RANK_MAX_ON_SKX,
> + .dimm_idx_max = DIMM_IDX_MAX_ON_SKX },
The '},'s should go on the line below.
> +};
> +
> +static int peci_client_get_cpu_gen_info(struct peci_mfd *priv)
Remove all mention of 'mfd'. It's not a thing.
> +{
> + u32 cpu_id;
> + int i, rc;
> +
> + rc = peci_get_cpu_id(priv->adapter, priv->addr, &cpu_id);
> + if (rc)
> + return rc;
> +
> + for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
> + if (FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id) +
> + FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id) ==
> + cpu_gen_info_table[i].family &&
> + FIELD_GET(CPU_ID_MODEL_MASK, cpu_id) ==
> + FIELD_GET(LOWER_NIBBLE_MASK,
> + cpu_gen_info_table[i].model) &&
> + FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id) ==
> + FIELD_GET(UPPER_NIBBLE_MASK,
> + cpu_gen_info_table[i].model)) {
> + break;
> + }
> + }
This is really read. Please reformat it, even if you have to use
local variables to make it more legible.
> + if (i >= ARRAY_SIZE(cpu_gen_info_table))
> + return -ENODEV;
Do you really want to fail silently?
> + priv->gen_info = &cpu_gen_info_table[i];
If you do this in the for(), you can then test priv->gen_info instead
of seeing if the iterator maxed out. Much nicer I think.
> + return 0;
> +}
> +
> +bool peci_temp_need_update(struct temp_data *temp)
> +{
> + if (temp->valid &&
> + time_before(jiffies, temp->last_updated + UPDATE_INTERVAL))
> + return false;
> +
> + return true;
> +}
> +EXPORT_SYMBOL_GPL(peci_temp_need_update);
> +
> +void peci_temp_mark_updated(struct temp_data *temp)
> +{
> + temp->valid = 1;
> + temp->last_updated = jiffies;
> +}
> +EXPORT_SYMBOL_GPL(peci_temp_mark_updated);
These are probably better suited as inline functions to be placed in
a header file. No need to export them, since they only use their own
data.
> +int peci_client_command(struct peci_mfd *priv, enum peci_cmd cmd, void *vmsg)
> +{
> + return peci_command(priv->adapter, cmd, vmsg);
> +}
> +EXPORT_SYMBOL_GPL(peci_client_command);
If you share the adaptor with the client, you can call peci_command()
directly. There should also be some locking in here somewhere too.
> +int peci_client_rd_pkg_cfg_cmd(struct peci_mfd *priv, u8 mbx_idx,
This is gobbledegook. What's rd? Read?
> + u16 param, u8 *data)
> +{
> + struct peci_rd_pkg_cfg_msg msg;
> + int rc;
> +
> + msg.addr = priv->addr;
> + msg.index = mbx_idx;
> + msg.param = param;
> + msg.rx_len = 4;
> +
> + rc = peci_command(priv->adapter, PECI_CMD_RD_PKG_CFG, &msg);
> + if (!rc)
> + memcpy(data, msg.pkg_config, 4);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL_GPL(peci_client_rd_pkg_cfg_cmd);
This too should be set-up as an inline function, not an exported one.
> +static int peci_client_probe(struct peci_client *client)
> +{
> + struct device *dev = &client->dev;
> + struct peci_mfd *priv;
> + int rc;
'ret' is more common.
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + dev_set_drvdata(dev, priv);
> + priv->client = client;
> + priv->dev = dev;
> + priv->adapter = client->adapter;
> + priv->addr = client->addr;
You're already saving client. No need to save its individual
attributes as well.
> + priv->cpu_no = client->addr - PECI_BASE_ADDR;
This seems clunky. Does the spec say that the addresses have to be in
numerical order with no gaps? What if someone chooses to implement 4
CPUs at 0x30, 0x35, 0x45 and 0x60?
What do you then do with cpu_no?
> + snprintf(priv->name, PECI_NAME_SIZE, "peci_client.cpu%d", priv->cpu_no);
> +
> + rc = peci_client_get_cpu_gen_info(priv);
> + if (rc)
> + return rc;
> +
> + rc = devm_mfd_add_devices(priv->dev, priv->cpu_no, peci_functions,
> + ARRAY_SIZE(peci_functions), NULL, 0, NULL);
> + if (rc < 0) {
> + dev_err(priv->dev, "devm_mfd_add_devices failed: %d\n", rc);
This to too specific.
"Failed to register child devices"
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id peci_client_of_table[] = {
> + { .compatible = "intel,peci-client" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, peci_client_of_table);
> +#endif
> +
> +static const struct peci_device_id peci_client_ids[] = {
> + { .name = "peci-client", .driver_data = 0 },
Remove .driver_data if you're not going to use it.
> + { }
> +};
> +MODULE_DEVICE_TABLE(peci, peci_client_ids);
> +
> +static struct peci_driver peci_client_driver = {
> + .probe = peci_client_probe,
> + .id_table = peci_client_ids,
> + .driver = {
> + .name = "peci-client",
> + .of_match_table =
> + of_match_ptr(peci_client_of_table),
> + },
> +};
Odd tabbing.
> +module_peci_driver(peci_client_driver);
> +
> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
> +MODULE_DESCRIPTION("PECI client MFD driver");
Remove "MFD".
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
> new file mode 100644
> index 000000000000..7ec272cddceb
> --- /dev/null
> +++ b/include/linux/mfd/intel-peci-client.h
> @@ -0,0 +1,81 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2018 Intel Corporation */
> +
> +#ifndef __LINUX_MFD_PECI_CLIENT_H
> +#define __LINUX_MFD_PECI_CLIENT_H
> +
> +#include <linux/peci.h>
> +
> +#if IS_ENABLED(CONFIG_X86)
> +#include <asm/intel-family.h>
> +#else
> +/**
> + * Architectures other than x86 cannot include the header file so define these
> + * at here. These are needed for detecting type of client x86 CPUs behind a PECI
> + * connection.
> + */
> +#define INTEL_FAM6_HASWELL_X 0x3F
> +#define INTEL_FAM6_BROADWELL_X 0x4F
> +#define INTEL_FAM6_SKYLAKE_X 0x55
> +#endif
> +
> +#define LOWER_NIBBLE_MASK GENMASK(3, 0)
> +#define UPPER_NIBBLE_MASK GENMASK(7, 4)
> +
> +#define CPU_ID_MODEL_MASK GENMASK(7, 4)
> +#define CPU_ID_FAMILY_MASK GENMASK(11, 8)
> +#define CPU_ID_EXT_MODEL_MASK GENMASK(19, 16)
> +#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
> +
> +#define CORE_MAX_ON_HSX 18 /* Max number of cores on Haswell */
> +#define CHAN_RANK_MAX_ON_HSX 8 /* Max number of channel ranks on Haswell */
> +#define DIMM_IDX_MAX_ON_HSX 3 /* Max DIMM index per channel on Haswell */
> +
> +#define CORE_MAX_ON_BDX 24 /* Max number of cores on Broadwell */
> +#define CHAN_RANK_MAX_ON_BDX 4 /* Max number of channel ranks on Broadwell */
> +#define DIMM_IDX_MAX_ON_BDX 3 /* Max DIMM index per channel on Broadwell */
> +
> +#define CORE_MAX_ON_SKX 28 /* Max number of cores on Skylake */
> +#define CHAN_RANK_MAX_ON_SKX 6 /* Max number of channel ranks on Skylake */
> +#define DIMM_IDX_MAX_ON_SKX 2 /* Max DIMM index per channel on Skylake */
> +
> +#define CORE_NUMS_MAX CORE_MAX_ON_SKX
> +#define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX
> +#define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX
> +#define DIMM_NUMS_MAX (CHAN_RANK_MAX * DIMM_IDX_MAX)
> +
> +#define TEMP_TYPE_PECI 6 /* Sensor type 6: Intel PECI */
> +
> +#define UPDATE_INTERVAL HZ
> +
> +struct temp_data {
> + uint valid;
> + s32 value;
> + ulong last_updated;
> +};
This should not live in here.
> +struct cpu_gen_info {
> + u16 family;
> + u8 model;
> + uint core_max;
> + uint chan_rank_max;
> + uint dimm_idx_max;
> +};
> +
> +struct peci_mfd {
Remove "_mfd".
> + struct peci_client *client;
> + struct device *dev;
> + struct peci_adapter *adapter;
> + char name[PECI_NAME_SIZE];
What is this used for?
> + u8 addr;
> + uint cpu_no;
> + const struct cpu_gen_info *gen_info;
Where is this used?
> +};
Both of these structs need kerneldoc headers.
> +bool peci_temp_need_update(struct temp_data *temp);
> +void peci_temp_mark_updated(struct temp_data *temp);
These should live in a temperature driver.
> +int peci_client_command(struct peci_mfd *mfd, enum peci_cmd cmd, void *vmsg);
> +int peci_client_rd_pkg_cfg_cmd(struct peci_mfd *mfd, u8 mbx_idx,
> + u16 param, u8 *data);
> +
> +#endif /* __LINUX_MFD_PECI_CLIENT_H */
--
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v8 08/12] mfd: intel-peci-client: Add PECI client MFD driver
2018-10-24 10:59 ` Lee Jones
@ 2018-10-24 21:10 ` Jae Hyun Yoo
2018-10-25 5:30 ` Lee Jones
0 siblings, 1 reply; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-10-24 21:10 UTC (permalink / raw)
To: Lee Jones
Cc: Rob Herring, Jean Delvare, Guenter Roeck, Mark Rutland,
Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen, linux-hwmon,
devicetree, linux-kernel, linux-arm-kernel, linux-aspeed,
linux-doc, openbmc, James Feist, Jason M Biils, Vernon Mauery
Hi Lee,
On 10/24/2018 3:59 AM, Lee Jones wrote:
> On Tue, 18 Sep 2018, Jae Hyun Yoo wrote:
>
>> This commit adds PECI client MFD driver.
>>
<snip>
>> +config MFD_INTEL_PECI_CLIENT
>> + bool "Intel PECI client"
>> + depends on (PECI || COMPILE_TEST)
>> + select MFD_CORE
>> + help
>> + If you say yes to this option, support will be included for the
>> + multi-functional Intel PECI (Platform Environment Control Interface)
>
> Remove 'multi-functional' from this sentence.
>
Will remove the word.
<snip>
>> +static struct mfd_cell peci_functions[] = {
>> + {
>> + .name = "peci-cputemp",
>> + },
>> + {
>> + .name = "peci-dimmtemp",
>> + },
>
> { .name = "peci-cputemp", },
> { .name = "peci-dimmtemp", },
>
Will change it like you suggested.
> Do these have 2 different drivers? Where are you putting them?
>
Yes, these have 2 different drivers as hwmon subsystem drivers.
I submitted them into this patch series.
Patch 10/12: https://lkml.org/lkml/2018/9/18/1524
Patch 11/12: https://lkml.org/lkml/2018/9/18/1523
>> + /* TODO: Add additional PECI sideband functions into here */
>
> When will this be done?
>
I'm hoping it will be done by the end of this year.
>> +};
>> +
>> +static const struct cpu_gen_info cpu_gen_info_table[] = {
>> + [CPU_GEN_HSX] = {
>> + .family = 6, /* Family code */
>> + .model = INTEL_FAM6_HASWELL_X,
>> + .core_max = CORE_MAX_ON_HSX,
>> + .chan_rank_max = CHAN_RANK_MAX_ON_HSX,
>> + .dimm_idx_max = DIMM_IDX_MAX_ON_HSX },
>> + [CPU_GEN_BRX] = {
>> + .family = 6, /* Family code */
>> + .model = INTEL_FAM6_BROADWELL_X,
>> + .core_max = CORE_MAX_ON_BDX,
>> + .chan_rank_max = CHAN_RANK_MAX_ON_BDX,
>> + .dimm_idx_max = DIMM_IDX_MAX_ON_BDX },
>> + [CPU_GEN_SKX] = {
>> + .family = 6, /* Family code */
>> + .model = INTEL_FAM6_SKYLAKE_X,
>> + .core_max = CORE_MAX_ON_SKX,
>> + .chan_rank_max = CHAN_RANK_MAX_ON_SKX,
>> + .dimm_idx_max = DIMM_IDX_MAX_ON_SKX },
>
> The '},'s should go on the line below.
>
Okay. Will fix it.
>> +};
>> +
>> +static int peci_client_get_cpu_gen_info(struct peci_mfd *priv)
>
> Remove all mention of 'mfd'. It's not a thing.
>
Will remove 'mfd'.
>> +{
>> + u32 cpu_id;
>> + int i, rc;
>> +
>> + rc = peci_get_cpu_id(priv->adapter, priv->addr, &cpu_id);
>> + if (rc)
>> + return rc;
>> +
>> + for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
>> + if (FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id) +
>> + FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id) ==
>> + cpu_gen_info_table[i].family &&
>> + FIELD_GET(CPU_ID_MODEL_MASK, cpu_id) ==
>> + FIELD_GET(LOWER_NIBBLE_MASK,
>> + cpu_gen_info_table[i].model) &&
>> + FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id) ==
>> + FIELD_GET(UPPER_NIBBLE_MASK,
>> + cpu_gen_info_table[i].model)) {
>> + break;
>> + }
>> + }
>
> This is really read. Please reformat it, even if you have to use
> local variables to make it more legible.
>
Will reformat it using local variables for better readability as you
suggest.
>> + if (i >= ARRAY_SIZE(cpu_gen_info_table))
>> + return -ENODEV;
>
> Do you really want to fail silently?
>
No. I'll add a dev_err printing.
>> + priv->gen_info = &cpu_gen_info_table[i];
>
> If you do this in the for(), you can then test priv->gen_info instead
> of seeing if the iterator maxed out. Much nicer I think.
>
Yes, that would be much nicer. Will fix it.
>> + return 0;
>> +}
>> +
>> +bool peci_temp_need_update(struct temp_data *temp)
>> +{
>> + if (temp->valid &&
>> + time_before(jiffies, temp->last_updated + UPDATE_INTERVAL))
>> + return false;
>> +
>> + return true;
>> +}
>> +EXPORT_SYMBOL_GPL(peci_temp_need_update);
>> +
>> +void peci_temp_mark_updated(struct temp_data *temp)
>> +{
>> + temp->valid = 1;
>> + temp->last_updated = jiffies;
>> +}
>> +EXPORT_SYMBOL_GPL(peci_temp_mark_updated);
>
> These are probably better suited as inline functions to be placed in
> a header file. No need to export them, since they only use their own
> data.
>
Yes, that makes sense. I'll change these to inline functions.
>> +int peci_client_command(struct peci_mfd *priv, enum peci_cmd cmd, void *vmsg)
>> +{
>> + return peci_command(priv->adapter, cmd, vmsg);
>> +}
>> +EXPORT_SYMBOL_GPL(peci_client_command);
>
> If you share the adaptor with the client, you can call peci_command()
> directly. There should also be some locking in here somewhere too.
>
Yes, the client->adapter can be referenced from child drivers. Will make
them call peci_command() directly.
>> +int peci_client_rd_pkg_cfg_cmd(struct peci_mfd *priv, u8 mbx_idx,
>
> This is gobbledegook. What's rd? Read?
>
Yes, the 'rd' means 'read'. I intended to keep command names as listed
in the PECI specification such as RdPkgConfig, WrPkgConfig and so on.
Should I change it to 'peci_client_read_package_config_command' ?
>> + u16 param, u8 *data)
>> +{
>> + struct peci_rd_pkg_cfg_msg msg;
>> + int rc;
>> +
>> + msg.addr = priv->addr;
>> + msg.index = mbx_idx;
>> + msg.param = param;
>> + msg.rx_len = 4;
>> +
>> + rc = peci_command(priv->adapter, PECI_CMD_RD_PKG_CFG, &msg);
>> + if (!rc)
>> + memcpy(data, msg.pkg_config, 4);
>> +
>> + return rc;
>> +}
>> +EXPORT_SYMBOL_GPL(peci_client_rd_pkg_cfg_cmd);
>
> This too should be set-up as an inline function, not an exported one.
>
Will change it to inline function.
>> +static int peci_client_probe(struct peci_client *client)
>> +{
>> + struct device *dev = &client->dev;
>> + struct peci_mfd *priv;
>> + int rc;
>
> 'ret' is more common.
>
Will fix it.
>> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> + if (!priv)
>> + return -ENOMEM;
>> +
>> + dev_set_drvdata(dev, priv);
>> + priv->client = client;
>> + priv->dev = dev;
>
>> + priv->adapter = client->adapter;
>> + priv->addr = client->addr;
>
> You're already saving client. No need to save its individual
> attributes as well.
>
I intended to reduce pointer referencing depth because adapter and addr
are frequently used, but you are right. I'll remove adapter and addr
from the struct.
>> + priv->cpu_no = client->addr - PECI_BASE_ADDR;
>
> This seems clunky. Does the spec say that the addresses have to be in
> numerical order with no gaps? What if someone chooses to implement 4
> CPUs at 0x30, 0x35, 0x45 and 0x60?
>
The CPU address will be assigned by H/W strap settings in order. Also,
there would be a case of using some CPU slots left empty, so gaps could
be happen on the addresses but it's still acceptable.
> What do you then do with cpu_no?
>
It is being used for child device id parameter when this code calls
devm_mfd_add_devices() below. Also, it is referenced from cputemp and
dimmtemp hwmon drivers but it isn't needed because the hwmon drivers
already know client->addr. I'll change cpu_no as a local variable in
here for child device id and will change the hwmon drivers.
>> + snprintf(priv->name, PECI_NAME_SIZE, "peci_client.cpu%d", priv->cpu_no);
>> +
>> + rc = peci_client_get_cpu_gen_info(priv);
>> + if (rc)
>> + return rc;
>> +
>> + rc = devm_mfd_add_devices(priv->dev, priv->cpu_no, peci_functions,
>> + ARRAY_SIZE(peci_functions), NULL, 0, NULL);
>> + if (rc < 0) {
>> + dev_err(priv->dev, "devm_mfd_add_devices failed: %d\n", rc);
>
> This to too specific.
>
> "Failed to register child devices"
>
Will fix it like you suggested.
>> + return rc;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +#ifdef CONFIG_OF
>> +static const struct of_device_id peci_client_of_table[] = {
>> + { .compatible = "intel,peci-client" },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(of, peci_client_of_table);
>> +#endif
>> +
>> +static const struct peci_device_id peci_client_ids[] = {
>> + { .name = "peci-client", .driver_data = 0 },
>
> Remove .driver_data if you're not going to use it.
>
Okay. I'll remove .driver_data.
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(peci, peci_client_ids);
>> +
>> +static struct peci_driver peci_client_driver = {
>> + .probe = peci_client_probe,
>> + .id_table = peci_client_ids,
>> + .driver = {
>> + .name = "peci-client",
>> + .of_match_table =
>> + of_match_ptr(peci_client_of_table),
>> + },
>> +};
>
> Odd tabbing.
>
Will fix the indentation.
>> +module_peci_driver(peci_client_driver);
>> +
>> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
>> +MODULE_DESCRIPTION("PECI client MFD driver");
>
> Remove "MFD".
>
Will remove it.
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h
>> new file mode 100644
>> index 000000000000..7ec272cddceb
>> --- /dev/null
>> +++ b/include/linux/mfd/intel-peci-client.h
>> @@ -0,0 +1,81 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/* Copyright (c) 2018 Intel Corporation */
>> +
>> +#ifndef __LINUX_MFD_PECI_CLIENT_H
>> +#define __LINUX_MFD_PECI_CLIENT_H
>> +
>> +#include <linux/peci.h>
>> +
>> +#if IS_ENABLED(CONFIG_X86)
>> +#include <asm/intel-family.h>
>> +#else
>> +/**
>> + * Architectures other than x86 cannot include the header file so define these
>> + * at here. These are needed for detecting type of client x86 CPUs behind a PECI
>> + * connection.
>> + */
>> +#define INTEL_FAM6_HASWELL_X 0x3F
>> +#define INTEL_FAM6_BROADWELL_X 0x4F
>> +#define INTEL_FAM6_SKYLAKE_X 0x55
>> +#endif
>> +
>> +#define LOWER_NIBBLE_MASK GENMASK(3, 0)
>> +#define UPPER_NIBBLE_MASK GENMASK(7, 4)
>> +
>> +#define CPU_ID_MODEL_MASK GENMASK(7, 4)
>> +#define CPU_ID_FAMILY_MASK GENMASK(11, 8)
>> +#define CPU_ID_EXT_MODEL_MASK GENMASK(19, 16)
>> +#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
>> +
>> +#define CORE_MAX_ON_HSX 18 /* Max number of cores on Haswell */
>> +#define CHAN_RANK_MAX_ON_HSX 8 /* Max number of channel ranks on Haswell */
>> +#define DIMM_IDX_MAX_ON_HSX 3 /* Max DIMM index per channel on Haswell */
>> +
>> +#define CORE_MAX_ON_BDX 24 /* Max number of cores on Broadwell */
>> +#define CHAN_RANK_MAX_ON_BDX 4 /* Max number of channel ranks on Broadwell */
>> +#define DIMM_IDX_MAX_ON_BDX 3 /* Max DIMM index per channel on Broadwell */
>> +
>> +#define CORE_MAX_ON_SKX 28 /* Max number of cores on Skylake */
>> +#define CHAN_RANK_MAX_ON_SKX 6 /* Max number of channel ranks on Skylake */
>> +#define DIMM_IDX_MAX_ON_SKX 2 /* Max DIMM index per channel on Skylake */
>> +
>> +#define CORE_NUMS_MAX CORE_MAX_ON_SKX
>> +#define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX
>> +#define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX
>> +#define DIMM_NUMS_MAX (CHAN_RANK_MAX * DIMM_IDX_MAX)
>> +
>> +#define TEMP_TYPE_PECI 6 /* Sensor type 6: Intel PECI */
>> +
>> +#define UPDATE_INTERVAL HZ
>> +
>> +struct temp_data {
>> + uint valid;
>> + s32 value;
>> + ulong last_updated;
>> +};
>
> This should not live in here.
>
Will move it.
>> +struct cpu_gen_info {
>> + u16 family;
>> + u8 model;
>> + uint core_max;
>> + uint chan_rank_max;
>> + uint dimm_idx_max;
>> +};
>> +
>> +struct peci_mfd {
>
> Remove "_mfd".
>
Will remove 'mfd'.
>> + struct peci_client *client;
>> + struct device *dev;
>> + struct peci_adapter *adapter;
>> + char name[PECI_NAME_SIZE];
>
> What is this used for?
>
This isn't needed. Thanks for your pointing out. I'll remove it.
>> + u8 addr;
>> + uint cpu_no;
>> + const struct cpu_gen_info *gen_info;
>
> Where is this used?
>
It is referenced from cputemp and dimmtemp hwmon drivers to specify CPU
generation.
>> +};
>
> Both of these structs need kerneldoc headers.
>
Will add kerneldoc headers.
>> +bool peci_temp_need_update(struct temp_data *temp);
>> +void peci_temp_mark_updated(struct temp_data *temp);
>
> These should live in a temperature driver.
>
Agreed. I'll move it to temperature driver.
Thanks a lot for your careful review. I'll submit a new patch set soon.
Jae
>> +int peci_client_command(struct peci_mfd *mfd, enum peci_cmd cmd, void *vmsg);
>> +int peci_client_rd_pkg_cfg_cmd(struct peci_mfd *mfd, u8 mbx_idx,
>> + u16 param, u8 *data);
>> +
>> +#endif /* __LINUX_MFD_PECI_CLIENT_H */
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v8 08/12] mfd: intel-peci-client: Add PECI client MFD driver
2018-10-24 21:10 ` Jae Hyun Yoo
@ 2018-10-25 5:30 ` Lee Jones
2018-10-25 16:16 ` Jae Hyun Yoo
0 siblings, 1 reply; 23+ messages in thread
From: Lee Jones @ 2018-10-25 5:30 UTC (permalink / raw)
To: Jae Hyun Yoo
Cc: Rob Herring, Jean Delvare, Guenter Roeck, Mark Rutland,
Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen, linux-hwmon,
devicetree, linux-kernel, linux-arm-kernel, linux-aspeed,
linux-doc, openbmc, James Feist, Jason M Biils, Vernon Mauery
On Wed, 24 Oct 2018, Jae Hyun Yoo wrote:
> On 10/24/2018 3:59 AM, Lee Jones wrote:
> > On Tue, 18 Sep 2018, Jae Hyun Yoo wrote:
> >
> > > This commit adds PECI client MFD driver.
> > >
>
> <snip>
[...]
> > > +bool peci_temp_need_update(struct temp_data *temp)
> > > +{
> > > + if (temp->valid &&
> > > + time_before(jiffies, temp->last_updated + UPDATE_INTERVAL))
> > > + return false;
> > > +
> > > + return true;
> > > +}
> > > +EXPORT_SYMBOL_GPL(peci_temp_need_update);
> > > +
> > > +void peci_temp_mark_updated(struct temp_data *temp)
> > > +{
> > > + temp->valid = 1;
> > > + temp->last_updated = jiffies;
> > > +}
> > > +EXPORT_SYMBOL_GPL(peci_temp_mark_updated);
> >
> > These are probably better suited as inline functions to be placed in
> > a header file. No need to export them, since they only use their own
> > data.
Also move them into the HWMON header file.
They have no business in MFD.
[...]
> > > +int peci_client_rd_pkg_cfg_cmd(struct peci_mfd *priv, u8 mbx_idx,
> >
> > This is gobbledegook. What's rd? Read?
> >
>
> Yes, the 'rd' means 'read'. I intended to keep command names as listed
> in the PECI specification such as RdPkgConfig, WrPkgConfig and so on.
> Should I change it to 'peci_client_read_package_config_command' ?
I looks/reads a lot nicer, don't you think?
[...]
--
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v8 08/12] mfd: intel-peci-client: Add PECI client MFD driver
2018-10-25 5:30 ` Lee Jones
@ 2018-10-25 16:16 ` Jae Hyun Yoo
0 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-10-25 16:16 UTC (permalink / raw)
To: Lee Jones
Cc: Rob Herring, Jean Delvare, Guenter Roeck, Mark Rutland,
Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen, linux-hwmon,
devicetree, linux-kernel, linux-arm-kernel, linux-aspeed,
linux-doc, openbmc, James Feist, Jason M Biils, Vernon Mauery
On 10/24/2018 10:30 PM, Lee Jones wrote:
> On Wed, 24 Oct 2018, Jae Hyun Yoo wrote:
>> On 10/24/2018 3:59 AM, Lee Jones wrote:
>>> On Tue, 18 Sep 2018, Jae Hyun Yoo wrote:
>>>
>>>> This commit adds PECI client MFD driver.
>>>>
>>
>> <snip>
>
> [...]
>
>>>> +bool peci_temp_need_update(struct temp_data *temp)
>>>> +{
>>>> + if (temp->valid &&
>>>> + time_before(jiffies, temp->last_updated + UPDATE_INTERVAL))
>>>> + return false;
>>>> +
>>>> + return true;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(peci_temp_need_update);
>>>> +
>>>> +void peci_temp_mark_updated(struct temp_data *temp)
>>>> +{
>>>> + temp->valid = 1;
>>>> + temp->last_updated = jiffies;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(peci_temp_mark_updated);
>>>
>>> These are probably better suited as inline functions to be placed in
>>> a header file. No need to export them, since they only use their own
>>> data.
>
> Also move them into the HWMON header file.
>
> They have no business in MFD.
>
Agreed. I'll move them into the HWMON header.
> [...]
>
>>>> +int peci_client_rd_pkg_cfg_cmd(struct peci_mfd *priv, u8 mbx_idx,
>>>
>>> This is gobbledegook. What's rd? Read?
>>>
>>
>> Yes, the 'rd' means 'read'. I intended to keep command names as listed
>> in the PECI specification such as RdPkgConfig, WrPkgConfig and so on.
>> Should I change it to 'peci_client_read_package_config_command' ?
>
> I looks/reads a lot nicer, don't you think?
>
Okay. I'll change it too.
Thanks again for your review, Lee!
Jae
> [...]
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v8 09/12] Documentation: hwmon: Add documents for PECI hwmon client drivers
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
` (7 preceding siblings ...)
2018-09-18 21:51 ` [PATCH v8 08/12] mfd: intel-peci-client: Add PECI client MFD driver Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 10/12] hwmon: Add PECI cputemp driver Jae Hyun Yoo
` (2 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo, Jason M Biils
This commit adds hwmon documents for PECI cputemp and dimmtemp drivers.
Cc: Jean Delvare <jdelvare@suse.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
---
Documentation/hwmon/peci-cputemp | 78 +++++++++++++++++++++++++++++++
Documentation/hwmon/peci-dimmtemp | 50 ++++++++++++++++++++
2 files changed, 128 insertions(+)
create mode 100644 Documentation/hwmon/peci-cputemp
create mode 100644 Documentation/hwmon/peci-dimmtemp
diff --git a/Documentation/hwmon/peci-cputemp b/Documentation/hwmon/peci-cputemp
new file mode 100644
index 000000000000..821a9258f2e6
--- /dev/null
+++ b/Documentation/hwmon/peci-cputemp
@@ -0,0 +1,78 @@
+Kernel driver peci-cputemp
+==========================
+
+Supported chips:
+ One of Intel server CPUs listed below which is connected to a PECI bus.
+ * Intel Xeon E5/E7 v3 server processors
+ Intel Xeon E5-14xx v3 family
+ Intel Xeon E5-24xx v3 family
+ Intel Xeon E5-16xx v3 family
+ Intel Xeon E5-26xx v3 family
+ Intel Xeon E5-46xx v3 family
+ Intel Xeon E7-48xx v3 family
+ Intel Xeon E7-88xx v3 family
+ * Intel Xeon E5/E7 v4 server processors
+ Intel Xeon E5-16xx v4 family
+ Intel Xeon E5-26xx v4 family
+ Intel Xeon E5-46xx v4 family
+ Intel Xeon E7-48xx v4 family
+ Intel Xeon E7-88xx v4 family
+ * Intel Xeon Scalable server processors
+ Intel Xeon Bronze family
+ Intel Xeon Silver family
+ Intel Xeon Gold family
+ Intel Xeon Platinum family
+ Addresses scanned: PECI client address 0x30 - 0x37
+ Datasheet: Available from http://www.intel.com/design/literature.htm
+
+Author:
+ Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+Description
+-----------
+
+This driver implements a generic PECI hwmon feature which provides Digital
+Thermal Sensor (DTS) thermal readings of the CPU package and CPU cores that are
+accessible using the PECI Client Command Suite via the processor PECI client.
+
+All temperature values are given in millidegree Celsius and will be measurable
+only when the target CPU is powered on.
+
+sysfs attributes
+----------------
+
+temp1_label "Die"
+temp1_input Provides current die temperature of the CPU package.
+temp1_max Provides thermal control temperature of the CPU package
+ which is also known as Tcontrol.
+temp1_crit Provides shutdown temperature of the CPU package which
+ is also known as the maximum processor junction
+ temperature, Tjmax or Tprochot.
+temp1_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
+ the CPU package.
+
+temp2_label "Tcontrol"
+temp2_input Provides current Tcontrol temperature of the CPU
+ package which is also known as Fan Temperature target.
+ Indicates the relative value from thermal monitor trip
+ temperature at which fans should be engaged.
+temp2_crit Provides Tcontrol critical value of the CPU package
+ which is same to Tjmax.
+
+temp3_label "Tthrottle"
+temp3_input Provides current Tthrottle temperature of the CPU
+ package. Used for throttling temperature. If this value
+ is allowed and lower than Tjmax - the throttle will
+ occur and reported at lower than Tjmax.
+
+temp4_label "Tjmax"
+temp4_input Provides the maximum junction temperature, Tjmax of the
+ CPU package.
+
+temp[5-*]_label Provides string "Core X", where X is resolved core
+ number.
+temp[5-*]_input Provides current temperature of each core.
+temp[5-*]_max Provides thermal control temperature of the core.
+temp[5-*]_crit Provides shutdown temperature of the core.
+temp[5-*]_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
+ the core.
diff --git a/Documentation/hwmon/peci-dimmtemp b/Documentation/hwmon/peci-dimmtemp
new file mode 100644
index 000000000000..c54f2526188c
--- /dev/null
+++ b/Documentation/hwmon/peci-dimmtemp
@@ -0,0 +1,50 @@
+Kernel driver peci-dimmtemp
+===========================
+
+Supported chips:
+ One of Intel server CPUs listed below which is connected to a PECI bus.
+ * Intel Xeon E5/E7 v3 server processors
+ Intel Xeon E5-14xx v3 family
+ Intel Xeon E5-24xx v3 family
+ Intel Xeon E5-16xx v3 family
+ Intel Xeon E5-26xx v3 family
+ Intel Xeon E5-46xx v3 family
+ Intel Xeon E7-48xx v3 family
+ Intel Xeon E7-88xx v3 family
+ * Intel Xeon E5/E7 v4 server processors
+ Intel Xeon E5-16xx v4 family
+ Intel Xeon E5-26xx v4 family
+ Intel Xeon E5-46xx v4 family
+ Intel Xeon E7-48xx v4 family
+ Intel Xeon E7-88xx v4 family
+ * Intel Xeon Scalable server processors
+ Intel Xeon Bronze family
+ Intel Xeon Silver family
+ Intel Xeon Gold family
+ Intel Xeon Platinum family
+ Addresses scanned: PECI client address 0x30 - 0x37
+ Datasheet: Available from http://www.intel.com/design/literature.htm
+
+Author:
+ Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+Description
+-----------
+
+This driver implements a generic PECI hwmon feature which provides Digital
+Thermal Sensor (DTS) thermal readings of DIMM components that are accessible
+using the PECI Client Command Suite via the processor PECI client.
+
+All temperature values are given in millidegree Celsius and will be measurable
+only when the target CPU is powered on.
+
+sysfs attributes
+----------------
+
+temp[N]_label Provides string "DIMM CI", where C is DIMM channel and
+ I is DIMM index of the populated DIMM.
+temp[N]_input Provides current temperature of the populated DIMM.
+
+Note:
+ DIMM temperature attributes will appear when the client CPU's BIOS
+ completes memory training and testing.
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v8 10/12] hwmon: Add PECI cputemp driver
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
` (8 preceding siblings ...)
2018-09-18 21:51 ` [PATCH v8 09/12] Documentation: hwmon: Add documents for PECI hwmon client drivers Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 11/12] hwmon: Add PECI dimmtemp driver Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 12/12] Add maintainers for the PECI subsystem Jae Hyun Yoo
11 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo, Alan Cox,
Andy Shevchenko, Jason M Biils, Miguel Ojeda, Andrew Lunn,
Stef van Os
This commit adds PECI cputemp hwmon driver.
Cc: Jean Delvare <jdelvare@suse.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/Kconfig | 14 ++
drivers/hwmon/Makefile | 1 +
drivers/hwmon/peci-cputemp.c | 392 +++++++++++++++++++++++++++++++++++
3 files changed, 407 insertions(+)
create mode 100644 drivers/hwmon/peci-cputemp.c
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 81da17a42dc9..f08b4a670ac5 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1306,6 +1306,20 @@ config SENSORS_PCF8591
These devices are hard to detect and rarely found on mainstream
hardware. If unsure, say N.
+config SENSORS_PECI_CPUTEMP
+ tristate "PECI CPU temperature monitoring support"
+ depends on PECI
+ select MFD_INTEL_PECI_CLIENT
+ help
+ If you say yes here you get support for the generic Intel PECI
+ cputemp driver which provides Digital Thermal Sensor (DTS) thermal
+ readings of the CPU package and CPU cores that are accessible using
+ the PECI Client Command Suite via the processor PECI client.
+ Check Documentation/hwmon/peci-cputemp for details.
+
+ This driver can also be built as a module. If so, the module
+ will be called peci-cputemp.
+
source drivers/hwmon/pmbus/Kconfig
config SENSORS_PWM_FAN
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 93f7f41ea4ad..13ebde089ad5 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -141,6 +141,7 @@ obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
+obj-$(CONFIG_SENSORS_PECI_CPUTEMP) += peci-cputemp.o
obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o
obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o
diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
new file mode 100644
index 000000000000..20a48200c786
--- /dev/null
+++ b/drivers/hwmon/peci-cputemp.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Intel Corporation
+
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#define DEFAULT_CHANNEL_NUMS 4
+#define CORETEMP_CHANNEL_NUMS CORE_NUMS_MAX
+#define CPUTEMP_CHANNEL_NUMS (DEFAULT_CHANNEL_NUMS + CORETEMP_CHANNEL_NUMS)
+
+/* The RESOLVED_CORES register in PCU of a client CPU */
+#define REG_RESOLVED_CORES_BUS 1
+#define REG_RESOLVED_CORES_DEVICE 30
+#define REG_RESOLVED_CORES_FUNCTION 3
+#define REG_RESOLVED_CORES_OFFSET 0xB4
+
+struct temp_group {
+ struct temp_data die;
+ struct temp_data tcontrol;
+ struct temp_data tthrottle;
+ struct temp_data tjmax;
+ struct temp_data core[CORETEMP_CHANNEL_NUMS];
+};
+
+struct peci_cputemp {
+ struct peci_mfd *mfd;
+ struct device *dev;
+ char name[PECI_NAME_SIZE];
+ u8 addr;
+ const struct cpu_gen_info *gen_info;
+ struct temp_group temp;
+ u32 core_mask;
+ u32 temp_config[CPUTEMP_CHANNEL_NUMS + 1];
+ uint config_idx;
+ struct hwmon_channel_info temp_info;
+ const struct hwmon_channel_info *info[2];
+ struct hwmon_chip_info chip;
+};
+
+enum cputemp_channels {
+ channel_die,
+ channel_tcontrol,
+ channel_tthrottle,
+ channel_tjmax,
+ channel_core,
+};
+
+static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
+ /* Die temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+ HWMON_T_CRIT_HYST,
+
+ /* Tcontrol temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
+
+ /* Tthrottle temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT,
+
+ /* Tjmax temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT,
+
+ /* Core temperature - for all core channels */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+ HWMON_T_CRIT_HYST,
+};
+
+static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = {
+ "Die",
+ "Tcontrol",
+ "Tthrottle",
+ "Tjmax",
+ "Core 0", "Core 1", "Core 2", "Core 3",
+ "Core 4", "Core 5", "Core 6", "Core 7",
+ "Core 8", "Core 9", "Core 10", "Core 11",
+ "Core 12", "Core 13", "Core 14", "Core 15",
+ "Core 16", "Core 17", "Core 18", "Core 19",
+ "Core 20", "Core 21", "Core 22", "Core 23",
+};
+
+static s32 ten_dot_six_to_millidegree(s32 val)
+{
+ return ((val ^ 0x8000) - 0x8000) * 1000 / 64;
+}
+
+static int get_temp_targets(struct peci_cputemp *priv)
+{
+ s32 tthrottle_offset;
+ s32 tcontrol_margin;
+ u8 pkg_cfg[4];
+ int rc;
+
+ /**
+ * Just use only the tcontrol marker to determine if target values need
+ * update.
+ */
+ if (!peci_temp_need_update(&priv->temp.tcontrol))
+ return 0;
+
+ rc = peci_client_rd_pkg_cfg_cmd(priv->mfd, MBX_INDEX_TEMP_TARGET,
+ 0, pkg_cfg);
+ if (rc)
+ return rc;
+
+ priv->temp.tjmax.value = pkg_cfg[2] * 1000;
+
+ tcontrol_margin = pkg_cfg[1];
+ tcontrol_margin = ((tcontrol_margin ^ 0x80) - 0x80) * 1000;
+ priv->temp.tcontrol.value = priv->temp.tjmax.value - tcontrol_margin;
+
+ tthrottle_offset = (pkg_cfg[3] & 0x2f) * 1000;
+ priv->temp.tthrottle.value = priv->temp.tjmax.value - tthrottle_offset;
+
+ peci_temp_mark_updated(&priv->temp.tcontrol);
+
+ return 0;
+}
+
+static int get_die_temp(struct peci_cputemp *priv)
+{
+ struct peci_get_temp_msg msg;
+ int rc;
+
+ if (!peci_temp_need_update(&priv->temp.die))
+ return 0;
+
+ msg.addr = priv->addr;
+
+ rc = peci_client_command(priv->mfd, PECI_CMD_GET_TEMP, &msg);
+ if (rc)
+ return rc;
+
+ /* Note that the tjmax should be available before calling it */
+ priv->temp.die.value = priv->temp.tjmax.value +
+ (msg.temp_raw * 1000 / 64);
+
+ peci_temp_mark_updated(&priv->temp.die);
+
+ return 0;
+}
+
+static int get_core_temp(struct peci_cputemp *priv, int core_index)
+{
+ s32 core_dts_margin;
+ u8 pkg_cfg[4];
+ int rc;
+
+ if (!peci_temp_need_update(&priv->temp.core[core_index]))
+ return 0;
+
+ rc = peci_client_rd_pkg_cfg_cmd(priv->mfd, MBX_INDEX_PER_CORE_DTS_TEMP,
+ core_index, pkg_cfg);
+ if (rc)
+ return rc;
+
+ core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
+
+ /**
+ * Processors return a value of the core DTS reading in 10.6 format
+ * (10 bits signed decimal, 6 bits fractional).
+ * Error codes:
+ * 0x8000: General sensor error
+ * 0x8001: Reserved
+ * 0x8002: Underflow on reading value
+ * 0x8003-0x81ff: Reserved
+ */
+ if (core_dts_margin >= 0x8000 && core_dts_margin <= 0x81ff)
+ return -EIO;
+
+ core_dts_margin = ten_dot_six_to_millidegree(core_dts_margin);
+
+ /* Note that the tjmax should be available before calling it */
+ priv->temp.core[core_index].value = priv->temp.tjmax.value +
+ core_dts_margin;
+
+ peci_temp_mark_updated(&priv->temp.core[core_index]);
+
+ return 0;
+}
+
+static int cputemp_read_string(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ if (attr != hwmon_temp_label)
+ return -EOPNOTSUPP;
+
+ *str = cputemp_label[channel];
+ return 0;
+}
+
+static int cputemp_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct peci_cputemp *priv = dev_get_drvdata(dev);
+ int rc, core_index;
+
+ if (channel >= CPUTEMP_CHANNEL_NUMS ||
+ !(priv->temp_config[channel] & BIT(attr)))
+ return -EOPNOTSUPP;
+
+ rc = get_temp_targets(priv);
+ if (rc)
+ return rc;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ switch (channel) {
+ case channel_die:
+ rc = get_die_temp(priv);
+ if (rc)
+ break;
+
+ *val = priv->temp.die.value;
+ break;
+ case channel_tcontrol:
+ *val = priv->temp.tcontrol.value;
+ break;
+ case channel_tthrottle:
+ *val = priv->temp.tthrottle.value;
+ break;
+ case channel_tjmax:
+ *val = priv->temp.tjmax.value;
+ break;
+ default:
+ core_index = channel - DEFAULT_CHANNEL_NUMS;
+ rc = get_core_temp(priv, core_index);
+ if (rc)
+ break;
+
+ *val = priv->temp.core[core_index].value;
+ break;
+ }
+ break;
+ case hwmon_temp_max:
+ *val = priv->temp.tcontrol.value;
+ break;
+ case hwmon_temp_crit:
+ *val = priv->temp.tjmax.value;
+ break;
+ case hwmon_temp_crit_hyst:
+ *val = priv->temp.tjmax.value - priv->temp.tcontrol.value;
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ break;
+ }
+
+ return rc;
+}
+
+static umode_t cputemp_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct peci_cputemp *priv = data;
+
+ if (priv->temp_config[channel] & BIT(attr))
+ if (channel < DEFAULT_CHANNEL_NUMS ||
+ (channel >= DEFAULT_CHANNEL_NUMS &&
+ (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS))))
+ return 0444;
+
+ return 0;
+}
+
+static const struct hwmon_ops cputemp_ops = {
+ .is_visible = cputemp_is_visible,
+ .read_string = cputemp_read_string,
+ .read = cputemp_read,
+};
+
+static int check_resolved_cores(struct peci_cputemp *priv)
+{
+ struct peci_rd_pci_cfg_local_msg msg;
+ int rc;
+
+ /* Get the RESOLVED_CORES register value */
+ msg.addr = priv->addr;
+ msg.bus = REG_RESOLVED_CORES_BUS;
+ msg.device = REG_RESOLVED_CORES_DEVICE;
+ msg.function = REG_RESOLVED_CORES_FUNCTION;
+ msg.reg = REG_RESOLVED_CORES_OFFSET;
+ msg.rx_len = 4;
+
+ rc = peci_client_command(priv->mfd, PECI_CMD_RD_PCI_CFG_LOCAL, &msg);
+ if (rc)
+ return rc;
+
+ priv->core_mask = le32_to_cpup((__le32 *)msg.pci_config);
+ if (!priv->core_mask)
+ return -EAGAIN;
+
+ dev_dbg(priv->dev, "Scanned resolved cores: 0x%x\n", priv->core_mask);
+ return 0;
+}
+
+static int create_core_temp_info(struct peci_cputemp *priv)
+{
+ int rc, i;
+
+ rc = check_resolved_cores(priv);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < priv->gen_info->core_max; i++)
+ if (priv->core_mask & BIT(i))
+ while (i + DEFAULT_CHANNEL_NUMS >= priv->config_idx)
+ priv->temp_config[priv->config_idx++] =
+ config_table[channel_core];
+
+ return 0;
+}
+
+static int peci_cputemp_probe(struct platform_device *pdev)
+{
+ struct peci_mfd *mfd = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct peci_cputemp *priv;
+ struct device *hwmon_dev;
+ int rc;
+
+ if ((mfd->adapter->cmd_mask &
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->mfd = mfd;
+ priv->dev = dev;
+ priv->addr = mfd->addr;
+ priv->gen_info = mfd->gen_info;
+
+ snprintf(priv->name, PECI_NAME_SIZE, "peci_cputemp.cpu%d", mfd->cpu_no);
+
+ priv->temp_config[priv->config_idx++] = config_table[channel_die];
+ priv->temp_config[priv->config_idx++] = config_table[channel_tcontrol];
+ priv->temp_config[priv->config_idx++] = config_table[channel_tthrottle];
+ priv->temp_config[priv->config_idx++] = config_table[channel_tjmax];
+
+ rc = create_core_temp_info(priv);
+ if (rc)
+ dev_dbg(dev, "Skipped creating core temp info\n");
+
+ priv->chip.ops = &cputemp_ops;
+ priv->chip.info = priv->info;
+
+ priv->info[0] = &priv->temp_info;
+
+ priv->temp_info.type = hwmon_temp;
+ priv->temp_info.config = priv->temp_config;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
+ priv->name,
+ priv,
+ &priv->chip,
+ NULL);
+
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ dev_dbg(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), priv->name);
+
+ return 0;
+}
+
+static const struct platform_device_id peci_cputemp_ids[] = {
+ { .name = "peci-cputemp", .driver_data = 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, peci_cputemp_ids);
+
+static struct platform_driver peci_cputemp_driver = {
+ .probe = peci_cputemp_probe,
+ .id_table = peci_cputemp_ids,
+ .driver = {
+ .name = "peci-cputemp",
+ },
+};
+module_platform_driver(peci_cputemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI cputemp driver");
+MODULE_LICENSE("GPL v2");
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v8 11/12] hwmon: Add PECI dimmtemp driver
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
` (9 preceding siblings ...)
2018-09-18 21:51 ` [PATCH v8 10/12] hwmon: Add PECI cputemp driver Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
2018-09-18 21:51 ` [PATCH v8 12/12] Add maintainers for the PECI subsystem Jae Hyun Yoo
11 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo, Alan Cox,
Andy Shevchenko, Jason M Biils, Miguel Ojeda, Andrew Lunn,
Stef van Os
This commit adds PECI dimmtemp hwmon driver.
Cc: Jean Delvare <jdelvare@suse.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
Reviewed-by: James Feist <james.feist@linux.intel.com>
Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/Kconfig | 14 ++
drivers/hwmon/Makefile | 1 +
drivers/hwmon/peci-dimmtemp.c | 286 ++++++++++++++++++++++++++++++++++
3 files changed, 301 insertions(+)
create mode 100644 drivers/hwmon/peci-dimmtemp.c
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index f08b4a670ac5..d2299d850508 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1320,6 +1320,20 @@ config SENSORS_PECI_CPUTEMP
This driver can also be built as a module. If so, the module
will be called peci-cputemp.
+config SENSORS_PECI_DIMMTEMP
+ tristate "PECI DIMM temperature monitoring support"
+ depends on PECI
+ select MFD_INTEL_PECI_CLIENT
+ help
+ If you say yes here you get support for the generic Intel PECI hwmon
+ driver which provides Digital Thermal Sensor (DTS) thermal readings of
+ DIMM components that are accessible using the PECI Client Command
+ Suite via the processor PECI client.
+ Check Documentation/hwmon/peci-dimmtemp for details.
+
+ This driver can also be built as a module. If so, the module
+ will be called peci-dimmtemp.
+
source drivers/hwmon/pmbus/Kconfig
config SENSORS_PWM_FAN
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 13ebde089ad5..b7cf35bcfbaf 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -142,6 +142,7 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_PECI_CPUTEMP) += peci-cputemp.o
+obj-$(CONFIG_SENSORS_PECI_DIMMTEMP) += peci-dimmtemp.o
obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o
obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o
diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c
new file mode 100644
index 000000000000..0b0155fa627b
--- /dev/null
+++ b/drivers/hwmon/peci-dimmtemp.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Intel Corporation
+
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/intel-peci-client.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+
+#define DIMM_MASK_CHECK_DELAY_JIFFIES msecs_to_jiffies(5000)
+#define DIMM_MASK_CHECK_RETRY_MAX 60 /* 60 x 5 secs = 5 minutes */
+
+struct peci_dimmtemp {
+ struct peci_mfd *mfd;
+ struct device *dev;
+ char name[PECI_NAME_SIZE];
+ u8 addr;
+ const struct cpu_gen_info *gen_info;
+ struct workqueue_struct *work_queue;
+ struct delayed_work work_handler;
+ struct temp_data temp[DIMM_NUMS_MAX];
+ u32 dimm_mask;
+ int retry_count;
+ u32 temp_config[DIMM_NUMS_MAX + 1];
+ struct hwmon_channel_info temp_info;
+ const struct hwmon_channel_info *info[2];
+ struct hwmon_chip_info chip;
+};
+
+static const char *dimmtemp_label[CHAN_RANK_MAX][DIMM_IDX_MAX] = {
+ { "DIMM A1", "DIMM A2", "DIMM A3" },
+ { "DIMM B1", "DIMM B2", "DIMM B3" },
+ { "DIMM C1", "DIMM C2", "DIMM C3" },
+ { "DIMM D1", "DIMM D2", "DIMM D3" },
+ { "DIMM E1", "DIMM E2", "DIMM E3" },
+ { "DIMM F1", "DIMM F2", "DIMM F3" },
+ { "DIMM G1", "DIMM G2", "DIMM G3" },
+ { "DIMM H1", "DIMM H2", "DIMM H3" },
+};
+
+static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no)
+{
+ int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
+ int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
+ u8 cfg_data[4];
+ int rc;
+
+ if (!peci_temp_need_update(&priv->temp[dimm_no]))
+ return 0;
+
+ rc = peci_client_rd_pkg_cfg_cmd(priv->mfd, MBX_INDEX_DDR_DIMM_TEMP,
+ chan_rank, cfg_data);
+ if (rc)
+ return rc;
+
+ priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000;
+
+ peci_temp_mark_updated(&priv->temp[dimm_no]);
+
+ return 0;
+}
+
+static int dimmtemp_read_string(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+ u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
+ int chan_rank, dimm_idx;
+
+ if (attr != hwmon_temp_label)
+ return -EOPNOTSUPP;
+
+ chan_rank = channel / dimm_idx_max;
+ dimm_idx = channel % dimm_idx_max;
+ *str = dimmtemp_label[chan_rank][dimm_idx];
+ return 0;
+}
+
+static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct peci_dimmtemp *priv = dev_get_drvdata(dev);
+ int rc;
+
+ if (attr != hwmon_temp_input)
+ return -EOPNOTSUPP;
+
+ rc = get_dimm_temp(priv, channel);
+ if (rc)
+ return rc;
+
+ *val = priv->temp[channel].value;
+ return 0;
+}
+
+static umode_t dimmtemp_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct peci_dimmtemp *priv = data;
+
+ if (priv->temp_config[channel] & BIT(attr) &&
+ priv->dimm_mask & BIT(channel))
+ return 0444;
+
+ return 0;
+}
+
+static const struct hwmon_ops dimmtemp_ops = {
+ .is_visible = dimmtemp_is_visible,
+ .read_string = dimmtemp_read_string,
+ .read = dimmtemp_read,
+};
+
+static int check_populated_dimms(struct peci_dimmtemp *priv)
+{
+ u32 chan_rank_max = priv->gen_info->chan_rank_max;
+ u32 dimm_idx_max = priv->gen_info->dimm_idx_max;
+ int chan_rank, dimm_idx, rc;
+ u8 cfg_data[4];
+
+ for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
+ rc = peci_client_rd_pkg_cfg_cmd(priv->mfd,
+ MBX_INDEX_DDR_DIMM_TEMP,
+ chan_rank, cfg_data);
+ if (rc) {
+ priv->dimm_mask = 0;
+ return rc;
+ }
+
+ for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
+ if (cfg_data[dimm_idx])
+ priv->dimm_mask |= BIT(chan_rank *
+ dimm_idx_max +
+ dimm_idx);
+ }
+
+ if (!priv->dimm_mask)
+ return -EAGAIN;
+
+ dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", priv->dimm_mask);
+ return 0;
+}
+
+static int create_dimm_temp_info(struct peci_dimmtemp *priv)
+{
+ int rc, i, config_idx, channels;
+ struct device *hwmon_dev;
+
+ rc = check_populated_dimms(priv);
+ if (rc) {
+ if (rc == -EAGAIN) {
+ if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) {
+ queue_delayed_work(priv->work_queue,
+ &priv->work_handler,
+ DIMM_MASK_CHECK_DELAY_JIFFIES);
+ priv->retry_count++;
+ dev_dbg(priv->dev,
+ "Deferred DIMM temp info creation\n");
+ } else {
+ dev_err(priv->dev,
+ "Timeout DIMM temp info creation\n");
+ rc = -ETIMEDOUT;
+ }
+ }
+
+ return rc;
+ }
+
+ channels = priv->gen_info->chan_rank_max *
+ priv->gen_info->dimm_idx_max;
+ for (i = 0, config_idx = 0; i < channels; i++)
+ if (priv->dimm_mask & BIT(i))
+ while (i >= config_idx)
+ priv->temp_config[config_idx++] =
+ HWMON_T_LABEL | HWMON_T_INPUT;
+
+ priv->chip.ops = &dimmtemp_ops;
+ priv->chip.info = priv->info;
+
+ priv->info[0] = &priv->temp_info;
+
+ priv->temp_info.type = hwmon_temp;
+ priv->temp_info.config = priv->temp_config;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
+ priv->name,
+ priv,
+ &priv->chip,
+ NULL);
+ rc = PTR_ERR_OR_ZERO(hwmon_dev);
+ if (!rc)
+ dev_dbg(priv->dev, "%s: sensor '%s'\n",
+ dev_name(hwmon_dev), priv->name);
+
+ return rc;
+}
+
+static void create_dimm_temp_info_delayed(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct peci_dimmtemp *priv = container_of(dwork, struct peci_dimmtemp,
+ work_handler);
+ int rc;
+
+ rc = create_dimm_temp_info(priv);
+ if (rc && rc != -EAGAIN)
+ dev_dbg(priv->dev, "Failed to create DIMM temp info\n");
+}
+
+static int peci_dimmtemp_probe(struct platform_device *pdev)
+{
+ struct peci_mfd *mfd = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct peci_dimmtemp *priv;
+ int rc;
+
+ if ((mfd->adapter->cmd_mask &
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
+ (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG)))
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->mfd = mfd;
+ priv->dev = dev;
+ priv->addr = mfd->addr;
+ priv->gen_info = mfd->gen_info;
+
+ snprintf(priv->name, PECI_NAME_SIZE, "peci_dimmtemp.cpu%d",
+ priv->mfd->cpu_no);
+
+ priv->work_queue = alloc_ordered_workqueue(priv->name, 0);
+ if (!priv->work_queue)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&priv->work_handler, create_dimm_temp_info_delayed);
+
+ rc = create_dimm_temp_info(priv);
+ if (rc && rc != -EAGAIN) {
+ dev_err(dev, "Failed to create DIMM temp info\n");
+ goto err_free_wq;
+ }
+
+ return 0;
+
+err_free_wq:
+ destroy_workqueue(priv->work_queue);
+ return rc;
+}
+
+static int peci_dimmtemp_remove(struct platform_device *pdev)
+{
+ struct peci_dimmtemp *priv = dev_get_drvdata(&pdev->dev);
+
+ cancel_delayed_work_sync(&priv->work_handler);
+ destroy_workqueue(priv->work_queue);
+
+ return 0;
+}
+
+static const struct platform_device_id peci_dimmtemp_ids[] = {
+ { .name = "peci-dimmtemp", .driver_data = 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, peci_dimmtemp_ids);
+
+static struct platform_driver peci_dimmtemp_driver = {
+ .probe = peci_dimmtemp_probe,
+ .remove = peci_dimmtemp_remove,
+ .id_table = peci_dimmtemp_ids,
+ .driver = {
+ .name = "peci-dimmtemp",
+ },
+};
+module_platform_driver(peci_dimmtemp_driver);
+
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI dimmtemp driver");
+MODULE_LICENSE("GPL v2");
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v8 12/12] Add maintainers for the PECI subsystem
2018-09-18 21:51 [PATCH v8 00/12] PECI device driver introduction Jae Hyun Yoo
` (10 preceding siblings ...)
2018-09-18 21:51 ` [PATCH v8 11/12] hwmon: Add PECI dimmtemp driver Jae Hyun Yoo
@ 2018-09-18 21:51 ` Jae Hyun Yoo
11 siblings, 0 replies; 23+ messages in thread
From: Jae Hyun Yoo @ 2018-09-18 21:51 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Jean Delvare, Guenter Roeck,
Mark Rutland, Joel Stanley, Andrew Jeffery, Jonathan Corbet,
Greg Kroah-Hartman, Gustavo Pimentel, Kishon Vijay Abraham I,
Lorenzo Pieralisi, Darrick J . Wong, Eric Sandeen, Arnd Bergmann,
Wu Hao, Tomohiro Kusumi, Bryant G . Ly, Frederic Barrat,
David S . Miller, Mauro Carvalho Chehab, Andrew Morton,
Randy Dunlap, Philippe Ombredanne, Vinod Koul, Stephen Boyd,
David Kershner, Uwe Kleine-Konig, Sagar Dharia, Johan Hovold,
Thomas Gleixner, Juergen Gross, Cyrille Pitchen
Cc: linux-hwmon, devicetree, linux-kernel, linux-arm-kernel,
linux-aspeed, linux-doc, openbmc, Jae Hyun Yoo, Linus Walleij,
Jason M Biils
This commit adds maintainer information for the PECI subsystem.
Cc: David S. Miller <davem@davemloft.net>
Cc: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Jason M Biils <jason.m.bills@linux.intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
MAINTAINERS | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 4ece30f15777..b9ffc217ec68 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11361,6 +11361,19 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/peaq-wmi.c
+PECI SUBSYSTEM
+M: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+M: Jason M Biils <jason.m.bills@linux.intel.com>
+L: openbmc@lists.ozlabs.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/peci/
+F: drivers/mfd/intel-peci-client.c
+F: drivers/peci/
+F: drivers/hwmon/peci-*.c
+F: include/linux/mfd/intel-peci-client.h
+F: include/linux/peci.h
+F: include/uapi/linux/peci-ioctl.h
+
PER-CPU MEMORY ALLOCATOR
M: Dennis Zhou <dennis@kernel.org>
M: Tejun Heo <tj@kernel.org>
--
2.18.0
^ permalink raw reply related [flat|nested] 23+ messages in thread