Linux内核驱动 --- CCF框架 provider驱动的编写

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等这些函数即可,这些函数的实现可以单独放在一个文件中,如clk-tx5112/clk-tx5215.clk/等等。
对于不同芯片的编译的区分在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(&paras->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)

这里面的宏的写法比较复杂,展开后ts_gate_ids里自动列出TS_CLK_TX5112_GATE_##id, id为dt-bindings里的所有宏,列出的枚举量从0自增,最后一个值为TS_CLK_GATE_NONE。

ts_tx5112_gates中重定义了GATE宏,此时GATE_LIST里所有的item都会被扩展为对struct ts_gate_params对象的初始化,存在ts_tx5112_gates里。

REGISTER_GATE宏用于将初始化的 ts_tx5112_gates 数组中的对象,按id取出来,并调用clk_dm来对tx_tx5112_clks进行填充
tx_tx5112_clks这个数组最后会被of_clk_add_provider一起提交。

static inline void clk_dm(ulong id, const char *name, struct clk *clk)
{
	int ret;

	if (id >= TS_CLK_TX5112_MAX) {
		pr_err("clk_register_clkdev %s failed\n", name);
		return;
	}

	if (IS_ERR_OR_NULL(clk)) {
		pr_err("%s %lu failed\n", __func__, id);
		return;
	}

	ret = clk_register_clkdev(clk, name, NULL);
	if (ret) {
		pr_err("clk_register_clkdev %s failed\n", name);
		return;
	}

	tx_tx5112_clks[id] = clk;
}

clk_register_gate为内核提供的针对gate类型的clk对象的注册函数,上一节已经详细说过
其内部调用了clk_hw_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_hw *hw;

	hw = clk_hw_register_gate(dev, name, parent_name, flags, reg,
				  bit_idx, clk_gate_flags, lock);
	if (IS_ERR(hw))
		return ERR_CAST(hw);
	return hw->clk;
}
EXPORT_SYMBOL_GPL(clk_register_gate);

framework提供了struct clk_hw结构,从clock provider的角度,描述clock
clock provider负责把系统中每个clock的静态数据准备好,然后交给clock framework的核心逻辑,剩下的事情,clock provider就不用操心了。这个过程,就是clock driver的编写过程。
现在我们静态数据已经准备好,通过clk_hw_register_gate提交即可,所以这里就是provider的注册重点。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值