From nobody Fri Sep 25 18:21:33 2026 Received: from smtpbguseast1.qq.com (smtpbguseast1.qq.com [54.204.34.129]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DF164579812 for ; Wed, 9 Sep 2026 14:07:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.204.34.129 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788962847; cv=none; b=Gswe7o88dzRNgPOfMsGcA5ypb1BiacwpSnnWB9+6WImSw/Vp+vd/cFYZoLRLQXyNzszv0Db37lRQwy82PMm5VlPolwzlR387LXxl17QWXCUzuVkc0Hh9uGUNASh7jjsoINhmRLwurHLzTn/MOw9Yv6SzMJPHu33G4jq6dmCJq40= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788962847; c=relaxed/simple; bh=w0QtlbTKpWa2vOrkQ4ReD72OhJcbUe7SlfJVrcZKo+s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=r3YvWbBt9TkN5pwjDepYdFbVe43QMw6ie7YAg4GZE4arJJwouWJFE54HKw5V8Q38d6+LVrEkVKlYVFYTP6xn7PTMVp6VhHeRWYANn9A9oZfahHt1SkwomNykITnMpODSNV9CzOKM98Z4VAbaeqoUf8mZe8JXjMBtemfyc7KPx48= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=OGdmQCb8; arc=none smtp.client-ip=54.204.34.129 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="OGdmQCb8" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1788962837; bh=2lqK4nkUEBzZtoYDgRY3977IcZ7JI7mh3h2ZTDbGxbQ=; h=From:Date:Subject:MIME-Version:Message-Id:To; b=OGdmQCb8Zcq+gPs5q1P2UyWuBjPsySQOBkpfSMag2CUQH579gd3G494Nv2K5j0w38 t/ir0yhZqkp9snTfSyuRiAbZliEMCZaHj2oRJJJNYlfTyrnuC5Z20dmfZqQPnjOa4a t73KuOnQwTDxUqO12K8qMxJ8aHW8WMB+dE1AYhCo= X-QQ-mid: zesmtpsz3t1788962836tdc282f0c X-QQ-Originating-IP: oMXzk2MIuN6L9wUzy4Z4OAOTi7U5ntfMro+/NaRnODc= Received: from = ( [120.237.158.181]) by bizesmtp.qq.com (ESMTP) with id ; Wed, 09 Sep 2026 22:07:13 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 17223578547801668794 EX-QQ-RecipientCnt: 12 From: Troy Mitchell Date: Wed, 09 Sep 2026 22:07:01 +0800 Subject: [PATCH 1/5] clk: spacemit: derive PLL rates from hardware Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-spacemit-pll-init-v1-1-b3065ad5a4ac@linux.spacemit.com> References: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com> In-Reply-To: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com> To: Stephen Boyd , Brian Masney , Jerome Brunet , Yixun Lan , Alex Elder , Inochi Amaoto , Haylen Chu Cc: linux-clk@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, Troy Mitchell X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=5466; i=troy.mitchell@linux.spacemit.com; h=from:subject:message-id; bh=w0QtlbTKpWa2vOrkQ4ReD72OhJcbUe7SlfJVrcZKo+s=; b=owGbwMvMwCU2g/N9w09jE33G02pJDFkLM9jb9i5ds+lqqIZS5O3Qyl6rmKSj7yK4D6TMzZpy8 hHbkyU6HaUsDGJcDLJiiizdD3i2FfhE2RYIFPrCzGFlAhnCwMUpABOZ9YfhD+fDl3Gr2qVvXf5o zLfhBmu7xpv49Wd+VW7QUFb+8+Go61GG/8GyZsKN3rH/3Pb8jvF4ejpjT6RFpJTfg5ym7z8116g e5gAA X-Developer-Key: i=troy.mitchell@linux.spacemit.com; a=openpgp; fpr=3FE5535CF1B0E658E57DB59BAE1C2FBEA7DB42E1 X-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz3a-0 X-QQ-XMAILINFO: MxX5kg4KifQ2NBTFuR5jNMcWHtyiaAkKng7v1d1+09X1ncQjgQcI5qHn 25BECUDrZ18OkfwW/7Q6jVAhOmKfQynQhFmCSDRB/oI2z2c65RsNw62CG5f2h8yYfzQhN2J 4DudHEuqhO5FTvUHrClFdWMf7tRskfBzlPN96G0yn7GQGujwBvu0hp7KmiBiBwmbKJdIqu9 W7V7e3FlP5xAF/D8ibM0V17QfRmORzsgadwiBUIS4gwYgrsQkB0bc0YqPo5TxGMmSkEciXI FLzNgt9R/cMvriILcHezNQbuO5rHXs7QYOAAZ6g6K7vyeSvg3eAPS6yh0rDa3T5X1YRE7+k 3kTw9jfywgdtBazEQhJyi3zdehvtEO/x47ZB5N9XcVtCkj8oPHcbt85pqqY7DgsZh2Iu4tT pf74XLpE3OquX+pkeVQXTJH7LtvPe52pUaUtzntotkaiFDm0g9AjZw95grJR60P1WgBrAVr nOPJT9pODFzpRrkwJMJuqk2/Iip09MTalStPpe9O8jJN/6rjW+tcAVgTlzGUc3XoXjVwhIh tC7P7k0kB2Wgi/ZuxxxV9wzvcHbtZaMD6Wv59Lza3txyu/2FAxs2HFJRCbIDbefI7p86eD8 UmhQWeWmDtQL4QfIUGAvRO7KYJ5hZtdlLEOh4Sx50B7aobxDERJjp1IOrCzADveMAdz8rXI 4j59yvn2gcsWu5CzR4p0Dq4bSgJxoHT+KgnQXD5YoQoBMw4PFYRhN/AR07oNFlmDLbAqS/G XNTPN211koyLL5PgYR8eoHgm/cYLZ7+hQ+LVz5gEuHIFe6bqtG4HOFMbZsO/kOCdcFxOVKx J2Gte4FPzC7SCuclbWmZg1iY96QS9lO98Kg8APe0DsnsIXmkCsgR6J/X0K+31GOV4vqwVHO 6TzxueFyRkfMZZrwGxek44b1fLr2FMhKQnGdFwcdz+BOS7NW3OGxy37ULfmdldq0ALMFqaL 1gecikzc+CajRgVpH/sPtWl4u2wsK9vDwn2GxGRXOjVLGmVEqKjPZCBZkNAe4BY/5jJ52hj NSmjuKVO+YcxMt8yIQdr0syH+u5yxRSZQAuCUELOGKDLB90EkJ/3NU+iNkm4pAZqHcKHajr SPD13hR5FiEVg6ZtlNy0NjkgVlDLr/bZ+XbO/2QmaR83ljBHLqe22UN6wNMiM1o4omXru9w EK75Xa6RGTTB55nttetTRviIxWSL/BRAi7gMXMadPjW13yM= X-QQ-XMRINFO: Mp0Kj//9VHAxzExpfF+O8yhSrljjwrznVg== X-QQ-RECHKSPAM: 0 Firmware can program valid PLL settings that have no exact register match in the rate table, even when they produce a listed frequency. An exact table lookup in recalc_rate() therefore returns zero instead of the hardware rate. Replace the register-table lookup with calculations from the K1 PLL and K3 PLLA register fields, accounting for signed and unsigned fractional feedback respectively. Return zero for unsupported modes or register read failures. Rate selection and programming remain table-based. Signed-off-by: Troy Mitchell --- drivers/clk/spacemit/ccu_pll.c | 100 +++++++++++++++++++++++++++++++++++--= ---- 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/drivers/clk/spacemit/ccu_pll.c b/drivers/clk/spacemit/ccu_pll.c index d4066a0ed4526..c9852fed31017 100644 --- a/drivers/clk/spacemit/ccu_pll.c +++ b/drivers/clk/spacemit/ccu_pll.c @@ -4,8 +4,10 @@ * Copyright (c) 2024-2025 Haylen Chu */ =20 +#include #include #include +#include #include =20 #include "ccu_common.h" @@ -14,12 +16,24 @@ #define PLL_TIMEOUT_US 3000 #define PLL_DELAY_US 5 =20 +#define PLL_SWCR1_PREDIV GENMASK(13, 12) +#define PLL_SWCR1_INTERNAL BIT(29) +#define PLL_SWCR3_INT GENMASK(30, 24) +#define PLL_SWCR3_FRAC GENMASK(23, 0) + #define PLL_SWCR3_EN ((u32)BIT(31)) #define PLL_SWCR3_MASK GENMASK(30, 0) =20 #define PLLA_SWCR2_EN ((u32)BIT(16)) #define PLLA_SWCR2_MASK GENMASK(15, 8) =20 +#define PLLA_SWCR1_USER_MODE BIT(25) +#define PLLA_SWCR1_INT GENMASK(22, 16) +#define PLLA_SWCR1_REFSEL GENMASK(15, 14) +#define PLLA_SWCR1_FRAC GENMASK(13, 0) +#define PLLA_SWCR3_PREDIV GENMASK(21, 20) +#define PLL_FRAC_BITS 22 + static const struct ccu_pll_rate_tbl *ccu_pll_lookup_best_rate(struct ccu_= pll *pll, unsigned long rate) { @@ -115,17 +129,78 @@ static int ccu_pll_set_rate(struct clk_hw *hw, unsign= ed long rate, return 0; } =20 -static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) +static int ccu_pll_get_params(struct ccu_pll *pll, + struct ccu_pll_rate_tbl *params, bool plla) { - struct ccu_pll *pll =3D hw_to_ccu_pll(hw); - const struct ccu_pll_rate_tbl *entry; + struct ccu_common *common =3D &pll->common; + int ret; + + ret =3D regmap_read(common->regmap, common->reg_swcr1, ¶ms->swcr1); + if (ret) + return ret; + params->swcr2 =3D 0; + if (plla) { + ret =3D regmap_read(common->regmap, common->reg_swcr2, ¶ms->swcr2); + if (ret) + return ret; + } + return regmap_read(common->regmap, common->reg_swcr3, ¶ms->swcr3); +} + +static unsigned long ccu_pll_calc_rate(const struct ccu_pll_rate_tbl *para= ms, + unsigned long parent_rate) +{ + u32 swcr1 =3D params->swcr1, swcr3 =3D params->swcr3, prediv; + s64 divider; + u64 rate; + + /* The programmed divider is not used in internal configuration mode. */ + if (swcr1 & PLL_SWCR1_INTERNAL) + return 0; + + prediv =3D FIELD_GET(PLL_SWCR1_PREDIV, swcr1) + 1; + divider =3D (s64)FIELD_GET(PLL_SWCR3_INT, swcr3) << PLL_FRAC_BITS; + /* The 24-bit fractional code is signed, with an LSB of 2^-22. */ + divider +=3D sign_extend32(FIELD_GET(PLL_SWCR3_FRAC, swcr3), 23); + if (divider <=3D 0) + return 0; + + /* Fvco =3D Fref * Npre * (Nint + Nfrac). */ + rate =3D (u64)parent_rate * prediv * divider; + return DIV_ROUND_CLOSEST_ULL(rate, BIT_ULL(PLL_FRAC_BITS)); +} + +static unsigned long ccu_plla_calc_rate(const struct ccu_pll_rate_tbl *par= ams, + unsigned long parent_rate) +{ + u32 swcr1 =3D params->swcr1, swcr2 =3D params->swcr2; + u32 swcr3 =3D params->swcr3, prediv, frac; + u64 divider, rate; + + /* Decode the software-controlled mode described by the PLL calculator. */ + if (!(swcr1 & PLLA_SWCR1_USER_MODE) || + (swcr1 & PLLA_SWCR1_REFSEL)) + return 0; + + prediv =3D FIELD_GET(PLLA_SWCR3_PREDIV, swcr3) + 1; + frac =3D FIELD_GET(PLLA_SWCR1_FRAC, swcr1) << 8; + frac |=3D FIELD_GET(PLLA_SWCR2_MASK, swcr2); + divider =3D (u64)FIELD_GET(PLLA_SWCR1_INT, swcr1) << PLL_FRAC_BITS; + divider +=3D frac; =20 - entry =3D ccu_pll_lookup_matched_entry(pll); + /* Fvco =3D Fref * Npre * (Nint + Nfrac), with an unsigned fraction. */ + rate =3D (u64)parent_rate * prediv * divider; + return DIV_ROUND_CLOSEST_ULL(rate, BIT_ULL(PLL_FRAC_BITS)); +} =20 - WARN_ON_ONCE(!entry); +static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ccu_pll_rate_tbl params; =20 - return entry ? entry->rate : 0; + if (ccu_pll_get_params(hw_to_ccu_pll(hw), ¶ms, false)) + return 0; + return ccu_pll_calc_rate(¶ms, parent_rate); } =20 static int ccu_pll_determine_rate(struct clk_hw *hw, @@ -232,14 +307,11 @@ static int ccu_plla_set_rate(struct clk_hw *hw, unsig= ned long rate, static unsigned long ccu_plla_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct ccu_pll *pll =3D hw_to_ccu_pll(hw); - const struct ccu_pll_rate_tbl *entry; - - entry =3D ccu_plla_lookup_matched_entry(pll); + struct ccu_pll_rate_tbl params; =20 - WARN_ON_ONCE(!entry); - - return entry ? entry->rate : 0; + if (ccu_pll_get_params(hw_to_ccu_pll(hw), ¶ms, true)) + return 0; + return ccu_plla_calc_rate(¶ms, parent_rate); } =20 static int ccu_plla_init(struct clk_hw *hw) --=20 2.55.0 From nobody Fri Sep 25 18:21:33 2026 Received: from smtpbguseast3.qq.com (smtpbguseast3.qq.com [54.243.244.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0DB3557C73A for ; Wed, 9 Sep 2026 14:07:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.243.244.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788962849; cv=none; b=L8H+7e0kekQfuZdjgqeULf1jHjXBg3SI72kw9LmeHRuF4tv/E1f0KYotBSqUo4gkRxVPiklygHICzd7bVYwh9OJsJy+RLmrZtDQA0zmkTvbEojBEtTB3svFy+dhMDe1tjOMsnD8+Bj7D/hIrAcpAWiEj9pYbFLiGgPR2yxtz5YQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788962849; c=relaxed/simple; bh=vxttLKkfUwd27JQRHStfdT7Y0lW42eEikPZgsnpxuJo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WGoyTpgUtyRGIWDrm2BdptBPtl6RGwh6DXmiWffKD2tnqSxbfvBNqZrJB30ewHmj5X+/6oUWOU5NBffGS5gPOOKf/vI41rrrf/JVc7hPsiY7w3J9OZqRuJ/n6gCyS9lz3s1eaFZo+XWtd19sgKVjGDPksKlgSzQdcGX6bYvfKsg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=E8rASIgU; arc=none smtp.client-ip=54.243.244.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="E8rASIgU" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1788962841; bh=nKAq6RetJp6ZFR2CRDPWMZ27CnaeKTaTs4jXaZMXCHY=; h=From:Date:Subject:MIME-Version:Message-Id:To; b=E8rASIgU50HOpuIwBjkaKV351rZOPPo2u4zDPt32mPksLIeiu8rWN+uDIOIC8MXfD zjjvP36dRU06hNp2u1pu2QPrg2LqDRWP5Eib8JyqNIbPLaE5C/G8Ge5taEIgiuhzNP Nraa8vlq31y85O7JZNMoCqW5/cFurxag92LoJYsU= X-QQ-mid: esmtpsz11t1788962840tc0335ed2 X-QQ-Originating-IP: AgpmG5w8G+w7oi58LYPI+uQCHoCX373EEv/NMTsxbek= Received: from = ( [120.237.158.181]) by bizesmtp.qq.com (ESMTP) with id ; Wed, 09 Sep 2026 22:07:17 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 7195780411193005217 EX-QQ-RecipientCnt: 12 From: Troy Mitchell Date: Wed, 09 Sep 2026 22:07:02 +0800 Subject: [PATCH 2/5] clk: spacemit: make MIX rate selection consistent Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-spacemit-pll-init-v1-2-b3065ad5a4ac@linux.spacemit.com> References: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com> In-Reply-To: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com> To: Stephen Boyd , Brian Masney , Jerome Brunet , Yixun Lan , Alex Elder , Inochi Amaoto , Haylen Chu Cc: linux-clk@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, Troy Mitchell X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=3117; i=troy.mitchell@linux.spacemit.com; h=from:subject:message-id; bh=vxttLKkfUwd27JQRHStfdT7Y0lW42eEikPZgsnpxuJo=; b=owGbwMvMwCU2g/N9w09jE33G02pJDFkLMzjinlz8Xrvua+CsDF8X54L1XMcWLS51+5MvauSbY rG4wqSgo5SFQYyLQVZMkaX7Ac+2Ap8o2wKBQl+YOaxMIEMYuDgFYCLWFxj+Z1xWOjtp++QX31J6 vzTOXmu35MP3jIwZ/C+MFW6JXS4SbWT4K5FqvJp15uINP6pven1Pcj60OWIPQ2bDsxULfu6dem6 FLxcA X-Developer-Key: i=troy.mitchell@linux.spacemit.com; a=openpgp; fpr=3FE5535CF1B0E658E57DB59BAE1C2FBEA7DB42E1 X-QQ-SENDSIZE: 520 Feedback-ID: esmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz3a-0 X-QQ-XMAILINFO: OXnZbW5+Xs4aTlR5s/s3h18p6Fz1lbpFssNrg9cBa7S79elqE7pOhzE0 yLdSiY5bHOOaKa1iaDZq9+61jdPUOqXC4I57BgQDLc78JwINh9bhhXzL1ro9L3bHao0+37b jhYLEAWmQ1DAlHytV7PphRkoi7dWxe/86LXU2ZDKNEjtLXYNyjJAQnKZ0UDIiZCxjZxmp2x ksJRLmmuep1yjjfpKwLmOC9vyL2QXRRA/W0QekJr94Lo223PrjAoG24ptScnizUjn71Qwe+ 20coQrCQSvxFuML1sFvM498cr3nEU67ZU65bsn7DytKR84fOLd7RJXAxfqpJWghoNBuFm2z tb9dXBmo8P29ev5RHlWTKOwonvUJx2oMtrgf+6vm/xG7FIes1R8km30EG0k70BEEF/p1Z70 aZ/MOxd4dDXkzGOOgahuUxYoQX+PeNzbienUpWO/IFFL0qIDsmg0gQpUx3C1QM+V8/OOnpd SoGYHISbjlmDRvHMmH0MZOLue6jP+RUnsxlagrSHTqTiB6ZZFIlt3Jk7H7hHCObmR0NtcZl P0sXeNIvtJQ5sxbX2GxMWJvMUfoNKbJLfJdmJlvJExyY2tEp7lqm+xzePNGl+xfK1Xqpms8 GtcVfZ7x5Y90lpO+BvpFzO2uY5GwIqkmirelyAlFpu03N4fCocRuQE+y60TJorgykBtHs/B VnGowOMKCNByz2KTeo67OPhovUwOiwYGl2VSG5CrfM3HRqUlZN+Ex5UxEoWDNHE5J21zI4J jxA+a+g5R68Btc8yit1X7eP8XdnUc6HcZ2VfXdEQthihGm4spYud3bqb3OZDCLgmTLQSOl/ Yd7cEg04P5Qh2oxxH3RmF5LdQC96eBlexw+5wAyx/kcDcffVG1AV2PJQeBI97I0FN/FzKny Af9zGm/abmtA4RqesGNGrsGKxDoj+ShNEo0sMZYO7Ywi6zasJCCAoPfpagINBRbFkP5GjKi ZnN2jXzIQMSf9xDdlo8tUFzspiX/o6id8Xc7EbgVUARWlDGTrkwBKwa2cGCjXhA9HeNdKhk IFc8UA9kf8e+8WgLByDHjQ0vRVvs1asyVQp5QrN/l+Lz4AoVJa+4njXlQbqvcim+WZFUW0J GUTAqok8w9l9lcuM3qcd3tHTJYb1mcTkTMzTQhLXDKN4v8apsiHo+JlcNBies2m19vJ5Juv 1jmdZoaLMmX1KMY= X-QQ-XMRINFO: NS+P29fieYNwqS3WCnRCOn9D1NpZuCnCRA== X-QQ-RECHKSPAM: 0 CCF passes the selected parent's rate to set_rate(). Searching other parents at that point can produce a divider for a different source, making the programmed rate disagree with CCF's selection. Restrict divider selection to the supplied parent rate and use the same rounding as divider_recalc_rate(). Track the best error separately so low-rate requests do not leave the initial zero-Hz candidate selected. Skip zero-rate parents and have determine_rate() reject requests when no usable parent exists. Fixes: 1b72c59db0ad ("clk: spacemit: Add clock support for SpacemiT K1 SoC") Signed-off-by: Troy Mitchell --- drivers/clk/spacemit/ccu_mix.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c index a8b407049bf4d..da3c5685d4f65 100644 --- a/drivers/clk/spacemit/ccu_mix.c +++ b/drivers/clk/spacemit/ccu_mix.c @@ -107,22 +107,27 @@ ccu_mix_calc_best_rate(struct clk_hw *hw, unsigned lo= ng rate, struct ccu_mix *mix =3D hw_to_ccu_mix(hw); unsigned int parent_num =3D clk_hw_get_num_parents(hw); struct ccu_div_config *div =3D &mix->div; - u32 div_max =3D 1 << div->width; unsigned long best_rate =3D 0; + unsigned long best_delta =3D ULONG_MAX; =20 for (int i =3D 0; i < parent_num; i++) { struct clk_hw *parent =3D clk_hw_get_parent_by_index(hw, i); unsigned long parent_rate; + u32 div_max =3D 1 << div->width; =20 if (!parent) continue; =20 parent_rate =3D clk_hw_get_rate(parent); + if (!parent_rate) + continue; =20 for (int j =3D 1; j <=3D div_max; j++) { - unsigned long tmp =3D DIV_ROUND_CLOSEST_ULL(parent_rate, j); + unsigned long tmp =3D DIV_ROUND_UP_ULL(parent_rate, j); + unsigned long delta =3D abs_diff(tmp, rate); =20 - if (abs(tmp - rate) < abs(best_rate - rate)) { + if (delta < best_delta) { + best_delta =3D delta; best_rate =3D tmp; =20 if (div_val) @@ -146,7 +151,7 @@ static int ccu_mix_determine_rate(struct clk_hw *hw, &req->best_parent_hw, &req->best_parent_rate, NULL); - return 0; + return req->rate ? 0 : -EINVAL; } =20 static int ccu_mix_set_rate(struct clk_hw *hw, unsigned long rate, @@ -155,9 +160,19 @@ static int ccu_mix_set_rate(struct clk_hw *hw, unsigne= d long rate, struct ccu_mix *mix =3D hw_to_ccu_mix(hw); struct ccu_common *common =3D &mix->common; struct ccu_div_config *div =3D &mix->div; - u32 current_div, target_div, mask; + u32 current_div, target_div =3D 0, mask; + unsigned long best_delta =3D ULONG_MAX; + + /* set_rate must use the parent selected by CCF, not search other parents= . */ + for (u32 i =3D 1; i <=3D BIT(div->width); i++) { + unsigned long divided =3D DIV_ROUND_UP_ULL(parent_rate, i); + unsigned long delta =3D abs_diff(divided, rate); =20 - ccu_mix_calc_best_rate(hw, rate, NULL, NULL, &target_div); + if (delta < best_delta) { + best_delta =3D delta; + target_div =3D i - 1; + } + } =20 current_div =3D ccu_read(common, ctrl) >> div->shift; current_div &=3D (1 << div->width) - 1; --=20 2.55.0 From nobody Fri Sep 25 18:21:33 2026 Received: from smtpbgeu1.qq.com (smtpbgeu1.qq.com [52.59.177.22]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C3EBC56C621 for ; Wed, 9 Sep 2026 14:07:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=52.59.177.22 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788962857; cv=none; b=D8rgJPFzE7ZiKNEovLC0n/n1KH+qTtAGG29JvtEDffq+qqGHcuNLbcprEETOU5sYc8rd5hHMm20p5BT4aed1xdM0P6OTyjKA3BG+BFlOSff7Io/f5d6KaIk0cuPJrDAniSYWJ3rSCEvG+c0uTyOlGkB6DBz3+dXRx64Y+3zDbaU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788962857; c=relaxed/simple; bh=kc1jhhPV4B4H0LLHv+h5hMvCUxZcLg+g4PDrVyRqrPI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VclLynJBsAdcVtruVkB+xIkvo0AJNBBC5pIPv2t9MjNAvxC7eAB1idHpJm2PIq9Ur83sONeEkni7+yHDa/iUuSSsLbBqqa1ChW5rlZmLTWJzrT5NoarDcU4wVv754JqSRaF2askvVTechzO7xQiruojUfm2lIH4Z0isS2DO9rSQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=g8KHUdjV; arc=none smtp.client-ip=52.59.177.22 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="g8KHUdjV" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1788962846; bh=4KXL1mV8nBn/Eb2PorgwoF3U40pxXFOZNDMCd5izesM=; h=From:Date:Subject:MIME-Version:Message-Id:To; b=g8KHUdjVDuOfUxOBOXErU3yyJIIxucq3G+gj6W4UbfBM9BX/QMUjwExtaM6jdvQyI qpOW8AgtcVHgQWTpJxQ3p4/9bOHXAIOsk/mT+4Y75BAjjig+DY6KreTfpbsHh2AIIc eBvkyAotvuvCCysO8v1zqAJZ8ZHpjp75J+cG3Y+Y= X-QQ-mid: esmtpsz10t1788962845tfb88b909 X-QQ-Originating-IP: P2oj2hlnzIUEVvUpdJUJoTz8U+V2gQJgQslq5KARWGA= Received: from = ( [120.237.158.181]) by bizesmtp.qq.com (ESMTP) with id ; Wed, 09 Sep 2026 22:07:22 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 4345606267829825780 EX-QQ-RecipientCnt: 12 From: Troy Mitchell Date: Wed, 09 Sep 2026 22:07:03 +0800 Subject: [PATCH 3/5] clk: spacemit: describe CPU clock dividers and shared PLL muxes Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-spacemit-pll-init-v1-3-b3065ad5a4ac@linux.spacemit.com> References: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com> In-Reply-To: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com> To: Stephen Boyd , Brian Masney , Jerome Brunet , Yixun Lan , Alex Elder , Inochi Amaoto , Haylen Chu Cc: linux-clk@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, Troy Mitchell X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=10418; i=troy.mitchell@linux.spacemit.com; h=from:subject:message-id; bh=kc1jhhPV4B4H0LLHv+h5hMvCUxZcLg+g4PDrVyRqrPI=; b=owGbwMvMwCU2g/N9w09jE33G02pJDFkLMzg95s0qfLmwLkm4eK1vUjPrJUnHXq85V92ruNylD +85VmjUUcrCIMbFICumyNL9gGdbgU+UbYFAoS/MHFYmkCEMXJwCMBH9uQz/Hc9eOpLNHCXAEfzk THHtpIdFSSErNzwrXugcr+qv/Pg9DyPD1mTXwgimF5VZe5N7F/jf3r9G48YSBva6WwoBZ0Kk1p5 iAQA= X-Developer-Key: i=troy.mitchell@linux.spacemit.com; a=openpgp; fpr=3FE5535CF1B0E658E57DB59BAE1C2FBEA7DB42E1 X-QQ-SENDSIZE: 520 Feedback-ID: esmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz3a-0 X-QQ-XMAILINFO: MeU9MjzNc2/gwmOTqdCadX1fbWLFtaUQqPQHQDZ0DjWMjfggFFOsBkxy kY4Xjq8tYGa+egovgRyvIjaonYCQq0XQShg67W/qK+Ya3cINEpyPnYpXJlOrJ1kC7BpBK3H nkc+6JkAjpuOGua25Uo0vwnmljNp0EsqOypdYDrQ4dMeVZJhRm9ecq8BQoejJXsinNkvU8r U/2WiRgY9i3LaPoIExYwXyKFBdG+9DCaRUyOBYYKzhcVXE53yHFa4JbUlG9SFscyfPC58Y8 U9lo+NKvgZpT09k7dohtJt3wov546YXtsZRBlHiro49XPZfBcIdffCqEQo77FoJeOpLMOsE B+peprBlPg17VRYrO++RMlhfY/KHhqfn2Tb19Lj4f0DdXCeEjqahFfvjcgJyXM7uH0T9g3q 98h3pSkCwnsuBrmP2OHBEdlzJmecE5AJw0qF/L9tNjUjfIT2SGQ2amicmmATluQ6YftJCB2 LZ0volrIHwEdNbV6O1jI8ZJ8Iy9FyNyvUKLS01oDbBc7/slRm/RJvib8trEv+fUKHTqSRcA pzQgKI4A5o3TQlI27bpqMqfyySNaNVYDoB92J4yP+aiVL+JjZU74o677dc/rtfkbJ+P1OqN k54qb2OZ7m714k8IpUJmke9XIrBJFHjzTdAWsOKZPDYegTPS9Rq+oqd3p2V5kYzP26oMQDl iwXAWG4gdF+FWoDU9g2kB7JEHoFAdxxNWVAX69xXSqoWVsVRLkCRq29cZRxFV+kTEi2dNz8 iMxPyGHznhV81bWnVvaTb/xsevnT1Aybr7EbvtS60OcMu2cnL6BzcI0ekxTwWXSJJv6Wf5v oGgSFp2py2Gc3CZDDUuWoOlMZJXHIrcQU4pnJSSQI8gxoz2zPSxG4GDYTHLWa/3JSIBbZ5B Y4jXIkdbGrei8sJ8sUlH82liEpJT9uPcjWha8BgTU0lsFPVOguKKgeHix4X2C2GtRHYpAYU wXMRlTn4XFE4oLcvsI9aaJe0muYnxBQEzf+LX4NU/E/J4udtNy/5cGjRa1NpOQqEJD4nhDL b9eXIyFCBLG+dMxTT9fWoKsHCB06GOu6YfWfRme8xxokTbwtQLE5jLuaHovSJPRofDGorPn tivwcxKmmb06eKk+ajTuQ4x5XzbWav3n+p5aTjrchIy1XQ06R/UPQNx4E8mk9mOoZ1uIJOj 2EsB4XVIJq9ZkSGdcFNZRFPAxg+biyO8VzeZ X-QQ-XMRINFO: NS+P29fieYNwqS3WCnRCOn9D1NpZuCnCRA== X-QQ-RECHKSPAM: 0 The K1 CPU clocks have a core divider, while K3 CPU clocks bypass theirs for some sources. K3 also allows C1/C3 to share the C0/C2 PLLs. Omitting these details from the clock model can give CCF an incorrect rate or parent. Model the dividers, K3 bypass conditions and shared parents. Register the sharing muxes as internal clocks without adding DT clock IDs. Exclude K3 selector 4 from the modeled parents to match BSP policy. Fixes: 1b72c59db0ad ("clk: spacemit: Add clock support for SpacemiT K1 SoC") Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree") Signed-off-by: Troy Mitchell --- drivers/clk/spacemit/ccu-k1.c | 8 +++---- drivers/clk/spacemit/ccu-k3.c | 47 +++++++++++++++++++++++++++--------= ---- drivers/clk/spacemit/ccu_common.c | 12 ++++++++++ drivers/clk/spacemit/ccu_common.h | 2 ++ drivers/clk/spacemit/ccu_mix.c | 10 ++++++++- drivers/clk/spacemit/ccu_mix.h | 18 +++++++++++++++ 6 files changed, 78 insertions(+), 19 deletions(-) diff --git a/drivers/clk/spacemit/ccu-k1.c b/drivers/clk/spacemit/ccu-k1.c index dee14d25f75d4..40cc96c4ee876 100644 --- a/drivers/clk/spacemit/ccu-k1.c +++ b/drivers/clk/spacemit/ccu-k1.c @@ -391,8 +391,8 @@ static const struct clk_parent_data cpu_c0_clk_parents[= ] =3D { CCU_PARENT_HW(pll2_d3), CCU_PARENT_HW(cpu_c0_hi_clk), }; -CCU_MUX_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU_C0_CLK_CTR= L, BIT(12), 0, 3, - CLK_IS_CRITICAL); +CCU_MUX_DIV_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU_C0_CLK= _CTRL, + 3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL); CCU_DIV_DEFINE(cpu_c0_ace_clk, CCU_PARENT_HW(cpu_c0_core_clk), APMU_CPU_C0= _CLK_CTRL, 6, 3, CLK_IS_CRITICAL); CCU_DIV_DEFINE(cpu_c0_tcm_clk, CCU_PARENT_HW(cpu_c0_core_clk), APMU_CPU_C0= _CLK_CTRL, 9, 3, @@ -413,8 +413,8 @@ static const struct clk_parent_data cpu_c1_clk_parents[= ] =3D { CCU_PARENT_HW(pll2_d3), CCU_PARENT_HW(cpu_c1_hi_clk), }; -CCU_MUX_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU_C1_CLK_CTR= L, BIT(12), 0, 3, - CLK_IS_CRITICAL); +CCU_MUX_DIV_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU_C1_CLK= _CTRL, + 3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL); CCU_DIV_DEFINE(cpu_c1_ace_clk, CCU_PARENT_HW(cpu_c1_core_clk), APMU_CPU_C1= _CLK_CTRL, 6, 3, CLK_IS_CRITICAL); =20 diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 92b930d5ff305..2f0bd74f7da4d 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -587,52 +587,64 @@ static const struct clk_parent_data cpu_c0_clk_parent= s[] =3D { CCU_PARENT_HW(pll1_d5_491p52), CCU_PARENT_HW(pll1_d4_614p4), CCU_PARENT_HW(pll2_d3), - CCU_PARENT_HW(pll3_d2), + { .index =3D -1 }, /* Leave selector 4 unmodeled, as in the BSP. */ CCU_PARENT_HW(pll1_d2_1228p8), CCU_PARENT_HW(pll2_d2), CCU_PARENT_HW(pll3_d1), }; -CCU_MUX_DIV_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU_C0_CLK= _CTRL, - 3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL); +CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU= _C0_CLK_CTRL, + 3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4)); + +static const struct clk_parent_data cpu_c1_pll_src_parents[] =3D { + CCU_PARENT_HW(pll4_d1), + CCU_PARENT_HW(pll3_d1), +}; +CCU_MUX_DEFINE(cpu_c1_pll_src, cpu_c1_pll_src_parents, APMU_CPU_C1_CLK_CTR= L, 13, 1, 0); =20 static const struct clk_parent_data cpu_c1_clk_parents[] =3D { CCU_PARENT_HW(pll1_d3_819p2), CCU_PARENT_HW(pll1_d5_491p52), CCU_PARENT_HW(pll1_d4_614p4), CCU_PARENT_HW(pll2_d3), - CCU_PARENT_HW(pll4_d2), + { .index =3D -1 }, /* Leave selector 4 unmodeled, as in the BSP. */ CCU_PARENT_HW(pll1_d2_1228p8), CCU_PARENT_HW(pll2_d2), - CCU_PARENT_HW(pll4_d1), + CCU_PARENT_HW(cpu_c1_pll_src), }; -CCU_MUX_DIV_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU_C1_CLK= _CTRL, - 3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL); +CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU= _C1_CLK_CTRL, + 3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4)); =20 static const struct clk_parent_data cpu_c2_clk_parents[] =3D { CCU_PARENT_HW(pll1_d3_819p2), CCU_PARENT_HW(pll1_d5_491p52), CCU_PARENT_HW(pll1_d4_614p4), CCU_PARENT_HW(pll2_d3), - CCU_PARENT_HW(pll5_d2), + { .index =3D -1 }, /* Leave selector 4 unmodeled, as in the BSP. */ CCU_PARENT_HW(pll1_d2_1228p8), CCU_PARENT_HW(pll2_d2), CCU_PARENT_HW(pll5_d1), }; -CCU_MUX_DIV_FC_DEFINE(cpu_c2_core_clk, cpu_c2_clk_parents, APMU_CPU_C2_CLK= _CTRL, - 3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL); +CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c2_core_clk, cpu_c2_clk_parents, APMU_CPU= _C2_CLK_CTRL, + 3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4)); + +static const struct clk_parent_data cpu_c3_pll_src_parents[] =3D { + CCU_PARENT_HW(pll8_d1), + CCU_PARENT_HW(pll5_d1), +}; +CCU_MUX_DEFINE(cpu_c3_pll_src, cpu_c3_pll_src_parents, APMU_CPU_C3_CLK_CTR= L, 13, 1, 0); =20 static const struct clk_parent_data cpu_c3_clk_parents[] =3D { CCU_PARENT_HW(pll1_d3_819p2), CCU_PARENT_HW(pll1_d5_491p52), CCU_PARENT_HW(pll1_d4_614p4), CCU_PARENT_HW(pll2_d3), - CCU_PARENT_HW(pll8_d2), + { .index =3D -1 }, /* Leave selector 4 unmodeled, as in the BSP. */ CCU_PARENT_HW(pll1_d2_1228p8), CCU_PARENT_HW(pll2_d2), - CCU_PARENT_HW(pll8_d1), + CCU_PARENT_HW(cpu_c3_pll_src), }; -CCU_MUX_DIV_FC_DEFINE(cpu_c3_core_clk, cpu_c3_clk_parents, APMU_CPU_C3_CLK= _CTRL, - 3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL); +CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c3_core_clk, cpu_c3_clk_parents, APMU_CPU= _C3_CLK_CTRL, + 3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4)); =20 static const struct clk_parent_data ccic2phy_parents[] =3D { CCU_PARENT_HW(pll1_d24_102p4), @@ -1447,10 +1459,17 @@ static struct clk_hw *k3_ccu_apmu_hws[] =3D { [CLK_APMU_ISIM_VCLK3] =3D &isim_vclk_out3.common.hw, }; =20 +static struct clk_hw *k3_ccu_apmu_internal_hws[] =3D { + &cpu_c1_pll_src.common.hw, + &cpu_c3_pll_src.common.hw, +}; + static const struct spacemit_ccu_data k3_ccu_apmu_data =3D { .reset_name =3D "k3-apmu-reset", .hws =3D k3_ccu_apmu_hws, .num =3D ARRAY_SIZE(k3_ccu_apmu_hws), + .internal_hws =3D k3_ccu_apmu_internal_hws, + .num_internal =3D ARRAY_SIZE(k3_ccu_apmu_internal_hws), }; =20 static struct clk_hw *k3_ccu_dciu_hws[] =3D { diff --git a/drivers/clk/spacemit/ccu_common.c b/drivers/clk/spacemit/ccu_c= ommon.c index 8696bb9cba2d8..759da6988ca78 100644 --- a/drivers/clk/spacemit/ccu_common.c +++ b/drivers/clk/spacemit/ccu_common.c @@ -30,6 +30,18 @@ static int spacemit_ccu_register(struct device *dev, =20 clk_data->num =3D data->num; =20 + /* Internal muxes have no binding IDs but must precede their children. */ + for (i =3D 0; i < data->num_internal; i++) { + struct clk_hw *hw =3D data->internal_hws[i]; + struct ccu_common *common =3D hw_to_ccu_common(hw); + + common->regmap =3D regmap; + common->lock_regmap =3D lock_regmap; + ret =3D devm_clk_hw_register(dev, hw); + if (ret) + return ret; + } + for (i =3D 0; i < data->num; i++) { struct clk_hw *hw =3D data->hws[i]; struct ccu_common *common; diff --git a/drivers/clk/spacemit/ccu_common.h b/drivers/clk/spacemit/ccu_c= ommon.h index 8691698e007d9..659dc68ab902a 100644 --- a/drivers/clk/spacemit/ccu_common.h +++ b/drivers/clk/spacemit/ccu_common.h @@ -43,6 +43,8 @@ struct spacemit_ccu_data { const char *reset_name; struct clk_hw **hws; size_t num; + struct clk_hw **internal_hws; + size_t num_internal; }; =20 #define ccu_read(c, reg) \ diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c index da3c5685d4f65..68596b9fa5b8f 100644 --- a/drivers/clk/spacemit/ccu_mix.c +++ b/drivers/clk/spacemit/ccu_mix.c @@ -13,6 +13,8 @@ #define MIX_FC_TIMEOUT_US 10000 #define MIX_FC_DELAY_US 5 =20 +static u8 ccu_mux_get_parent(struct clk_hw *hw); + static void ccu_gate_disable(struct clk_hw *hw) { struct ccu_mix *mix =3D hw_to_ccu_mix(hw); @@ -57,6 +59,9 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *h= w, struct ccu_div_config *div =3D &mix->div; unsigned long val; =20 + if (div->bypass & BIT(ccu_mux_get_parent(hw))) + return parent_rate; + val =3D ccu_read(&mix->common, ctrl) >> div->shift; val &=3D (1 << div->width) - 1; =20 @@ -113,7 +118,7 @@ ccu_mix_calc_best_rate(struct clk_hw *hw, unsigned long= rate, for (int i =3D 0; i < parent_num; i++) { struct clk_hw *parent =3D clk_hw_get_parent_by_index(hw, i); unsigned long parent_rate; - u32 div_max =3D 1 << div->width; + u32 div_max =3D div->bypass & BIT(i) ? 1 : 1 << div->width; =20 if (!parent) continue; @@ -163,6 +168,9 @@ static int ccu_mix_set_rate(struct clk_hw *hw, unsigned= long rate, u32 current_div, target_div =3D 0, mask; unsigned long best_delta =3D ULONG_MAX; =20 + if (div->bypass & BIT(ccu_mux_get_parent(hw))) + return rate =3D=3D parent_rate ? 0 : -EINVAL; + /* set_rate must use the parent selected by CCF, not search other parents= . */ for (u32 i =3D 1; i <=3D BIT(div->width); i++) { unsigned long divided =3D DIV_ROUND_UP_ULL(parent_rate, i); diff --git a/drivers/clk/spacemit/ccu_mix.h b/drivers/clk/spacemit/ccu_mix.h index dbba9bf49b3bd..ac88c282d660a 100644 --- a/drivers/clk/spacemit/ccu_mix.h +++ b/drivers/clk/spacemit/ccu_mix.h @@ -36,6 +36,8 @@ struct ccu_mux_config { struct ccu_div_config { u8 shift; u8 width; + /* Parent indices which bypass the divider; zero means no bypass. */ + u32 bypass; }; =20 struct ccu_mix { @@ -203,6 +205,22 @@ static struct ccu_mix _name =3D { \ }, \ } =20 +#define CCU_MUX_DIV_BYPASS_FC_DEFINE(_name, _parents, _reg_ctrl, \ + _mshift, _mwidth, _mask_fc, \ + _muxshift, _muxwidth, _flags, \ + _bypass) \ +static struct ccu_mix _name =3D { \ + .div =3D { .shift =3D _mshift, .width =3D _mwidth, .bypass =3D _bypass },= \ + .mux =3D CCU_MUX_INIT(_muxshift, _muxwidth), \ + .common =3D { \ + .reg_ctrl =3D _reg_ctrl, \ + .reg_fc =3D _reg_ctrl, \ + .mask_fc =3D _mask_fc, \ + CCU_MIX_INITHW_PARENTS(_name, _parents, \ + spacemit_ccu_mux_div_ops, _flags), \ + }, \ +} + #define CCU_MUX_FC_DEFINE(_name, _parents, _reg_ctrl, _mask_fc, _muxshift,= \ _muxwidth, _flags) \ static struct ccu_mix _name =3D { \ --=20 2.55.0 From nobody Fri Sep 25 18:21:33 2026 Received: from smtpbgjp3.qq.com (smtpbgjp3.qq.com [54.92.39.34]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A84CC57982B for ; Wed, 9 Sep 2026 14:07:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.92.39.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788962860; cv=none; b=m7nN/1zEZZW3HqDAGQxwmlcymLnfjRGfaglxQ7hyYFvfD9GrtLkGw8hVv0IX8m69NJ3YDpRjRO18Ozy6zk1nBEhAn/L3DLA9mWOUfSprewenBDeYAYVXruB8uq64emHAABgx3vjc3S6mp2N062eaQV3UhLc9AVZ0aTMasUHGP1k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788962860; c=relaxed/simple; bh=HcMGpQWFpVaKN+ZuQCSNEuXR6Mis65bMQ5jbV2jMtaI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LFNVitPAuD1IU+aqpWqw+2VgMZ04GppKpXobpg59KHftG/7MlMzMq447OYKDSjY7dkNOcpA9ATizp/LMaHEdWf7kQhgLuZdvASjntpkmfdSGcLDVWd4X+GvdR9PffMoZC4NzLfhsQ6SOgkWTXQNbHlTeaVKzmoupj9Ap5n/7HJU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=UtdXWV24; arc=none smtp.client-ip=54.92.39.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="UtdXWV24" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1788962850; bh=GrJe/zGuOLChEqPY2kuCKE1B/uVgBajiPVFz4of7w88=; h=From:Date:Subject:MIME-Version:Message-Id:To; b=UtdXWV24lcSG7Rm2odP/Inu4wkWc/kqK0MmS17X3k/IF5INiWSN7oqIfzqQSBBCC1 wc5FrPPVK9B/QMTwKTx0OGwmBIlL2cHkn/s2fXnHrD9uFsR0/YPvr+v1kBn0+r5IVR zfU84xKTa/RlTt+YGS2PmTAe4h/6yb9L/pP3+/qM= X-QQ-mid: zesmtpsz9t1788962849t68360cbb X-QQ-Originating-IP: sLsoD0HDmN9VygtU83mXe6+BU5Tsu8vKAe2RTOBchSU= Received: from = ( [120.237.158.181]) by bizesmtp.qq.com (ESMTP) with id ; Wed, 09 Sep 2026 22:07:26 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 8200674829812587946 EX-QQ-RecipientCnt: 12 From: Troy Mitchell Date: Wed, 09 Sep 2026 22:07:04 +0800 Subject: [PATCH 4/5] clk: spacemit: reject rate changes to running firmware PLLs Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-spacemit-pll-init-v1-4-b3065ad5a4ac@linux.spacemit.com> References: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com> In-Reply-To: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com> To: Stephen Boyd , Brian Masney , Jerome Brunet , Yixun Lan , Alex Elder , Inochi Amaoto , Haylen Chu Cc: linux-clk@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, Troy Mitchell X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=6831; i=troy.mitchell@linux.spacemit.com; h=from:subject:message-id; bh=HcMGpQWFpVaKN+ZuQCSNEuXR6Mis65bMQ5jbV2jMtaI=; b=owGbwMvMwCU2g/N9w09jE33G02pJDFkLM7iyma+a6QW+OX3FSd/C/ITI7E4VrjTBulW9Fw8eU dhwc21PRykLgxgXg6yYIkv3A55tBT5RtgUChb4wc1iZQIYwcHEKwEQsbRgZplZNLpqx2vrxtj2N XL0XixmCVbxC6neeNGTlUuedtSMwheEPXxBfXrVr+IeSyy88n96Y/eBW3eydn6IOn1ggu3VR/Ro tHgA= X-Developer-Key: i=troy.mitchell@linux.spacemit.com; a=openpgp; fpr=3FE5535CF1B0E658E57DB59BAE1C2FBEA7DB42E1 X-QQ-SENDSIZE: 520 Feedback-ID: zesmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz3a-0 X-QQ-XMAILINFO: NOt1V41YjURb6VK9dmN4/++zlfvQ7L4e8Spnev7LS9OcsVgg+2GhJ/b2 F23C91MCE4+iQqtHkwoo1VWulMvBEjt+FMeAoSDbwTELRRPY0ffXWegjEHQNytPYKru/Di0 OlgAHFHms55SRmKMCikYSwNyH2pXyjP98J65TIb3nts6lZ4dIjwwV3/B1GzUGesk07T80y/ eb5btvB4hJMF4fK/UmIwxfEnSSOimJTlhjnD7L0MxSEpNXqk6byFOFiiVj4JdIga6w2Yj7B ot8w8/wmt70kjoZFSajm7F3G5sce8xqU3D6VRH6kxxfMYgeSgVbqqWYwN0ZMZqWPy7UQjJx J7Xdf4wR/pug9B4Ct3S7AnJnUPByXHzhn04003a/7WxYxK/K2FwI6UHInmGhWRNstLn53vD Apt1W54bvV3VlqDN8I0hk/sQ9j+FnH8vLFDVHM6iYjKjF/I3Owvzq4P63T7GVJeBj0NJw0V VsDFN6EPT/RrY+ARGwXpTYq3in0knQwrwgQiOmNBl2+Ahq8V7vQjbUPxuOoBvVQrSwJ3BG4 vLmp/xFC3HqpPZfsMvYbaAVGI85wsZxTB736zxsFA4bUzZihA1cYD78tFFWX+Nf267f+onZ hP6rs7hgGSRQ38aRGlTAWUYtpXMPmJ3JJWkBrTi668d75qqmiXlqKSh0mOLzbhNF3FBKh9O o1EjTL9ywCnXd0F9NcGcUYUwi4ufML9DJW5clWiEA8DcUOsqmQofeX/ebC3ccWFEQQQeFSj ZPUDERnMn1wt84AAlf+9jEyOKYyXkPAtzRvNNxhbwaj5kTYTJbAzJZZjAmAFh/FsIQUYahp aSq+U1e4bEz53/tPeM9WAkDUHRpU66qED3xcZUDxTMF4J5EZVCjEKFRivnO7Pflx5XYlld2 V8/dl88ZuDkN5+/DIoYXeWMC7W6a57sHAIqun/uFzZRKvAZ44+fuU189TGfaSOBEUCMWjMT Ctd2pT1DWOX+PAIbn3DE9XJdCveMWx0V3213hgpE8ekfpBBV86m9WB2/GTT9LCa7pSchoce q+uoteRIa1UmlZv0oKpP6O2JHjyXEVwsopf2BlXIaALjBzTfZvH+pEFMODy6i9KDPzOpYYD RAK21Og6oxM/ighu/S/fjZ5HR5xhGLOC7qH5pMX7k3zgvX8gRZ03NLG2yIzLQYTeG+6TXys 2haISA5jwsSjVduimEdV+wL1ow== X-QQ-XMRINFO: MSVp+SPm3vtSI1QTLgDHQqIV1w2oNKDqfg== X-QQ-RECHKSPAM: 0 CLK_SET_RATE_GATE only protects clocks prepared through CCF. A PLL left running by firmware can have a zero prepare count, so this flag alone cannot prevent set_rate() from reprogramming a live PLL. Check the hardware state and reject set_rate() while either the enable or lock bit is set on K1 PLLs and K3 PLLAs. Propagate register read/write failures and reject rate requests with no table candidate. Callers must still move consumers away and stop the PLL before changing its rate. Fixes: 1b72c59db0ad ("clk: spacemit: Add clock support for SpacemiT K1 SoC") Fixes: 3a086236c600 ("clk: spacemit: ccu_pll: add plla type clock") Signed-off-by: Troy Mitchell --- drivers/clk/spacemit/ccu_pll.c | 92 +++++++++++++++++++++++++++++++++-----= ---- 1 file changed, 72 insertions(+), 20 deletions(-) diff --git a/drivers/clk/spacemit/ccu_pll.c b/drivers/clk/spacemit/ccu_pll.c index c9852fed31017..2573b8396cefa 100644 --- a/drivers/clk/spacemit/ccu_pll.c +++ b/drivers/clk/spacemit/ccu_pll.c @@ -38,9 +38,9 @@ static const struct ccu_pll_rate_tbl *ccu_pll_lookup_best= _rate(struct ccu_pll *p unsigned long rate) { struct ccu_pll_config *config =3D &pll->config; - const struct ccu_pll_rate_tbl *best_entry; + const struct ccu_pll_rate_tbl *best_entry =3D NULL; unsigned long best_delta =3D ULONG_MAX; - int i; + u32 i; =20 for (i =3D 0; i < config->tbl_num; i++) { const struct ccu_pll_rate_tbl *entry =3D &config->rate_tbl[i]; @@ -75,19 +75,25 @@ static const struct ccu_pll_rate_tbl *ccu_pll_lookup_ma= tched_entry(struct ccu_pl return NULL; } =20 -static void ccu_pll_update_param(struct ccu_pll *pll, const struct ccu_pll= _rate_tbl *entry) +static int ccu_pll_update_param(struct ccu_pll *pll, const struct ccu_pll_= rate_tbl *entry) { struct ccu_common *common =3D &pll->common; + int ret; =20 - regmap_write(common->regmap, common->reg_swcr1, entry->swcr1); - ccu_update(common, swcr3, PLL_SWCR3_MASK, entry->swcr3); + ret =3D regmap_write(common->regmap, common->reg_swcr1, entry->swcr1); + if (ret) + return ret; + return ccu_update(common, swcr3, PLL_SWCR3_MASK, entry->swcr3); } =20 static int ccu_pll_is_enabled(struct clk_hw *hw) { struct ccu_common *common =3D hw_to_ccu_common(hw); + u32 val; + int ret; =20 - return ccu_read(common, swcr3) & PLL_SWCR3_EN; + ret =3D regmap_read(common->regmap, common->reg_swcr3, &val); + return ret ? ret : !!(val & PLL_SWCR3_EN); } =20 static int ccu_pll_enable(struct clk_hw *hw) @@ -95,8 +101,11 @@ static int ccu_pll_enable(struct clk_hw *hw) struct ccu_pll *pll =3D hw_to_ccu_pll(hw); struct ccu_common *common =3D &pll->common; unsigned int tmp; + int ret; =20 - ccu_update(common, swcr3, PLL_SWCR3_EN, PLL_SWCR3_EN); + ret =3D ccu_update(common, swcr3, PLL_SWCR3_EN, PLL_SWCR3_EN); + if (ret) + return ret; =20 /* check lock status */ return regmap_read_poll_timeout_atomic(common->lock_regmap, @@ -113,6 +122,17 @@ static void ccu_pll_disable(struct clk_hw *hw) ccu_update(common, swcr3, PLL_SWCR3_EN, 0); } =20 +static int ccu_pll_check_stopped(struct ccu_pll *pll) +{ + u32 val; + int ret; + + ret =3D regmap_read(pll->common.lock_regmap, pll->config.reg_lock, &val); + if (ret) + return ret; + return val & pll->config.mask_lock ? -EBUSY : 0; +} + /* * PLLs must be gated before changing rate, which is ensured by * flag CLK_SET_RATE_GATE. @@ -122,11 +142,20 @@ static int ccu_pll_set_rate(struct clk_hw *hw, unsign= ed long rate, { struct ccu_pll *pll =3D hw_to_ccu_pll(hw); const struct ccu_pll_rate_tbl *entry; + int ret; =20 - entry =3D ccu_pll_lookup_best_rate(pll, rate); - ccu_pll_update_param(pll, entry); + /* CLK_SET_RATE_GATE does not account for firmware-only users. */ + ret =3D ccu_pll_is_enabled(hw); + if (ret) + return ret < 0 ? ret : -EBUSY; + ret =3D ccu_pll_check_stopped(pll); + if (ret) + return ret; =20 - return 0; + entry =3D ccu_pll_lookup_best_rate(pll, rate); + if (!entry) + return -EINVAL; + return ccu_pll_update_param(pll, entry); } =20 static int ccu_pll_get_params(struct ccu_pll *pll, @@ -207,8 +236,12 @@ static int ccu_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct ccu_pll *pll =3D hw_to_ccu_pll(hw); + const struct ccu_pll_rate_tbl *entry; =20 - req->rate =3D ccu_pll_lookup_best_rate(pll, req->rate)->rate; + entry =3D ccu_pll_lookup_best_rate(pll, req->rate); + if (!entry) + return -EINVAL; + req->rate =3D entry->rate; =20 return 0; } @@ -249,20 +282,28 @@ static const struct ccu_pll_rate_tbl *ccu_plla_lookup= _matched_entry(struct ccu_p return NULL; } =20 -static void ccu_plla_update_param(struct ccu_pll *pll, const struct ccu_pl= l_rate_tbl *entry) +static int ccu_plla_update_param(struct ccu_pll *pll, const struct ccu_pll= _rate_tbl *entry) { struct ccu_common *common =3D &pll->common; + int ret; =20 - regmap_write(common->regmap, common->reg_swcr1, entry->swcr1); - regmap_write(common->regmap, common->reg_swcr3, entry->swcr3); - ccu_update(common, swcr2, PLLA_SWCR2_MASK, entry->swcr2); + ret =3D regmap_write(common->regmap, common->reg_swcr1, entry->swcr1); + if (ret) + return ret; + ret =3D regmap_write(common->regmap, common->reg_swcr3, entry->swcr3); + if (ret) + return ret; + return ccu_update(common, swcr2, PLLA_SWCR2_MASK, entry->swcr2); } =20 static int ccu_plla_is_enabled(struct clk_hw *hw) { struct ccu_common *common =3D hw_to_ccu_common(hw); + u32 val; + int ret; =20 - return ccu_read(common, swcr2) & PLLA_SWCR2_EN; + ret =3D regmap_read(common->regmap, common->reg_swcr2, &val); + return ret ? ret : !!(val & PLLA_SWCR2_EN); } =20 static int ccu_plla_enable(struct clk_hw *hw) @@ -270,8 +311,11 @@ static int ccu_plla_enable(struct clk_hw *hw) struct ccu_pll *pll =3D hw_to_ccu_pll(hw); struct ccu_common *common =3D &pll->common; unsigned int tmp; + int ret; =20 - ccu_update(common, swcr2, PLLA_SWCR2_EN, PLLA_SWCR2_EN); + ret =3D ccu_update(common, swcr2, PLLA_SWCR2_EN, PLLA_SWCR2_EN); + if (ret) + return ret; =20 /* check lock status */ return regmap_read_poll_timeout_atomic(common->lock_regmap, @@ -297,11 +341,19 @@ static int ccu_plla_set_rate(struct clk_hw *hw, unsig= ned long rate, { struct ccu_pll *pll =3D hw_to_ccu_pll(hw); const struct ccu_pll_rate_tbl *entry; + int ret; =20 - entry =3D ccu_pll_lookup_best_rate(pll, rate); - ccu_plla_update_param(pll, entry); + ret =3D ccu_plla_is_enabled(hw); + if (ret) + return ret < 0 ? ret : -EBUSY; + ret =3D ccu_pll_check_stopped(pll); + if (ret) + return ret; =20 - return 0; + entry =3D ccu_pll_lookup_best_rate(pll, rate); + if (!entry) + return -EINVAL; + return ccu_plla_update_param(pll, entry); } =20 static unsigned long ccu_plla_recalc_rate(struct clk_hw *hw, --=20 2.55.0 From nobody Fri Sep 25 18:21:33 2026 Received: from smtpbgau2.qq.com (smtpbgau2.qq.com [54.206.34.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9CDF157F73E for ; Wed, 9 Sep 2026 14:07:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=54.206.34.216 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788962868; cv=none; b=a+OMOYAUWDF2biH5eHt538D5XdZTM5bQ70b3TQ9mGVa824Z7UfeofGydNL5qC7+C85Mc1DXsBseh29n+WnoXJUGxK6DeggS7rYmrXg7pQb7ASQDPlM5QAkGmtp/QQTH1ihY1rJOqodoTJTVnK/67Bo1AYjhRpine1X5an40GDe4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788962868; c=relaxed/simple; bh=MMJ6WoUH0bJSC62+CibBdbIZ4f1vNyReAZIMSZnTvV8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=M4wxL1aFOIK+7OalK4ERtI4h1nLUhjAOG9gsbQvFT32iyfHRxSKXI9QUZUHr5ZBKxL3pqrhvdhqSnFEEElCBKQ48yFPNJMuiWa+yA+QZKBzX1+sPiNZzS8wWvzyfq87kiy99q7PIkBgeWJO8v+sgbUBUpG2iOoX4XFIWScYO/TQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com; spf=none smtp.mailfrom=linux.spacemit.com; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b=Ma1G3MRm; arc=none smtp.client-ip=54.206.34.216 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.spacemit.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.spacemit.com header.i=@linux.spacemit.com header.b="Ma1G3MRm" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.spacemit.com; s=mxsw2412; t=1788962854; bh=QnO1nc1K0d982KAo+kP7NIOhiY2NMQFMv1F53E+MIcs=; h=From:Date:Subject:MIME-Version:Message-Id:To; b=Ma1G3MRmpywunbUiIn7RELH1/DsMB54AmPyK/6nQVvJDn5wPcVKEfFX4La4rkjqJC 5qKeNh0UpJ/0J0E0C5Dx6BFaVRrx+ZJ5WC2Ralltneo3B+70r71dW4qIaJWWLTGH2a 0Uf26n/fGCcnoZvK8O0CoDaBp/7sIASm6vMNgHDk= X-QQ-mid: esmtpsz10t1788962853tf408563b X-QQ-Originating-IP: WNIxRnk1s6XuJVI+VNdzWT8eq256IVsHu2C8KgoeCE4= Received: from = ( [120.237.158.181]) by bizesmtp.qq.com (ESMTP) with id ; Wed, 09 Sep 2026 22:07:30 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 0 X-BIZMAIL-ID: 13866533234236857726 EX-QQ-RecipientCnt: 12 From: Troy Mitchell Date: Wed, 09 Sep 2026 22:07:05 +0800 Subject: [PATCH 5/5] clk: spacemit: safely synchronize PLL parameters during init Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260909-spacemit-pll-init-v1-5-b3065ad5a4ac@linux.spacemit.com> References: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com> In-Reply-To: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com> To: Stephen Boyd , Brian Masney , Jerome Brunet , Yixun Lan , Alex Elder , Inochi Amaoto , Haylen Chu Cc: linux-clk@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, Troy Mitchell X-Mailer: b4 0.15.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=30962; i=troy.mitchell@linux.spacemit.com; h=from:subject:message-id; bh=MMJ6WoUH0bJSC62+CibBdbIZ4f1vNyReAZIMSZnTvV8=; b=owGbwMvMwCU2g/N9w09jE33G02pJDFkLM3iqRVk9xdYJFFXsUVa8f9r+h9H8m7df/LD6s8rDP PYi71bJjlIWBjEuBlkxRZbuBzzbCnyibAsECn1h5rAygQxh4OIUgImsnszwv2bOxoAshc0/P840 YZdLa9DIrWjLe1HYzuFyc9b/jS01uxgZ5sikTGLdvcfny8rb3urvV1rEaXBHT75ZyF4stPfSxfS t3AA= X-Developer-Key: i=troy.mitchell@linux.spacemit.com; a=openpgp; fpr=3FE5535CF1B0E658E57DB59BAE1C2FBEA7DB42E1 X-QQ-SENDSIZE: 520 Feedback-ID: esmtpsz:linux.spacemit.com:qybglogicsvrgz:qybglogicsvrgz3a-0 X-QQ-XMAILINFO: NRKrrPIB11wHSOoe3xBFi4QO/Rd1mpaqlOe4+VZ9B0dxOSUd8i7wWQmo MwCjFQH6Ekj4A6zFXp7CC/RsOQEDzTz2MwTxGgCr/3bndMyUVSHqlQ/i6KlRcDceRDxJtdn Pzdu2Esa5CkopTrBxndsHC3so3kIUMMISA87wxDfpvaLPONy2wPC7mvagikSJcecMEb/jvm Elxp/umKH+GaDGuJ/NGnfu1U6VA/wNvFvzsmblEDHxs7/Q9/VkwWyXnQmvUXuQ/kGStFBhZ gMQ+JPKOxoGZA88Cz6BinvkgL4xd16X9nnKmxduaROOvTFdMRQN2Q9Rla6vsLfUo0yT1zLH tcsdHhVOuvJjh9K/6ZwDSW1kg3/juMmNSSEj1jkb7xK4jjWqrJL7s8WHyDkdoaI9msFUsuW O8mC66ctIXBsyeqwORUQXojupk23psldFsvxEaFOAtMVFjgU3/aLI1FQ09fIJvZAtJ5mwML zcL8ycIFXmxWO0y3MG82gvdMMK3aEKoonT+Un7oKUIgBgoeetJ2pQ/bva+HNxbrsza7a8Kl ugV4c/Jry22LaoMWOzgSL6Ua/t043k6vVBH39alg+MZG7HNvp3eONO4rz7Iibw4GpioouWI wXjRXRgFWrVVgs2LpDN5hTeyK26drSND4YEuRgtCP623lwBkYGvaKQj/RFXDMq2hn/oJ8MI VGAx9eQ0QczDS5iG05Cxkffl9gmva1v/0DdlIC4yzHiHlbQayAOlOc/fYoHMVm2QUZvlzQK Gn332r0UFeKVGxDZte59LWnyK0EIsvkzAT/dx47pvNzol125ac0lA/A241NMjqAazDvP8VL MlsED21GiUVLsB5l/AcApvhomaXcF4YVyvcvOZ4S4KvTJHF24WLAHm9GDyST0e9Rjegnpfz NEUnpKAo4RessM+EcnY8hU5maxi8GMwL0WHuQ9QuR/LiL6+VlE+a9O5cmfXTmqhCjoGBw6t W9lXtj4p17npoPrIUFaRttscss56JVI9htQCS+/fLSIPdjrKDbhORZUZwWOWUv4o4Zw1hRl 0q/PMDYh/cPuhHVVIEbWHUSoQnqoVOjXpi6MKW4cTlHb+6tX7pI2TSxJgPzmIhMJ8F6vVu9 dZSY2FDrcIPJiJodIhS/QVdwJfORx9YDd7F8kZevNArjpB9jJEo54yS+Os+1IzWQjx1GYQr ONXtusqQX97eSjGmAA855zClwMPJx/KFsTmkdNLt8FXBewA= X-QQ-XMRINFO: MPJ6Tf5t3I/ylTmHUqvI8+Wpn+Gzalws3A== X-QQ-RECHKSPAM: 0 Firmware PLL settings can be valid without matching a rate-table entry. Falling back to the first entry during init can halt CPUs by disabling their clock source. Replace that fallback with opt-in parameter synchronization. Preserve unlisted rates and PLLs without a sync descriptor, including PLL1. Leave already matching parameters unchanged. For differing parameters, require an entry that decodes to the same rate and no enabled outputs outside the described CPU paths. Park mapped CPU consumers on validated PLL1 branches no faster than their current clock before stopping their PLL and programming the matching entry. Re-enable previously enabled PLLs and wait for lock before restoring CPU parents; leave initially disabled PLLs disabled. Handle K1 PLL3 and K3 PLL3/4/5/8, parking sharing clusters first and restoring them last. K3 CPU parking still depends on firmware cluster initialization for FC completion. After a programming or relock failure, attempt to restore the old PLL parameters. Leave parked CPUs on PLL1 if PLL recovery fails, and keep the fallback gates critical in case an FC handshake does not complete. Keep synchronization in init, before CCF links the PLL to its children. Fixes: 1b72c59db0ad ("clk: spacemit: Add clock support for SpacemiT K1 SoC") Fixes: 3a086236c600 ("clk: spacemit: ccu_pll: add plla type clock") Signed-off-by: Troy Mitchell --- drivers/clk/spacemit/ccu-k1.c | 52 ++++- drivers/clk/spacemit/ccu-k3.c | 90 +++++++-- drivers/clk/spacemit/ccu_pll.c | 447 +++++++++++++++++++++++++++++++------= ---- drivers/clk/spacemit/ccu_pll.h | 43 +++- 4 files changed, 485 insertions(+), 147 deletions(-) diff --git a/drivers/clk/spacemit/ccu-k1.c b/drivers/clk/spacemit/ccu-k1.c index 40cc96c4ee876..0085179f8d7a7 100644 --- a/drivers/clk/spacemit/ccu-k1.c +++ b/drivers/clk/spacemit/ccu-k1.c @@ -41,17 +41,47 @@ static const struct ccu_pll_rate_tbl pll3_rate_tbl[] = =3D { CCU_PLL_RATE(3200000000UL, 0x0050dd67, 0x43eaaaab), }; =20 -CCU_PLL_DEFINE(pll1, pll1_rate_tbl, APBS_PLL1_SWCR1, APBS_PLL1_SWCR3, MPMU= _POSR, POSR_PLL1_LOCK, - CLK_SET_RATE_GATE); -CCU_PLL_DEFINE(pll2, pll2_rate_tbl, APBS_PLL2_SWCR1, APBS_PLL2_SWCR3, MPMU= _POSR, POSR_PLL2_LOCK, - CLK_SET_RATE_GATE); -CCU_PLL_DEFINE(pll3, pll3_rate_tbl, APBS_PLL3_SWCR1, APBS_PLL3_SWCR3, MPMU= _POSR, POSR_PLL3_LOCK, - CLK_SET_RATE_GATE); - -CCU_FACTOR_GATE_DEFINE(pll1_d2, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 1), 2, 1); +/* PLL1 supplies the fallback and must retain its firmware configuration. = */ +CCU_PLL_SYNC_DEFINE(pll1, pll1_rate_tbl, APBS_PLL1_SWCR1, APBS_PLL1_SWCR2, + APBS_PLL1_SWCR3, MPMU_POSR, POSR_PLL1_LOCK, + CLK_SET_RATE_GATE, NULL); + +static const struct ccu_pll_sync pll2_sync =3D { + .safe_pll =3D &pll1, +}; + +static const struct ccu_pll_cpu_mux pll3_cpu_muxes[] =3D { + { APMU_CPU_C1_CLK_CTRL, GENMASK(2, 0), 5 }, + { APMU_CPU_C1_CLK_CTRL, GENMASK(2, 0), 7 }, + { APMU_CPU_C0_CLK_CTRL, GENMASK(2, 0), 5 }, + { APMU_CPU_C0_CLK_CTRL, GENMASK(2, 0), 7 }, +}; + +static const struct ccu_pll_sync pll3_sync =3D { + .safe_pll =3D &pll1, + .apmu_compatible =3D "spacemit,k1-syscon-apmu", + .muxes =3D pll3_cpu_muxes, + .num_muxes =3D ARRAY_SIZE(pll3_cpu_muxes), + .cpu_outputs =3D GENMASK(2, 0), + .safe_sel =3D 4, + .slow_sel =3D 3, + .reg_safe_gate =3D MPMU_ACGR, +}; + +CCU_PLL_SYNC_DEFINE(pll2, pll2_rate_tbl, APBS_PLL2_SWCR1, APBS_PLL2_SWCR2, + APBS_PLL2_SWCR3, MPMU_POSR, POSR_PLL2_LOCK, + CLK_SET_RATE_GATE, &pll2_sync); +CCU_PLL_SYNC_DEFINE(pll3, pll3_rate_tbl, APBS_PLL3_SWCR1, APBS_PLL3_SWCR2, + APBS_PLL3_SWCR3, MPMU_POSR, POSR_PLL3_LOCK, + CLK_SET_RATE_GATE, &pll3_sync); + +/* A failed CPU FC handshake must not lose either fallback clock. */ +CCU_FACTOR_GATE_FLAGS_DEFINE(pll1_d2, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2= , BIT(1), 2, 1, + CLK_IS_CRITICAL); CCU_FACTOR_GATE_DEFINE(pll1_d3, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 2), 3, 1); CCU_FACTOR_GATE_DEFINE(pll1_d4, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 3), 4, 1); -CCU_FACTOR_GATE_DEFINE(pll1_d5, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 4), 5, 1); +CCU_FACTOR_GATE_FLAGS_DEFINE(pll1_d5, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2= , BIT(4), 5, 1, + CLK_IS_CRITICAL); CCU_FACTOR_GATE_DEFINE(pll1_d6, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 5), 6, 1); CCU_FACTOR_GATE_DEFINE(pll1_d7, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 6), 7, 1); CCU_FACTOR_GATE_FLAGS_DEFINE(pll1_d8, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2= , BIT(7), 8, 1, @@ -111,7 +141,7 @@ CCU_FACTOR_DEFINE(pll1_d3072_0p8, CCU_PARENT_HW(pll1_d3= 84_6p4), 8, 1); CCU_GATE_DEFINE(pll1_d6_409p6, CCU_PARENT_HW(pll1_d6), MPMU_ACGR, BIT(0), = 0); CCU_FACTOR_GATE_DEFINE(pll1_d12_204p8, CCU_PARENT_HW(pll1_d6), MPMU_ACGR, = BIT(5), 2, 1); =20 -CCU_GATE_DEFINE(pll1_d5_491p52, CCU_PARENT_HW(pll1_d5), MPMU_ACGR, BIT(21)= , 0); +CCU_GATE_DEFINE(pll1_d5_491p52, CCU_PARENT_HW(pll1_d5), MPMU_ACGR, BIT(21)= , CLK_IS_CRITICAL); CCU_FACTOR_GATE_DEFINE(pll1_d10_245p76, CCU_PARENT_HW(pll1_d5), MPMU_ACGR,= BIT(18), 2, 1); =20 CCU_GATE_DEFINE(pll1_d4_614p4, CCU_PARENT_HW(pll1_d4), MPMU_ACGR, BIT(15),= 0); @@ -120,7 +150,7 @@ CCU_FACTOR_GATE_DEFINE(pll1_d78_31p5, CCU_PARENT_HW(pll= 1_d4), MPMU_ACGR, BIT(6), =20 CCU_GATE_DEFINE(pll1_d3_819p2, CCU_PARENT_HW(pll1_d3), MPMU_ACGR, BIT(14),= 0); =20 -CCU_GATE_DEFINE(pll1_d2_1228p8, CCU_PARENT_HW(pll1_d2), MPMU_ACGR, BIT(16)= , 0); +CCU_GATE_DEFINE(pll1_d2_1228p8, CCU_PARENT_HW(pll1_d2), MPMU_ACGR, BIT(16)= , CLK_IS_CRITICAL); =20 CCU_GATE_DEFINE(slow_uart, CCU_PARENT_NAME(osc), MPMU_ACGR, BIT(1), CLK_IG= NORE_UNUSED); CCU_DDN_DEFINE(slow_uart1_14p74, pll1_d16_153p6, MPMU_SUCCR, 16, 13, 0, 13= , 2, 0); diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 2f0bd74f7da4d..b01f2ce5d394f 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -55,25 +55,77 @@ static const struct ccu_pll_rate_tbl pll8_rate_tbl[] = =3D { =20 CCU_PLLA_DEFINE(pll1, pll1_rate_tbl, APBS_PLL1_SWCR1, APBS_PLL1_SWCR2, APB= S_PLL1_SWCR3, MPMU_POSR, POSR_PLL1_LOCK, CLK_SET_RATE_GATE); -CCU_PLLA_DEFINE(pll2, pll2_rate_tbl, APBS_PLL2_SWCR1, APBS_PLL2_SWCR2, APB= S_PLL2_SWCR3, - MPMU_POSR, POSR_PLL2_LOCK, CLK_SET_RATE_GATE); -CCU_PLLA_DEFINE(pll3, pll3_rate_tbl, APBS_PLL3_SWCR1, APBS_PLL3_SWCR2, APB= S_PLL3_SWCR3, - MPMU_POSR, POSR_PLL3_LOCK, CLK_SET_RATE_GATE); -CCU_PLLA_DEFINE(pll4, pll4_rate_tbl, APBS_PLL4_SWCR1, APBS_PLL4_SWCR2, APB= S_PLL4_SWCR3, - MPMU_POSR, POSR_PLL4_LOCK, CLK_SET_RATE_GATE); -CCU_PLLA_DEFINE(pll5, pll5_rate_tbl, APBS_PLL5_SWCR1, APBS_PLL5_SWCR2, APB= S_PLL5_SWCR3, - MPMU_POSR, POSR_PLL5_LOCK, CLK_SET_RATE_GATE); -CCU_PLLA_DEFINE(pll6, pll6_rate_tbl, APBS_PLL6_SWCR1, APBS_PLL6_SWCR2, APB= S_PLL6_SWCR3, - MPMU_POSR, POSR_PLL6_LOCK, CLK_SET_RATE_GATE); -CCU_PLLA_DEFINE(pll7, pll7_rate_tbl, APBS_PLL7_SWCR1, APBS_PLL7_SWCR2, APB= S_PLL7_SWCR3, - MPMU_POSR, POSR_PLL7_LOCK, CLK_SET_RATE_GATE); -CCU_PLLA_DEFINE(pll8, pll8_rate_tbl, APBS_PLL8_SWCR1, APBS_PLL8_SWCR2, APB= S_PLL8_SWCR3, - MPMU_POSR, POSR_PLL8_LOCK, CLK_SET_RATE_GATE); - -CCU_FACTOR_GATE_DEFINE(pll1_d2, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 1), 2, 1); + +/* PLLs without a managed CPU path require all output gates to be off. */ +static const struct ccu_pll_sync idle_pll_sync =3D { + .safe_pll =3D &pll1, +}; + +/* Park the secondary cluster first when it shares the primary cluster PLL= . */ +static const struct ccu_pll_cpu_mux pll3_cpu_muxes[] =3D { + { APMU_CPU_C1_CLK_CTRL, BIT(13) | GENMASK(2, 0), BIT(13) | 7 }, + { APMU_CPU_C0_CLK_CTRL, GENMASK(2, 0), 7 }, +}; + +static const struct ccu_pll_cpu_mux pll4_cpu_muxes[] =3D { + { APMU_CPU_C1_CLK_CTRL, BIT(13) | GENMASK(2, 0), 7 }, +}; + +static const struct ccu_pll_cpu_mux pll5_cpu_muxes[] =3D { + { APMU_CPU_C3_CLK_CTRL, BIT(13) | GENMASK(2, 0), BIT(13) | 7 }, + { APMU_CPU_C2_CLK_CTRL, GENMASK(2, 0), 7 }, +}; + +static const struct ccu_pll_cpu_mux pll8_cpu_muxes[] =3D { + { APMU_CPU_C3_CLK_CTRL, BIT(13) | GENMASK(2, 0), 7 }, +}; + +#define K3_CPU_PLL_SYNC(_pll) \ +static const struct ccu_pll_sync _pll##_sync =3D { \ + .safe_pll =3D &pll1, \ + .apmu_compatible =3D "spacemit,k3-syscon-apmu", \ + .muxes =3D _pll##_cpu_muxes, \ + .num_muxes =3D ARRAY_SIZE(_pll##_cpu_muxes), \ + .cpu_outputs =3D BIT(0), \ + .safe_sel =3D 5, \ + .slow_sel =3D 1, \ + .reg_safe_gate =3D MPMU_ACGR, \ +} + +K3_CPU_PLL_SYNC(pll3); +K3_CPU_PLL_SYNC(pll4); +K3_CPU_PLL_SYNC(pll5); +K3_CPU_PLL_SYNC(pll8); + +CCU_PLLA_SYNC_DEFINE(pll2, pll2_rate_tbl, APBS_PLL2_SWCR1, APBS_PLL2_SWCR2, + APBS_PLL2_SWCR3, MPMU_POSR, POSR_PLL2_LOCK, + CLK_SET_RATE_GATE, &idle_pll_sync); +CCU_PLLA_SYNC_DEFINE(pll3, pll3_rate_tbl, APBS_PLL3_SWCR1, APBS_PLL3_SWCR2, + APBS_PLL3_SWCR3, MPMU_POSR, POSR_PLL3_LOCK, + CLK_SET_RATE_GATE, &pll3_sync); +CCU_PLLA_SYNC_DEFINE(pll4, pll4_rate_tbl, APBS_PLL4_SWCR1, APBS_PLL4_SWCR2, + APBS_PLL4_SWCR3, MPMU_POSR, POSR_PLL4_LOCK, + CLK_SET_RATE_GATE, &pll4_sync); +CCU_PLLA_SYNC_DEFINE(pll5, pll5_rate_tbl, APBS_PLL5_SWCR1, APBS_PLL5_SWCR2, + APBS_PLL5_SWCR3, MPMU_POSR, POSR_PLL5_LOCK, + CLK_SET_RATE_GATE, &pll5_sync); +CCU_PLLA_SYNC_DEFINE(pll6, pll6_rate_tbl, APBS_PLL6_SWCR1, APBS_PLL6_SWCR2, + APBS_PLL6_SWCR3, MPMU_POSR, POSR_PLL6_LOCK, + CLK_SET_RATE_GATE, &idle_pll_sync); +CCU_PLLA_SYNC_DEFINE(pll7, pll7_rate_tbl, APBS_PLL7_SWCR1, APBS_PLL7_SWCR2, + APBS_PLL7_SWCR3, MPMU_POSR, POSR_PLL7_LOCK, + CLK_SET_RATE_GATE, &idle_pll_sync); +CCU_PLLA_SYNC_DEFINE(pll8, pll8_rate_tbl, APBS_PLL8_SWCR1, APBS_PLL8_SWCR2, + APBS_PLL8_SWCR3, MPMU_POSR, POSR_PLL8_LOCK, + CLK_SET_RATE_GATE, &pll8_sync); + +/* A failed CPU FC handshake must not lose either fallback clock. */ +CCU_FACTOR_GATE_FLAGS_DEFINE(pll1_d2, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2= , BIT(1), 2, 1, + CLK_IS_CRITICAL); CCU_FACTOR_GATE_DEFINE(pll1_d3, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 2), 3, 1); CCU_FACTOR_GATE_DEFINE(pll1_d4, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 3), 4, 1); -CCU_FACTOR_GATE_DEFINE(pll1_d5, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 4), 5, 1); +CCU_FACTOR_GATE_FLAGS_DEFINE(pll1_d5, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2= , BIT(4), 5, 1, + CLK_IS_CRITICAL); CCU_FACTOR_GATE_DEFINE(pll1_d6, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 5), 6, 1); CCU_FACTOR_GATE_DEFINE(pll1_d7, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2, BIT(= 6), 7, 1); CCU_FACTOR_GATE_FLAGS_DEFINE(pll1_d8, CCU_PARENT_HW(pll1), APBS_PLL1_SWCR2= , BIT(7), 8, 1, @@ -178,7 +230,7 @@ CCU_FACTOR_DEFINE(pll1_d3072_0p8, CCU_PARENT_HW(pll1_d3= 84_6p4), 8, 1); CCU_GATE_DEFINE(pll1_d6_409p6, CCU_PARENT_HW(pll1_d6), MPMU_ACGR, BIT(0), = 0); CCU_FACTOR_GATE_DEFINE(pll1_d12_204p8, CCU_PARENT_HW(pll1_d6), MPMU_ACGR, = BIT(5), 2, 1); =20 -CCU_GATE_DEFINE(pll1_d5_491p52, CCU_PARENT_HW(pll1_d5), MPMU_ACGR, BIT(21)= , 0); +CCU_GATE_DEFINE(pll1_d5_491p52, CCU_PARENT_HW(pll1_d5), MPMU_ACGR, BIT(21)= , CLK_IS_CRITICAL); CCU_FACTOR_GATE_DEFINE(pll1_d10_245p76, CCU_PARENT_HW(pll1_d5), MPMU_ACGR,= BIT(18), 2, 1); =20 CCU_GATE_DEFINE(pll1_d4_614p4, CCU_PARENT_HW(pll1_d4), MPMU_ACGR, BIT(15),= 0); @@ -187,7 +239,7 @@ CCU_FACTOR_GATE_DEFINE(pll1_d78_31p5, CCU_PARENT_HW(pll= 1_d4), MPMU_ACGR, BIT(6), =20 CCU_GATE_DEFINE(pll1_d3_819p2, CCU_PARENT_HW(pll1_d3), MPMU_ACGR, BIT(14),= 0); =20 -CCU_GATE_DEFINE(pll1_d2_1228p8, CCU_PARENT_HW(pll1_d2), MPMU_ACGR, BIT(16)= , 0); +CCU_GATE_DEFINE(pll1_d2_1228p8, CCU_PARENT_HW(pll1_d2), MPMU_ACGR, BIT(16)= , CLK_IS_CRITICAL); =20 static const struct clk_parent_data apb_parents[] =3D { CCU_PARENT_HW(pll1_d96_25p6), diff --git a/drivers/clk/spacemit/ccu_pll.c b/drivers/clk/spacemit/ccu_pll.c index 2573b8396cefa..3d8624c4ce5d1 100644 --- a/drivers/clk/spacemit/ccu_pll.c +++ b/drivers/clk/spacemit/ccu_pll.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include =20 #include "ccu_common.h" @@ -34,6 +36,15 @@ #define PLLA_SWCR3_PREDIV GENMASK(21, 20) #define PLL_FRAC_BITS 22 =20 +#define PLL_OUTPUT_GATES GENMASK(7, 0) +#define PLL_POWERDOWN_BYPASS BIT(23) +#define PLL_SAFE_OUTPUT_GATE (BIT(1) | BIT(4)) +#define PLL_SAFE_MPMU_GATE (BIT(16) | BIT(21)) +#define PLL_CPU_SEL GENMASK(2, 0) +#define PLL_CPU_FC BIT(12) +#define PLL_CPU_TIMEOUT_US 10000 +#define PLL_MAX_CPU_MUXES 4 + static const struct ccu_pll_rate_tbl *ccu_pll_lookup_best_rate(struct ccu_= pll *pll, unsigned long rate) { @@ -55,26 +66,6 @@ static const struct ccu_pll_rate_tbl *ccu_pll_lookup_bes= t_rate(struct ccu_pll *p return best_entry; } =20 -static const struct ccu_pll_rate_tbl *ccu_pll_lookup_matched_entry(struct = ccu_pll *pll) -{ - struct ccu_pll_config *config =3D &pll->config; - u32 swcr1, swcr3; - int i; - - swcr1 =3D ccu_read(&pll->common, swcr1); - swcr3 =3D ccu_read(&pll->common, swcr3); - swcr3 &=3D PLL_SWCR3_MASK; - - for (i =3D 0; i < config->tbl_num; i++) { - const struct ccu_pll_rate_tbl *entry =3D &config->rate_tbl[i]; - - if (swcr1 =3D=3D entry->swcr1 && swcr3 =3D=3D entry->swcr3) - return entry; - } - - return NULL; -} - static int ccu_pll_update_param(struct ccu_pll *pll, const struct ccu_pll_= rate_tbl *entry) { struct ccu_common *common =3D &pll->common; @@ -158,24 +149,6 @@ static int ccu_pll_set_rate(struct clk_hw *hw, unsigne= d long rate, return ccu_pll_update_param(pll, entry); } =20 -static int ccu_pll_get_params(struct ccu_pll *pll, - struct ccu_pll_rate_tbl *params, bool plla) -{ - struct ccu_common *common =3D &pll->common; - int ret; - - ret =3D regmap_read(common->regmap, common->reg_swcr1, ¶ms->swcr1); - if (ret) - return ret; - params->swcr2 =3D 0; - if (plla) { - ret =3D regmap_read(common->regmap, common->reg_swcr2, ¶ms->swcr2); - if (ret) - return ret; - } - return regmap_read(common->regmap, common->reg_swcr3, ¶ms->swcr3); -} - static unsigned long ccu_pll_calc_rate(const struct ccu_pll_rate_tbl *para= ms, unsigned long parent_rate) { @@ -199,39 +172,6 @@ static unsigned long ccu_pll_calc_rate(const struct cc= u_pll_rate_tbl *params, return DIV_ROUND_CLOSEST_ULL(rate, BIT_ULL(PLL_FRAC_BITS)); } =20 -static unsigned long ccu_plla_calc_rate(const struct ccu_pll_rate_tbl *par= ams, - unsigned long parent_rate) -{ - u32 swcr1 =3D params->swcr1, swcr2 =3D params->swcr2; - u32 swcr3 =3D params->swcr3, prediv, frac; - u64 divider, rate; - - /* Decode the software-controlled mode described by the PLL calculator. */ - if (!(swcr1 & PLLA_SWCR1_USER_MODE) || - (swcr1 & PLLA_SWCR1_REFSEL)) - return 0; - - prediv =3D FIELD_GET(PLLA_SWCR3_PREDIV, swcr3) + 1; - frac =3D FIELD_GET(PLLA_SWCR1_FRAC, swcr1) << 8; - frac |=3D FIELD_GET(PLLA_SWCR2_MASK, swcr2); - divider =3D (u64)FIELD_GET(PLLA_SWCR1_INT, swcr1) << PLL_FRAC_BITS; - divider +=3D frac; - - /* Fvco =3D Fref * Npre * (Nint + Nfrac), with an unsigned fraction. */ - rate =3D (u64)parent_rate * prediv * divider; - return DIV_ROUND_CLOSEST_ULL(rate, BIT_ULL(PLL_FRAC_BITS)); -} - -static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct ccu_pll_rate_tbl params; - - if (ccu_pll_get_params(hw_to_ccu_pll(hw), ¶ms, false)) - return 0; - return ccu_pll_calc_rate(¶ms, parent_rate); -} - static int ccu_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { @@ -246,42 +186,6 @@ static int ccu_pll_determine_rate(struct clk_hw *hw, return 0; } =20 -static int ccu_pll_init(struct clk_hw *hw) -{ - struct ccu_pll *pll =3D hw_to_ccu_pll(hw); - - if (ccu_pll_lookup_matched_entry(pll)) - return 0; - - ccu_pll_disable(hw); - ccu_pll_update_param(pll, &pll->config.rate_tbl[0]); - - return 0; -} - -static const struct ccu_pll_rate_tbl *ccu_plla_lookup_matched_entry(struct= ccu_pll *pll) -{ - struct ccu_pll_config *config =3D &pll->config; - const struct ccu_pll_rate_tbl *entry; - u32 i, swcr1, swcr2, swcr3; - - swcr1 =3D ccu_read(&pll->common, swcr1); - swcr2 =3D ccu_read(&pll->common, swcr2); - swcr2 &=3D PLLA_SWCR2_MASK; - swcr3 =3D ccu_read(&pll->common, swcr3); - - for (i =3D 0; i < config->tbl_num; i++) { - entry =3D &config->rate_tbl[i]; - - if (swcr1 =3D=3D entry->swcr1 && - swcr2 =3D=3D entry->swcr2 && - swcr3 =3D=3D entry->swcr3) - return entry; - } - - return NULL; -} - static int ccu_plla_update_param(struct ccu_pll *pll, const struct ccu_pll= _rate_tbl *entry) { struct ccu_common *common =3D &pll->common; @@ -356,6 +260,68 @@ static int ccu_plla_set_rate(struct clk_hw *hw, unsign= ed long rate, return ccu_plla_update_param(pll, entry); } =20 +static unsigned long ccu_plla_calc_rate(const struct ccu_pll_rate_tbl *par= ams, + unsigned long parent_rate) +{ + u32 swcr1 =3D params->swcr1, swcr2 =3D params->swcr2; + u32 swcr3 =3D params->swcr3, prediv, frac; + u64 divider, rate; + + /* Decode the software-controlled mode described by the PLL calculator. */ + if (!(swcr1 & PLLA_SWCR1_USER_MODE) || + (swcr1 & PLLA_SWCR1_REFSEL)) + return 0; + + prediv =3D FIELD_GET(PLLA_SWCR3_PREDIV, swcr3) + 1; + frac =3D FIELD_GET(PLLA_SWCR1_FRAC, swcr1) << 8; + frac |=3D FIELD_GET(PLLA_SWCR2_MASK, swcr2); + divider =3D (u64)FIELD_GET(PLLA_SWCR1_INT, swcr1) << PLL_FRAC_BITS; + divider +=3D frac; + + /* Fvco =3D Fref * Npre * (Nint + Nfrac), with an unsigned fraction. */ + rate =3D (u64)parent_rate * prediv * divider; + return DIV_ROUND_CLOSEST_ULL(rate, BIT_ULL(PLL_FRAC_BITS)); +} + +static int ccu_pll_get_params(struct ccu_pll *pll, + struct ccu_pll_rate_tbl *params, bool plla) +{ + struct ccu_common *common =3D &pll->common; + int ret; + + ret =3D regmap_read(common->regmap, common->reg_swcr1, ¶ms->swcr1); + if (ret) + return ret; + params->swcr2 =3D 0; + if (plla) { + ret =3D regmap_read(common->regmap, common->reg_swcr2, ¶ms->swcr2); + if (ret) + return ret; + } + return regmap_read(common->regmap, common->reg_swcr3, ¶ms->swcr3); +} + +static bool ccu_pll_params_equal(const struct ccu_pll_rate_tbl *a, + const struct ccu_pll_rate_tbl *b, bool plla) +{ + if (a->swcr1 !=3D b->swcr1) + return false; + if (plla) + return a->swcr3 =3D=3D b->swcr3 && + !((a->swcr2 ^ b->swcr2) & PLLA_SWCR2_MASK); + return !((a->swcr3 ^ b->swcr3) & PLL_SWCR3_MASK); +} + +static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ccu_pll_rate_tbl params; + + if (ccu_pll_get_params(hw_to_ccu_pll(hw), ¶ms, false)) + return 0; + return ccu_pll_calc_rate(¶ms, parent_rate); +} + static unsigned long ccu_plla_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -366,19 +332,278 @@ static unsigned long ccu_plla_recalc_rate(struct clk= _hw *hw, return ccu_plla_calc_rate(¶ms, parent_rate); } =20 -static int ccu_plla_init(struct clk_hw *hw) +struct ccu_pll_park { + struct regmap *apmu; + u32 saved[PLL_MAX_CPU_MUXES]; + u32 selected[PLL_MAX_CPU_MUXES]; + unsigned long parked; +}; + +static int ccu_pll_select_cpu(struct regmap *regmap, u32 reg, u32 sel) +{ + u32 val; + int ret; + + ret =3D regmap_update_bits(regmap, reg, PLL_CPU_SEL, sel); + if (ret) + return ret; + ret =3D regmap_update_bits(regmap, reg, PLL_CPU_FC, PLL_CPU_FC); + if (ret) + return ret; + ret =3D regmap_read_poll_timeout_atomic(regmap, reg, val, + !(val & PLL_CPU_FC), PLL_DELAY_US, + PLL_CPU_TIMEOUT_US); + if (ret) + return ret; + return (val & PLL_CPU_SEL) =3D=3D sel ? 0 : -EIO; +} + +static void ccu_pll_unpark(const struct ccu_pll_sync *sync, + struct ccu_pll_park *park) +{ + int i; + + /* Restore sharing-capable secondary clusters last. */ + for (i =3D sync->num_muxes - 1; i >=3D 0; i--) { + if (!(park->parked & BIT(i))) + continue; + if (!ccu_pll_select_cpu(park->apmu, sync->muxes[i].reg, + park->saved[i] & PLL_CPU_SEL)) + park->parked &=3D ~BIT(i); + } + + /* + * The fallback gates remain critical: after an FC timeout the selector + * register alone cannot prove that the CPU has left the temporary path. + */ +} + +static int ccu_pll_park_cpus(const struct ccu_pll_sync *sync, + struct ccu_pll_park *park, bool plla, + unsigned long rate, unsigned long parent_rate) +{ + struct ccu_pll *pll1 =3D sync->safe_pll; + struct ccu_common *safe =3D &pll1->common; + struct ccu_pll_rate_tbl params; + struct device_node *np; + unsigned long safe_rate; + u32 val; + u32 i; + int ret; + bool needed =3D false; + + if (!sync->num_muxes) + return 0; + if (sync->num_muxes > PLL_MAX_CPU_MUXES) + return -EINVAL; + /* Like CCU probe, create the regmap before its clocks are registered. */ + np =3D of_find_compatible_node(NULL, NULL, sync->apmu_compatible); + if (!np) + return -ENODEV; + park->apmu =3D device_node_to_regmap(np); + of_node_put(np); + if (IS_ERR(park->apmu)) + return PTR_ERR(park->apmu); + + for (i =3D 0; i < sync->num_muxes; i++) { + const struct ccu_pll_cpu_mux *mux =3D &sync->muxes[i]; + + ret =3D regmap_read(park->apmu, mux->reg, &park->saved[i]); + if (ret) + return ret; + if (park->saved[i] & PLL_CPU_FC) + return -EBUSY; + /* K3 selector 4 is unmodeled; do not infer a live parent. */ + if (plla && (park->saved[i] & PLL_CPU_SEL) =3D=3D 4) + return -EINVAL; + needed |=3D (park->saved[i] & mux->mask) =3D=3D mux->value; + } + if (!needed) + return 0; + + /* PLL1 is never repaired here, nor used as a fallback if unrecognized. */ + if (!safe->regmap || !safe->lock_regmap) + return -ENODEV; + if (!pll1->config.tbl_num) + return -EINVAL; + ret =3D ccu_pll_get_params(pll1, ¶ms, plla); + if (ret) + return ret; + if (!ccu_pll_params_equal(¶ms, &pll1->config.rate_tbl[0], plla)) + return -EINVAL; + ret =3D regmap_read(safe->lock_regmap, pll1->config.reg_lock, &val); + if (ret) + return ret; + if (!(val & pll1->config.mask_lock)) + return -EBUSY; + + safe_rate =3D plla ? ccu_plla_calc_rate(¶ms, parent_rate) : + ccu_pll_calc_rate(¶ms, parent_rate); + for (i =3D 0; i < sync->num_muxes; i++) { + u32 old =3D park->saved[i], sel =3D old & PLL_CPU_SEL; + u32 div =3D ((old >> 3) & 7) + 1; + unsigned long cpu_rate, fast_rate, slow_rate; + + if ((old & sync->muxes[i].mask) !=3D sync->muxes[i].value) + continue; + if (plla) { + cpu_rate =3D rate; + fast_rate =3D safe_rate / 2; + } else { + cpu_rate =3D rate / (sel =3D=3D 5 ? 3 : (old & BIT(13) ? 1 : 2)); + cpu_rate /=3D div; + fast_rate =3D safe_rate / 2 / div; + } + slow_rate =3D safe_rate / 5 / div; + /* Never increase a CPU's rate without a corresponding voltage vote. */ + if (fast_rate <=3D cpu_rate) + park->selected[i] =3D sync->safe_sel; + else if (slow_rate <=3D cpu_rate) + park->selected[i] =3D sync->slow_sel; + else + return -ERANGE; + } + /* Both candidate parents are derived from the always-on PLL1. */ + ret =3D ccu_update(safe, swcr2, PLL_SAFE_OUTPUT_GATE, PLL_SAFE_OUTPUT_GAT= E); + if (ret) + return ret; + ret =3D regmap_update_bits(safe->lock_regmap, sync->reg_safe_gate, + PLL_SAFE_MPMU_GATE, PLL_SAFE_MPMU_GATE); + if (ret) + return ret; + + for (i =3D 0; i < sync->num_muxes; i++) { + const struct ccu_pll_cpu_mux *mux =3D &sync->muxes[i]; + + if ((park->saved[i] & mux->mask) !=3D mux->value) + continue; + park->parked |=3D BIT(i); + ret =3D ccu_pll_select_cpu(park->apmu, mux->reg, park->selected[i]); + if (ret) + return ret; + } + return 0; +} + +static int ccu_pll_stop(struct ccu_pll *pll, bool plla) +{ + struct ccu_common *common =3D &pll->common; + u32 val; + int ret; + + ret =3D plla ? ccu_update(common, swcr2, PLLA_SWCR2_EN, 0) : + ccu_update(common, swcr3, PLL_SWCR3_EN, 0); + if (ret) + return ret; + /* Hardware or firmware may override the software enable bit. */ + return regmap_read_poll_timeout_atomic(common->lock_regmap, + pll->config.reg_lock, val, + !(val & pll->config.mask_lock), + PLL_DELAY_US, PLL_TIMEOUT_US); +} + +static int ccu_pll_sync_init(struct clk_hw *hw, bool plla) { struct ccu_pll *pll =3D hw_to_ccu_pll(hw); + const struct ccu_pll_sync *sync =3D pll->config.sync; + const struct ccu_pll_rate_tbl *entry =3D NULL; + unsigned long (*calc)(const struct ccu_pll_rate_tbl *params, + unsigned long parent_rate); + int (*update)(struct ccu_pll *pll, const struct ccu_pll_rate_tbl *params); + int (*enable)(struct clk_hw *hw); + struct ccu_pll_rate_tbl old; + struct ccu_pll_park park =3D {}; + struct clk_hw *parent; + unsigned long parent_rate, rate; + u32 outputs, lock, i; + bool enabled; + int ret; =20 - if (ccu_plla_lookup_matched_entry(pll)) + /* Synchronization is opt-in; in particular PLL1 has no sync descriptor. = */ + if (!sync) + return 0; + parent =3D clk_hw_get_parent_by_index(hw, 0); + if (!parent) + return 0; + parent_rate =3D clk_hw_get_rate(parent); + calc =3D plla ? ccu_plla_calc_rate : ccu_pll_calc_rate; + update =3D plla ? ccu_plla_update_param : ccu_pll_update_param; + enable =3D plla ? ccu_plla_enable : ccu_pll_enable; + ret =3D ccu_pll_get_params(pll, &old, plla); + if (ret) + goto warn; + rate =3D calc(&old, parent_rate); + if (!rate) return 0; =20 - ccu_plla_disable(hw); - ccu_plla_update_param(pll, &pll->config.rate_tbl[0]); + /* Compare encoded rates, including fractional-divider quantization. */ + for (i =3D 0; i < pll->config.tbl_num; i++) { + if (rate =3D=3D calc(&pll->config.rate_tbl[i], parent_rate)) { + entry =3D &pll->config.rate_tbl[i]; + break; + } + } + if (!entry || ccu_pll_params_equal(&old, entry, plla)) + return 0; + if ((plla ? old.swcr3 : old.swcr1) & PLL_POWERDOWN_BYPASS) + return 0; + ret =3D regmap_read(pll->common.regmap, pll->common.reg_swcr2, &outputs); + if (ret) + goto warn; + /* Do not interrupt peripheral users, including unregistered consumers. */ + if (outputs & PLL_OUTPUT_GATES & ~sync->cpu_outputs) + return 0; + ret =3D regmap_read(pll->common.lock_regmap, pll->config.reg_lock, &lock); + if (ret) + goto warn; + enabled =3D plla ? old.swcr2 & PLLA_SWCR2_EN : old.swcr3 & PLL_SWCR3_EN; + if (enabled !=3D !!(lock & pll->config.mask_lock)) + return 0; =20 + /* + * .init runs under the CCF prepare lock, before this PLL is linked to + * its children. Restore the hardware muxes before CCF adopts them. + */ + ret =3D ccu_pll_park_cpus(sync, &park, plla, rate, parent_rate); + if (ret) + goto unpark; + ret =3D ccu_pll_stop(pll, plla); + if (ret) + goto restart; + ret =3D update(pll, entry); + if (!ret && enabled) + ret =3D enable(hw); + if (!ret) + goto unpark; + + /* Restore the old parameters before considering the original parents. */ + if (ccu_pll_stop(pll, plla) || update(pll, &old)) + goto warn; +restart: + if (enabled && enable(hw)) + goto warn; +unpark: + ccu_pll_unpark(sync, &park); + if (park.parked) + ret =3D -ETIMEDOUT; +warn: + if (ret) + pr_warn("%s: PLL synchronization failed: %d; retaining safe clocks\n", + clk_hw_get_name(hw), ret); + /* Failed synchronization must not unwind clocks needed to keep booting. = */ return 0; } =20 +static int ccu_pll_init(struct clk_hw *hw) +{ + return ccu_pll_sync_init(hw, false); +} + +static int ccu_plla_init(struct clk_hw *hw) +{ + return ccu_pll_sync_init(hw, true); +} + const struct clk_ops spacemit_ccu_pll_ops =3D { .init =3D ccu_pll_init, .enable =3D ccu_pll_enable, diff --git a/drivers/clk/spacemit/ccu_pll.h b/drivers/clk/spacemit/ccu_pll.h index e41db5c97c1a8..f77afafddba55 100644 --- a/drivers/clk/spacemit/ccu_pll.h +++ b/drivers/clk/spacemit/ccu_pll.h @@ -46,11 +46,31 @@ struct ccu_pll_rate_tbl { =20 struct ccu_pll_config { const struct ccu_pll_rate_tbl *rate_tbl; + const struct ccu_pll_sync *sync; u32 tbl_num; u32 reg_lock; u32 mask_lock; }; =20 +/* A CPU mux selection which consumes this PLL. */ +struct ccu_pll_cpu_mux { + u32 reg; + u32 mask; + u32 value; +}; + +/* Only CPU-exclusive outputs may remain gated on during synchronization. = */ +struct ccu_pll_sync { + struct ccu_pll *safe_pll; + const char *apmu_compatible; + const struct ccu_pll_cpu_mux *muxes; + u32 num_muxes; + u32 cpu_outputs; + u32 safe_sel; + u32 slow_sel; + u32 reg_safe_gate; +}; + #define CCU_PLL_RATE(_rate, _swcr1, _swcr3) \ { \ .rate =3D _rate, \ @@ -71,12 +91,13 @@ struct ccu_pll { struct ccu_pll_config config; }; =20 -#define CCU_PLL_CONFIG(_table, _reg_lock, _mask_lock) \ +#define CCU_PLL_CONFIG(_table, _reg_lock, _mask_lock, _sync) \ { \ .rate_tbl =3D _table, \ .tbl_num =3D ARRAY_SIZE(_table), \ .reg_lock =3D (_reg_lock), \ .mask_lock =3D (_mask_lock), \ + .sync =3D _sync, \ } =20 #define CCU_PLL_COMMON_HWINIT(_name, _ops, _flags) \ @@ -89,9 +110,9 @@ struct ccu_pll { }) =20 #define CCU_PLL_X_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, _reg_swcr3= , \ - _reg_lock, _mask_lock, _ops, _flags) \ + _reg_lock, _mask_lock, _ops, _flags, _sync) \ static struct ccu_pll _name =3D { \ - .config =3D CCU_PLL_CONFIG(_table, _reg_lock, _mask_lock), \ + .config =3D CCU_PLL_CONFIG(_table, _reg_lock, _mask_lock, _sync), \ .common =3D { \ .reg_swcr1 =3D _reg_swcr1, \ .reg_swcr2 =3D _reg_swcr2, \ @@ -103,12 +124,22 @@ static struct ccu_pll _name =3D { \ #define CCU_PLL_DEFINE(_name, _table, _reg_swcr1, _reg_swcr3, _reg_lock, \ _mask_lock, _flags) \ CCU_PLL_X_DEFINE(_name, _table, _reg_swcr1, 0, _reg_swcr3, \ - _reg_lock, _mask_lock, &spacemit_ccu_pll_ops, _flags) + _reg_lock, _mask_lock, &spacemit_ccu_pll_ops, _flags, NULL) + +#define CCU_PLL_SYNC_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, \ + _reg_swcr3, _reg_lock, _mask_lock, _flags, _sync) \ + CCU_PLL_X_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, _reg_swcr3, \ + _reg_lock, _mask_lock, &spacemit_ccu_pll_ops, _flags, _sync) =20 #define CCU_PLLA_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, _reg_swcr3,= \ - _reg_lock, _mask_lock, _flags) \ + _reg_lock, _mask_lock, _flags) \ + CCU_PLL_X_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, _reg_swcr3, \ + _reg_lock, _mask_lock, &spacemit_ccu_plla_ops, _flags, NULL) + +#define CCU_PLLA_SYNC_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, \ + _reg_swcr3, _reg_lock, _mask_lock, _flags, _sync) \ CCU_PLL_X_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, _reg_swcr3, \ - _reg_lock, _mask_lock, &spacemit_ccu_plla_ops, _flags) + _reg_lock, _mask_lock, &spacemit_ccu_plla_ops, _flags, _sync) =20 static inline struct ccu_pll *hw_to_ccu_pll(struct clk_hw *hw) { --=20 2.55.0