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

相关文章

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

Linux之platform平台设备驱动详解

《Linux之platform平台设备驱动详解》Linux设备驱动模型中,Platform总线作为虚拟总线统一管理无物理总线依赖的嵌入式设备,通过platform_driver和platform_de... 目录platform驱动注册platform设备注册设备树Platform驱动和设备的关系总结在 l

Olingo分析和实践之EDM 辅助序列化器详解(最佳实践)

《Olingo分析和实践之EDM辅助序列化器详解(最佳实践)》EDM辅助序列化器是ApacheOlingoOData框架中无需完整EDM模型的智能序列化工具,通过运行时类型推断实现灵活数据转换,适用... 目录概念与定义什么是 EDM 辅助序列化器?核心概念设计目标核心特点1. EDM 信息可选2. 智能类

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

Olingo分析和实践之ODataImpl详细分析(重要方法详解)

《Olingo分析和实践之ODataImpl详细分析(重要方法详解)》ODataImpl.java是ApacheOlingoOData框架的核心工厂类,负责创建序列化器、反序列化器和处理器等组件,... 目录概述主要职责类结构与继承关系核心功能分析1. 序列化器管理2. 反序列化器管理3. 处理器管理重要方

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期