linux CLK时钟驱动

来源:互联网 发布:php跳转id代码 编辑:程序博客网 时间:2024/05/20 12:24

前述:本篇linux时钟驱动以UART6串口为例。

一、时钟设备寄存器配置

1. UART6有两种时钟源选择APLL\UPLL(可通过技术手册查看),如图

clk[uart6_aplldiv] = nuc970_clk_divider("uart6_aplldiv", "apll", REG_CLK_DIV5, 16, 3);

clk[uart6_uplldiv] = nuc970_clk_divider("uart6_uplldiv", "upll", REG_CLK_DIV5, 16, 3);

 

2. UART6有两种时钟源选择APLL\UPLL,选择其中一种


clk[uart6_eclk_mux] = nuc970_clk_mux("uart6_eclk_mux", REG_CLK_DIV5, 19, 2, uart6_sel_clks, ARRAY_SIZE(uart6_sel_clks));

3. UART6时钟分频选择


clk[uart6_eclk_div] = nuc970_clk_divider("uart6_eclk_div", "uart6_eclk_mux", REG_CLK_DIV5, 21, 3);

 

4. UART6时钟使能、禁止函数配置

clk[uart6_eclk_gate]  = nuc970_clk_gate("uart6_eclk_gate", "uart6_eclk_div", REG_CLK_PCLKEN0, 22);

 

5. UART6时钟设备注册,这个函数将相应的时钟设备注册到drivers/clk/clkdev.c中的链表clocks为了找到它,坑了我好久

clk_register_clkdev(clk[uart6_eclk_gate], "uart6_eclk", NULL);int clk_register_clkdev(struct clk *clk, const char *con_id, const char *dev_fmt, ...){struct clk_lookup *cl;va_list ap; if (IS_ERR(clk))return PTR_ERR(clk); va_start(ap, dev_fmt);cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);va_end(ap); if (!cl)return -ENOMEM;clkdev_add(cl); return 0;}void clkdev_add(struct clk_lookup *cl){mutex_lock(&clocks_mutex);//将时钟设备加入到clocks链表中list_add_tail(&cl->node, &clocks);mutex_unlock(&clocks_mutex);}


二、UART6驱动时钟操作

1. 根据时钟设备名"uart6_eclk"(见一、5中的函数)从链表clocks中找到匹配的资源

clk = clk_get(NULL, "uart6_eclk"); struct clk *clk_get(struct device *dev, const char *con_id){const char *dev_id = dev ? dev_name(dev) : NULL;struct clk *clk;//dev为NULL,条件不成立if (dev) {clk = of_clk_get_by_name(dev->of_node, con_id);if (!IS_ERR(clk) && __clk_get(clk))return clk;}return clk_get_sys(dev_id, con_id);} struct clk *clk_get_sys(const char *dev_id, const char *con_id){struct clk_lookup *cl; mutex_lock(&clocks_mutex); // 根据dev_id, con_id找到正确的结构体时钟,见下面cl = clk_find(dev_id, con_id);if (cl && !__clk_get(cl->clk))cl = NULL;mutex_unlock(&clocks_mutex); return cl ? cl->clk : ERR_PTR(-ENOENT);} static struct clk_lookup *clk_find(const char *dev_id, const char *con_id){struct clk_lookup *p, *cl = NULL;int match, best_found = 0, best_possible = 0; if (dev_id)best_possible += 2;if (con_id)best_possible += 1;//在链表clocks中查找匹配的时钟结构体,我们查询的是UART6,即“uart6_eclk” list_for_each_entry(p, &clocks, node) {match = 0;if (p->dev_id) {if (!dev_id || strcmp(p->dev_id, dev_id))continue;match += 2;}if (p->con_id) {if (!con_id || strcmp(p->con_id, con_id))continue;match += 1;}//遍历链表,匹配个数最多的if (match > best_found) {cl = p;if (match != best_possible)best_found = match;elsebreak;}return cl;}

2. 时钟设备准备

clk_prepare(clk);int clk_prepare(struct clk *clk){int ret; clk_prepare_lock();ret = __clk_prepare(clk);clk_prepare_unlock(); return ret;} int __clk_prepare(struct clk *clk){int ret = 0; if (!clk)return 0; if (clk->prepare_count == 0) {ret = __clk_prepare(clk->parent);if (ret)return ret;//这里的操作见一、5中的函数,内部进行了函数集绑定if (clk->ops->prepare) {ret = clk->ops->prepare(clk->hw);if (ret) {__clk_unprepare(clk->parent);return ret;}}} clk->prepare_count++; return 0;}

3.时钟使能

clk_enable(clk); 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;//这里的操作见一、5中的函数,内部进行了函数集绑定 if (clk->ops->enable) {ret = clk->ops->enable(clk->hw);if (ret) {__clk_disable(clk->parent);return ret;}}}clk->enable_count++;return 0;}

4.时钟禁止

clk_disable(clk)static void __clk_disable(struct clk *clk){if (!clk)return; if (WARN_ON(IS_ERR(clk)))return; if (WARN_ON(clk->enable_count == 0))return; if (--clk->enable_count > 0)return; if (clk->ops->disable)clk->ops->disable(clk->hw); __clk_disable(clk->parent);}


0 0
原创粉丝点击