2022-01-25 08:42:27

by Winiarska, Iwona

[permalink] [raw]
Subject: [PATCH v6 05/13] peci: Add peci-aspeed controller driver

From: Jae Hyun Yoo <[email protected]>

ASPEED AST24xx/AST25xx/AST26xx SoCs support the PECI electrical
interface (a.k.a PECI wire) that provides a communication channel with
Intel processors.
This driver allows BMC to discover devices connected to it and
communicate with them using PECI protocol.

Signed-off-by: Jae Hyun Yoo <[email protected]>
Co-developed-by: Iwona Winiarska <[email protected]>
Signed-off-by: Iwona Winiarska <[email protected]>
Reviewed-by: Pierre-Louis Bossart <[email protected]>
Reviewed-by: Joel Stanley <[email protected]>
---
MAINTAINERS | 8 +
drivers/peci/Kconfig | 6 +
drivers/peci/Makefile | 3 +
drivers/peci/controller/Kconfig | 18 +
drivers/peci/controller/Makefile | 3 +
drivers/peci/controller/peci-aspeed.c | 599 ++++++++++++++++++++++++++
6 files changed, 637 insertions(+)
create mode 100644 drivers/peci/controller/Kconfig
create mode 100644 drivers/peci/controller/Makefile
create mode 100644 drivers/peci/controller/peci-aspeed.c

diff --git a/MAINTAINERS b/MAINTAINERS
index aa7ae643fdb0..6c9b12004e4e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2985,6 +2985,14 @@ S: Maintained
F: Documentation/devicetree/bindings/net/asix,ax88796c.yaml
F: drivers/net/ethernet/asix/ax88796c_*

+ASPEED PECI CONTROLLER
+M: Iwona Winiarska <[email protected]>
+L: [email protected] (moderated for non-subscribers)
+L: [email protected] (moderated for non-subscribers)
+S: Supported
+F: Documentation/devicetree/bindings/peci/peci-aspeed.yaml
+F: drivers/peci/controller/peci-aspeed.c
+
ASPEED PINCTRL DRIVERS
M: Andrew Jeffery <[email protected]>
L: [email protected] (moderated for non-subscribers)
diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig
index 71a4ad81225a..99279df97a78 100644
--- a/drivers/peci/Kconfig
+++ b/drivers/peci/Kconfig
@@ -13,3 +13,9 @@ menuconfig PECI

This support is also available as a module. If so, the module
will be called peci.
+
+if PECI
+
+source "drivers/peci/controller/Kconfig"
+
+endif # PECI
diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile
index e789a354e842..926d8df15cbd 100644
--- a/drivers/peci/Makefile
+++ b/drivers/peci/Makefile
@@ -3,3 +3,6 @@
# Core functionality
peci-y := core.o
obj-$(CONFIG_PECI) += peci.o
+
+# Hardware specific bus drivers
+obj-y += controller/
diff --git a/drivers/peci/controller/Kconfig b/drivers/peci/controller/Kconfig
new file mode 100644
index 000000000000..32bd8d685c0d
--- /dev/null
+++ b/drivers/peci/controller/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config PECI_ASPEED
+ tristate "ASPEED PECI support"
+ depends on ARCH_ASPEED || COMPILE_TEST
+ depends on OF
+ depends on HAS_IOMEM
+ select COMMON_CLK
+ help
+ This option enables PECI controller driver for ASPEED AST2400,
+ AST2500 and AST2600 SoCs. It allows BMC to discover devices
+ connected to it, and communicate with them using PECI protocol.
+
+ Say Y here if your system runs on ASPEED SoC and you are using it
+ as BMC for Intel platform.
+
+ This driver can also be built as a module. If so, the module will
+ be called peci-aspeed.
diff --git a/drivers/peci/controller/Makefile b/drivers/peci/controller/Makefile
new file mode 100644
index 000000000000..022c28ef1bf0
--- /dev/null
+++ b/drivers/peci/controller/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o
diff --git a/drivers/peci/controller/peci-aspeed.c b/drivers/peci/controller/peci-aspeed.c
new file mode 100644
index 000000000000..1925ddc13f00
--- /dev/null
+++ b/drivers/peci/controller/peci-aspeed.c
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2012-2017 ASPEED Technology Inc.
+// Copyright (c) 2018-2021 Intel Corporation
+
+#include <asm/unaligned.h>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/jiffies.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+/* ASPEED PECI Registers */
+/* Control Register */
+#define ASPEED_PECI_CTRL 0x00
+#define ASPEED_PECI_CTRL_SAMPLING_MASK GENMASK(19, 16)
+#define ASPEED_PECI_CTRL_RD_MODE_MASK GENMASK(13, 12)
+#define ASPEED_PECI_CTRL_RD_MODE_DBG BIT(13)
+#define ASPEED_PECI_CTRL_RD_MODE_COUNT BIT(12)
+#define ASPEED_PECI_CTRL_CLK_SRC_HCLK BIT(11)
+#define ASPEED_PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8)
+#define ASPEED_PECI_CTRL_INVERT_OUT BIT(7)
+#define ASPEED_PECI_CTRL_INVERT_IN BIT(6)
+#define ASPEED_PECI_CTRL_BUS_CONTENTION_EN BIT(5)
+#define ASPEED_PECI_CTRL_PECI_EN BIT(4)
+#define ASPEED_PECI_CTRL_PECI_CLK_EN BIT(0)
+
+/* Timing Negotiation Register */
+#define ASPEED_PECI_TIMING_NEGOTIATION 0x04
+#define ASPEED_PECI_T_NEGO_MSG_MASK GENMASK(15, 8)
+#define ASPEED_PECI_T_NEGO_ADDR_MASK GENMASK(7, 0)
+
+/* Command Register */
+#define ASPEED_PECI_CMD 0x08
+#define ASPEED_PECI_CMD_PIN_MONITORING BIT(31)
+#define ASPEED_PECI_CMD_STS_MASK GENMASK(27, 24)
+#define ASPEED_PECI_CMD_STS_ADDR_T_NEGO 0x3
+#define ASPEED_PECI_CMD_IDLE_MASK \
+ (ASPEED_PECI_CMD_STS_MASK | ASPEED_PECI_CMD_PIN_MONITORING)
+#define ASPEED_PECI_CMD_FIRE BIT(0)
+
+/* Read/Write Length Register */
+#define ASPEED_PECI_RW_LENGTH 0x0c
+#define ASPEED_PECI_AW_FCS_EN BIT(31)
+#define ASPEED_PECI_RD_LEN_MASK GENMASK(23, 16)
+#define ASPEED_PECI_WR_LEN_MASK GENMASK(15, 8)
+#define ASPEED_PECI_TARGET_ADDR_MASK GENMASK(7, 0)
+
+/* Expected FCS Data Register */
+#define ASPEED_PECI_EXPECTED_FCS 0x10
+#define ASPEED_PECI_EXPECTED_RD_FCS_MASK GENMASK(23, 16)
+#define ASPEED_PECI_EXPECTED_AW_FCS_AUTO_MASK GENMASK(15, 8)
+#define ASPEED_PECI_EXPECTED_WR_FCS_MASK GENMASK(7, 0)
+
+/* Captured FCS Data Register */
+#define ASPEED_PECI_CAPTURED_FCS 0x14
+#define ASPEED_PECI_CAPTURED_RD_FCS_MASK GENMASK(23, 16)
+#define ASPEED_PECI_CAPTURED_WR_FCS_MASK GENMASK(7, 0)
+
+/* Interrupt Register */
+#define ASPEED_PECI_INT_CTRL 0x18
+#define ASPEED_PECI_TIMING_NEGO_SEL_MASK GENMASK(31, 30)
+#define ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO 0
+#define ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO 1
+#define ASPEED_PECI_MESSAGE_NEGO 2
+#define ASPEED_PECI_INT_MASK GENMASK(4, 0)
+#define ASPEED_PECI_INT_BUS_TIMEOUT BIT(4)
+#define ASPEED_PECI_INT_BUS_CONTENTION BIT(3)
+#define ASPEED_PECI_INT_WR_FCS_BAD BIT(2)
+#define ASPEED_PECI_INT_WR_FCS_ABORT BIT(1)
+#define ASPEED_PECI_INT_CMD_DONE BIT(0)
+
+/* Interrupt Status Register */
+#define ASPEED_PECI_INT_STS 0x1c
+#define ASPEED_PECI_INT_TIMING_RESULT_MASK GENMASK(29, 16)
+ /* bits[4..0]: Same bit fields in the 'Interrupt Register' */
+
+/* Rx/Tx Data Buffer Registers */
+#define ASPEED_PECI_WR_DATA0 0x20
+#define ASPEED_PECI_WR_DATA1 0x24
+#define ASPEED_PECI_WR_DATA2 0x28
+#define ASPEED_PECI_WR_DATA3 0x2c
+#define ASPEED_PECI_RD_DATA0 0x30
+#define ASPEED_PECI_RD_DATA1 0x34
+#define ASPEED_PECI_RD_DATA2 0x38
+#define ASPEED_PECI_RD_DATA3 0x3c
+#define ASPEED_PECI_WR_DATA4 0x40
+#define ASPEED_PECI_WR_DATA5 0x44
+#define ASPEED_PECI_WR_DATA6 0x48
+#define ASPEED_PECI_WR_DATA7 0x4c
+#define ASPEED_PECI_RD_DATA4 0x50
+#define ASPEED_PECI_RD_DATA5 0x54
+#define ASPEED_PECI_RD_DATA6 0x58
+#define ASPEED_PECI_RD_DATA7 0x5c
+#define ASPEED_PECI_DATA_BUF_SIZE_MAX 32
+
+/* Timing Negotiation */
+#define ASPEED_PECI_CLK_FREQUENCY_MIN 2000
+#define ASPEED_PECI_CLK_FREQUENCY_DEFAULT 1000000
+#define ASPEED_PECI_CLK_FREQUENCY_MAX 2000000
+#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT 8
+/* Timeout */
+#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_US (50 * USEC_PER_MSEC)
+#define ASPEED_PECI_IDLE_CHECK_INTERVAL_US (10 * USEC_PER_MSEC)
+#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT 1000
+#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX 1000
+
+#define ASPEED_PECI_CLK_DIV1(msg_timing) (4 * (msg_timing) + 1)
+#define ASPEED_PECI_CLK_DIV2(clk_div_exp) BIT(clk_div_exp)
+#define ASPEED_PECI_CLK_DIV(msg_timing, clk_div_exp) \
+ (4 * ASPEED_PECI_CLK_DIV1(msg_timing) * ASPEED_PECI_CLK_DIV2(clk_div_exp))
+
+struct aspeed_peci {
+ struct peci_controller *controller;
+ struct device *dev;
+ void __iomem *base;
+ struct reset_control *rst;
+ int irq;
+ spinlock_t lock; /* to sync completion status handling */
+ struct completion xfer_complete;
+ struct clk *clk;
+ u32 clk_frequency;
+ u32 status;
+ u32 cmd_timeout_ms;
+};
+
+struct clk_aspeed_peci {
+ struct clk_hw hw;
+ struct aspeed_peci *aspeed_peci;
+};
+
+static void aspeed_peci_controller_enable(struct aspeed_peci *priv)
+{
+ u32 val = readl(priv->base + ASPEED_PECI_CTRL);
+
+ val |= ASPEED_PECI_CTRL_PECI_CLK_EN;
+ val |= ASPEED_PECI_CTRL_PECI_EN;
+
+ writel(val, priv->base + ASPEED_PECI_CTRL);
+}
+
+static void aspeed_peci_init_regs(struct aspeed_peci *priv)
+{
+ u32 val;
+
+ /* Clear interrupts */
+ writel(ASPEED_PECI_INT_MASK, priv->base + ASPEED_PECI_INT_STS);
+
+ /* Set timing negotiation mode and enable interrupts */
+ val = FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK, ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO);
+ val |= ASPEED_PECI_INT_MASK;
+ writel(val, priv->base + ASPEED_PECI_INT_CTRL);
+
+ val = FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT);
+ writel(val, priv->base + ASPEED_PECI_CTRL);
+}
+
+static int aspeed_peci_check_idle(struct aspeed_peci *priv)
+{
+ u32 cmd_sts = readl(priv->base + ASPEED_PECI_CMD);
+ int ret;
+
+ /*
+ * Under normal circumstances, we expect to be idle here.
+ * In case there were any errors/timeouts that led to the situation
+ * where the hardware is not in idle state - we need to reset and
+ * reinitialize it to avoid potential controller hang.
+ */
+ if (FIELD_GET(ASPEED_PECI_CMD_STS_MASK, cmd_sts)) {
+ ret = reset_control_assert(priv->rst);
+ if (ret) {
+ dev_err(priv->dev, "cannot assert reset control\n");
+ return ret;
+ }
+
+ ret = reset_control_deassert(priv->rst);
+ if (ret) {
+ dev_err(priv->dev, "cannot deassert reset control\n");
+ return ret;
+ }
+
+ aspeed_peci_init_regs(priv);
+
+ ret = clk_set_rate(priv->clk, priv->clk_frequency);
+ if (ret < 0) {
+ dev_err(priv->dev, "cannot set clock frequency\n");
+ return ret;
+ }
+
+ aspeed_peci_controller_enable(priv);
+ }
+
+ return readl_poll_timeout(priv->base + ASPEED_PECI_CMD,
+ cmd_sts,
+ !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK),
+ ASPEED_PECI_IDLE_CHECK_INTERVAL_US,
+ ASPEED_PECI_IDLE_CHECK_TIMEOUT_US);
+}
+
+static int aspeed_peci_xfer(struct peci_controller *controller,
+ u8 addr, struct peci_request *req)
+{
+ struct aspeed_peci *priv = dev_get_drvdata(controller->dev.parent);
+ unsigned long timeout = msecs_to_jiffies(priv->cmd_timeout_ms);
+ u32 peci_head;
+ int ret, i;
+
+ if (req->tx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX ||
+ req->rx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX)
+ return -EINVAL;
+
+ /* Check command sts and bus idle state */
+ ret = aspeed_peci_check_idle(priv);
+ if (ret)
+ return ret; /* -ETIMEDOUT */
+
+ spin_lock_irq(&priv->lock);
+ reinit_completion(&priv->xfer_complete);
+
+ peci_head = FIELD_PREP(ASPEED_PECI_TARGET_ADDR_MASK, addr) |
+ FIELD_PREP(ASPEED_PECI_WR_LEN_MASK, req->tx.len) |
+ FIELD_PREP(ASPEED_PECI_RD_LEN_MASK, req->rx.len);
+
+ writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH);
+
+ for (i = 0; i < req->tx.len; i += 4) {
+ u32 reg = (i < 16 ? ASPEED_PECI_WR_DATA0 : ASPEED_PECI_WR_DATA4) + i % 16;
+
+ writel(get_unaligned_le32(&req->tx.buf[i]), priv->base + reg);
+ }
+
+#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG)
+ dev_dbg(priv->dev, "HEAD : %#08x\n", peci_head);
+ print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, req->tx.buf, req->tx.len);
+#endif
+
+ priv->status = 0;
+ writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD);
+ spin_unlock_irq(&priv->lock);
+
+ ret = wait_for_completion_interruptible_timeout(&priv->xfer_complete, timeout);
+ if (ret < 0)
+ return ret;
+
+ if (ret == 0) {
+ dev_dbg(priv->dev, "timeout waiting for a response\n");
+ return -ETIMEDOUT;
+ }
+
+ spin_lock_irq(&priv->lock);
+
+ if (priv->status != ASPEED_PECI_INT_CMD_DONE) {
+ spin_unlock_irq(&priv->lock);
+ dev_dbg(priv->dev, "no valid response, status: %#02x\n", priv->status);
+ return -EIO;
+ }
+
+ spin_unlock_irq(&priv->lock);
+
+ /*
+ * We need to use dword reads for register access, make sure that the
+ * buffer size is multiple of 4-bytes.
+ */
+ BUILD_BUG_ON(PECI_REQUEST_MAX_BUF_SIZE % 4);
+
+ for (i = 0; i < req->rx.len; i += 4) {
+ u32 reg = (i < 16 ? ASPEED_PECI_RD_DATA0 : ASPEED_PECI_RD_DATA4) + i % 16;
+ u32 rx_data = readl(priv->base + reg);
+
+ put_unaligned_le32(rx_data, &req->rx.buf[i]);
+ }
+
+#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG)
+ print_hex_dump_bytes("RX : ", DUMP_PREFIX_NONE, req->rx.buf, req->rx.len);
+#endif
+ return 0;
+}
+
+static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg)
+{
+ struct aspeed_peci *priv = arg;
+ u32 status;
+
+ spin_lock(&priv->lock);
+ status = readl(priv->base + ASPEED_PECI_INT_STS);
+ writel(status, priv->base + ASPEED_PECI_INT_STS);
+ priv->status |= (status & ASPEED_PECI_INT_MASK);
+
+ /*
+ * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit
+ * set even in an error case.
+ */
+ if (status & ASPEED_PECI_INT_CMD_DONE)
+ complete(&priv->xfer_complete);
+
+ writel(0, priv->base + ASPEED_PECI_CMD);
+
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void clk_aspeed_peci_find_div_values(unsigned long rate, int *msg_timing, int *clk_div_exp)
+{
+ unsigned long best_diff = ~0ul, diff;
+ int msg_timing_temp, clk_div_exp_temp, i, j;
+
+ for (i = 1; i <= 255; i++)
+ for (j = 0; j < 8; j++) {
+ diff = abs(rate - ASPEED_PECI_CLK_DIV1(i) * ASPEED_PECI_CLK_DIV2(j));
+ if (diff < best_diff) {
+ msg_timing_temp = i;
+ clk_div_exp_temp = j;
+ best_diff = diff;
+ }
+ }
+
+ *msg_timing = msg_timing_temp;
+ *clk_div_exp = clk_div_exp_temp;
+}
+
+static int clk_aspeed_peci_get_div(unsigned long rate, const unsigned long *prate)
+{
+ unsigned long this_rate = *prate / (4 * rate);
+ int msg_timing, clk_div_exp;
+
+ clk_aspeed_peci_find_div_values(this_rate, &msg_timing, &clk_div_exp);
+
+ return ASPEED_PECI_CLK_DIV(msg_timing, clk_div_exp);
+}
+
+static int clk_aspeed_peci_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate)
+{
+ struct clk_aspeed_peci *peci_clk = container_of(hw, struct clk_aspeed_peci, hw);
+ struct aspeed_peci *aspeed_peci = peci_clk->aspeed_peci;
+ unsigned long this_rate = prate / (4 * rate);
+ int clk_div_exp, msg_timing;
+ u32 val;
+
+ clk_aspeed_peci_find_div_values(this_rate, &msg_timing, &clk_div_exp);
+
+ val = readl(aspeed_peci->base + ASPEED_PECI_CTRL);
+ val |= FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, clk_div_exp);
+ writel(val, aspeed_peci->base + ASPEED_PECI_CTRL);
+
+ val = FIELD_PREP(ASPEED_PECI_T_NEGO_MSG_MASK, msg_timing);
+ val |= FIELD_PREP(ASPEED_PECI_T_NEGO_ADDR_MASK, msg_timing);
+ writel(val, aspeed_peci->base + ASPEED_PECI_TIMING_NEGOTIATION);
+
+ return 0;
+}
+
+static long clk_aspeed_peci_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ int div = clk_aspeed_peci_get_div(rate, prate);
+
+ return DIV_ROUND_UP_ULL(*prate, div);
+}
+
+static unsigned long clk_aspeed_peci_recalc_rate(struct clk_hw *hw, unsigned long prate)
+{
+ struct clk_aspeed_peci *peci_clk = container_of(hw, struct clk_aspeed_peci, hw);
+ struct aspeed_peci *aspeed_peci = peci_clk->aspeed_peci;
+ int div, msg_timing, addr_timing, clk_div_exp;
+ u32 reg;
+
+ reg = readl(aspeed_peci->base + ASPEED_PECI_TIMING_NEGOTIATION);
+ msg_timing = FIELD_GET(ASPEED_PECI_T_NEGO_MSG_MASK, reg);
+ addr_timing = FIELD_GET(ASPEED_PECI_T_NEGO_ADDR_MASK, reg);
+
+ if (msg_timing != addr_timing)
+ return 0;
+
+ reg = readl(aspeed_peci->base + ASPEED_PECI_CTRL);
+ clk_div_exp = FIELD_GET(ASPEED_PECI_CTRL_CLK_DIV_MASK, reg);
+
+ div = ASPEED_PECI_CLK_DIV(msg_timing, clk_div_exp);
+
+ return DIV_ROUND_UP_ULL(prate, div);
+}
+
+static const struct clk_ops clk_aspeed_peci_ops = {
+ .set_rate = clk_aspeed_peci_set_rate,
+ .round_rate = clk_aspeed_peci_round_rate,
+ .recalc_rate = clk_aspeed_peci_recalc_rate,
+};
+
+/*
+ * PECI HW contains a clock divider which is a combination of:
+ * div0: 4 (fixed divider)
+ * div1: x + 1
+ * div2: 1 << y
+ * In other words, out_clk = in_clk / (div0 * div1 * div2)
+ * The resulting frequency is used by PECI Controller to drive the PECI bus to
+ * negotiate optimal transfer rate.
+ */
+static struct clk *devm_aspeed_peci_register_clk_div(struct device *dev, struct clk *parent,
+ struct aspeed_peci *priv)
+{
+ struct clk_aspeed_peci *peci_clk;
+ struct clk_init_data init;
+ const char *parent_name;
+ char name[32];
+ int ret;
+
+ snprintf(name, sizeof(name), "%s_div", dev_name(dev));
+
+ parent_name = __clk_get_name(parent);
+
+ init.ops = &clk_aspeed_peci_ops;
+ init.name = name;
+ init.parent_names = (const char* []) { parent_name };
+ init.num_parents = 1;
+ init.flags = 0;
+
+ peci_clk = devm_kzalloc(dev, sizeof(struct clk_aspeed_peci), GFP_KERNEL);
+ if (!peci_clk)
+ return ERR_PTR(-ENOMEM);
+
+ peci_clk->hw.init = &init;
+ peci_clk->aspeed_peci = priv;
+
+ ret = devm_clk_hw_register(dev, &peci_clk->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return peci_clk->hw.clk;
+}
+
+static void aspeed_peci_property_sanitize(struct device *dev, const char *propname,
+ u32 min, u32 max, u32 default_val, u32 *propval)
+{
+ u32 val;
+ int ret;
+
+ ret = device_property_read_u32(dev, propname, &val);
+ if (ret) {
+ val = default_val;
+ } else if (val > max || val < min) {
+ dev_warn(dev, "invalid %s: %u, falling back to: %u\n",
+ propname, val, default_val);
+
+ val = default_val;
+ }
+
+ *propval = val;
+}
+
+static void aspeed_peci_property_setup(struct aspeed_peci *priv)
+{
+ aspeed_peci_property_sanitize(priv->dev, "clock-frequency",
+ ASPEED_PECI_CLK_FREQUENCY_MIN, ASPEED_PECI_CLK_FREQUENCY_MAX,
+ ASPEED_PECI_CLK_FREQUENCY_DEFAULT, &priv->clk_frequency);
+ aspeed_peci_property_sanitize(priv->dev, "cmd-timeout-ms",
+ 1, ASPEED_PECI_CMD_TIMEOUT_MS_MAX,
+ ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT, &priv->cmd_timeout_ms);
+}
+
+static struct peci_controller_ops aspeed_ops = {
+ .xfer = aspeed_peci_xfer,
+};
+
+static void aspeed_peci_reset_control_release(void *data)
+{
+ reset_control_assert(data);
+}
+
+static int devm_aspeed_peci_reset_control_deassert(struct device *dev, struct reset_control *rst)
+{
+ int ret;
+
+ ret = reset_control_deassert(rst);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, aspeed_peci_reset_control_release, rst);
+}
+
+static void aspeed_peci_clk_release(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
+static int devm_aspeed_peci_clk_enable(struct device *dev, struct clk *clk)
+{
+ int ret;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, aspeed_peci_clk_release, clk);
+}
+
+static int aspeed_peci_probe(struct platform_device *pdev)
+{
+ struct peci_controller *controller;
+ struct aspeed_peci *priv;
+ struct clk *ref_clk;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+ dev_set_drvdata(priv->dev, priv);
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (!priv->irq)
+ return priv->irq;
+
+ ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler,
+ 0, "peci-aspeed", priv);
+ if (ret)
+ return ret;
+
+ init_completion(&priv->xfer_complete);
+ spin_lock_init(&priv->lock);
+
+ priv->rst = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->rst))
+ return dev_err_probe(priv->dev, PTR_ERR(priv->rst),
+ "failed to get reset control\n");
+
+ ret = devm_aspeed_peci_reset_control_deassert(priv->dev, priv->rst);
+ if (ret)
+ return dev_err_probe(priv->dev, ret, "cannot deassert reset control\n");
+
+ aspeed_peci_property_setup(priv);
+
+ aspeed_peci_init_regs(priv);
+
+ ref_clk = devm_clk_get(priv->dev, NULL);
+ if (IS_ERR(ref_clk))
+ return dev_err_probe(priv->dev, PTR_ERR(ref_clk), "failed to get ref clock\n");
+
+ priv->clk = devm_aspeed_peci_register_clk_div(priv->dev, ref_clk, priv);
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(priv->dev, PTR_ERR(priv->clk), "cannot register clock\n");
+
+ ret = clk_set_rate(priv->clk, priv->clk_frequency);
+ if (ret < 0)
+ return dev_err_probe(priv->dev, ret, "cannot set clock frequency\n");
+
+ ret = devm_aspeed_peci_clk_enable(priv->dev, priv->clk);
+ if (ret)
+ return dev_err_probe(priv->dev, ret, "failed to enable clock\n");
+
+ aspeed_peci_controller_enable(priv);
+
+ controller = devm_peci_controller_add(priv->dev, &aspeed_ops);
+ if (IS_ERR(controller))
+ return dev_err_probe(priv->dev, PTR_ERR(controller),
+ "failed to add aspeed peci controller\n");
+
+ priv->controller = controller;
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_peci_of_table[] = {
+ { .compatible = "aspeed,ast2400-peci", },
+ { .compatible = "aspeed,ast2500-peci", },
+ { .compatible = "aspeed,ast2600-peci", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aspeed_peci_of_table);
+
+static struct platform_driver aspeed_peci_driver = {
+ .probe = aspeed_peci_probe,
+ .driver = {
+ .name = "peci-aspeed",
+ .of_match_table = aspeed_peci_of_table,
+ },
+};
+module_platform_driver(aspeed_peci_driver);
+
+MODULE_AUTHOR("Ryan Chen <[email protected]>");
+MODULE_AUTHOR("Jae Hyun Yoo <[email protected]>");
+MODULE_DESCRIPTION("ASPEED PECI driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PECI);
--
2.31.1


2022-01-25 19:46:03

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v6 05/13] peci: Add peci-aspeed controller driver

Hi Iwona,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on groeck-staging/hwmon-next]
[also build test ERROR on linux/master linus/master v5.17-rc1 next-20220125]
[cannot apply to joel-aspeed/for-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url: https://github.com/0day-ci/linux/commits/Iwona-Winiarska/Introduce-PECI-subsystem/20220125-115946
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
config: sh-allmodconfig (https://download.01.org/0day-ci/archive/20220125/[email protected]/config)
compiler: sh4-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/35075a61a26913806122a9b500915dc66ad678bd
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Iwona-Winiarska/Introduce-PECI-subsystem/20220125-115946
git checkout 35075a61a26913806122a9b500915dc66ad678bd
# save the config file to linux build tree
mkdir build_dir
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=sh SHELL=/bin/bash drivers/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>):

>> drivers/clk/clk.c:856:6: error: redefinition of 'clk_unprepare'
856 | void clk_unprepare(struct clk *clk)
| ^~~~~~~~~~~~~
In file included from drivers/clk/clk.c:9:
include/linux/clk.h:303:20: note: previous definition of 'clk_unprepare' with type 'void(struct clk *)'
303 | static inline void clk_unprepare(struct clk *clk)
| ^~~~~~~~~~~~~
>> drivers/clk/clk.c:937:5: error: redefinition of 'clk_prepare'
937 | int clk_prepare(struct clk *clk)
| ^~~~~~~~~~~
In file included from drivers/clk/clk.c:9:
include/linux/clk.h:271:19: note: previous definition of 'clk_prepare' with type 'int(struct clk *)'
271 | static inline int clk_prepare(struct clk *clk)
| ^~~~~~~~~~~
>> drivers/clk/clk.c:1183:6: error: redefinition of 'clk_is_enabled_when_prepared'
1183 | bool clk_is_enabled_when_prepared(struct clk *clk)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/clk/clk.c:9:
include/linux/clk.h:284:20: note: previous definition of 'clk_is_enabled_when_prepared' with type 'bool(struct clk *)' {aka '_Bool(struct clk *)'}
284 | static inline bool clk_is_enabled_when_prepared(struct clk *clk)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

Kconfig warnings: (for reference only)
WARNING: unmet direct dependencies detected for COMMON_CLK
Depends on !HAVE_LEGACY_CLK
Selected by
- PECI_ASPEED && PECI && (ARCH_ASPEED || COMPILE_TEST && OF && HAS_IOMEM


vim +/clk_unprepare +856 drivers/clk/clk.c

a6adc30ba7bef8 Dong Aisheng 2016-06-30 844
4dff95dc9477a3 Stephen Boyd 2015-04-30 845 /**
4dff95dc9477a3 Stephen Boyd 2015-04-30 846 * clk_unprepare - undo preparation of a clock source
4dff95dc9477a3 Stephen Boyd 2015-04-30 847 * @clk: the clk being unprepared
4dff95dc9477a3 Stephen Boyd 2015-04-30 848 *
4dff95dc9477a3 Stephen Boyd 2015-04-30 849 * clk_unprepare may sleep, which differentiates it from clk_disable. In a
4dff95dc9477a3 Stephen Boyd 2015-04-30 850 * simple case, clk_unprepare can be used instead of clk_disable to gate a clk
4dff95dc9477a3 Stephen Boyd 2015-04-30 851 * if the operation may sleep. One example is a clk which is accessed over
4dff95dc9477a3 Stephen Boyd 2015-04-30 852 * I2c. In the complex case a clk gate operation may require a fast and a slow
4dff95dc9477a3 Stephen Boyd 2015-04-30 853 * part. It is this reason that clk_unprepare and clk_disable are not mutually
4dff95dc9477a3 Stephen Boyd 2015-04-30 854 * exclusive. In fact clk_disable must be called before clk_unprepare.
4dff95dc9477a3 Stephen Boyd 2015-04-30 855 */
4dff95dc9477a3 Stephen Boyd 2015-04-30 @856 void clk_unprepare(struct clk *clk)
b2476490ef1113 Mike Turquette 2012-03-15 857 {
4dff95dc9477a3 Stephen Boyd 2015-04-30 858 if (IS_ERR_OR_NULL(clk))
4dff95dc9477a3 Stephen Boyd 2015-04-30 859 return;
b2476490ef1113 Mike Turquette 2012-03-15 860
a6adc30ba7bef8 Dong Aisheng 2016-06-30 861 clk_core_unprepare_lock(clk->core);
1e435256d625c2 Olof Johansson 2013-04-27 862 }
4dff95dc9477a3 Stephen Boyd 2015-04-30 863 EXPORT_SYMBOL_GPL(clk_unprepare);
1e435256d625c2 Olof Johansson 2013-04-27 864
4dff95dc9477a3 Stephen Boyd 2015-04-30 865 static int clk_core_prepare(struct clk_core *core)
4dff95dc9477a3 Stephen Boyd 2015-04-30 866 {
4dff95dc9477a3 Stephen Boyd 2015-04-30 867 int ret = 0;
b2476490ef1113 Mike Turquette 2012-03-15 868
a63347251907d7 Stephen Boyd 2015-05-06 869 lockdep_assert_held(&prepare_lock);
a63347251907d7 Stephen Boyd 2015-05-06 870
4dff95dc9477a3 Stephen Boyd 2015-04-30 871 if (!core)
4dff95dc9477a3 Stephen Boyd 2015-04-30 872 return 0;
b2476490ef1113 Mike Turquette 2012-03-15 873
4dff95dc9477a3 Stephen Boyd 2015-04-30 874 if (core->prepare_count == 0) {
9a34b45397e5a3 Marek Szyprowski 2017-08-21 875 ret = clk_pm_runtime_get(core);
4dff95dc9477a3 Stephen Boyd 2015-04-30 876 if (ret)
4dff95dc9477a3 Stephen Boyd 2015-04-30 877 return ret;
b2476490ef1113 Mike Turquette 2012-03-15 878
9a34b45397e5a3 Marek Szyprowski 2017-08-21 879 ret = clk_core_prepare(core->parent);
9a34b45397e5a3 Marek Szyprowski 2017-08-21 880 if (ret)
9a34b45397e5a3 Marek Szyprowski 2017-08-21 881 goto runtime_put;
9a34b45397e5a3 Marek Szyprowski 2017-08-21 882
4dff95dc9477a3 Stephen Boyd 2015-04-30 883 trace_clk_prepare(core);
1c155b3dfe0835 Ulf Hansson 2013-03-12 884
4dff95dc9477a3 Stephen Boyd 2015-04-30 885 if (core->ops->prepare)
4dff95dc9477a3 Stephen Boyd 2015-04-30 886 ret = core->ops->prepare(core->hw);
1c155b3dfe0835 Ulf Hansson 2013-03-12 887
4dff95dc9477a3 Stephen Boyd 2015-04-30 888 trace_clk_prepare_complete(core);
b2476490ef1113 Mike Turquette 2012-03-15 889
9a34b45397e5a3 Marek Szyprowski 2017-08-21 890 if (ret)
9a34b45397e5a3 Marek Szyprowski 2017-08-21 891 goto unprepare;
b2476490ef1113 Mike Turquette 2012-03-15 892 }
b2476490ef1113 Mike Turquette 2012-03-15 893
4dff95dc9477a3 Stephen Boyd 2015-04-30 894 core->prepare_count++;
b2476490ef1113 Mike Turquette 2012-03-15 895
9461f7b33d11cb Jerome Brunet 2018-06-19 896 /*
9461f7b33d11cb Jerome Brunet 2018-06-19 897 * CLK_SET_RATE_GATE is a special case of clock protection
9461f7b33d11cb Jerome Brunet 2018-06-19 898 * Instead of a consumer claiming exclusive rate control, it is
9461f7b33d11cb Jerome Brunet 2018-06-19 899 * actually the provider which prevents any consumer from making any
9461f7b33d11cb Jerome Brunet 2018-06-19 900 * operation which could result in a rate change or rate glitch while
9461f7b33d11cb Jerome Brunet 2018-06-19 901 * the clock is prepared.
9461f7b33d11cb Jerome Brunet 2018-06-19 902 */
9461f7b33d11cb Jerome Brunet 2018-06-19 903 if (core->flags & CLK_SET_RATE_GATE)
9461f7b33d11cb Jerome Brunet 2018-06-19 904 clk_core_rate_protect(core);
9461f7b33d11cb Jerome Brunet 2018-06-19 905
4dff95dc9477a3 Stephen Boyd 2015-04-30 906 return 0;
9a34b45397e5a3 Marek Szyprowski 2017-08-21 907 unprepare:
9a34b45397e5a3 Marek Szyprowski 2017-08-21 908 clk_core_unprepare(core->parent);
9a34b45397e5a3 Marek Szyprowski 2017-08-21 909 runtime_put:
9a34b45397e5a3 Marek Szyprowski 2017-08-21 910 clk_pm_runtime_put(core);
9a34b45397e5a3 Marek Szyprowski 2017-08-21 911 return ret;
b2476490ef1113 Mike Turquette 2012-03-15 912 }
b2476490ef1113 Mike Turquette 2012-03-15 913
a6adc30ba7bef8 Dong Aisheng 2016-06-30 914 static int clk_core_prepare_lock(struct clk_core *core)
a6adc30ba7bef8 Dong Aisheng 2016-06-30 915 {
a6adc30ba7bef8 Dong Aisheng 2016-06-30 916 int ret;
a6adc30ba7bef8 Dong Aisheng 2016-06-30 917
a6adc30ba7bef8 Dong Aisheng 2016-06-30 918 clk_prepare_lock();
a6adc30ba7bef8 Dong Aisheng 2016-06-30 919 ret = clk_core_prepare(core);
a6adc30ba7bef8 Dong Aisheng 2016-06-30 920 clk_prepare_unlock();
a6adc30ba7bef8 Dong Aisheng 2016-06-30 921
a6adc30ba7bef8 Dong Aisheng 2016-06-30 922 return ret;
a6adc30ba7bef8 Dong Aisheng 2016-06-30 923 }
a6adc30ba7bef8 Dong Aisheng 2016-06-30 924
4dff95dc9477a3 Stephen Boyd 2015-04-30 925 /**
4dff95dc9477a3 Stephen Boyd 2015-04-30 926 * clk_prepare - prepare a clock source
4dff95dc9477a3 Stephen Boyd 2015-04-30 927 * @clk: the clk being prepared
4dff95dc9477a3 Stephen Boyd 2015-04-30 928 *
4dff95dc9477a3 Stephen Boyd 2015-04-30 929 * clk_prepare may sleep, which differentiates it from clk_enable. In a simple
4dff95dc9477a3 Stephen Boyd 2015-04-30 930 * case, clk_prepare can be used instead of clk_enable to ungate a clk if the
4dff95dc9477a3 Stephen Boyd 2015-04-30 931 * operation may sleep. One example is a clk which is accessed over I2c. In
4dff95dc9477a3 Stephen Boyd 2015-04-30 932 * the complex case a clk ungate operation may require a fast and a slow part.
4dff95dc9477a3 Stephen Boyd 2015-04-30 933 * It is this reason that clk_prepare and clk_enable are not mutually
4dff95dc9477a3 Stephen Boyd 2015-04-30 934 * exclusive. In fact clk_prepare must be called before clk_enable.
4dff95dc9477a3 Stephen Boyd 2015-04-30 935 * Returns 0 on success, -EERROR otherwise.
4dff95dc9477a3 Stephen Boyd 2015-04-30 936 */
4dff95dc9477a3 Stephen Boyd 2015-04-30 @937 int clk_prepare(struct clk *clk)
b2476490ef1113 Mike Turquette 2012-03-15 938 {
035a61c314eb3d Tomeu Vizoso 2015-01-23 939 if (!clk)
4dff95dc9477a3 Stephen Boyd 2015-04-30 940 return 0;
035a61c314eb3d Tomeu Vizoso 2015-01-23 941
a6adc30ba7bef8 Dong Aisheng 2016-06-30 942 return clk_core_prepare_lock(clk->core);
7ef3dcc8145263 James Hogan 2013-07-29 943 }
4dff95dc9477a3 Stephen Boyd 2015-04-30 944 EXPORT_SYMBOL_GPL(clk_prepare);
035a61c314eb3d Tomeu Vizoso 2015-01-23 945

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]