From patchwork Thu Sep 12 18:24:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arturs Artamonovs via B4 Relay X-Patchwork-Id: 13802425 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 4F837EED63F for ; Thu, 12 Sep 2024 18:20:15 +0000 (UTC) Received: by smtp.kernel.org (Postfix) id 3A96AC4CEE6; Thu, 12 Sep 2024 18:20:15 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPS id 1BC91C4CEDA; Thu, 12 Sep 2024 18:20:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1726165215; bh=WvuwUE3VkVrJz1HbbjLyC5fbTxNBjf8aLWKIvGiS8ZI=; h=From:Date:Subject:References:In-Reply-To:List-Id:To:Cc:Reply-To: From; b=b+YSqrPxZMaWAWsU7bwfST26nLi93Ml85js3T7Ir1g2oMK7q33Lmw8NbN/YkR1fLv S3Ajsi1PYZH22+NgsRDq13/lFT4WEzn1cPZIF1oukpRfpwHp+uDk5HkhC4bLQ3p0tb 8aL3QRCQaZ1U30kHN2FjT7GSIZyzjSvvWSH9u+5mwpWRLEu3HWLkl4qLBNQs+hzYr0 xkOLjgIUp7P5MqZh8C2Jk+dhmHPMwaII8ibWBMwesssmZEpzs01+Tk1RGpPJHGpTtD +jErwxUBqSZVALuJlGvl/Y6xojm3IObwvs5v6HlzvDThMHw0lXZD6E41BYCKXNVyVL GVH5SLLgTFMnw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1369DEEE240; Thu, 12 Sep 2024 18:20:15 +0000 (UTC) From: Arturs Artamonovs via B4 Relay Date: Thu, 12 Sep 2024 19:24:50 +0100 Subject: [PATCH 05/21] clock:Add driver for ADI ADSP-SC5xx PLL MIME-Version: 1.0 Message-Id: <20240912-test-v1-5-458fa57c8ccf@analog.com> References: <20240912-test-v1-0-458fa57c8ccf@analog.com> In-Reply-To: <20240912-test-v1-0-458fa57c8ccf@analog.com> List-Id: To: Catalin Marinas , Will Deacon , Greg Malysa , Philipp Zabel , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Utsav Agarwal , Michael Turquette , Stephen Boyd , Linus Walleij , Bartosz Golaszewski , Thomas Gleixner , Andi Shyti , Greg Kroah-Hartman , Jiri Slaby , Arnd Bergmann , Olof Johansson , soc@kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, linux-i2c@vger.kernel.org, linux-serial@vger.kernel.org, Arturs Artamonovs , adsp-linux@analog.com, Arturs Artamonovs , Nathan Barrett-Morrison X-Mailer: b4 0.15-dev-7be4f X-Developer-Signature: v=1; a=ed25519-sha256; t=1726165513; l=7757; i=arturs.artamonovs@analog.com; s=20240909; h=from:subject:message-id; bh=kKwWyaM2Blr339gT53LjtoBsxg/6T1Q1BsxHjj63Pk0=; b=J7Br682QHi9UgPDIRSCY68wh2/Zg7TIwYyngp7ewWcshD/o13+AQ7Ebd+zKvBFcOqD1LLjSHb obWccR+jIskA5qWU4GP7vAE8q8I9icPmqNtPJy6OZA0E52BtDddhgpK X-Developer-Key: i=arturs.artamonovs@analog.com; a=ed25519; pk=UXODIid/MrmBXvqkX4PeEfetDaNAw9xKMINHIc5oZCk= X-Endpoint-Received: by B4 Relay for arturs.artamonovs@analog.com/20240909 with auth_id=206 X-Original-From: Arturs Artamonovs Reply-To: arturs.artamonovs@analog.com From: Arturs Artamonovs Implements clock tree, no dynamic pll rate change. Signed-off-by: Arturs Artamonovs Co-developed-by: Nathan Barrett-Morrison Signed-off-by: Nathan Barrett-Morrison Co-developed-by: Greg Malysa Signed-off-by: Greg Malysa --- drivers/clk/adi/clk-adi-pll.c | 151 ++++++++++++++++++++++++++++++++++++++++++ drivers/clk/adi/clk.h | 99 +++++++++++++++++++++++++++ 2 files changed, 250 insertions(+) diff --git a/drivers/clk/adi/clk-adi-pll.c b/drivers/clk/adi/clk-adi-pll.c new file mode 100644 index 0000000000000000000000000000000000000000..39fcc78b3170c0aa5962af5268f499082efb3686 --- /dev/null +++ b/drivers/clk/adi/clk-adi-pll.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CGU PLL driver for ADI SC59X processors + * + * Copyright 2022-2024 - Analog Devices Inc. + */ + +#include +#include +#include +#include + +#include "clk.h" + +struct clk_sc5xx_cgu_pll { + struct clk_hw hw; + void __iomem *base; + spinlock_t *lock; + int prepared; + u32 mask; + u32 msel; + u32 m_offset; + u8 shift; +}; + +struct clk_sc5xx_cgu_pll *to_clk_sc5xx_cgu_pll(struct clk_hw *hw) +{ + return container_of(hw, struct clk_sc5xx_cgu_pll, hw); +} + +static long sc5xx_cgu_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_sc5xx_cgu_pll *pll = to_clk_sc5xx_cgu_pll(hw); + unsigned long m, m2, new_rate, nr2, prate2; + unsigned long prate = *parent_rate; + struct clk_hw *parent_hw; + int parent_inc; + + parent_hw = clk_hw_get_parent(hw); + + m = rate / prate; + + if (m > pll->msel) { + /* cannot scale this far, need bigger input */ + parent_inc = m / pll->msel; + prate = clk_hw_round_rate(parent_hw, prate * (parent_inc + 1)); + } else if (m == 0) { + pr_err("%s: Cannot use VCO to reduce parent clock rate, requested %lu, clamping to %lu\n", + __func__, rate, prate); + return prate; + } + + new_rate = prate * m; + + if (new_rate != rate) { + /* + * Check if we could get an integer match by halving parent rate since we + * know at least about the DF bit before the VCO, although we don't know + * if we're already using it or not + */ + prate2 = clk_hw_round_rate(parent_hw, prate / 2); + m2 = rate / prate2; + nr2 = prate * m2; + if (m2 <= pll->msel && nr2 == rate) { + m = m2; + new_rate = nr2; + prate = prate2; + } + } + + *parent_rate = prate; + return new_rate; +} + +static unsigned long sc5xx_cgu_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_sc5xx_cgu_pll *pll = to_clk_sc5xx_cgu_pll(hw); + u32 reg = readl(pll->base); + u32 m = ((reg & pll->mask) >> pll->shift) + pll->m_offset; + + if (m == 0) + m = pll->msel; + + return parent_rate * m; + +} + +static int sc5xx_cgu_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_sc5xx_cgu_pll *pll = to_clk_sc5xx_cgu_pll(hw); + u32 m; + + m = (rate / parent_rate) - pll->m_offset; + + if (m >= pll->msel) + m = 0; + + /* reminder for implementation: lock around read/modify to control reg */ + pr_err("%s: set_rate not permitted yet, but we would write %d to m\n", __func__, + m); + return -ENOENT; +} + +static const struct clk_ops clk_sc5xx_cgu_pll_ops = { + .recalc_rate = sc5xx_cgu_pll_recalc_rate, + .round_rate = sc5xx_cgu_pll_round_rate, + .set_rate = sc5xx_cgu_pll_set_rate, +}; + +struct clk *sc5xx_cgu_pll(const char *name, const char *parent_name, + void __iomem *base, u8 shift, u8 width, u32 m_offset, + spinlock_t *lock) +{ + struct clk_sc5xx_cgu_pll *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = &parent_name; + init.num_parents = 1; + init.ops = &clk_sc5xx_cgu_pll_ops; + + pll->base = base; + pll->hw.init = &init; + pll->lock = lock; + pll->shift = shift; + pll->mask = GENMASK(width-1, 0) << shift; + pll->msel = pll->mask + 1; + pll->m_offset = m_offset; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: Failed to register, code %lu\n", __func__, + PTR_ERR(clk)); + } + + return clk; +} + +MODULE_DESCRIPTION("Analog Devices CLock PLL driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Greg Malysa "); + diff --git a/drivers/clk/adi/clk.h b/drivers/clk/adi/clk.h new file mode 100644 index 0000000000000000000000000000000000000000..e17aa719c2170149a6a1a60dd4390a29f06e7296 --- /dev/null +++ b/drivers/clk/adi/clk.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Clock support for ADI processors + * + * Copyright 2022-2024 - Analog Devices Inc. + */ + +#ifndef CLK_ADI_CLK_H +#define CLK_ADI_CLK_H + +#include + +#define CGU_CTL 0x00 +#define CGU_PLLCTL 0x04 +#define CGU_STAT 0x08 +#define CGU_DIV 0x0C +#define CGU_CLKOUTSEL 0x10 +#define CGU_OSCWDCTL 0x14 +#define CGU_TSCTL 0x18 +#define CGU_TSVALUE0 0x1C +#define CGU_TSVALUE1 0x20 +#define CGU_TSCOUNT0 0x24 +#define CGU_TSCOUNT1 0x28 +#define CGU_CCBF_DIS 0x2C +#define CGU_CCBF_STAT 0x30 +#define CGU_SCBF_DIS 0x38 +#define CGU_SCBF_STAT 0x3C +#define CGU_DIVEX 0x40 +#define CGU_REVID 0x48 + +#define CDU_CFG0 0x00 +#define CDU_CFG1 0x04 +#define CDU_CFG2 0x08 +#define CDU_CFG3 0x0C +#define CDU_CFG4 0x10 +#define CDU_CFG5 0x14 +#define CDU_CFG6 0x18 +#define CDU_CFG7 0x1C +#define CDU_CFG8 0x20 +#define CDU_CFG9 0x24 +#define CDU_CFG10 0x28 +#define CDU_CFG11 0x2C +#define CDU_CFG12 0x30 +#define CDU_CFG13 0x34 +#define CDU_CFG14 0x38 + +#define PLL3_OFFSET 0x2c + +#define CDU_CLKINSEL 0x44 + +#define CGU_MSEL_SHIFT 8 +#define CGU_MSEL_WIDTH 7 + +#define PLL3_MSEL_SHIFT 4 +#define PLL3_MSEL_WIDTH 7 + +#define CDU_MUX_SIZE 4 +#define CDU_MUX_SHIFT 1 +#define CDU_MUX_WIDTH 2 +#define CDU_EN_BIT 0 + +struct clk_sc5xx_cgu_pll *to_clk_sc5xx_cgu_pll(struct clk_hw *hw); + +struct clk *sc5xx_cgu_pll(const char *name, const char *parent_name, + void __iomem *base, u8 shift, u8 width, u32 m_offset, spinlock_t *lock); + +/** + * All CDU clock muxes are the same size + */ +static inline struct clk *cdu_mux(const char *name, void __iomem *reg, + const char * const *parents, spinlock_t *cdu_lock) +{ + return clk_register_mux(NULL, name, parents, CDU_MUX_SIZE, + CLK_SET_RATE_PARENT, reg, CDU_MUX_SHIFT, CDU_MUX_WIDTH, 0, + cdu_lock); +} + +static inline struct clk *cgu_divider(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 width, u8 extra_flags, spinlock_t *cdu_lock) +{ + return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT, + reg, shift, width, CLK_DIVIDER_MAX_AT_ZERO | extra_flags, cdu_lock); +} + +static inline struct clk *cdu_gate(const char *name, const char *parent, + void __iomem *reg, u32 flags, spinlock_t *cdu_lock) +{ + return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT | flags, + reg, CDU_EN_BIT, 0, cdu_lock); +} + +static inline struct clk *cgu_gate(const char *name, const char *parent, + void __iomem *reg, u8 bit, spinlock_t *cdu_lock) +{ + return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg, bit, + CLK_GATE_SET_TO_DISABLE, cdu_lock); +} + +#endif