clk子系统 - 代码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/l289123557/article/details/80166078

通过clk驱动框架可以看出,clk主要分core和hardware两层,而core层的主要函数是clk_register,它是把clk注册到系统中,而hardware层的注册函数因种类而异,不过最终都会统一调用clk_register来注册,本文只拿gate来举例;另外本文介绍下操作clk的主要API的过程

1.clk_register

clk_register首先分配一个结构体clk,然后调用_clk_register来进行实际的注册

1.1_clk_register

static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk)

这个函数主要根据hardware结构体clk_hw来对clk进行初始化:

clk->ops = hw->init->ops;
clk->hw = hw;
clk->flags = hw->init->flags;
clk->num_parents = hw->init->num_parents;

for (i = 0; i < clk->num_parents; i++) {
    clk->parent_names[i] = kstrdup(hw->init->parent_names[i],
                    GFP_KERNEL);
}

接着调用__clk_init来进行初始化

1.2__clk_init

int __clk_init(struct device *dev, struct clk *clk)

下面根据代码分步骤来介绍下初始化过程:

  • 检查各个clk字段是否符合要求
  • 如果clk有多个parent,那么分配一段内存保存多个parent,并寻找现存系统中适配的parent
  • 调用__clk_init_parent来找到parent,此步骤主要是用clk自己的ops->get_parent来寻找parent(上一步已经适配了parent,为什么还要多此一举,首先上一步有了条件是多个parent,另外上一步在适配parent的时候是利用的parent_names来做的,不能保证一定会寻找到)
  • 把clk加入系统链表:clk有parent,那么要加入clk->parent->children;如果clk没有parent,那么clk有标志CLK_IS_ROOT,就加入到clk_root_list,否则加入到clk_orphan_list
static HLIST_HEAD(clk_root_list);--------clk的parent链表
static HLIST_HEAD(clk_orphan_list);------clk的孤儿链表
  • 设置clk的rate:

    if (clk->ops->recalc_rate)
     clk->rate = clk->ops->recalc_rate(clk->hw,
             __clk_get_rate(clk->parent));
    else if (clk->parent)
     clk->rate = clk->parent->rate;
    else
     clk->rate = 0;
    

2clk_register_HARDWARE

2.1注册函数

不同hardware的注册函数如下:
(include/linux/clk-provider.h)

struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
        const char *parent_name, unsigned long flags,
        unsigned long fixed_rate);

struct clk *clk_register_gate(struct device *dev, const char *name,
        const char *parent_name, unsigned long flags,
        void __iomem *reg, u8 bit_idx,
        u8 clk_gate_flags, spinlock_t *lock);

struct clk *clk_register_divider(struct device *dev, const char *name,
        const char *parent_name, unsigned long flags,
        void __iomem *reg, u8 shift, u8 width,
        u8 clk_divider_flags, spinlock_t *lock);

struct clk *clk_register_mux(struct device *dev, const char *name,
        const char **parent_names, u8 num_parents, unsigned long flags,
        void __iomem *reg, u8 shift, u8 width,
        u8 clk_mux_flags, spinlock_t *lock);


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 *clk_register_composite(struct device *dev, const char *name,
        const char **parent_names, int num_parents,
        struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
        struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
        struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
        unsigned long flags);

2.2clk_register_gate


struct clk *clk_register_gate(struct device *dev, const char *name,
        const char *parent_name, unsigned long flags,
        void __iomem *reg, u8 bit_idx,
        u8 clk_gate_flags, spinlock_t *lock)
{
    struct clk_gate *gate;
    struct clk *clk;
    struct clk_init_data init;

    if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
        if (bit_idx > 16) {
            pr_err("gate bit exceeds LOWORD field\n");
            return ERR_PTR(-EINVAL);
        }
    }

    /* allocate the gate */
    gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
    if (!gate) {
        pr_err("%s: could not allocate gated clk\n", __func__);
        return ERR_PTR(-ENOMEM);
    }

    init.name = name;
    init.ops = &clk_gate_ops;
    init.flags = flags | CLK_IS_BASIC;
    init.parent_names = (parent_name ? &parent_name: NULL);
    init.num_parents = (parent_name ? 1 : 0);

    /* struct clk_gate assignments */
    gate->reg = reg;
    gate->bit_idx = bit_idx;
    gate->flags = clk_gate_flags;
    gate->lock = lock;
    gate->hw.init = &init;

    clk = clk_register(dev, &gate->hw);

    if (IS_ERR(clk))
        kfree(gate);

    return clk;
}

从代码可以看出,不同的hardware注册函数,主要是根据其需要分别初始化不同的clk_hw,最终都要调用clk_register来进行注册

3几个clk-API的执行流程

clk的API有许多,具体参考include/linux/clk.h,下面介绍两个典型的:使能clk和设置clk-rate

3.1 clk-enable

int clk_enable(struct clk *clk)
{
    unsigned long flags;
    int ret;

    flags = clk_enable_lock();
    ret = __clk_enable(clk);
    clk_enable_unlock(flags);

    return ret;
}
static int __clk_enable(struct clk *clk)
{
    int ret = 0;

    if (!clk)
        return 0;

    if (WARN_ON(clk->prepare_count == 0))
        return -ESHUTDOWN;

    if (clk->enable_count == 0) {
        ret = __clk_enable(clk->parent);

        if (ret)
            return ret;

        if (clk->ops->enable) {
            ret = clk->ops->enable(clk->hw);--------最终调用ops->enable函数
            if (ret) {
                __clk_disable(clk->parent);
                return ret;
            }
        }
    }

    clk->enable_count++;
    return 0;
}

3.2 clk-set-rate

ref.

linux3.10

猜你喜欢

转载自blog.csdn.net/l289123557/article/details/80166078
今日推荐