内核中三星默认是没选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为基准的