目录
1.fixed-factor-clk的注册流程
2. of_fixed_factor_clk_setup
3. clk_register_fixed_factor
4. clk_hw_register_fixed_factor
5. clk_factor_round_rate
6. clk_factor_set_rate
7. clk_factor_recalc_rate
1.fixed-factor-clk的注册流程
fix-factor-clk (factor: 因素; 要素; 因子; 因数; (增或减的) 数量,倍数)描述的是固定分频的clk,leo_clk_match数组中fix-factor-clk类型的元素
/**
* drivers/clk/faraday/clk-leo.c
*/
{ .compatible = "leo,rosc0_div4", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,pll2_div5", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,pll2_div50", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,fastboot_div2", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,ddrmclk", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,lcclk", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,gmacclk", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,uart_uclk_src", .data = of_fixed_factor_clk_setup, },
{ .compatible = "leo,irda", .data = of_fixed_factor_clk_setup, },
对应设备树节点
rosc0_div4: rosc0_div4 {
#clock-cells = <0>;
compatible = "leo,rosc0_div4";
clock-mult = <1>;
clock-div = <4>;
clock-output-names = "rosc0_div4";
clocks = <&rosc0>;
};
pll1_div4: pll1_div4 {
#clock-cells = <0>;
compatible = "leo,pll1_div4";
clock-mult = <1>;
clock-div = <4>;
clock-output-names = "pll1_div4";
clocks = <&pll1>;
};
pll2_div5: pll2_div5 {
#clock-cells = <0>;
compatible = "leo,pll2_div5";
clock-mult = <1>;
clock-div = <5>;
clock-output-names = "pll2_div5";
clocks = <&pll2>;
};
pll2_div50: pll2_div50 {
#clock-cells = <0>;
compatible = "leo,pll2_div50";
clock-mult = <1>;
clock-div = <50>;
clock-output-names = "pll2_div50";
clocks = <&pll2>;
};
fastboot_div2: fastboot_div2 {
#clock-cells = <0>;
compatible = "leo,fastboot_div2";
clock-output-names = "fastboot_div2";
clock-mult = <1>;
clock-div = <2>;
clocks = <&fastboot>;
};
ddrmclk: ddrmclk {
#clock-cells = <0>;
compatible = "leo,ddrmclk";
clock-mult = <1>;
clock-div = <1>;
clock-output-names = "ddrmclk";
clocks = <&pll4>;
};
lcclk: lcclk {
#clock-cells = <0>;
compatible = "leo,lcclk";
clock-output-names = "lcclk";
clock-mult = <1>;
clock-div = <2>;
clocks = <&pll5>;
};
uart_uclk_src: uart_uclk_src {
#clock-cells = <0>;
compatible = "leo,uart_uclk_src";
clock-mult = <100>;
clock-div = <425>;
clock-output-names = "uart_uclk_src";
clocks = <&pll1_div4>;
};
irdaclk: irdaclk {
#clock-cells = <0>;
compatible = "leo,irda";
clock-mult = <1>;
clock-div = <5>;
clock-output-names = "irda";
clocks = <&pll2>;
};
gmacclk: gmacclk {
#clock-cells = <0>;
compatible = "leo,gmacclk";
clock-mult = <1>;
clock-div = <20>;
clock-output-names = "gmacclk";
clocks = <&pll1>;
};
2. of_fixed_factor_clk_setup
/**
* drivers/clk/clk-fixed-factor.c
*/
/**
* of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
*/
void __init of_fixed_factor_clk_setup(struct device_node *node)
{
_of_fixed_factor_clk_setup(node);
}
CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
of_fixed_factor_clk_setup);
/**
* drivers/clk/clk-fixed-factor.c
*/
static struct clk *_of_fixed_factor_clk_setup(struct device_node *node)
{
struct clk *clk;
const char *clk_name = node->name;
const char *parent_name;
unsigned long flags = 0;
u32 div, mult;
int ret;
if (of_property_read_u32(node, "clock-div", &div)) {
pr_err("%s Fixed factor clock <%s> must have a clock-div property\n",
__func__, node->name);
return ERR_PTR(-EIO);
}
if (of_property_read_u32(node, "clock-mult", &mult)) {
pr_err("%s Fixed factor clock <%s> must have a clock-mult property\n",
__func__, node->name);
return ERR_PTR(-EIO);
}
of_property_read_string(node, "clock-output-names", &clk_name);
parent_name = of_clk_get_parent_name(node, 0);
if (of_match_node(set_rate_parent_matches, node))
flags |= CLK_SET_RATE_PARENT;
/**
* 见3. clk_register_fixed_factor
*/
clk = clk_register_fixed_factor(NULL, clk_name, parent_name, flags,
mult, div);
if (IS_ERR(clk)) {
/*
* If parent clock is not registered, registration would fail.
* Clear OF_POPULATED flag so that clock registration can be
* attempted again from probe function.
*/
of_node_clear_flag(node, OF_POPULATED);
return clk;
}
/**
*
*/
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
if (ret) {
clk_unregister(clk);
return ERR_PTR(ret);
}
return clk;
}
3. clk_register_fixed_factor
/**
* drivers/clk/clk-fixed-factor.c
*/
struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
unsigned int mult, unsigned int div)
{
struct clk_hw *hw;
/**
* 见4. clk_hw_register_fixed_factor
*/
hw = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult,
div);
if (IS_ERR(hw))
return ERR_CAST(hw);
return hw->clk;
}
EXPORT_SYMBOL_GPL(clk_register_fixed_factor);
4. clk_hw_register_fixed_factor
/**
* include/linux/clk-provider.h
*/
/**
* struct clk_fixed_factor - fixed multiplier and divider clock
*
* @hw: handle between common and hardware-specific interfaces
* @mult: multiplier
* @div: divider
*
* Clock with a fixed multiplier and divider. The output frequency is the
* parent clock rate divided by div and multiplied by mult.
* Implements .recalc_rate, .set_rate and .round_rate
*/
struct clk_fixed_factor {
struct clk_hw hw;
unsigned int mult;
unsigned int div;
};
/**
* drivers/clk/clk-fixed-factor.c
*/
struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
unsigned int mult, unsigned int div)
{
struct clk_fixed_factor *fix;
struct clk_init_data init;
struct clk_hw *hw;
int ret;
fix = kmalloc(sizeof(*fix), GFP_KERNEL);
if (!fix)
return ERR_PTR(-ENOMEM);
/* struct clk_fixed_factor assignments */
fix->mult = mult;
fix->div = div;
fix->hw.init = &init;
init.name = name;
init.ops = &clk_fixed_factor_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = &parent_name;
init.num_parents = 1;
hw = &fix->hw;
/**
* 见Linux clk子系统(4)-fixed-clk文章中的4.clk_hw_register
*/
ret = clk_hw_register(dev, hw);
if (ret) {
kfree(fix);
hw = ERR_PTR(ret);
}
return hw;
}
EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor);
/**
* drivers/clk/clk-fixed-factor.c
*/
const struct clk_ops clk_fixed_factor_ops = {
/**
*见5. clk_factor_round_rate
*/
.round_rate = clk_factor_round_rate,
/**
*见6. clk_factor_set_rate
*/
.set_rate = clk_factor_set_rate,
/**
*见7. clk_factor_recalc_rate
*/
.recalc_rate = clk_factor_recalc_rate,
};
EXPORT_SYMBOL_GPL(clk_fixed_factor_ops);
5. clk_factor_round_rate
/**
* drivers/clk/clk-fixed-factor.c
*/
static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
unsigned long best_parent;
best_parent = (rate / fix->mult) * fix->div;
*prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
}
return (*prate / fix->div) * fix->mult;
}
/**
* drivers/clk/clk.c
*/
unsigned long clk_hw_get_flags(const struct clk_hw *hw)
{
return hw->core->flags;
}
EXPORT_SYMBOL_GPL(clk_hw_get_flags);
/**
* drivers/clk/clk.c
*/
unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate)
{
int ret;
struct clk_rate_request req;
clk_core_get_boundaries(hw->core, &req.min_rate, &req.max_rate);
req.rate = rate;
ret = clk_core_round_rate_nolock(hw->core, &req);
if (ret)
return 0;
return req.rate;
}
EXPORT_SYMBOL_GPL(clk_hw_round_rate);
6. clk_factor_set_rate
/**
* drivers/clk/clk-fixed-factor.c
*/
static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
/*
* We must report success but we can do so unconditionally because
* clk_factor_round_rate returns values that ensure this call is a
* nop.
*/
return 0;
}
7. clk_factor_recalc_rate
/**
* drivers/clk/clk-fixed-factor.c
*/
static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
unsigned long long int rate;
rate = (unsigned long long int)parent_rate * fix->mult;
do_div(rate, fix->div);
return (unsigned long)rate;
}
10. fixed-factor-clk总结
fixed-factor-clk类型的clk也为固定频率的,描述系统经过fix-clk经过固定分频出来的,系统已经提供好相应的实现。