从零开始之驱动发开、linux驱动(三十三、PWM子系统)

内核中三星默认是没选PWM支持的,我们先配置一下:

make menuconfig

 

Device Drivers  --->   
    [*] Pulse-Width Modulation (PWM) Support  --->
        <*>   Samsung PWM support  


 

因为这里涉及到了pwm子系统,所以这里简单的介绍一下pwm子系统,后面再介绍平台驱动部分

首先是pwm子系统类的创建

static int __init pwm_sysfs_init(void)
{
	return class_register(&pwm_class);
}
subsys_initcall(pwm_sysfs_init);

 

 

接下来介绍一下注册和注销


/**
 * 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.
 */
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)
		return -EINVAL;

	mutex_lock(&pwm_lock);

    /* pwm子系统和input子系统一样,按顺序填充的,唯一就是pwm子系统没有设备号 */
	ret = alloc_pwms(chip->base, chip->npwm);
	if (ret < 0)
		goto out;

    /* 申请n个pwm空间 */
	chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL);
	if (!chip->pwms) {
		ret = -ENOMEM;
		goto out;
	}

	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;
        
        /* 并插入radix的pwm设备树种 */
		radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
	}

    /* 标记已经注册了的pwm */
	bitmap_set(allocated_pwms, chip->base, chip->npwm);

    /* 初始化并把这个chip加入到pwm_list */
	INIT_LIST_HEAD(&chip->list);
	list_add(&chip->list, &pwm_chips);

	ret = 0;

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

    /* 这里才是最主要的,导出pwm的属性文件,方便用户设置pwm */
	pwmchip_sysfs_export(chip);

out:
	mutex_unlock(&pwm_lock);
	return ret;
}
EXPORT_SYMBOL_GPL(pwmchip_add);

/**
 * pwmchip_remove() - remove a PWM chip
 * @chip: the PWM chip to remove
 *
 * Removes a PWM chip. This function may return busy if the PWM chip provides
 * a PWM device that is still requested.
 */
int pwmchip_remove(struct pwm_chip *chip)
{
	unsigned int i;
	int ret = 0;

    /* 卸载chip下挂在的nr个注册的pwm */
	pwmchip_sysfs_unexport_children(chip);

	mutex_lock(&pwm_lock);

    /* 清已用标记 */
	for (i = 0; i < chip->npwm; i++) {
		struct pwm_device *pwm = &chip->pwms[i];

		if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
			ret = -EBUSY;
			goto out;
		}
	}

    /* 从pwm_chips链表删除chip */
	list_del_init(&chip->list);

	if (IS_ENABLED(CONFIG_OF))
		of_pwmchip_remove(chip);

	free_pwms(chip);

    /* 卸载导出的属性文件 */
	pwmchip_sysfs_unexport(chip);

out:
	mutex_unlock(&pwm_lock);
	return ret;
}
EXPORT_SYMBOL_GPL(pwmchip_remove);

从上面我们可以看到,pwm类的注册是以chip来注册的,同时在注册之初每个chip有最多支持多少个pwm是已经知道的,比如三星一个chip最多支持5个pwm。

 

 


void pwmchip_sysfs_export(struct pwm_chip *chip)
{
	struct device *parent;

	/*
	 * If device_create() fails the pwm_chip is still usable by
	 * the kernel its just not exported.
	 */
    /* 在sysfs中导出pwm类的属性文件(注意这里设备号是0,实际是不会再sysfs具体的设备下创建dev设备号信息文件的,即不会折设备文件) */
	parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip,
			       "pwmchip%d", chip->base);
	if (IS_ERR(parent)) {
		dev_warn(chip->dev,
			 "device_create failed for pwm_chip sysfs export\n");
	}
}

void pwmchip_sysfs_unexport(struct pwm_chip *chip)
{
	struct device *parent;

	parent = class_find_device(&pwm_class, NULL, chip,
				   pwmchip_sysfs_match);
	if (parent) {
		/* for class_find_device() */
		put_device(parent);
		device_unregister(parent);
	}
}

void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
{
	struct device *parent;
	unsigned int i;

	parent = class_find_device(&pwm_class, NULL, chip,
				   pwmchip_sysfs_match);
	if (!parent)
		return;

    /* 删除导出的所有设备属性(之前是多个pwm设备一块创建,这里只能一个一个删除) */
	for (i = 0; i < chip->npwm; i++) {
		struct pwm_device *pwm = &chip->pwms[i];

		if (test_bit(PWMF_EXPORTED, &pwm->flags))
			pwm_unexport_child(parent, pwm);
	}

	put_device(parent);
}



static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm)
{
	struct device *child;

	if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags))
		return -ENODEV;

	child = device_find_child(parent, pwm, pwm_unexport_match);
	if (!child)
		return -ENODEV;

	/* for device_find_child() */
	put_device(child);
	device_unregister(child);
	pwm_put(pwm);

	return 0;
}

上面可以看到对chip注册的时候是默认没注册pwm的(device_register),而注销chip的时候则是一次性注销掉所有的在这个chip已经注册的pwm的(device_unregister)。(原因看后面一步一步分析)

 

 

可以看到pwm类只是为了在sysfs/pwm/pwmchipx中导出attribute属性文件,方便用户层show和strow。没做其他事,下面就看有哪些属性。

chip层面的属性文件有哪些。

static struct class pwm_class = {
	.name		= "pwm",
	.owner		= THIS_MODULE,
	.dev_groups	= pwm_chip_groups,        /* 总的属性合起来放在一个组中 */
};

chip相关的属性如下,

作用分别是重新注册pwm,注销pwm,本chip中pwm个数(这个个数是在注册chip的时候确定的,所以这个肯定是只能读)


static struct attribute *pwm_chip_attrs[] = {
	&dev_attr_export.attr,
	&dev_attr_unexport.attr,
	&dev_attr_npwm.attr,
	NULL,
};

 


static void pwm_export_release(struct device *child)
{
	struct pwm_export *export = child_to_pwm_export(child);

	kfree(export);
}

/* 注册一个pwm */
static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
{
	struct pwm_export *export;
	int ret;

	if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags))
		return -EBUSY;

	export = kzalloc(sizeof(*export), GFP_KERNEL);
	if (!export) {
		clear_bit(PWMF_EXPORTED, &pwm->flags);
		return -ENOMEM;
	}

	export->pwm = pwm;

	export->child.release = pwm_export_release;
	export->child.parent = parent;
	export->child.devt = MKDEV(0, 0);
	export->child.groups = pwm_groups;    /* 这个里面又是具体到一个pwm的操作属性集合 */
	dev_set_name(&export->child, "pwm%u", pwm->hwpwm);

	ret = device_register(&export->child);    /* 注册设备 */
	if (ret) {
		clear_bit(PWMF_EXPORTED, &pwm->flags);
		kfree(export);
		return ret;
	}

	return 0;
}

static int pwm_unexport_match(struct device *child, void *data)
{
	return child_to_pwm_device(child) == data;
}

/* 注销一个pwm */
static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm)
{
	struct device *child;

	if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags))
		return -ENODEV;

	child = device_find_child(parent, pwm, pwm_unexport_match);
	if (!child)
		return -ENODEV;

	/* for device_find_child() */
	put_device(child);
	device_unregister(child);
	pwm_put(pwm);

	return 0;
}
/* 注册一个设备 */
static ssize_t pwm_export_store(struct device *parent,
				struct device_attribute *attr,
				const char *buf, size_t len)
{
	struct pwm_chip *chip = dev_get_drvdata(parent);
	struct pwm_device *pwm;
	unsigned int hwpwm;
	int ret;

    /* 字符串转数字,转换好的数字通过hwpwm返回 */
	ret = kstrtouint(buf, 0, &hwpwm);
	if (ret < 0)
		return ret;

    /* 转换好的数字会作为该chip中要注册的那个pwm号注册 */
	if (hwpwm >= chip->npwm)
		return -ENODEV;

	pwm = pwm_request_from_chip(chip, hwpwm, "sysfs");
	if (IS_ERR(pwm))
		return PTR_ERR(pwm);

	ret = pwm_export_child(parent, pwm);
	if (ret < 0)
		pwm_put(pwm);

	return ret ? : len;
}
static DEVICE_ATTR(export, 0200, NULL, pwm_export_store);

/* 注销一个设备 */
static ssize_t pwm_unexport_store(struct device *parent,
				  struct device_attribute *attr,
				  const char *buf, size_t len)
{
	struct pwm_chip *chip = dev_get_drvdata(parent);
	unsigned int hwpwm;
	int ret;

	ret = kstrtouint(buf, 0, &hwpwm);
	if (ret < 0)
		return ret;

	if (hwpwm >= chip->npwm)
		return -ENODEV;

	ret = pwm_unexport_child(parent, &chip->pwms[hwpwm]);

	return ret ? : len;
}
static DEVICE_ATTR(unexport, 0200, NULL, pwm_unexport_store);

static ssize_t npwm_show(struct device *parent, struct device_attribute *attr,
			 char *buf)
{
	const struct pwm_chip *chip = dev_get_drvdata(parent);

	return sprintf(buf, "%u\n", chip->npwm);
}
static DEVICE_ATTR_RO(npwm);

上的主要就是三个属性操作函数的逻辑,这个chip中pwm个数show最简单。

其它两个export和unexport分别是通过sys/class/pwm/pwmchipx/下面的两个属性文件,来调用的函数。

其作用就是把要写的字符串转换成数字n,之后再以这个数字n为pwm号,注册该chip下面的第n个pwm。

当然既然pwm是通过sys中应用层的参数来注册的,那么pwm里面的参数也应该用对应的sys中创建attribute文件,来改写才对啊。

 

可以看到上面注册函数中又下面一句

export->child.groups = pwm_groups;    

这个就是具体某个pwm的属性操作函数。

 

上面注册时注册的pwm_groups有是在下面定义的,这些也是重点。

包括读写pwm的周期,占空比,使能,极性反转


static struct attribute *pwm_attrs[] = {
	&dev_attr_period.attr,
	&dev_attr_duty_cycle.attr,
	&dev_attr_enable.attr,
	&dev_attr_polarity.attr,
	NULL
};

 

主要的数据结构如下:


/**
 * enum pwm_polarity - polarity of a PWM signal
 * @PWM_POLARITY_NORMAL: a high signal for the duration of the duty-
 * cycle, followed by a low signal for the remainder of the pulse
 * period
 * @PWM_POLARITY_INVERSED: a low signal for the duration of the duty-
 * cycle, followed by a high signal for the remainder of the pulse
 * period
 */
enum pwm_polarity {
	PWM_POLARITY_NORMAL,
	PWM_POLARITY_INVERSED,
};

enum {
	PWMF_REQUESTED = 1 << 0,
	PWMF_ENABLED = 1 << 1,
	PWMF_EXPORTED = 1 << 2,
};

struct pwm_device {
	const char		*label;
	unsigned long		flags;
	unsigned int		hwpwm;
	unsigned int		pwm;
	struct pwm_chip		*chip;
	void			*chip_data;

	unsigned int		period; 	/* 周期in nanoseconds */
	unsigned int		duty_cycle;	/* 占空比in nanoseconds */
	enum pwm_polarity	polarity;    /* 时钟极性 */
 };

下面就是真正的属性操作方法了。


struct pwm_export {
	struct device child;
	struct pwm_device *pwm;
};

static struct pwm_export *child_to_pwm_export(struct device *child)
{
	return container_of(child, struct pwm_export, child);
}

static struct pwm_device *child_to_pwm_device(struct device *child)
{
	struct pwm_export *export = child_to_pwm_export(child);

	return export->pwm;
}

/* 打印周期 */
static ssize_t pwm_period_show(struct device *child,
			       struct device_attribute *attr,
			       char *buf)
{
	const struct pwm_device *pwm = child_to_pwm_device(child);

	return sprintf(buf, "%u\n", pwm->period);
}

/* 设置周期 */
static ssize_t pwm_period_store(struct device *child,
				struct device_attribute *attr,
				const char *buf, size_t size)
{
	struct pwm_device *pwm = child_to_pwm_device(child);
	unsigned int val;
	int ret;

	ret = kstrtouint(buf, 0, &val);
	if (ret)
		return ret;

	ret = pwm_config(pwm, pwm->duty_cycle, val);

	return ret ? : size;
}

/* 显示占空比 */
static ssize_t pwm_duty_cycle_show(struct device *child,
				   struct device_attribute *attr,
				   char *buf)
{
	const struct pwm_device *pwm = child_to_pwm_device(child);

	return sprintf(buf, "%u\n", pwm->duty_cycle);
}

/* 设置占空比 */
static ssize_t pwm_duty_cycle_store(struct device *child,
				    struct device_attribute *attr,
				    const char *buf, size_t size)
{
	struct pwm_device *pwm = child_to_pwm_device(child);
	unsigned int val;
	int ret;

	ret = kstrtouint(buf, 0, &val);
	if (ret)
		return ret;

	ret = pwm_config(pwm, val, pwm->period);

	return ret ? : size;
}

/* 显示pwm使能与否 */
static ssize_t pwm_enable_show(struct device *child,
			       struct device_attribute *attr,
			       char *buf)
{
	const struct pwm_device *pwm = child_to_pwm_device(child);
	int enabled = test_bit(PWMF_ENABLED, &pwm->flags);

	return sprintf(buf, "%d\n", enabled);
}

/* 设置pwm是否使能 */
static ssize_t pwm_enable_store(struct device *child,
				struct device_attribute *attr,
				const char *buf, size_t size)
{
	struct pwm_device *pwm = child_to_pwm_device(child);
	int val, ret;

	ret = kstrtoint(buf, 0, &val);
	if (ret)
		return ret;

	switch (val) {
	case 0:
		pwm_disable(pwm);
		break;
	case 1:
		ret = pwm_enable(pwm);
		break;
	default:
		ret = -EINVAL;
		break;
	}

	return ret ? : size;
}

/* 显示时钟极性 */
static ssize_t pwm_polarity_show(struct device *child,
				 struct device_attribute *attr,
				 char *buf)
{
	const struct pwm_device *pwm = child_to_pwm_device(child);

	return sprintf(buf, "%s\n", pwm->polarity ? "inversed" : "normal");
}

/* 设置时钟极性 */
static ssize_t pwm_polarity_store(struct device *child,
				  struct device_attribute *attr,
				  const char *buf, size_t size)
{
	struct pwm_device *pwm = child_to_pwm_device(child);
	enum pwm_polarity polarity;
	int ret;

	if (sysfs_streq(buf, "normal"))
		polarity = PWM_POLARITY_NORMAL;
	else if (sysfs_streq(buf, "inversed"))
		polarity = PWM_POLARITY_INVERSED;
	else
		return -EINVAL;

	ret = pwm_set_polarity(pwm, polarity);

	return ret ? : size;
}

static DEVICE_ATTR(period, 0644, pwm_period_show, pwm_period_store);
static DEVICE_ATTR(duty_cycle, 0644, pwm_duty_cycle_show, pwm_duty_cycle_store);
static DEVICE_ATTR(enable, 0644, pwm_enable_show, pwm_enable_store);
static DEVICE_ATTR(polarity, 0644, pwm_polarity_show, pwm_polarity_store);

 

上面的设置主要分为两种,一种是参数设置pwm_config,另一种是使能pwm_enable/pwm_disable

参数设置函数:


/**
 * pwm_config() - change a PWM device configuration
 * @pwm: PWM device
 * @duty_ns: "on" time (in nanoseconds)
 * @period_ns: duration (in nanoseconds) of one cycle
 */
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
{
	int err;

	if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns)
		return -EINVAL;

    /* 调用ops里面的config函数设置 */
	err = pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
	if (err)
		return err;

	pwm->duty_cycle = duty_ns;
	pwm->period = period_ns;

	return 0;
}

enable和disable也是调用ops里面的现成函数来实现。

/**
 * pwm_enable() - start a PWM output toggling
 * @pwm: PWM device
 */
int pwm_enable(struct pwm_device *pwm)
{
	if (pwm && !test_and_set_bit(PWMF_ENABLED, &pwm->flags))
		return pwm->chip->ops->enable(pwm->chip, pwm);

	return pwm ? 0 : -EINVAL;
}

/**
 * pwm_disable() - stop a PWM output toggling
 * @pwm: PWM device
 */
void pwm_disable(struct pwm_device *pwm)
{
	if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags))
		pwm->chip->ops->disable(pwm->chip, pwm);
}

当然上面的都是属于pwm子系统的,所以肯定不会有硬件操作。所以confgi,enable,disable都输用的函数指针,是具体的硬件厂商来提供的。

基本上面就是pwm子系统的知识,下面以三星的pwm为例来说明一下注册过程。

 

 

首先是平台设备部分(也就是硬件寄存器)

static struct resource samsung_pwm_resource[] = {
	DEFINE_RES_MEM(SAMSUNG_PA_TIMER, SZ_4K),
};

struct platform_device samsung_device_pwm = {
	.name		= "samsung-pwm",
	.id		= -1,
	.num_resources	= ARRAY_SIZE(samsung_pwm_resource),
	.resource	= samsung_pwm_resource,
};

void __init samsung_pwm_set_platdata(struct samsung_pwm_variant *pd)
{
	samsung_device_pwm.dev.platform_data = pd;
}

 

最后来说明一下平台驱动部分:

static struct platform_driver pwm_samsung_driver = {
	.driver		= {
		.name	= "samsung-pwm",
		.owner	= THIS_MODULE,
		.pm	= &pwm_samsung_pm_ops,
		.of_match_table = of_match_ptr(samsung_pwm_matches),
	},
	.probe		= pwm_samsung_probe,
	.remove		= pwm_samsung_remove,
};
module_platform_driver(pwm_samsung_driver);

 

这里主要就是probe和remove函数了。


static int pwm_samsung_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct samsung_pwm_chip *chip;
	struct resource *res;
	unsigned int chan;
	int ret;

    /* 申请一个pwm的chip */
	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
	if (chip == NULL)
		return -ENOMEM;

	chip->chip.dev = &pdev->dev;                /* 绑定设备传过来的dev */
	chip->chip.ops = &pwm_samsung_ops;          /* 主要操作函数 */
	chip->chip.base = -1;
	chip->chip.npwm = SAMSUNG_PWM_NUM;          /* 三星共5个pwm */
	chip->inverter_mask = BIT(SAMSUNG_PWM_NUM) - 1;

	if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
		ret = pwm_samsung_parse_dt(chip);    /* 设备树相关,没有则不会用 */
		if (ret)
			return ret;

		chip->chip.of_xlate = of_pwm_xlate_with_flags;
		chip->chip.of_pwm_n_cells = 3;
	} else {
		if (!pdev->dev.platform_data) {
			dev_err(&pdev->dev, "no platform data specified\n");
			return -EINVAL;
		}

		memcpy(&chip->variant, pdev->dev.platform_data,
							sizeof(chip->variant));
	}

    /* 得到寄存器资源 */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	chip->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(chip->base))
		return PTR_ERR(chip->base);
    /* 得到定时器时钟 */
	chip->base_clk = devm_clk_get(&pdev->dev, "timers");
	if (IS_ERR(chip->base_clk)) {
		dev_err(dev, "failed to get timer base clk\n");
		return PTR_ERR(chip->base_clk);
	}
    
    /* 使能定时器时钟 */
	ret = clk_prepare_enable(chip->base_clk);
	if (ret < 0) {
		dev_err(dev, "failed to enable base clock\n");
		return ret;
	}

    /* 设置我们要初始化的定时器通道寄存器的寄存器 */
	for (chan = 0; chan < SAMSUNG_PWM_NUM; ++chan)
		if (chip->variant.output_mask & BIT(chan))
			pwm_samsung_set_invert(chip, chan, true);

	/* Following clocks are optional. */
	chip->tclk0 = devm_clk_get(&pdev->dev, "pwm-tclk0");
	chip->tclk1 = devm_clk_get(&pdev->dev, "pwm-tclk1");
    
    
	platform_set_drvdata(pdev, chip);

    /* 注册三星的chip */
	ret = pwmchip_add(&chip->chip);
	if (ret < 0) {
		dev_err(dev, "failed to register PWM chip\n");
		clk_disable_unprepare(chip->base_clk);
		return ret;
	}

	dev_dbg(dev, "base_clk at %lu, tclk0 at %lu, tclk1 at %lu\n",
		clk_get_rate(chip->base_clk),
		!IS_ERR(chip->tclk0) ? clk_get_rate(chip->tclk0) : 0,
		!IS_ERR(chip->tclk1) ? clk_get_rate(chip->tclk1) : 0);

	return 0;
}

static int pwm_samsung_remove(struct platform_device *pdev)
{
	struct samsung_pwm_chip *chip = platform_get_drvdata(pdev);
	int ret;

    /* 卸载chip */
	ret = pwmchip_remove(&chip->chip);
	if (ret < 0)
		return ret;

    /* 关时钟 */
	clk_disable_unprepare(chip->base_clk);

	return 0;
}

这里回到我们最开的,可以看到。注册chip的时候是默认是没有注册具体pwm的,但卸载chip的时候,则可能有些pwm没被卸载,所以卸载chip的时候,首先要做的就是检测该chip中所有的pwm,查看没有那个pwm仍然没卸载,如果有则卸载掉该pwm再注卸载该chip。

 

最后我们看一下三星ops操作函数有哪些》

 


/**
 * struct pwm_ops - PWM controller operations
 * @request: optional hook for requesting a PWM
 * @free: optional hook for freeing a PWM
 * @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
 * @dbg_show: optional routine to show contents in debugfs
 * @owner: helps prevent removal of modules exporting active PWMs
 */
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;
};
static const struct pwm_ops pwm_samsung_ops = {
	.request	= pwm_samsung_request,
	.free		= pwm_samsung_free,
	.enable		= pwm_samsung_enable,
	.disable	= pwm_samsung_disable,
	.config		= pwm_samsung_config,
	.set_polarity	= pwm_samsung_set_polarity,
	.owner		= THIS_MODULE,
};

当然这些ops也都是pwm子系统中通函数指针来调用来实现具体硬件配置功能的。

 

/* pwm请求和释放,主要是申请空间 */
static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
	struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
	struct samsung_pwm_channel *our_chan;

	if (!(our_chip->variant.output_mask & BIT(pwm->hwpwm))) {
		dev_warn(chip->dev,
			"tried to request PWM channel %d without output\n",
			pwm->hwpwm);
		return -EINVAL;
	}

	our_chan = devm_kzalloc(chip->dev, sizeof(*our_chan), GFP_KERNEL);
	if (!our_chan)
		return -ENOMEM;

	pwm_set_chip_data(pwm, our_chan);

	return 0;
}

static void pwm_samsung_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
	devm_kfree(chip->dev, pwm_get_chip_data(pwm));
	pwm_set_chip_data(pwm, NULL);
}

 

/* PWM使能和禁止,主要是硬件寄存器相关使能和禁止 */
static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
	struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
	unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm);
	unsigned long flags;
	u32 tcon;

	spin_lock_irqsave(&samsung_pwm_lock, flags);

	tcon = readl(our_chip->base + REG_TCON);

	tcon &= ~TCON_START(tcon_chan);
	tcon |= TCON_MANUALUPDATE(tcon_chan);
	writel(tcon, our_chip->base + REG_TCON);

	tcon &= ~TCON_MANUALUPDATE(tcon_chan);
	tcon |= TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan);
	writel(tcon, our_chip->base + REG_TCON);

	spin_unlock_irqrestore(&samsung_pwm_lock, flags);

	return 0;
}

static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
	struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
	unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm);
	unsigned long flags;
	u32 tcon;

	spin_lock_irqsave(&samsung_pwm_lock, flags);

	tcon = readl(our_chip->base + REG_TCON);
	tcon &= ~TCON_AUTORELOAD(tcon_chan);
	writel(tcon, our_chip->base + REG_TCON);

	spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}

 

配置主要就是周期,占空比之类的参数设置


static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
			      int duty_ns, int period_ns)
{
	struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
	struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm);
	u32 tin_ns = chan->tin_ns, tcnt, tcmp;

	/*
	 * We currently avoid using 64bit arithmetic by using the
	 * fact that anything faster than 1Hz is easily representable
	 * by 32bits.
	 */
	if (period_ns > NSEC_PER_SEC)
		return -ERANGE;

	if (period_ns == chan->period_ns && duty_ns == chan->duty_ns)
		return 0;

	tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm));

	/* We need tick count for calculation, not last tick. */
	++tcnt;

	/* Check to see if we are changing the clock rate of the PWM. */
	if (chan->period_ns != period_ns) {
		unsigned long tin_rate;
		u32 period;

		period = NSEC_PER_SEC / period_ns;

		dev_dbg(our_chip->chip.dev, "duty_ns=%d, period_ns=%d (%u)\n",
						duty_ns, period_ns, period);

		tin_rate = pwm_samsung_calc_tin(our_chip, pwm->hwpwm, period);

		dev_dbg(our_chip->chip.dev, "tin_rate=%lu\n", tin_rate);

		tin_ns = NSEC_PER_SEC / tin_rate;
		tcnt = period_ns / tin_ns;
	}

	/* Period is too short. */
	if (tcnt <= 1)
		return -ERANGE;

	/* Note that counters count down. */
	tcmp = duty_ns / tin_ns;

	/* 0% duty is not available */
	if (!tcmp)
		++tcmp;

	tcmp = tcnt - tcmp;

	/* Decrement to get tick numbers, instead of tick counts. */
	--tcnt;
	/* -1UL will give 100% duty. */
	--tcmp;

	dev_dbg(our_chip->chip.dev,
				"tin_ns=%u, tcmp=%u/%u\n", tin_ns, tcmp, tcnt);

	/* Update PWM registers. */
	writel(tcnt, our_chip->base + REG_TCNTB(pwm->hwpwm));
	writel(tcmp, our_chip->base + REG_TCMPB(pwm->hwpwm));

	chan->period_ns = period_ns;
	chan->tin_ns = tin_ns;
	chan->duty_ns = duty_ns;

	return 0;
}

最后就是时钟极性相关的配置了,主要就是设置时钟极性是不是要反转。


static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip,
				   unsigned int channel, bool invert)
{
	unsigned int tcon_chan = to_tcon_channel(channel);
	unsigned long flags;
	u32 tcon;

	spin_lock_irqsave(&samsung_pwm_lock, flags);

	tcon = readl(chip->base + REG_TCON);

	if (invert) {
		chip->inverter_mask |= BIT(channel);
		tcon |= TCON_INVERT(tcon_chan);
	} else {
		chip->inverter_mask &= ~BIT(channel);
		tcon &= ~TCON_INVERT(tcon_chan);
	}

	writel(tcon, chip->base + REG_TCON);

	spin_unlock_irqrestore(&samsung_pwm_lock, flags);
}

static int pwm_samsung_set_polarity(struct pwm_chip *chip,
				    struct pwm_device *pwm,
				    enum pwm_polarity polarity)
{
	struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
	bool invert = (polarity == PWM_POLARITY_NORMAL);

	/* Inverted means normal in the hardware. */
	pwm_samsung_set_invert(our_chip, pwm->hwpwm, invert);

	return 0;
}

 

通过上面的分析,已经可以知道pwm子系统可以作为单独的子系统,而且不是作为一个设备存在的(无设备号,上面的都是MKDEV(0,0)

 

最后总结一下pwm类是通过注册chip来注册一个芯片中所有的pwm的。

具体哪个pwm可以用个sysfs中的attributre属性来注册。

同时注册好某个pwm后,它的周期,占空比,极性,开关(使能)也是可以在sysfs中直接操纵的。

这样极大程度的降低了驱动程序修改的次数,灵活性获得极大提高。

 

下面就看一下实战部分:

这里的pmwchip0就是我们注册的三星的。

可以看道pwmchip0下面就有我们上面说的几种属性操作文件。

 

下面我们一一查看。

比如可以cat查看nowm个数

可以创建陪chip里面具体的pwm,比如我们上面注册了pwm0

进入到pwm0既可以看到我们前面说过可以修改的属性文件,时钟极性,周期,占空比,是否使能。

 

可以看到我们这些都是可以进行设置,具体设什么值,驱动中几个sttribute函数分析一下就知道了。

 

比如时钟极性时inversed和normal

使能测用0和1

 

周期和占空比的事件则是以ns为基准的

 

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页