[PATCH 8/8] clk/qcom: fix rcg divider value
Caleb Connolly
caleb.connolly at linaro.org
Tue Oct 24 22:23:59 CEST 2023
The RCG divider field takes a value of (2*h - 1) where h is the divisor.
This allows fractional dividers to be supported by calculating them at
compile time using a macro.
However, the clk_rcg_set_rate_mnd() function was also performing the
calculation. Clean this all up and consistently use the F() macro to
calculate these at compile time and properly support fractional divisors.
Additionally, improve clk_bcr_update() to timeout with a warning rather
than hanging the board, and make the freq_tbl struct and helpers common
so that they can be reused by future platforms.
Signed-off-by: Caleb Connolly <caleb.connolly at linaro.org>
---
drivers/clk/qcom/clock-apq8016.c | 2 +-
drivers/clk/qcom/clock-apq8096.c | 2 +-
drivers/clk/qcom/clock-qcom.c | 40 ++++++++++++++++++++++++++++++++--------
drivers/clk/qcom/clock-qcom.h | 11 +++++++++++
drivers/clk/qcom/clock-qcs404.c | 16 ++++++++--------
drivers/clk/qcom/clock-sdm845.c | 26 --------------------------
6 files changed, 53 insertions(+), 44 deletions(-)
diff --git a/drivers/clk/qcom/clock-apq8016.c b/drivers/clk/qcom/clock-apq8016.c
index 5eba18739cfb..a1481cd5177b 100644
--- a/drivers/clk/qcom/clock-apq8016.c
+++ b/drivers/clk/qcom/clock-apq8016.c
@@ -53,7 +53,7 @@ static struct vote_clk gcc_blsp1_ahb_clk = {
/* SDHCI */
static int clk_init_sdc(struct qcom_cc_priv *priv, int slot, uint rate)
{
- int div = 8; /* 100MHz default */
+ int div = 15; /* 100MHz default */
if (rate == 200000000)
div = 4;
diff --git a/drivers/clk/qcom/clock-apq8096.c b/drivers/clk/qcom/clock-apq8096.c
index 48cac08eed67..ef81cd16223c 100644
--- a/drivers/clk/qcom/clock-apq8096.c
+++ b/drivers/clk/qcom/clock-apq8096.c
@@ -44,7 +44,7 @@ static struct vote_clk gcc_blsp2_ahb_clk = {
static int clk_init_sdc(struct qcom_cc_priv *priv, uint rate)
{
- int div = 3;
+ int div = 5;
clk_enable_cbc(priv->base + SDCC2_AHB_CBCR);
clk_rcg_set_rate_mnd(priv->base, &sdc_regs, div, 0, 0,
diff --git a/drivers/clk/qcom/clock-qcom.c b/drivers/clk/qcom/clock-qcom.c
index 7a6157bf123f..a83c74cd20ba 100644
--- a/drivers/clk/qcom/clock-qcom.c
+++ b/drivers/clk/qcom/clock-qcom.c
@@ -68,20 +68,46 @@ void clk_enable_vote_clk(phys_addr_t base, const struct vote_clk *vclk)
/* Update clock command via CMD_RCGR */
void clk_bcr_update(phys_addr_t apps_cmd_rcgr)
{
+ u32 count;
setbits_le32(apps_cmd_rcgr, APPS_CMD_RCGR_UPDATE);
/* Wait for frequency to be updated. */
- while (readl(apps_cmd_rcgr) & APPS_CMD_RCGR_UPDATE)
- ;
+ for (count = 0; count < 50000; count++) {
+ if (!(readl(apps_cmd_rcgr) & APPS_CMD_RCGR_UPDATE))
+ break;
+ udelay(1);
+ }
+ WARN(count == 50000, "WARNING: RCG @ %#llx [%#010x] stuck at off\n",
+ apps_cmd_rcgr, readl(apps_cmd_rcgr));
+}
+
+const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, uint rate)
+{
+ if (!f)
+ return NULL;
+
+ if (!f->freq)
+ return f;
+
+ for (; f->freq; f++)
+ if (rate <= f->freq)
+ return f;
+
+ /* Default to our fastest rate */
+ return f - 1;
}
#define CFG_MODE_DUAL_EDGE (0x2 << 12) /* Counter mode */
-#define CFG_MASK 0x3FFF
+// Disable the HW_CLK_CONTROL bit
+#define CFG_MASK (0x3FFF | (1 << 20))
#define CFG_DIVIDER_MASK 0x1F
-/* root set rate for clocks with half integer and MND divider */
+/*
+ * root set rate for clocks with half integer and MND divider
+ * div should be pre-calculated ((div * 2) - 1)
+ */
void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs,
int div, int m, int n, int source, u8 mnd_width)
{
@@ -99,17 +125,15 @@ void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs,
/* Program MND values */
setbits_le32(base + regs->M, m_val & mask);
setbits_le32(base + regs->N, n_val & mask);
- setbits_le32(base + regs->D, d_val & mask);
+ setbits_le32(base + regs->D, (d_val & mask) == mask ? 0 : (d_val & mask));
/* setup src select and divider */
cfg = readl(base + regs->cfg_rcgr);
cfg &= ~CFG_MASK;
cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */
- /* Set the divider; HW permits fraction dividers (+0.5), but
- for simplicity, we will support integers only */
if (div)
- cfg |= (2 * div - 1) & CFG_DIVIDER_MASK;
+ cfg |= div & CFG_DIVIDER_MASK;
if (n_val)
cfg |= CFG_MODE_DUAL_EDGE;
diff --git a/drivers/clk/qcom/clock-qcom.h b/drivers/clk/qcom/clock-qcom.h
index 6fa88fb40af8..f91e9d47dd22 100644
--- a/drivers/clk/qcom/clock-qcom.h
+++ b/drivers/clk/qcom/clock-qcom.h
@@ -30,6 +30,16 @@ struct bcr_regs {
uintptr_t D;
};
+struct freq_tbl {
+ uint freq;
+ uint src;
+ u8 pre_div;
+ u16 m;
+ u16 n;
+};
+
+#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
+
struct gate_clk {
uintptr_t reg;
u32 en_val;
@@ -69,6 +79,7 @@ void clk_enable_gpll0(phys_addr_t base, const struct pll_vote_clk *gpll0);
void clk_bcr_update(phys_addr_t apps_cmd_rgcr);
void clk_enable_cbc(phys_addr_t cbcr);
void clk_enable_vote_clk(phys_addr_t base, const struct vote_clk *vclk);
+const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, uint rate);
void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs,
int div, int m, int n, int source, u8 mnd_width);
void clk_rcg_set_rate(phys_addr_t base, const struct bcr_regs *regs, int div,
diff --git a/drivers/clk/qcom/clock-qcs404.c b/drivers/clk/qcom/clock-qcs404.c
index d10992ee58bf..b30d5c388d81 100644
--- a/drivers/clk/qcom/clock-qcs404.c
+++ b/drivers/clk/qcom/clock-qcs404.c
@@ -129,7 +129,7 @@ static ulong qcs404_set_rate(struct clk *clk, ulong rate)
break;
case GCC_SDCC1_APPS_CLK:
/* SDCC1: 200MHz */
- clk_rcg_set_rate_mnd(priv->base, &sdc_regs, 4, 0, 0,
+ clk_rcg_set_rate_mnd(priv->base, &sdc_regs, 7, 0, 0,
CFG_CLK_SRC_GPLL0, 8);
clk_enable_gpll0(priv->base, &gpll0_vote_clk);
clk_enable_cbc(priv->base + SDCC_APPS_CBCR(1));
@@ -139,16 +139,16 @@ static ulong qcs404_set_rate(struct clk *clk, ulong rate)
break;
case GCC_ETH_RGMII_CLK:
if (rate == 250000000)
- clk_rcg_set_rate_mnd(priv->base, &emac_regs, 2, 0, 0,
+ clk_rcg_set_rate_mnd(priv->base, &emac_regs, 3, 0, 0,
CFG_CLK_SRC_GPLL1, 8);
else if (rate == 125000000)
- clk_rcg_set_rate_mnd(priv->base, &emac_regs, 4, 0, 0,
+ clk_rcg_set_rate_mnd(priv->base, &emac_regs, 7, 0, 0,
CFG_CLK_SRC_GPLL1, 8);
else if (rate == 50000000)
- clk_rcg_set_rate_mnd(priv->base, &emac_regs, 10, 0, 0,
+ clk_rcg_set_rate_mnd(priv->base, &emac_regs, 19, 0, 0,
CFG_CLK_SRC_GPLL1, 8);
else if (rate == 5000000)
- clk_rcg_set_rate_mnd(priv->base, &emac_regs, 2, 1, 50,
+ clk_rcg_set_rate_mnd(priv->base, &emac_regs, 3, 1, 50,
CFG_CLK_SRC_GPLL1, 8);
break;
default:
@@ -165,7 +165,7 @@ static int qcs404_enable(struct clk *clk)
switch (clk->id) {
case GCC_USB30_MASTER_CLK:
clk_enable_cbc(priv->base + USB30_MASTER_CBCR);
- clk_rcg_set_rate_mnd(priv->base, &usb30_master_regs, 4, 0, 0,
+ clk_rcg_set_rate_mnd(priv->base, &usb30_master_regs, 7, 0, 0,
CFG_CLK_SRC_GPLL0, 8);
break;
case GCC_SYS_NOC_USB3_CLK:
@@ -187,14 +187,14 @@ static int qcs404_enable(struct clk *clk)
/* SPEED_1000: freq -> 250MHz */
clk_enable_cbc(priv->base + ETH_PTP_CBCR);
clk_enable_gpll0(priv->base, &gpll1_vote_clk);
- clk_rcg_set_rate_mnd(priv->base, &emac_ptp_regs, 2, 0, 0,
+ clk_rcg_set_rate_mnd(priv->base, &emac_ptp_regs, 3, 0, 0,
CFG_CLK_SRC_GPLL1, 8);
break;
case GCC_ETH_RGMII_CLK:
/* SPEED_1000: freq -> 250MHz */
clk_enable_cbc(priv->base + ETH_RGMII_CBCR);
clk_enable_gpll0(priv->base, &gpll1_vote_clk);
- clk_rcg_set_rate_mnd(priv->base, &emac_regs, 2, 0, 0,
+ clk_rcg_set_rate_mnd(priv->base, &emac_regs, 3, 0, 0,
CFG_CLK_SRC_GPLL1, 8);
break;
case GCC_ETH_SLAVE_AHB_CLK:
diff --git a/drivers/clk/qcom/clock-sdm845.c b/drivers/clk/qcom/clock-sdm845.c
index 9345d5293f1e..efe8495b7fb0 100644
--- a/drivers/clk/qcom/clock-sdm845.c
+++ b/drivers/clk/qcom/clock-sdm845.c
@@ -20,16 +20,6 @@
#include "clock-qcom.h"
-#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
-
-struct freq_tbl {
- uint freq;
- uint src;
- u8 pre_div;
- u16 m;
- u16 n;
-};
-
static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s0_clk_src[] = {
F(7372800, CFG_CLK_SRC_GPLL0_EVEN, 1, 384, 15625),
F(14745600, CFG_CLK_SRC_GPLL0_EVEN, 1, 768, 15625),
@@ -57,22 +47,6 @@ static const struct bcr_regs uart2_regs = {
.D = SE9_UART_APPS_D,
};
-const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, uint rate)
-{
- if (!f)
- return NULL;
-
- if (!f->freq)
- return f;
-
- for (; f->freq; f++)
- if (rate <= f->freq)
- return f;
-
- /* Default to our fastest rate */
- return f - 1;
-}
-
static ulong sdm845_set_rate(struct clk *clk, ulong rate)
{
struct qcom_cc_priv *priv = dev_get_priv(clk->dev);
--
2.42.0
More information about the U-Boot
mailing list