Provider驱动编写流程
复制上节内容中对Provider驱动编写流程的总结:
1)分析硬件的clock tree,按照上面所描述的分类,将这些clock分类。
2)将clock tree在DTS中描述出来,需要注意以下几2点:
a)对于fixed rate clocks,.compatible固定填充"fixed-clock",并提供"clock-frequency"和"clock-output-names"关键字。之后不需要再driver中做任何处理,clock framework core会帮我们搞定一切。
b)同样,对于fixed factor clock,.compatible为"fixed-factor-clock",并提供"clock-div"、"clock-mult"和"clock-output-names"关键字。clock framework core会帮我们搞定一切。
切记,尽量利用kernel已有资源,不要多写一行代码,简洁的就是美的!
3)对于不能由clock framework core处理的clock,需要在driver中使用struct of_device_id进行匹配,并在初始化时,调用OF模块,查找所有的DTS匹配项,并执行合适的regitser接口,注册clock。
4)注册clock的同时,将返回的struct clk指针,保存在一个数组中,并调用of_clk_add_provider接口,告知clock framework core。
5)最后,也是最重要的一点,多看kernel源代码,多模仿,多抄几遍,什么都熟悉了!
这一节用实际例子来写一个Provider驱动
平台:TsingMicro-TX5112
设计思路
不同芯片的时钟树上的clk对象和他们的继承关系肯定不同。如果为每颗芯片都实现一份clk驱动就会产生大量的冗余和重复。
可以将公共部分放在ts_clk.c中(公共部分指注册clk然后使用of_clk_add_provider告知clock framework core)。
设计一个接口,接口中包含所有种类的clk对象的注册函数。
针对一款芯片的时钟树上的对不同种类clk注册函数的实现 在clk的初始化阶段绑定到该接口的实例上:
这样每款芯片只需要专注于实现自己的clk_xxx_register_plls, clk_xxx_register_comps等这些函数即可。
对于不同芯片的编译的区分在makefile和defconfig中实现。
将需要注册的clk模块的一些信息抽象出来:
其中clk_onecell_data是内核提供的结构
struct clk_onecell_data {
struct clk **clks;
unsigned int clk_num;
};
struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data);
用于保存所有的clk对象以及描述clk对象的总体个数。
clock provider需要把这些struct clk结构保存起来,并调用clock framework的接口,将这些对应信息告知framework的OF模块,这样才可以帮助将clock consumer的DTS描述转换为struct clk结构。该接口如下:
int of_clk_add_provider(struct device_node *np,
struct clk *(*clk_src_get)(struct of_phandle_args *args, void *data),
void *data);
其中np为clk的设备节点,
clk_src_get为获取struct clk指针的回调函数,由clock provider根据实际的逻辑实现。典型的,对于onecell的clk对象内核提供的就是上面提到的of_clk_src_onecell_get函数,他有两个参数:
- args,struct of_phandle_args类型的指针,由DTS在解析参数时传递。例如上面的“clocks = <&clock 32>, <&clock 45>;”,32、45就是通过这个指针传进来的;
- data,保存struct clk结构的指针,通常是一个数组,具体由provider决定。
data,和回调函数中的data意义相同,只是这里由provider提供,get时由clock framework core传递给回调函数。
第0步 clk模块的初始化过程:
static bool probed;
static void __init ts_clk_init(struct device_node *np)
{
int ret;
struct ts_clk_params *paras = NULL;
//pr_info("%s enter.\n", __func__);
if (probed)
return;
paras = (struct ts_clk_params *)ts_clk_get_para();
ret = ts_clk_of_parse(np, paras);
if (ret) {
pr_err("%s of parse failed, ret %d\n", __func__, ret);
return;
}
ts_clk_init_funcs(¶s->funcs);
/* PLL clocks */
ret = ts_clk_register_plls(paras);
if (ret) {
pr_err("%s register plls failed, ret %d\n", __func__, ret);
return;
}
/* Composite clocks with mux, with or without divider or gate */
ret = ts_clk_register_comps(paras);
if (ret) {
pr_err("%s register comps, ret %d\n", __func__, ret);
return;
}
/* Composite clocks without mux */
ret = ts_clk_register_comps_without_mux(paras);
if (ret) {
pr_err("%s register comps without mux failed, ret %d\n",
__func__, ret);
return;
}
/* Dividing clocks */
ret = ts_clk_register_dividers(paras);
if (ret) {
pr_err("%s register dividers failed, ret %d\n", __func__, ret);
return;
}
/* Gated clocks */
ret = ts_clk_register_gates(paras);
if (ret) {
pr_err("%s register gates, ret %d\n", __func__, ret);
return;
}
ret = ts_clk_add_provider(np, paras);
if (ret) {
pr_err("%s add provider, ret %d\n", __func__, ret);
return;
}
probed = true;
//pr_info("%s exit.\n", __func__);
}
CLK_OF_DECLARE(ts_clk, "ts,ts-common-clk", ts_clk_init);
这里的ts_clk_register_xxx内部就调用到了接口中的相应的函数:
第2步:实现接口中对应的注册函数
分析硬件的clock tree,按照上面所描述的分类,将这些clock分类。
对于多种clk这里不可能全部讲一遍,挑选某种clk来讲解更现实,这里选取gate类clk的注册讲解。
针对一块芯片上的所有clk对象的描述,用静态定义的方式描述在代码中:
#define GATE_LIST \
GATE(TS_CLK_TX5112_SKE_CLK, BUS_CLK_AXI_EN, 4, 0) \
GATE(TS_CLK_TX5112_OCRAM_ACLK, BUS_CLK_AXI_EN, 3, 0) \
GATE(TS_CLK_TX5112_MEM_DMA_ACLK, BUS_CLK_AXI_EN, 2, 0) \
GATE(TS_CLK_TX5112_AUD_CODEC_HCLK, BUS_CLK_AHB_EN, 10, 0) \
GATE(TS_CLK_TX5112_OSPI_HCLK, BUS_CLK_AHB_EN, 9, 0) \
GATE(TS_CLK_TX5112_GMAC_HCLK, BUS_CLK_AHB_EN, 8, 0) \
GATE(TS_CLK_TX5112_USB2C_HCLK, BUS_CLK_AHB_EN, 7, 0) \
GATE(TS_CLK_TX5112_SDHC1_HCLK, BUS_CLK_AHB_EN, 6, 0) \
GATE(TS_CLK_TX5112_SDHC0_HCLK, BUS_CLK_AHB_EN, 5, 0) \
GATE(TS_CLK_TX5112_PERI_DMA1_HCLK, BUS_CLK_AHB_EN, 4, 0) \
GATE(TS_CLK_TX5112_PERI_DMA0_HCLK, BUS_CLK_AHB_EN, 3, 0) \
GATE(TS_CLK_TX5112_BOOTROM_HCLK, BUS_CLK_AHB_EN, 2, 0) \
GATE(TS_CLK_TX5112_GPIO_B_PCLK, BUS_CLK_APB_EN, 31, 4) \
GATE(TS_CLK_TX5112_GPIO_A_PCLK, BUS_CLK_APB_EN, 30, 4) \
GATE(TS_CLK_TX5112_PDM_PCLK, BUS_CLK_APB_EN, 29, 3) \
GATE(TS_CLK_TX5112_SYS_REG_PCLK, BUS_CLK_APB_EN, 27, 9) \
GATE(TS_CLK_TX5112_OTPC_PCLK, BUS_CLK_APB_EN, 26, 9) \
GATE(TS_CLK_TX5112_OSPI_PCLK, BUS_CLK_APB_EN, 25, 8) \
GATE(TS_CLK_TX5112_PWM_PCLK, BUS_CLK_APB_EN, 24, 7) \
GATE(TS_CLK_TX5112_TMR_PCLK, BUS_CLK_APB_EN, 23, 6) \
GATE(TS_CLK_TX5112_WDT_PCLK, BUS_CLK_APB_EN, 22, 5) \
GATE(TS_CLK_TX5112_GPIO_PCLK, BUS_CLK_APB_EN, 21, 4) \
GATE(TS_CLK_TX5112_ADC_PCLK, BUS_CLK_APB_EN, 20, 3) \
GATE(TS_CLK_TX5112_I2S0_PCLK, BUS_CLK_APB_EN, 19, 3) \
GATE(TS_CLK_TX5112_USI1_PCLK, BUS_CLK_APB_EN, 18, 2) \
GATE(TS_CLK_TX5112_USI0_PCLK, BUS_CLK_APB_EN, 17, 2) \
GATE(TS_CLK_TX5112_UART1_PCLK, BUS_CLK_APB_EN, 15, 1) \
GATE(TS_CLK_TX5112_UART0_PCLK, BUS_CLK_APB_EN, 14, 1) \
GATE(TS_CLK_TX5112_SD0_CCLK_SMPL, SDHC0_CCLK_CFG, 12, 0) \
GATE(TS_CLK_TX5112_SD0_CCLK_DRV, SDHC0_CCLK_CFG, 11, 0) \
GATE(TS_CLK_TX5112_SD0_CCLK_SMP_EDGE, SDHC0_CCLK_CFG, 10, 0) \
GATE(TS_CLK_TX5112_SD0_CCLK_DRV_EDGE, SDHC0_CCLK_CFG, 9, 0) \
GATE(TS_CLK_TX5112_SD0_CCLK, SDHC0_CCLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_SD1_CCLK_SMPL, SDHC1_CCLK_CFG, 12, 0) \
GATE(TS_CLK_TX5112_SD1_CCLK_DRV, SDHC1_CCLK_CFG, 11, 0) \
GATE(TS_CLK_TX5112_SD1_CCLK_SMP_EDGE, SDHC1_CCLK_CFG, 10, 0) \
GATE(TS_CLK_TX5112_SD1_CCLK_DRV_EDGE, SDHC1_CCLK_CFG, 9, 0) \
GATE(TS_CLK_TX5112_SD1_CCLK, SDHC1_CCLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_OSPI_REF_CLK, OSPI_RCLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_USB_REF_CLK, USB_PHY_CLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_I2C1_PCLK, I2C_ICCLK_CFG0, 3, 0) \
GATE(TS_CLK_TX5112_I2C0_PCLK, I2C_ICCLK_CFG0, 2, 0) \
GATE(TS_CLK_TX5112_I2C2_PCLK, I2C_ICCLK_CFG1, 3, 0) \
GATE(TS_CLK_TX5112_SPI_PCLK, SPI_SSICLK_CFG, 3, 0) \
GATE(TS_CLK_TX5112_TMR_T4CLK, TMR_TCLK_CFG0, 3, 0) \
GATE(TS_CLK_TX5112_TMR_T3CLK, TMR_TCLK_CFG0, 2, 0) \
GATE(TS_CLK_TX5112_TMR_T6CLK, TMR_TCLK_CFG1, 3, 0) \
GATE(TS_CLK_TX5112_TMR_T5CLK, TMR_TCLK_CFG1, 2, 0) \
GATE(TS_CLK_TX5112_TMR_T8CLK, TMR_TCLK_CFG2, 3, 0) \
GATE(TS_CLK_TX5112_TMR_T7CLK, TMR_TCLK_CFG2, 2, 0) \
GATE(TS_CLK_TX5112_I2S0_OCLK_O, I2S0_MCLK_CFG, 10, 0) \
GATE(TS_CLK_TX5112_I2S0_OCLK, I2S0_MCLK_CFG, 3, 0) \
GATE(TS_CLK_TX5112_I2S0_MCLK, I2S0_MCLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_PDM_MCLK, PDM_MCLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_AUD_DAC_PBCLK_INV, AUD_DAC_CLK_CFG0, 7, 0) \
GATE(TS_CLK_TX5112_AUD_DAC_CCLK, AUD_DAC_CLK_CFG0, 3, 0) \
GATE(TS_CLK_TX5112_AUD_DAC_PBCLK, AUD_DAC_CLK_CFG0, 2, 0) \
GATE(TS_CLK_TX5112_AUD_TMR_STRB, AUD_DAC_CLK_CFG1, 2, 0) \
GATE(TS_CLK_TX5112_AUD_ADC_CCLK, AUD_ADC_CLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_VI_DDR_ACLK, MCTL_ACLK_CFG0, 11, 0) \
GATE(TS_CLK_TX5112_DDRC_CORE_CLK, MCTL_ACLK_CFG0, 8, 0) \
GATE(TS_CLK_TX5112_DDR_PHY_PCLK, MCTL_ACLK_CFG0, 7, 0) \
GATE(TS_CLK_TX5112_UMCTL_PCLK, MCTL_ACLK_CFG0, 6, 0) \
GATE(TS_CLK_TX5112_MCTL_P3_ACLK, MCTL_ACLK_CFG0, 5, 0) \
GATE(TS_CLK_TX5112_MCTL_P2_ACLK, MCTL_ACLK_CFG0, 4, 0) \
GATE(TS_CLK_TX5112_MCTL_P1_ACLK, MCTL_ACLK_CFG0, 3, 0) \
GATE(TS_CLK_TX5112_MCTL_P0_ACLK, MCTL_ACLK_CFG0, 2, 0) \
GATE(TS_CLK_TX5112_HDR_SCLK, VI_BUS_CLK_EN, 16, 0) \
GATE(TS_CLK_TX5112_VPE_ISP_CLK, VI_BUS_CLK_EN, 15, 0) \
GATE(TS_CLK_TX5112_MIPI_RX1_PIXCLK, VI_BUS_CLK_EN, 14, 0) \
GATE(TS_CLK_TX5112_MIPI_RX0_PIXCLK1, VI_BUS_CLK_EN, 13, 0) \
GATE(TS_CLK_TX5112_MIPI_RX0_PIXCLK0, VI_BUS_CLK_EN, 12, 0) \
GATE(TS_CLK_TX5112_MIPI_RX1_PCLK, VI_BUS_CLK_EN, 10, 0) \
GATE(TS_CLK_TX5112_MIPI_RX0_PCLK, VI_BUS_CLK_EN, 9, 0) \
GATE(TS_CLK_TX5112_HDR_HCLK, VI_BUS_CLK_EN, 8, 0) \
GATE(TS_CLK_TX5112_VPE_HCLK, VI_BUS_CLK_EN, 7, 0) \
GATE(TS_CLK_TX5112_VI_CFG_HCLK, VI_BUS_CLK_EN, 6, 0) \
GATE(TS_CLK_TX5112_HDR_ACLK, VI_BUS_CLK_EN, 5, 0) \
GATE(TS_CLK_TX5112_VPE_ACLK, VI_BUS_CLK_EN, 4, 0) \
GATE(TS_CLK_TX5112_ISP_ACLK, VI_BUS_CLK_EN, 3, 0) \
GATE(TS_CLK_TX5112_ISP_SCLK, ISP_SCLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_VPE_CCLK, VPE_CCLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_MIPI_TXCLKESC, MIPI_CLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_AMR_HCLK, AMR_CCLK_CFG, 4, 0) \
GATE(TS_CLK_TX5112_AMR_ACLK, AMR_CCLK_CFG, 3, 0) \
GATE(TS_CLK_TX5112_AMR_CCLK, AMR_CCLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_RNE_CLK, RNE_CCLK_CFG, 6, 0) \
GATE(TS_CLK_TX5112_AI_ACLK, RNE_CCLK_CFG, 5, 0) \
GATE(TS_CLK_TX5112_RNE_HCLK, RNE_CCLK_CFG, 4, 0) \
GATE(TS_CLK_TX5112_RNE_ACLK, RNE_CCLK_CFG, 3, 0) \
GATE(TS_CLK_TX5112_RNE_CCLK, RNE_CCLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_VPU_HCLK, VPU_CLK_CFG, 7, 0) \
GATE(TS_CLK_TX5112_H265_CCLK, VPU_CLK_CFG, 6, 0) \
GATE(TS_CLK_TX5112_H265_PCLK, VPU_CLK_CFG, 5, 0) \
GATE(TS_CLK_TX5112_H265_ACLK, VPU_CLK_CFG, 4, 0) \
GATE(TS_CLK_TX5112_H264_PCLK, VPU_CLK_CFG, 3, 0) \
GATE(TS_CLK_TX5112_H264_ACLK, VPU_CLK_CFG, 2, 0) \
GATE(TS_CLK_TX5112_CHIP_OCLK_I3, CHIP_OCLK_CFG, 5, 0) \
GATE(TS_CLK_TX5112_CHIP_OCLK_I2, CHIP_OCLK_CFG, 4, 0) \
GATE(TS_CLK_TX5112_CHIP_OCLK_I1, CHIP_OCLK_CFG, 3, 0) \
GATE(TS_CLK_TX5112_CHIP_OCLK_I0, CHIP_OCLK_CFG, 2, 0)
#define _GATEIFY(id) TS_CLK_TX5112_GATE_##id
#define GATEIFY(id) _GATEIFY(id)
enum ts_gate_ids {
#define GATE(id, ...) GATEIFY(id),
GATE_LIST
#undef GATE
TS_CLK_GATE_NONE,
};
static const struct ts_gate_params ts_tx5112_gates[] = {
#define GATE(id, _off, _idx, _we) \
[GATEIFY(id)] = { \
.off = (_off), \
.bit_idx = (_idx), \
.we = (_we), \
},
GATE_LIST
#undef GATE
};
#define REGISTER_GATE(id, name, parent) do { \
const struct ts_gate_params *params = &ts_tx5112_gates[GATEIFY(id)]; \
clk_dm(id, name, \
clk_register_gate(NULL, name, parent, 0, \
top_base + params->off, \
params->bit_idx, 0, NULL)); \
} while (false)
这里面