08、pwm 控制器注册流程分析
对 pwm 控制器注册流程进行分析 :
一、设备树
首先来到 rk3568.dtsi 设备树, 找到 pwm 相关的节点如下所示:
C
pwm9: pwm@fe6f0010 {
compatible = "rockchip,rk3568-pwm", "rockchip,rk3328-pwm";
reg = <0x0 0xfe6f0010 0x0 0x10>;
#pwm-cells = <3>;
pinctrl-names = "active";
pinctrl-0 = <&pwm9m0_pins>;
clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>;
clock-names = "pwm", "pclk";
status = "disabled";
};
pwm10: pwm@fe6f0020 {
compatible = "rockchip,rk3568-pwm", "rockchip,rk3328-pwm";
reg = <0x0 0xfe6f0020 0x0 0x10>;
#pwm-cells = <3>;
pinctrl-names = "active";
pinctrl-0 = <&pwm10m0_pins>;
clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>;
clock-names = "pwm", "pclk";
status = "disabled";
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
二、probe
根据 PWM 节点的 compatible 属性进行查找, 可以找到瑞芯微的 PWM 驱动路径为内核目录下的a, 然后来看该驱动程序的 probe 函数, 具体内容如下所示:
C
static int rockchip_pwm_probe(struct platform_device *pdev)
{
const struct of_device_id *id;// 设备树匹配 ID
struct rockchip_pwm_chip *pc;// PWM 芯片结构体
struct resource *r;// 资源信息
int ret, count;// 返回值和计数变量
// 检查设备树匹配
id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev);
if (!id)
return -EINVAL;
// 分配 PWM 芯片结构体
pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
if (!pc)
return -ENOMEM;
// 获取内存资源并映射 IO 地址
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pc->base = devm_ioremap(&pdev->dev, r->start,
resource_size(r));
if (IS_ERR(pc->base))
return PTR_ERR(pc->base);
// 获取 PWM 总线时钟
pc->clk = devm_clk_get(&pdev->dev, "pwm");
if (IS_ERR(pc->clk)) {
pc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pc->clk)) {
ret = PTR_ERR(pc->clk);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Can't get bus clk: %d\n",
ret);
return ret;
}
}
// 获取 APB 时钟
count = of_count_phandle_with_args(pdev->dev.of_node,
"clocks", "#clock-cells");
if (count == 2)
pc->pclk = devm_clk_get(&pdev->dev, "pclk");
else
pc->pclk = pc->clk;
if (IS_ERR(pc->pclk)) {
ret = PTR_ERR(pc->pclk);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Can't get APB clk: %d\n", ret);
return ret;
}
// 使能总线时钟
ret = clk_prepare_enable(pc->clk);
if (ret) {
dev_err(&pdev->dev, "Can't prepare enable bus clk: %d\n", ret);
return ret;
}
// 使能 APB 时钟
ret = clk_prepare(pc->pclk);
if (ret) {
dev_err(&pdev->dev, "Can't prepare APB clk: %d\n", ret);
goto err_clk;
}
// 获取引脚控制器
pc->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(pc->pinctrl)) {
dev_err(&pdev->dev, "Get pinctrl failed!\n");
return PTR_ERR(pc->pinctrl);
}
// 获取引脚控制器的活动状态
pc->active_state = pinctrl_lookup_state(pc->pinctrl, "active");
if (IS_ERR(pc->active_state)) {
dev_err(&pdev->dev, "No active pinctrl state\n");
return PTR_ERR(pc->active_state);
}
// 设置驱动数据
platform_set_drvdata(pdev, pc);
// 初始化 PWM 芯片结构体
pc->data = id->data;
pc->chip.dev = &pdev->dev;
pc->chip.ops = &rockchip_pwm_ops;
pc->chip.base = -1;
pc->chip.npwm = 1;
pc->clk_rate = clk_get_rate(pc->clk);
// 如果支持极性设置, 则使用带有极性的 of_pwm_xlate 函数
if (pc->data->supports_polarity) {
pc->chip.of_xlate = of_pwm_xlate_with_flags;
pc->chip.of_pwm_n_cells = 3;
}
// 检查是否为中心对齐模式
pc->center_aligned =
device_property_read_bool(&pdev->dev, "center-aligned");
// 添加 PWM 芯片
ret = pwmchip_add(&pc->chip);
if (ret < 0) {
dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
goto err_pclk;
}
// 如果 PWM 未启用, 则禁用总线时钟
/* Keep the PWM clk enabled if the PWM appears to be up and running. */
if (!pwm_is_enabled(pc->chip.pwms))
clk_disable(pc->clk);
return 0;
err_pclk:
clk_unprepare(pc->pclk);
err_clk:
clk_disable_unprepare(pc->clk);
return ret;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
三、rockchip_pwm_chip
创建了一个 struct rockchip_pwm_chip 类型的指针变量, 它主要用于描述 Rockchip 系列 SoC 上的 PWM 控制器的硬件特性和配置信息, 具体内容如下所示:
c
struct rockchip_pwm_chip {
struct pwm_chip chip;
struct clk *clk;
struct clk *pclk;
struct pinctrl *pinctrl;
struct pinctrl_state *active_state;
struct delayed_work pwm_work;
const struct rockchip_pwm_data *data;
const struct rockchip_pwm_biphasic_config *biphasic_config;
struct resource *res;
struct dentry *debugfs;
void __iomem *base;
unsigned long clk_rate;
unsigned long is_clk_enabled;
bool vop_pwm_en; /* indicate voppwm mirror register state */
bool center_aligned;
bool oneshot_en;
bool capture_en;
bool wave_en;
bool global_ctrl_grant;
bool freq_meter_support;
bool counter_support;
bool wave_support;
bool biphasic_support;
bool freq_res_valid;
bool biphasic_res_valid;
int channel_id;
int irq;
u32 scaler;
u8 main_version;
u8 capture_cnt;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
PWM芯片结构体是PWM系统中对PWM控制器的一种简化和整合。它把PWM控制器的基础信息和操作功能都汇总到一个结构体里,这样上层的PWM管理系统使用起来更方便。这个结构体定义在include/linux/pwm.h
文件中。
c
/**
* struct pwm_chip - abstract a PWM controller
* @dev: device providing the PWMs
* @ops: callbacks for this PWM controller
* @base: number of first PWM controlled by this chip
* @npwm: number of PWMs controlled by this chip
* @of_xlate: request a PWM device given a device tree PWM specifier
* @of_pwm_n_cells: number of cells expected in the device tree PWM specifier
* @list: list node for internal use
* @pwms: array of PWM devices allocated by the framework
*/
struct pwm_chip {
struct device *dev;
const struct pwm_ops *ops;
int base;
unsigned int npwm;
struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
const struct of_phandle_args *args);
unsigned int of_pwm_n_cells;
/* only used internally by the PWM framework */
struct list_head list;
struct pwm_device *pwms;
ANDROID_KABI_RESERVE(1);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
关于结构体中每个成员的注释已经进行了添加, 其中第四行表示 PWM 控制器的操作函数集, 具体内容如下所示:
c
/**
* struct pwm_ops - PWM controller operations
* @request: optional hook for requesting a PWM
* @free: optional hook for freeing a PWM
* @capture: capture and report PWM signal
* @apply: atomically apply a new PWM config
* @get_state: get the current PWM state. This function is only
* called once per PWM device when the PWM chip is
* registered.
* @get_output_type_supported: get the supported output type of this PWM
* @owner: helps prevent removal of modules exporting active PWMs
* @config: configure duty cycles and period length for this PWM
* @set_polarity: configure the polarity of this PWM
* @enable: enable PWM output toggling
* @disable: disable PWM output toggling
*/
struct pwm_ops {
int (*request)(struct pwm_chip *chip, struct pwm_device *pwm);
void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
int (*capture)(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_capture *result, unsigned long timeout);
int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state);
void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state);
int (*get_output_type_supported)(struct pwm_chip *chip,
struct pwm_device *pwm);
struct module *owner;
/* Only used by legacy drivers */
int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns);
int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
enum pwm_polarity polarity);
int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
ANDROID_KABI_RESERVE(1);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
然后回到 drivers/pwm/pwm-rockchip.c 文件中找到 rockchip_pwm_ops 操作集, 可以发现瑞芯微只实现了状态获取和申请两个函数, 具体内容如下所示:
c
static const struct pwm_ops rockchip_pwm_ops = {
.capture = rockchip_pwm_capture,
.apply = rockchip_pwm_apply,
.get_state = rockchip_pwm_get_state,
.owner = THIS_MODULE,
};
1
2
3
4
5
6
2
3
4
5
6
而 PWM 还可以实现输入捕获的功能, 但瑞芯微只实现了这两个操作集函数, 所以要想用上 PWM 的输入捕获, 就需要我们自己编写驱动了
四、pwmchip_add
通过调用 pwmchip_add 函数将 PWM 控制器添加到 PWM 子系统中, pwmchip_add 函数定义在 drivers/pwm/core.c 文件中, 具体内容如下所示:
C
/**
* pwmchip_add() - register a new PWM chip
* @chip: the PWM chip to add
*
* Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
* will be used. The initial polarity for all channels is normal.
*
* Returns: 0 on success or a negative error code on failure.
*/
int pwmchip_add(struct pwm_chip *chip)
{
return pwmchip_add_with_polarity(chip, PWM_POLARITY_NORMAL);
}
EXPORT_SYMBOL_GPL(pwmchip_add);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
C
/**
* pwmchip_add_with_polarity() - register a new PWM chip
* @chip: the PWM chip to add
* @polarity: initial polarity of PWM channels
*
* Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
* will be used. The initial polarity for all channels is specified by the
* @polarity parameter.
*
* Returns: 0 on success or a negative error code on failure.
*/
int pwmchip_add_with_polarity(struct pwm_chip *chip,
enum pwm_polarity polarity)
{
struct pwm_device *pwm;
unsigned int i;
int ret;
// 检查 chip 结构体的有效性
if (!chip || !chip->dev || !chip->ops || !chip->npwm)
return -EINVAL;
// 检查 pwm_ops 结构体的有效性
if (!pwm_ops_check(chip->ops))
return -EINVAL;
// 获取全局 pwm_lock 互斥锁
mutex_lock(&pwm_lock);
// 为 chip 分配 PWM 设备索引号
ret = alloc_pwms(chip->base, chip->npwm);
if (ret < 0)
goto out;
// 动态分配 chip->npwm 个 pwm_device 结构体
chip->pwms = kcalloc(chip->npwm, sizeof(*pwm), GFP_KERNEL);
if (!chip->pwms) {
ret = -ENOMEM;
goto out;
}
// 保存分配的 PWM 设备索引号
chip->base = ret;
// 初始化每个 PWM 设备
for (i = 0; i < chip->npwm; i++) {
pwm = &chip->pwms[i];
pwm->chip = chip;
pwm->pwm = chip->base + i;
pwm->hwpwm = i;
pwm->state.polarity = polarity;
pwm->state.output_type = PWM_OUTPUT_FIXED;
// 如果 chip->ops->get_state 存在,则获取当前 PWM 设备的状态
if (chip->ops->get_state)
chip->ops->get_state(chip, pwm, &pwm->state);
// 将 PWM 设备添加到全局 PWM 设备树中
radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
}
// 标记分配的 PWM 设备为已使用
bitmap_set(allocated_pwms, chip->base, chip->npwm);
// 将 PWM 控制器添加到全局 PWM 控制器链表
INIT_LIST_HEAD(&chip->list);
list_add(&chip->list, &pwm_chips);
ret = 0;
// 如果内核开启了 Device Tree 支持,则注册 PWM 控制器到 Device Tree
if (IS_ENABLED(CONFIG_OF))
of_pwmchip_add(chip);
out:
// 释放全局 pwm_lock 互斥锁
mutex_unlock(&pwm_lock);
// 如果添加成功,则在 sysfs 中导出 PWM 控制器
if (!ret)
pwmchip_sysfs_export(chip);
return ret;
}
EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
这个函数的主要作用是初始化一个新的 PWM 控制器,并在第 62 行调用 list_add 函数将其添加到 PWM 子系统的管理中。