smart210 linux3.0.8内核蜂鸣器pwm驱动分析

2024-06-23 05:58

本文主要是介绍smart210 linux3.0.8内核蜂鸣器pwm驱动分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

刚刚移植这个pwm驱动到linux3.7.4,发现了一些问题,所以把这个3.0.8的pwm驱动看看,友善带的pwm驱动为mini210-pwm.c,这个只是这个驱动的一部分,直接搬到3.7.4内核下不能用,让我们来好好看看这个驱动的实现吧

static int __init mini210_pwm_dev_init(void) {int ret;ret = gpio_request(BUZZER_PMW_GPIO, DEVICE_NAME);申请一个io口资源if (ret) {printk("request GPIO %d for pwm failed\n", BUZZER_PMW_GPIO);return ret;}gpio_set_value(BUZZER_PMW_GPIO, 0);设置io口的值为低电平s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_OUTPUT);配置io口为输出pwm4buzzer = pwm_request(BUZZER_PWM_ID, DEVICE_NAME);这个函数为请求一个pwm资源,需要细看if (IS_ERR(pwm4buzzer)) {printk("request pwm %d for %s failed\n", BUZZER_PWM_ID, DEVICE_NAME);return -ENODEV;}pwm_stop();sema_init(&lock, 1);ret = misc_register(&mini210_misc_dev);printk(DEVICE_NAME "\tinitialized\n");return ret;
}
上面是pwm驱动的init函数,主要做的是注册了一个混杂驱动,配置了io口,申请了pwm资源,其中pwm4buzzer为struct pwm_device *类型的指针,lock为这个驱动定义的一个信号量,pwm_device为定义在arch\arm\plat-samsung\pwm.c中的一个结构体,用来描述一个pwm设备

struct pwm_device {struct list_head	 list;struct platform_device	*pdev;struct clk		*clk_div;
struct clk		*clk;const char		*label;unsigned int		 period_ns;unsigned int		 duty_ns;unsigned char		 tcon_base;unsigned char		 running;unsigned char		 use_count;unsigned char		 pwm_id;
};
驱动是怎样跟系统申请一个pwm资源的呢

struct pwm_device *pwm_request(int pwm_id, const char *label)
{struct pwm_device *pwm;int found = 0;mutex_lock(&pwm_lock);list_for_each_entry(pwm, &pwm_list, list) {遍历链表,寻找注册到系统中的pwm_deviceif (pwm->pwm_id == pwm_id) {如果系统中存在这个pwm_id号的pwm资源,found = 1;break;}}if (found) {如果成功找到系统资源,那么就再给pwm_device结构成员赋值if (pwm->use_count == 0) {pwm->use_count = 1;pwm->label = label;} elsepwm = ERR_PTR(-EBUSY);} elsepwm = ERR_PTR(-ENOENT);mutex_unlock(&pwm_lock);return pwm;成功返回一个,pwm_device结构体
}

关键问题来了,系统在哪把pwm_device添加到链表呢,

同样在上面那个pwm.c文件中,有下面这个函数,吧pwm_device这个结构的链表加入到全局链表pwm_list中

static int pwm_register(struct pwm_device *pwm)
{pwm->duty_ns = -1;pwm->period_ns = -1;mutex_lock(&pwm_lock);list_add_tail(&pwm->list, &pwm_list);mutex_unlock(&pwm_lock);return 0;
}
继续看pwm.c这个文件,这个文件也是一个驱动,是一个platform机制的模块

static int __init pwm_init(void)
{int ret;clk_scaler[0] = clk_get(NULL, "pwm-scaler0");clk_scaler[1] = clk_get(NULL, "pwm-scaler1");if (IS_ERR(clk_scaler[0]) || IS_ERR(clk_scaler[1])) {printk(KERN_ERR "%s: failed to get scaler clocks\n", __func__);return -EINVAL;}ret = platform_driver_register(&s3c_pwm_driver);if (ret)printk(KERN_ERR "%s: failed to add pwm driver\n", __func__);return ret;
}arch_initcall(pwm_init);

查看他的probe函数

static int s3c_pwm_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct pwm_device *pwm;unsigned long flags;unsigned long tcon;unsigned int id = pdev->id;int ret;if (id == 4) {dev_err(dev, "TIMER4 is currently not supported\n");return -ENXIO;}pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);if (pwm == NULL) {dev_err(dev, "failed to allocate pwm_device\n");return -ENOMEM;}pwm->pdev = pdev;pwm->pwm_id = id;/* calculate base of control bits in TCON */pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4;pwm->clk = clk_get(dev, "pwm-tin");if (IS_ERR(pwm->clk)) {dev_err(dev, "failed to get pwm tin clk\n");ret = PTR_ERR(pwm->clk);goto err_alloc;}pwm->clk_div = clk_get(dev, "pwm-tdiv");if (IS_ERR(pwm->clk_div)) {dev_err(dev, "failed to get pwm tdiv clk\n");ret = PTR_ERR(pwm->clk_div);goto err_clk_tin;}local_irq_save(flags);tcon = __raw_readl(S3C2410_TCON);tcon |= pwm_tcon_invert(pwm);__raw_writel(tcon, S3C2410_TCON);local_irq_restore(flags);ret = pwm_register(pwm);if (ret) {dev_err(dev, "failed to register pwm\n");goto err_clk_tdiv;}pwm_dbg(pwm, "config bits %02x\n",(__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f);dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n",clk_get_rate(pwm->clk),clk_get_rate(pwm->clk_div),pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base);platform_set_drvdata(pdev, pwm);return 0;err_clk_tdiv:clk_put(pwm->clk_div);err_clk_tin:clk_put(pwm->clk);err_alloc:kfree(pwm);return ret;
}
这个probe函数主要做的事是

1.分配pwm_device结构体内存

2初始化pwm_device,比如

pwm->pdev = pdev;
pwm->pwm_id = id;
pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4;

pwm->clk = clk_get(dev, "pwm-tin");

pwm->clk_div = clk_get(dev, "pwm-tdiv");
3.操作tcon寄存器,是对应的定时器的工作模式为TOUT_x Inverter-On 模式

local_irq_save(flags);tcon = __raw_readl(S3C2410_TCON);
tcon |= pwm_tcon_invert(pwm);
__raw_writel(tcon, S3C2410_TCON);

local_irq_restore(flags);

4.注册pwm_device到系统中,这个就是我们上面说的注册到链表

由上面分析可知,要想使用pwm功能用作蜂鸣器,除了要有蜂鸣器的驱动外,在系统中还需要注册pwm_device,要想注册这个pwm_device就要先注册一个pwm类型的platform平台设备,这个设备的定义在arch\arm\plat-samsung\dev-pwm.c中

#define TIMER_RESOURCE_SIZE (1)
#define TIMER_RESOURCE(_tmr, _irq)			\(struct resource [TIMER_RESOURCE_SIZE]) {	\[0] = {					\.start	= _irq,			\.end	= _irq,			\.flags	= IORESOURCE_IRQ	\}					\}#define DEFINE_S3C_TIMER(_tmr_no, _irq)			\.name		= "s3c24xx-pwm",		\.id		= _tmr_no,			\.num_resources	= TIMER_RESOURCE_SIZE,		\.resource	= TIMER_RESOURCE(_tmr_no, _irq),	\/** since we already have an static mapping for the timer,* we do not bother setting any IO resource for the base.*/
struct platform_device s3c_device_timer[] = {[0] = { DEFINE_S3C_TIMER(0, IRQ_TIMER0) },[1] = { DEFINE_S3C_TIMER(1, IRQ_TIMER1) },[2] = { DEFINE_S3C_TIMER(2, IRQ_TIMER2) },[3] = { DEFINE_S3C_TIMER(3, IRQ_TIMER3) },[4] = { DEFINE_S3C_TIMER(4, IRQ_TIMER4) },
};
EXPORT_SYMBOL(s3c_device_timer);
在开发板的板级配置文件mach-mini210.c中有

static struct platform_device *mini210_devices[] __initdata = {

.............................................

&s3c_device_timer[0],

&s3c_device_timer[MINI210_BL_PWM],
..................................

大体上就这样了,linux3.7.4的结构好像和3.0.8的区别很大,有需要的话把3.7.4的也分析一下

这篇关于smart210 linux3.0.8内核蜂鸣器pwm驱动分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1086321

相关文章

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

快速修复一个Panic的Linux内核的技巧

《快速修复一个Panic的Linux内核的技巧》Linux系统中运行了不当的mkinitcpio操作导致内核文件不能正常工作,重启的时候,内核启动中止于Panic状态,该怎么解决这个问题呢?下面我们就... 感谢China编程(www.chinasem.cn)网友 鸢一雨音 的投稿写这篇文章是有原因的。为了配置完

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

Java程序进程起来了但是不打印日志的原因分析

《Java程序进程起来了但是不打印日志的原因分析》:本文主要介绍Java程序进程起来了但是不打印日志的原因分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java程序进程起来了但是不打印日志的原因1、日志配置问题2、日志文件权限问题3、日志文件路径问题4、程序

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO