Linux PWM驱动框架 (二)

Linux内核提供看PWM的驱动框架,让驱动开发人员可以更好地进行PWM驱动的编写。

  • 1、PWM结构体
struct pwm_chip {
	struct device		*dev;
	struct list_head	list;  // 链表
	const struct pwm_ops	*ops; // PWM的操作函数
	int			base;         // 索引号
	unsigned int		npwm; // 一个PWM控制器下有几路PWM

	struct pwm_device	*pwms; // PWM设备

	struct pwm_device *	(*of_xlate)(struct pwm_chip *pc,
					    const struct of_phandle_args *args);
	unsigned int		of_pwm_n_cells; // 参数
	bool			can_sleep; // 是否可以睡眠
};

Linux内核把一个PWM控制器的一些基本属性都抽象出来,组成了pwm_chip 结构体,下面就来简单看一下每个属性的一些作用。
dev: PWM设备
list: PWM控制器链表
ops : PWM操作函数
base:PWM控制器的下标索引,通常设置为-1。
npwm : 一个PWM控制器下有多少路PWM
pwms : 指向具体的PWM设备
其他的属性可以不必关注。

  • 2、PWM操作函数

接下来看一下重点的PWM操作函数

struct pwm_ops {
	int			(*request)(struct pwm_chip *chip,
					   struct pwm_device *pwm);
	void			(*free)(struct pwm_chip *chip,
					struct pwm_device *pwm);
	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);
#ifdef CONFIG_DEBUG_FS
	void			(*dbg_show)(struct pwm_chip *chip,
					    struct seq_file *s);
#endif
	struct module		*owner;
};

request : 申请PWM设备
free : 释放PWM设备
config : PWM设备配置函数
set_polarity : 设置PWM输出极性
enable : PWM开启
disable : PWM关闭
dbg_show : 开启DEBUG_FS的时候用到。

其中我们重点需要实现config、enable 和disable 函数,其他函数可以不需要实现。

  • 3、注册PWM设备
    pwmchip_add函数的具体作用是用来注册PWM设备
int pwmchip_add(struct pwm_chip *chip)
{
	struct pwm_device *pwm;
	unsigned int i;
	int ret;

    // 一定需要的参数
	if (!chip || !chip->dev || !chip->ops || !chip->ops->config ||
	    !chip->ops->enable || !chip->ops->disable || !chip->npwm)
		return -EINVAL;

	mutex_lock(&pwm_lock);

	ret = alloc_pwms(chip->base, chip->npwm); // 分配内存空间
	if (ret < 0)
		goto out;

	chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL);
	if (!chip->pwms) {
		ret = -ENOMEM;
		goto out;
	}

	chip->base = ret;

	for (i = 0; i < chip->npwm; i++) {
		pwm = &chip->pwms[i];

		pwm->chip = chip;
		pwm->pwm = chip->base + i;
		pwm->hwpwm = i;

		radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
	}

	bitmap_set(allocated_pwms, chip->base, chip->npwm);

	INIT_LIST_HEAD(&chip->list);
	list_add(&chip->list, &pwm_chips);

	ret = 0;

	if (IS_ENABLED(CONFIG_OF))
		of_pwmchip_add(chip);

	pwmchip_sysfs_export(chip);

out:
	mutex_unlock(&pwm_lock);
	return ret;
}
  • 4、具体例子
    接下来以pwm-rockchip.c为例子简单讲一下PWM框架的具体使用。
static int rockchip_pwm_probe(struct platform_device *pdev)
{
	const struct of_device_id *id;
	struct rockchip_pwm_chip *pc;
	struct resource *r;
	int ret;

	id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev);
	if (!id)
		return -EINVAL;

	pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
	if (!pc)
		return -ENOMEM;

	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	pc->base = devm_ioremap_resource(&pdev->dev, r);
	if (IS_ERR(pc->base))
		return PTR_ERR(pc->base);

	pc->clk = devm_clk_get(&pdev->dev, NULL); // 获取PWM时钟
	if (IS_ERR(pc->clk))
		return PTR_ERR(pc->clk);

	ret = clk_prepare(pc->clk);
	if (ret)
		return ret;

	platform_set_drvdata(pdev, pc);

	pc->data = id->data;
	pc->chip.dev = &pdev->dev;
	pc->chip.ops = pc->data->ops;  // PWM操作函数
	pc->chip.base = -1;            // PWM控制器下标
	pc->chip.npwm = 1;             // PWM通道

	if (pc->data->ops->set_polarity) {
		pc->chip.of_xlate = of_pwm_xlate_with_flags;
		pc->chip.of_pwm_n_cells = 3;
	}

	ret = pwmchip_add(&pc->chip);  // 注册PWM
	if (ret < 0) {
		clk_unprepare(pc->clk);
		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
	}

	return ret;
}

上面的PWM控制器注册函数十分简单,主要是先获取PWM控制器的时钟,然后初始化pwm_chip结构体,包括PWM操作函数,PWM下标索引和PWM通道数量,最后调用pwmchip_add把PWM控制器注册进内核。接下来看一下具体的PWM操作函数。

  • 4.1、config配置函数
static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
			       int duty_ns, int period_ns)
{
	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
	unsigned long period, duty;
	u64 clk_rate, div;
	int ret;

	clk_rate = clk_get_rate(pc->clk);

	/*
	 * Since period and duty cycle registers have a width of 32
	 * bits, every possible input period can be obtained using the
	 * default prescaler value for all practical clock rate values.
	 */
	div = clk_rate * period_ns;
	do_div(div, pc->data->prescaler * NSEC_PER_SEC);
	period = div;

	div = clk_rate * duty_ns;
	do_div(div, pc->data->prescaler * NSEC_PER_SEC);
	duty = div;

	ret = clk_enable(pc->clk);
	if (ret)
		return ret;

	writel(period, pc->base + pc->data->regs.period);
	writel(duty, pc->base + pc->data->regs.duty);
	writel(0, pc->base + pc->data->regs.cntr);

	clk_disable(pc->clk);

	return 0;
}

config配置函数有3个参数
chip : PWM控制器
pwm :PWM设备
duty_ns : 高电平的周期
period_ns : PWM周期
PWM的配置函数主要的作用就是将PWM的周期和PWM的高电平时间写入寄存器。

4.2、PWM开启和关闭函数

static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
	int ret;

	ret = clk_enable(pc->clk);
	if (ret)
		return ret;

	pc->data->set_enable(chip, pwm, true);

	return 0;
}
static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);

	pc->data->set_enable(chip, pwm, false);

	clk_disable(pc->clk);
}

开启和关闭函数的实现就很简单了,就是让寄存器里面写1或0开启或关闭PWM。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值