本文主要是介绍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驱动分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!