clk/qcom: fix rcg divider value
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. Reviewed-by: Sumit Garg <sumit.garg@linaro.org> Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
This commit is contained in:
@@ -78,7 +78,7 @@ static struct vote_clk gcc_blsp1_ahb_clk = {
|
||||
/* SDHCI */
|
||||
static int clk_init_sdc(struct msm_clk_priv *priv, int slot, uint rate)
|
||||
{
|
||||
int div = 8; /* 100MHz default */
|
||||
int div = 15; /* 100MHz default */
|
||||
|
||||
if (rate == 200000000)
|
||||
div = 4;
|
||||
|
||||
@@ -65,7 +65,7 @@ static struct vote_clk gcc_blsp2_ahb_clk = {
|
||||
|
||||
static int clk_init_sdc(struct msm_clk_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,
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <dm/lists.h>
|
||||
#include <errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <reset-uclass.h>
|
||||
|
||||
@@ -68,30 +70,42 @@ 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));
|
||||
}
|
||||
|
||||
#define CFG_MODE_DUAL_EDGE (0x2 << 12) /* Counter mode */
|
||||
#define CFG_SRC_DIV_MASK 0b11111
|
||||
#define CFG_SRC_SEL_SHIFT 8
|
||||
#define CFG_SRC_SEL_MASK (0x7 << CFG_SRC_SEL_SHIFT)
|
||||
#define CFG_MODE_SHIFT 12
|
||||
#define CFG_MODE_MASK (0x3 << CFG_MODE_SHIFT)
|
||||
#define CFG_MODE_DUAL_EDGE (0x2 << CFG_MODE_SHIFT)
|
||||
#define CFG_HW_CLK_CTRL_MASK BIT(20)
|
||||
|
||||
#define CFG_MASK 0x3FFF
|
||||
|
||||
#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)
|
||||
{
|
||||
u32 cfg;
|
||||
/* M value for MND divider. */
|
||||
u32 m_val = m;
|
||||
u32 n_minus_m = n - m;
|
||||
/* NOT(N-M) value for MND divider. */
|
||||
u32 n_val = ~((n) - (m)) * !!(n);
|
||||
u32 n_val = ~n_minus_m * !!(n);
|
||||
/* NOT 2D value for MND divider. */
|
||||
u32 d_val = ~(n);
|
||||
u32 d_val = ~(clamp_t(u32, n, m, n_minus_m));
|
||||
u32 mask = BIT(mnd_width) - 1;
|
||||
|
||||
debug("m %#x n %#x d %#x div %#x mask %#x\n", m_val, n_val, d_val, div, mask);
|
||||
@@ -103,15 +117,13 @@ void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs,
|
||||
|
||||
/* setup src select and divider */
|
||||
cfg = readl(base + regs->cfg_rcgr);
|
||||
cfg &= ~CFG_MASK;
|
||||
cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */
|
||||
cfg &= ~(CFG_SRC_SEL_MASK | CFG_MODE_MASK | CFG_HW_CLK_CTRL_MASK);
|
||||
cfg |= source & CFG_SRC_SEL_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_SRC_DIV_MASK;
|
||||
|
||||
if (n_val)
|
||||
if (n && n != m)
|
||||
cfg |= CFG_MODE_DUAL_EDGE;
|
||||
|
||||
writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */
|
||||
@@ -128,7 +140,7 @@ void clk_rcg_set_rate(phys_addr_t base, const struct bcr_regs *regs, int div,
|
||||
|
||||
/* setup src select and divider */
|
||||
cfg = readl(base + regs->cfg_rcgr);
|
||||
cfg &= ~CFG_MASK;
|
||||
cfg &= ~(CFG_SRC_SEL_MASK | CFG_MODE_MASK | CFG_HW_CLK_CTRL_MASK);
|
||||
cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */
|
||||
|
||||
/*
|
||||
@@ -136,7 +148,7 @@ void clk_rcg_set_rate(phys_addr_t base, const struct bcr_regs *regs, int div,
|
||||
* for simplicity, we will support integers only
|
||||
*/
|
||||
if (div)
|
||||
cfg |= (2 * div - 1) & CFG_DIVIDER_MASK;
|
||||
cfg |= (2 * div - 1) & CFG_SRC_DIV_MASK;
|
||||
|
||||
writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */
|
||||
|
||||
@@ -144,6 +156,22 @@ void clk_rcg_set_rate(phys_addr_t base, const struct bcr_regs *regs, int div,
|
||||
clk_bcr_update(base + regs->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;
|
||||
}
|
||||
|
||||
static int msm_clk_probe(struct udevice *dev)
|
||||
{
|
||||
struct msm_clk_data *data = (struct msm_clk_data *)dev_get_driver_data(dev);
|
||||
|
||||
@@ -32,6 +32,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;
|
||||
@@ -71,6 +81,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,
|
||||
|
||||
@@ -203,7 +203,7 @@ static ulong qcs404_clk_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));
|
||||
@@ -213,16 +213,16 @@ static ulong qcs404_clk_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:
|
||||
@@ -239,7 +239,7 @@ static int qcs404_clk_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:
|
||||
@@ -261,14 +261,14 @@ static int qcs404_clk_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:
|
||||
|
||||
@@ -27,16 +27,6 @@
|
||||
#define SE9_UART_APPS_N 0x18154
|
||||
#define SE9_UART_APPS_D 0x18158
|
||||
|
||||
#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),
|
||||
@@ -64,22 +54,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_clk_set_rate(struct clk *clk, ulong rate)
|
||||
{
|
||||
struct msm_clk_priv *priv = dev_get_priv(clk->dev);
|
||||
|
||||
Reference in New Issue
Block a user