Commit 1561380e authored by Biju Das's avatar Biju Das Committed by Geert Uytterhoeven
Browse files

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: default avatarBiju 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: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
parent 0ab55cf1
Loading
Loading
Loading
Loading
+212 −0
Original line number Diff line number Diff line
@@ -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(&params, 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);
+23 −0
Original line number Diff line number Diff line
@@ -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