clk: renesas: rzv2h: Add support for dynamic switching divider clocks

Add support for dynamic switching divider clocks.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lore.kernel.org/20240828093822.162855-2-prabhakar.mahadev-lad.rj@bp.renesas.com
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
This commit is contained in:
Lad Prabhakar 2024-08-28 10:38:21 +01:00 committed by Geert Uytterhoeven
parent f0fe60cae6
commit bc4d25fdfa
2 changed files with 201 additions and 3 deletions

View File

@ -45,14 +45,19 @@
#define PDIV(val) FIELD_GET(GENMASK(5, 0), (val))
#define SDIV(val) FIELD_GET(GENMASK(2, 0), (val))
#define DDIV_DIVCTL_WEN(shift) BIT((shift) + 16)
#define GET_MOD_CLK_ID(base, index, bit) \
((base) + ((((index) * (16))) + (bit)))
#define CPG_CLKSTATUS0 (0x700)
/**
* struct rzv2h_cpg_priv - Clock Pulse Generator Private Data
*
* @dev: CPG device
* @base: CPG register block base address
* @rmw_lock: protects register accesses
* @clks: Array containing all Core and Module Clocks
* @num_core_clks: Number of Core Clocks in clks[]
* @num_mod_clks: Number of Module Clocks in clks[]
@ -64,6 +69,7 @@
struct rzv2h_cpg_priv {
struct device *dev;
void __iomem *base;
spinlock_t rmw_lock;
struct clk **clks;
unsigned int num_core_clks;
@ -108,6 +114,21 @@ struct mod_clock {
#define to_mod_clock(_hw) container_of(_hw, struct mod_clock, hw)
/**
* struct ddiv_clk - DDIV clock
*
* @priv: CPG private data
* @div: divider clk
* @mon: monitor bit in CPG_CLKSTATUS0 register
*/
struct ddiv_clk {
struct rzv2h_cpg_priv *priv;
struct clk_divider div;
u8 mon;
};
#define to_ddiv_clock(_div) container_of(_div, struct ddiv_clk, div)
static unsigned long rzv2h_cpg_pll_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
@ -161,7 +182,7 @@ rzv2h_cpg_pll_clk_register(const struct cpg_core_clk *core,
init.num_parents = 1;
pll_clk->hw.init = &init;
pll_clk->conf = core->conf;
pll_clk->conf = core->cfg.conf;
pll_clk->base = base;
pll_clk->priv = priv;
pll_clk->type = core->type;
@ -173,6 +194,143 @@ rzv2h_cpg_pll_clk_register(const struct cpg_core_clk *core,
return pll_clk->hw.clk;
}
static unsigned long rzv2h_ddiv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
unsigned int val;
val = readl(divider->reg) >> divider->shift;
val &= clk_div_mask(divider->width);
return divider_recalc_rate(hw, parent_rate, val, divider->table,
divider->flags, divider->width);
}
static long rzv2h_ddiv_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_divider *divider = to_clk_divider(hw);
return divider_round_rate(hw, rate, prate, divider->table,
divider->width, divider->flags);
}
static int rzv2h_ddiv_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_divider *divider = to_clk_divider(hw);
return divider_determine_rate(hw, req, divider->table, divider->width,
divider->flags);
}
static inline int rzv2h_cpg_wait_ddiv_clk_update_done(void __iomem *base, u8 mon)
{
u32 bitmask = BIT(mon);
u32 val;
return readl_poll_timeout_atomic(base + CPG_CLKSTATUS0, val, !(val & bitmask), 10, 200);
}
static int rzv2h_ddiv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
struct ddiv_clk *ddiv = to_ddiv_clock(divider);
struct rzv2h_cpg_priv *priv = ddiv->priv;
unsigned long flags = 0;
int value;
u32 val;
int ret;
value = divider_get_val(rate, parent_rate, divider->table,
divider->width, divider->flags);
if (value < 0)
return value;
spin_lock_irqsave(divider->lock, flags);
ret = rzv2h_cpg_wait_ddiv_clk_update_done(priv->base, ddiv->mon);
if (ret)
goto ddiv_timeout;
val = readl(divider->reg) | DDIV_DIVCTL_WEN(divider->shift);
val &= ~(clk_div_mask(divider->width) << divider->shift);
val |= (u32)value << divider->shift;
writel(val, divider->reg);
ret = rzv2h_cpg_wait_ddiv_clk_update_done(priv->base, ddiv->mon);
if (ret)
goto ddiv_timeout;
spin_unlock_irqrestore(divider->lock, flags);
return 0;
ddiv_timeout:
spin_unlock_irqrestore(divider->lock, flags);
return ret;
}
static const struct clk_ops rzv2h_ddiv_clk_divider_ops = {
.recalc_rate = rzv2h_ddiv_recalc_rate,
.round_rate = rzv2h_ddiv_round_rate,
.determine_rate = rzv2h_ddiv_determine_rate,
.set_rate = rzv2h_ddiv_set_rate,
};
static struct clk * __init
rzv2h_cpg_ddiv_clk_register(const struct cpg_core_clk *core,
struct rzv2h_cpg_priv *priv)
{
struct ddiv cfg_ddiv = core->cfg.ddiv;
struct clk_init_data init = {};
struct device *dev = priv->dev;
u8 shift = cfg_ddiv.shift;
u8 width = cfg_ddiv.width;
const struct clk *parent;
const char *parent_name;
struct clk_divider *div;
struct ddiv_clk *ddiv;
int ret;
parent = priv->clks[core->parent];
if (IS_ERR(parent))
return ERR_CAST(parent);
parent_name = __clk_get_name(parent);
if ((shift + width) > 16)
return ERR_PTR(-EINVAL);
ddiv = devm_kzalloc(priv->dev, sizeof(*ddiv), GFP_KERNEL);
if (!ddiv)
return ERR_PTR(-ENOMEM);
init.name = core->name;
init.ops = &rzv2h_ddiv_clk_divider_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
ddiv->priv = priv;
ddiv->mon = cfg_ddiv.monbit;
div = &ddiv->div;
div->reg = priv->base + cfg_ddiv.offset;
div->shift = shift;
div->width = width;
div->flags = core->flag;
div->lock = &priv->rmw_lock;
div->hw.init = &init;
div->table = core->dtable;
ret = devm_clk_hw_register(dev, &div->hw);
if (ret)
return ERR_PTR(ret);
return div->hw.clk;
}
static struct clk
*rzv2h_cpg_clk_src_twocell_get(struct of_phandle_args *clkspec,
void *data)
@ -254,6 +412,9 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core,
case CLK_TYPE_PLL:
clk = rzv2h_cpg_pll_clk_register(core, priv, &rzv2h_cpg_pll_ops);
break;
case CLK_TYPE_DDIV:
clk = rzv2h_cpg_ddiv_clk_register(core, priv);
break;
default:
goto fail;
}
@ -612,6 +773,8 @@ static int __init rzv2h_cpg_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->rmw_lock);
priv->dev = dev;
priv->base = devm_platform_ioremap_resource(pdev, 0);

View File

@ -8,6 +8,29 @@
#ifndef __RENESAS_RZV2H_CPG_H__
#define __RENESAS_RZV2H_CPG_H__
/**
* struct ddiv - Structure for dynamic switching divider
*
* @offset: register offset
* @shift: position of the divider bit
* @width: width of the divider
* @monbit: monitor bit in CPG_CLKSTATUS0 register
*/
struct ddiv {
unsigned int offset:11;
unsigned int shift:4;
unsigned int width:4;
unsigned int monbit:5;
};
#define DDIV_PACK(_offset, _shift, _width, _monbit) \
((struct ddiv){ \
.offset = _offset, \
.shift = _shift, \
.width = _width, \
.monbit = _monbit \
})
/**
* Definitions of CPG Core Clocks
*
@ -23,7 +46,12 @@ struct cpg_core_clk {
unsigned int div;
unsigned int mult;
unsigned int type;
unsigned int conf;
union {
unsigned int conf;
struct ddiv ddiv;
} cfg;
const struct clk_div_table *dtable;
u32 flag;
};
enum clk_types {
@ -31,6 +59,7 @@ enum clk_types {
CLK_TYPE_IN, /* External Clock Input */
CLK_TYPE_FF, /* Fixed Factor Clock */
CLK_TYPE_PLL,
CLK_TYPE_DDIV, /* Dynamic Switching Divider */
};
/* BIT(31) indicates if CLK1/2 are accessible or not */
@ -44,11 +73,17 @@ enum clk_types {
#define DEF_BASE(_name, _id, _type, _parent...) \
DEF_TYPE(_name, _id, _type, .parent = _parent)
#define DEF_PLL(_name, _id, _parent, _conf) \
DEF_TYPE(_name, _id, CLK_TYPE_PLL, .parent = _parent, .conf = _conf)
DEF_TYPE(_name, _id, CLK_TYPE_PLL, .parent = _parent, .cfg.conf = _conf)
#define DEF_INPUT(_name, _id) \
DEF_TYPE(_name, _id, CLK_TYPE_IN)
#define DEF_FIXED(_name, _id, _parent, _mult, _div) \
DEF_BASE(_name, _id, CLK_TYPE_FF, _parent, .div = _div, .mult = _mult)
#define DEF_DDIV(_name, _id, _parent, _ddiv_packed, _dtable) \
DEF_TYPE(_name, _id, CLK_TYPE_DDIV, \
.cfg.ddiv = _ddiv_packed, \
.parent = _parent, \
.dtable = _dtable, \
.flag = CLK_DIVIDER_HIWORD_MASK)
/**
* struct rzv2h_mod_clk - Module Clocks definitions