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_device
		if (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;
		} else
			pwm = ERR_PTR(-EBUSY);
	} else
		pwm = 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的也分析一下

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值