clk: renesas: rzg2l: Add FOUTPOSTDIV clk support
PLL5 generates FOUTPOSTDIV clk and is sourced by LCDC/DSI modules. The FOUTPOSTDIV is connected to PLL5_4 MUX. Video clock is sourced from DSI divider which is connected to PLL5_4 MUX. This patch adds support for generating FOUTPOSTDIV clk. Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com> Link: https://lore.kernel.org/r/20220430114156.6260-2-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
This commit is contained in:
parent
0ab55cf183
commit
1561380ee7
@ -27,6 +27,7 @@
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include <dt-bindings/clock/renesas-cpg-mssr.h>
|
||||
|
||||
@ -64,6 +65,21 @@ struct sd_hw_data {
|
||||
|
||||
#define to_sd_hw_data(_hw) container_of(_hw, struct sd_hw_data, hw)
|
||||
|
||||
struct rzg2l_pll5_param {
|
||||
u32 pl5_fracin;
|
||||
u8 pl5_refdiv;
|
||||
u8 pl5_intin;
|
||||
u8 pl5_postdiv1;
|
||||
u8 pl5_postdiv2;
|
||||
u8 pl5_spread;
|
||||
};
|
||||
|
||||
struct rzg2l_pll5_mux_dsi_div_param {
|
||||
u8 clksrc;
|
||||
u8 dsi_div_a;
|
||||
u8 dsi_div_b;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rzg2l_cpg_priv - Clock Pulse Generator Private Data
|
||||
*
|
||||
@ -77,6 +93,7 @@ struct sd_hw_data {
|
||||
* @num_resets: Number of Module Resets in info->resets[]
|
||||
* @last_dt_core_clk: ID of the last Core Clock exported to DT
|
||||
* @info: Pointer to platform data
|
||||
* @pll5_mux_dsi_div_params: pll5 mux and dsi div parameters
|
||||
*/
|
||||
struct rzg2l_cpg_priv {
|
||||
struct reset_controller_dev rcdev;
|
||||
@ -91,6 +108,8 @@ struct rzg2l_cpg_priv {
|
||||
unsigned int last_dt_core_clk;
|
||||
|
||||
const struct rzg2l_cpg_info *info;
|
||||
|
||||
struct rzg2l_pll5_mux_dsi_div_param mux_dsi_div_params;
|
||||
};
|
||||
|
||||
static void rzg2l_cpg_del_clk_provider(void *data)
|
||||
@ -264,6 +283,196 @@ rzg2l_cpg_sd_mux_clk_register(const struct cpg_core_clk *core,
|
||||
return clk_hw->clk;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
rzg2l_cpg_get_foutpostdiv_rate(struct rzg2l_pll5_param *params,
|
||||
unsigned long rate)
|
||||
{
|
||||
unsigned long foutpostdiv_rate;
|
||||
|
||||
params->pl5_intin = rate / MEGA;
|
||||
params->pl5_fracin = div_u64(((u64)rate % MEGA) << 24, MEGA);
|
||||
params->pl5_refdiv = 2;
|
||||
params->pl5_postdiv1 = 1;
|
||||
params->pl5_postdiv2 = 1;
|
||||
params->pl5_spread = 0x16;
|
||||
|
||||
foutpostdiv_rate =
|
||||
EXTAL_FREQ_IN_MEGA_HZ * MEGA / params->pl5_refdiv *
|
||||
((((params->pl5_intin << 24) + params->pl5_fracin)) >> 24) /
|
||||
(params->pl5_postdiv1 * params->pl5_postdiv2);
|
||||
|
||||
return foutpostdiv_rate;
|
||||
}
|
||||
|
||||
struct sipll5 {
|
||||
struct clk_hw hw;
|
||||
u32 conf;
|
||||
unsigned long foutpostdiv_rate;
|
||||
struct rzg2l_cpg_priv *priv;
|
||||
};
|
||||
|
||||
#define to_sipll5(_hw) container_of(_hw, struct sipll5, hw)
|
||||
|
||||
static unsigned long rzg2l_cpg_get_vclk_rate(struct clk_hw *hw,
|
||||
unsigned long rate)
|
||||
{
|
||||
struct sipll5 *sipll5 = to_sipll5(hw);
|
||||
struct rzg2l_cpg_priv *priv = sipll5->priv;
|
||||
unsigned long vclk;
|
||||
|
||||
vclk = rate / ((1 << priv->mux_dsi_div_params.dsi_div_a) *
|
||||
(priv->mux_dsi_div_params.dsi_div_b + 1));
|
||||
|
||||
if (priv->mux_dsi_div_params.clksrc)
|
||||
vclk /= 2;
|
||||
|
||||
return vclk;
|
||||
}
|
||||
|
||||
static unsigned long rzg2l_cpg_sipll5_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sipll5 *sipll5 = to_sipll5(hw);
|
||||
unsigned long pll5_rate = sipll5->foutpostdiv_rate;
|
||||
|
||||
if (!pll5_rate)
|
||||
pll5_rate = parent_rate;
|
||||
|
||||
return pll5_rate;
|
||||
}
|
||||
|
||||
static long rzg2l_cpg_sipll5_round_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
|
||||
static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sipll5 *sipll5 = to_sipll5(hw);
|
||||
struct rzg2l_cpg_priv *priv = sipll5->priv;
|
||||
struct rzg2l_pll5_param params;
|
||||
unsigned long vclk_rate;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* OSC --> PLL5 --> FOUTPOSTDIV-->|
|
||||
* | | -->MUX -->DIV_DSIA_B -->M3 -->VCLK
|
||||
* |--FOUT1PH0-->|
|
||||
*
|
||||
* Based on the dot clock, the DSI divider clock calculates the parent
|
||||
* rate and the pll5 parameters for generating FOUTPOSTDIV. It propagates
|
||||
* that info to sipll5 which sets parameters for generating FOUTPOSTDIV.
|
||||
*
|
||||
* OSC --> PLL5 --> FOUTPOSTDIV
|
||||
*/
|
||||
|
||||
if (!rate)
|
||||
return -EINVAL;
|
||||
|
||||
vclk_rate = rzg2l_cpg_get_vclk_rate(hw, rate);
|
||||
sipll5->foutpostdiv_rate =
|
||||
rzg2l_cpg_get_foutpostdiv_rate(¶ms, vclk_rate);
|
||||
|
||||
/* Put PLL5 into standby mode */
|
||||
writel(CPG_SIPLL5_STBY_RESETB_WEN, priv->base + CPG_SIPLL5_STBY);
|
||||
ret = readl_poll_timeout(priv->base + CPG_SIPLL5_MON, val,
|
||||
!(val & CPG_SIPLL5_MON_PLL5_LOCK), 100, 250000);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to release pll5 lock");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Output clock setting 1 */
|
||||
writel(CPG_SIPLL5_CLK1_POSTDIV1_WEN | CPG_SIPLL5_CLK1_POSTDIV2_WEN |
|
||||
CPG_SIPLL5_CLK1_REFDIV_WEN | (params.pl5_postdiv1 << 0) |
|
||||
(params.pl5_postdiv2 << 4) | (params.pl5_refdiv << 8),
|
||||
priv->base + CPG_SIPLL5_CLK1);
|
||||
|
||||
/* Output clock setting, SSCG modulation value setting 3 */
|
||||
writel((params.pl5_fracin << 8), priv->base + CPG_SIPLL5_CLK3);
|
||||
|
||||
/* Output clock setting 4 */
|
||||
writel(CPG_SIPLL5_CLK4_RESV_LSB | (params.pl5_intin << 16),
|
||||
priv->base + CPG_SIPLL5_CLK4);
|
||||
|
||||
/* Output clock setting 5 */
|
||||
writel(params.pl5_spread, priv->base + CPG_SIPLL5_CLK5);
|
||||
|
||||
/* PLL normal mode setting */
|
||||
writel(CPG_SIPLL5_STBY_DOWNSPREAD_WEN | CPG_SIPLL5_STBY_SSCG_EN_WEN |
|
||||
CPG_SIPLL5_STBY_RESETB_WEN | CPG_SIPLL5_STBY_RESETB,
|
||||
priv->base + CPG_SIPLL5_STBY);
|
||||
|
||||
/* PLL normal mode transition, output clock stability check */
|
||||
ret = readl_poll_timeout(priv->base + CPG_SIPLL5_MON, val,
|
||||
(val & CPG_SIPLL5_MON_PLL5_LOCK), 100, 250000);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failed to lock pll5");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops rzg2l_cpg_sipll5_ops = {
|
||||
.recalc_rate = rzg2l_cpg_sipll5_recalc_rate,
|
||||
.round_rate = rzg2l_cpg_sipll5_round_rate,
|
||||
.set_rate = rzg2l_cpg_sipll5_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
rzg2l_cpg_sipll5_register(const struct cpg_core_clk *core,
|
||||
struct clk **clks,
|
||||
struct rzg2l_cpg_priv *priv)
|
||||
{
|
||||
const struct clk *parent;
|
||||
struct clk_init_data init;
|
||||
const char *parent_name;
|
||||
struct sipll5 *sipll5;
|
||||
struct clk_hw *clk_hw;
|
||||
int ret;
|
||||
|
||||
parent = clks[core->parent & 0xffff];
|
||||
if (IS_ERR(parent))
|
||||
return ERR_CAST(parent);
|
||||
|
||||
sipll5 = devm_kzalloc(priv->dev, sizeof(*sipll5), GFP_KERNEL);
|
||||
if (!sipll5)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = core->name;
|
||||
parent_name = __clk_get_name(parent);
|
||||
init.ops = &rzg2l_cpg_sipll5_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
sipll5->hw.init = &init;
|
||||
sipll5->conf = core->conf;
|
||||
sipll5->priv = priv;
|
||||
|
||||
writel(CPG_SIPLL5_STBY_SSCG_EN_WEN | CPG_SIPLL5_STBY_RESETB_WEN |
|
||||
CPG_SIPLL5_STBY_RESETB, priv->base + CPG_SIPLL5_STBY);
|
||||
|
||||
clk_hw = &sipll5->hw;
|
||||
clk_hw->init = &init;
|
||||
|
||||
ret = devm_clk_hw_register(priv->dev, clk_hw);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
priv->mux_dsi_div_params.clksrc = 1; /* Use clk src 1 for DSI */
|
||||
priv->mux_dsi_div_params.dsi_div_a = 1; /* Divided by 2 */
|
||||
priv->mux_dsi_div_params.dsi_div_b = 2; /* Divided by 3 */
|
||||
|
||||
return clk_hw->clk;
|
||||
}
|
||||
|
||||
struct pll_clk {
|
||||
struct clk_hw hw;
|
||||
unsigned int conf;
|
||||
@ -418,6 +627,9 @@ rzg2l_cpg_register_core_clk(const struct cpg_core_clk *core,
|
||||
clk = rzg2l_cpg_pll_clk_register(core, priv->clks,
|
||||
priv->base, priv);
|
||||
break;
|
||||
case CLK_TYPE_SIPLL5:
|
||||
clk = rzg2l_cpg_sipll5_register(core, priv->clks, priv);
|
||||
break;
|
||||
case CLK_TYPE_DIV:
|
||||
clk = rzg2l_cpg_div_clk_register(core, priv->clks,
|
||||
priv->base, priv);
|
||||
|
@ -9,6 +9,12 @@
|
||||
#ifndef __RENESAS_RZG2L_CPG_H__
|
||||
#define __RENESAS_RZG2L_CPG_H__
|
||||
|
||||
#define CPG_SIPLL5_STBY (0x140)
|
||||
#define CPG_SIPLL5_CLK1 (0x144)
|
||||
#define CPG_SIPLL5_CLK3 (0x14C)
|
||||
#define CPG_SIPLL5_CLK4 (0x150)
|
||||
#define CPG_SIPLL5_CLK5 (0x154)
|
||||
#define CPG_SIPLL5_MON (0x15C)
|
||||
#define CPG_PL1_DDIV (0x200)
|
||||
#define CPG_PL2_DDIV (0x204)
|
||||
#define CPG_PL3A_DDIV (0x208)
|
||||
@ -19,6 +25,16 @@
|
||||
#define CPG_PL6_SSEL (0x414)
|
||||
#define CPG_PL6_ETH_SSEL (0x418)
|
||||
|
||||
#define CPG_SIPLL5_STBY_RESETB BIT(0)
|
||||
#define CPG_SIPLL5_STBY_RESETB_WEN BIT(16)
|
||||
#define CPG_SIPLL5_STBY_SSCG_EN_WEN BIT(18)
|
||||
#define CPG_SIPLL5_STBY_DOWNSPREAD_WEN BIT(20)
|
||||
#define CPG_SIPLL5_CLK1_POSTDIV1_WEN BIT(16)
|
||||
#define CPG_SIPLL5_CLK1_POSTDIV2_WEN BIT(20)
|
||||
#define CPG_SIPLL5_CLK1_REFDIV_WEN BIT(24)
|
||||
#define CPG_SIPLL5_CLK4_RESV_LSB (0xFF)
|
||||
#define CPG_SIPLL5_MON_PLL5_LOCK BIT(4)
|
||||
|
||||
#define CPG_CLKSTATUS_SELSDHI0_STS BIT(28)
|
||||
#define CPG_CLKSTATUS_SELSDHI1_STS BIT(29)
|
||||
|
||||
@ -49,6 +65,8 @@
|
||||
#define SEL_SDHI0 DDIV_PACK(CPG_PL2SDHI_DSEL, 0, 2)
|
||||
#define SEL_SDHI1 DDIV_PACK(CPG_PL2SDHI_DSEL, 4, 2)
|
||||
|
||||
#define EXTAL_FREQ_IN_MEGA_HZ (24)
|
||||
|
||||
/**
|
||||
* Definitions of CPG Core Clocks
|
||||
*
|
||||
@ -86,6 +104,9 @@ enum clk_types {
|
||||
|
||||
/* Clock with SD clock source selector */
|
||||
CLK_TYPE_SD_MUX,
|
||||
|
||||
/* Clock for SIPLL5 */
|
||||
CLK_TYPE_SIPLL5,
|
||||
};
|
||||
|
||||
#define DEF_TYPE(_name, _id, _type...) \
|
||||
@ -109,6 +130,8 @@ enum clk_types {
|
||||
#define DEF_SD_MUX(_name, _id, _conf, _parent_names, _num_parents) \
|
||||
DEF_TYPE(_name, _id, CLK_TYPE_SD_MUX, .conf = _conf, \
|
||||
.parent_names = _parent_names, .num_parents = _num_parents)
|
||||
#define DEF_PLL5_FOUTPOSTDIV(_name, _id, _parent) \
|
||||
DEF_TYPE(_name, _id, CLK_TYPE_SIPLL5, .parent = _parent)
|
||||
|
||||
/**
|
||||
* struct rzg2l_mod_clk - Module Clocks definitions
|
||||
|
Loading…
Reference in New Issue
Block a user