clk子系統 - 程式碼分析
阿新 • • 發佈:2018-11-03
通過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