mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-19 15:57:56 -05:00
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:
committed by
Geert Uytterhoeven
parent
f0fe60cae6
commit
bc4d25fdfa
@@ -45,14 +45,19 @@
|
|||||||
#define PDIV(val) FIELD_GET(GENMASK(5, 0), (val))
|
#define PDIV(val) FIELD_GET(GENMASK(5, 0), (val))
|
||||||
#define SDIV(val) FIELD_GET(GENMASK(2, 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) \
|
#define GET_MOD_CLK_ID(base, index, bit) \
|
||||||
((base) + ((((index) * (16))) + (bit)))
|
((base) + ((((index) * (16))) + (bit)))
|
||||||
|
|
||||||
|
#define CPG_CLKSTATUS0 (0x700)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct rzv2h_cpg_priv - Clock Pulse Generator Private Data
|
* struct rzv2h_cpg_priv - Clock Pulse Generator Private Data
|
||||||
*
|
*
|
||||||
* @dev: CPG device
|
* @dev: CPG device
|
||||||
* @base: CPG register block base address
|
* @base: CPG register block base address
|
||||||
|
* @rmw_lock: protects register accesses
|
||||||
* @clks: Array containing all Core and Module Clocks
|
* @clks: Array containing all Core and Module Clocks
|
||||||
* @num_core_clks: Number of Core Clocks in clks[]
|
* @num_core_clks: Number of Core Clocks in clks[]
|
||||||
* @num_mod_clks: Number of Module Clocks in clks[]
|
* @num_mod_clks: Number of Module Clocks in clks[]
|
||||||
@@ -64,6 +69,7 @@
|
|||||||
struct rzv2h_cpg_priv {
|
struct rzv2h_cpg_priv {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
spinlock_t rmw_lock;
|
||||||
|
|
||||||
struct clk **clks;
|
struct clk **clks;
|
||||||
unsigned int num_core_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)
|
#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,
|
static unsigned long rzv2h_cpg_pll_clk_recalc_rate(struct clk_hw *hw,
|
||||||
unsigned long parent_rate)
|
unsigned long parent_rate)
|
||||||
{
|
{
|
||||||
@@ -161,7 +182,7 @@ rzv2h_cpg_pll_clk_register(const struct cpg_core_clk *core,
|
|||||||
init.num_parents = 1;
|
init.num_parents = 1;
|
||||||
|
|
||||||
pll_clk->hw.init = &init;
|
pll_clk->hw.init = &init;
|
||||||
pll_clk->conf = core->conf;
|
pll_clk->conf = core->cfg.conf;
|
||||||
pll_clk->base = base;
|
pll_clk->base = base;
|
||||||
pll_clk->priv = priv;
|
pll_clk->priv = priv;
|
||||||
pll_clk->type = core->type;
|
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;
|
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
|
static struct clk
|
||||||
*rzv2h_cpg_clk_src_twocell_get(struct of_phandle_args *clkspec,
|
*rzv2h_cpg_clk_src_twocell_get(struct of_phandle_args *clkspec,
|
||||||
void *data)
|
void *data)
|
||||||
@@ -254,6 +412,9 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core,
|
|||||||
case CLK_TYPE_PLL:
|
case CLK_TYPE_PLL:
|
||||||
clk = rzv2h_cpg_pll_clk_register(core, priv, &rzv2h_cpg_pll_ops);
|
clk = rzv2h_cpg_pll_clk_register(core, priv, &rzv2h_cpg_pll_ops);
|
||||||
break;
|
break;
|
||||||
|
case CLK_TYPE_DDIV:
|
||||||
|
clk = rzv2h_cpg_ddiv_clk_register(core, priv);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -612,6 +773,8 @@ static int __init rzv2h_cpg_probe(struct platform_device *pdev)
|
|||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spin_lock_init(&priv->rmw_lock);
|
||||||
|
|
||||||
priv->dev = dev;
|
priv->dev = dev;
|
||||||
|
|
||||||
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||||
|
|||||||
@@ -8,6 +8,29 @@
|
|||||||
#ifndef __RENESAS_RZV2H_CPG_H__
|
#ifndef __RENESAS_RZV2H_CPG_H__
|
||||||
#define __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
|
* Definitions of CPG Core Clocks
|
||||||
*
|
*
|
||||||
@@ -23,7 +46,12 @@ struct cpg_core_clk {
|
|||||||
unsigned int div;
|
unsigned int div;
|
||||||
unsigned int mult;
|
unsigned int mult;
|
||||||
unsigned int type;
|
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 {
|
enum clk_types {
|
||||||
@@ -31,6 +59,7 @@ enum clk_types {
|
|||||||
CLK_TYPE_IN, /* External Clock Input */
|
CLK_TYPE_IN, /* External Clock Input */
|
||||||
CLK_TYPE_FF, /* Fixed Factor Clock */
|
CLK_TYPE_FF, /* Fixed Factor Clock */
|
||||||
CLK_TYPE_PLL,
|
CLK_TYPE_PLL,
|
||||||
|
CLK_TYPE_DDIV, /* Dynamic Switching Divider */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* BIT(31) indicates if CLK1/2 are accessible or not */
|
/* BIT(31) indicates if CLK1/2 are accessible or not */
|
||||||
@@ -44,11 +73,17 @@ enum clk_types {
|
|||||||
#define DEF_BASE(_name, _id, _type, _parent...) \
|
#define DEF_BASE(_name, _id, _type, _parent...) \
|
||||||
DEF_TYPE(_name, _id, _type, .parent = _parent)
|
DEF_TYPE(_name, _id, _type, .parent = _parent)
|
||||||
#define DEF_PLL(_name, _id, _parent, _conf) \
|
#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) \
|
#define DEF_INPUT(_name, _id) \
|
||||||
DEF_TYPE(_name, _id, CLK_TYPE_IN)
|
DEF_TYPE(_name, _id, CLK_TYPE_IN)
|
||||||
#define DEF_FIXED(_name, _id, _parent, _mult, _div) \
|
#define DEF_FIXED(_name, _id, _parent, _mult, _div) \
|
||||||
DEF_BASE(_name, _id, CLK_TYPE_FF, _parent, .div = _div, .mult = _mult)
|
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
|
* struct rzv2h_mod_clk - Module Clocks definitions
|
||||||
|
|||||||
Reference in New Issue
Block a user