smart210驱动(11)蜂鸣器-pwm

buzzer_pwm_dev.c

/*
* linux-3.10.27
* arm-linux-gcc-4.5.1
*
* @ buzzer pwm device  (base platform device driver)
*/

#include <linux/module.h>
#include <linux/init.h>   /* module_init, ... */
#include <linux/kernel.h> /* everything */

#include <linux/cdev.h>   /* cdev_init, ... */
#include <linux/fs.h>     /* file_operations,  */
#include <linux/device.h>  /* class_create,... */
#include <linux/platform_device.h>
#include <linux/slab.h>   /* kmalloc, ... */
#include <asm/io.h>       /* ioremap,... */



#include <linux/gpio.h>

static struct resource buzzer_res[] = 
{
	{
		.start = 0xE02000A0,   // gpd0_0 GPD0CON
		.end = 0xE02000C0,
		.flags = IORESOURCE_MEM,
	},
	{
		.start = 0xE2500000,   // timer
		.end = 0xE2500044,
		.flags = IORESOURCE_MEM,
	},
};


static void	buzzer_dev_release(struct device *dev)
{
	pr_info("%s called.\n", __func__);
}

static struct platform_device buzzer_dev = 
{
	.name = "smart210,buzzer",
	.dev = {
		.release = buzzer_dev_release,
	},
	.resource = buzzer_res,
	.num_resources = ARRAY_SIZE(buzzer_res),
};

static int __init buzzer_platform_dev_init(void)
{
	int ret = 0;
	
	pr_info("buzzer platform dev init.\n");

	ret = platform_device_register(&buzzer_dev);
	if(ret){
		pr_err("platform register err.\n");
	}
	
	return ret;
}

static void __exit buzzer_platform_dev_exit(void)
{
	pr_info("buzzer platform dev exit.\n");

	platform_device_unregister(&buzzer_dev);
}

module_init(buzzer_platform_dev_init);
module_exit(buzzer_platform_dev_exit);
MODULE_LICENSE("GPL");

buzzer_pwm_drv.c

/*
* linux-3.10.27
* arm-linux-gcc-4.5.1
*
* @ buzzer driver  (base platform device driver)
*/

#include <linux/module.h>
#include <linux/init.h>   /* module_init, ... */
#include <linux/kernel.h> /* everything */

#include <linux/cdev.h>   /* cdev_init, ... */
#include <linux/fs.h>     /* file_operations,  */
#include <linux/device.h>  /* class_create,... */
#include <linux/platform_device.h>
#include <linux/slab.h>   /* kmalloc, ... */
#include <linux/io.h>       /* ioremap,... */

#include <linux/uaccess.h>   /* copy_from_user, ... */

#define BIT(x)			(1 << (x))

struct buzzer_gpio
{
	unsigned long con;
	unsigned long dat;
	unsigned long pud;
	unsigned long drv;
	unsigned long conpdn;
	unsigned long pudpdn;
};

struct s5pv210_timer
{
	unsigned long tcfg0;
	unsigned long tcfg1;
	unsigned long tcon;
	unsigned long tcntb0;
	unsigned long tcmpb0;
	unsigned long tcnto0;
	unsigned long tcntb1;
	unsigned long tcmpb1;
	unsigned long tcnto1;
	unsigned long tcntb2;
	unsigned long tcmpb2;
	unsigned long tcnto2;
	unsigned long tcntb3;
	unsigned long tcmpb3;
	unsigned long tcnto3;
	unsigned long tcntb4;
	unsigned long tcnto4;
	unsigned long tint_cstat;
};

#define DEF_DEV_NAME		"myBuzzer"
#define DEF_MAJOR			0
struct priv_data
{
	char *name;
	int major;

	dev_t dev;
	struct cdev *cdev;
	struct class *cls;
	struct buzzer_gpio *base;
	struct s5pv210_timer *timer;
};

static struct priv_data *priv_data;

static int smart210_buzzer_open (struct inode *inode, struct file *pfile)
{
	pr_info("%s called.\n", __func__);

	if(!priv_data)
		return -1;

	/* set gpd0_0 as TOUT_0 */
	priv_data->base->con = 0x2;


	/*
	* The TCNTBn value of 100 is loaded into the down-counter, 
	* and then the output TOUTn is set to low
	* If down-counter counts down the value from TCNTBn to value in the TCMPBn 
	* register 0, the output changes from low to high
	*
	* The down-counter automatically reloads TCNTBn. This restarts the cycle.
	*/


	/*
	* clk = PCLK / (tcfg0 + 1) / (*tcfg1 + 1) = 66MHz / 66  = 1MHz
	*/
	priv_data->timer->tcfg0 = 65;
	priv_data->timer->tcfg1 = 0;

	priv_data->timer->tcntb0 = 1000;
	priv_data->timer->tcmpb0 = 1000;   // default low

	priv_data->timer->tcon |= BIT(3) + BIT(0);
	
	return 0;
}

static int smart210_buzzer_close (struct inode *inode, struct file *pfile)
{
	pr_info("%s called.\n", __func__);

	return 0;
}

static ssize_t smart210_buzzer_write (struct file *file, 
	const char __user *usrbuff, size_t len, loff_t *offset)
{
	char recvbuf[8] = {0};
	char cmd, opt;

	if(copy_from_user(recvbuf, usrbuff, len)){
		return -EINVAL;
	}

	cmd = recvbuf[0];
    opt = recvbuf[1];
	printk(KERN_NOTICE "cmd : %d opt : %d  \n", cmd, opt);
	switch(cmd){
		case 0:     /* stop */
			priv_data->timer->tcon &= ~BIT(0);  /* stop */
			priv_data->timer->tcmpb0 = 0;
			break;

		case 1:    /* start */
			priv_data->timer->tcmpb0 = (opt > 100) ? 100 : opt; 
			break;

		default:
			break;
	}
	
	return 0;
}

const struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = smart210_buzzer_open,
	.release = smart210_buzzer_close,
	.write = smart210_buzzer_write,
};

static int buzzer_pwm_drv_probe(struct platform_device *pdev)
{
	int ret = 0;
	struct device *dev = &pdev->dev;
	struct resource *res0, *res1;
	
	pr_info("%s called .\n", __func__);

	priv_data = devm_kzalloc(&pdev->dev, sizeof(struct priv_data), GFP_KERNEL);
	if(!priv_data){
		pr_err("no mem.\n");
		return -ENOMEM;
	}

	priv_data->name = DEF_DEV_NAME;
	priv_data->major = DEF_MAJOR;

	if(priv_data->major){
		priv_data->dev = MKDEV(priv_data->major, 0);
		ret = register_chrdev_region(priv_data->dev, 1, priv_data->name);
	}else{
		ret = alloc_chrdev_region(&priv_data->dev, 1, 1, priv_data->name);
		priv_data->major = MAJOR(priv_data->dev);
	}

	if(ret){
		pr_err("chrdev register err.\n");
		return ret;
	}

	/* add cdev */
	priv_data->cdev = cdev_alloc();
	if(!priv_data->cdev){
		pr_err("cdev alloc err.\n");
		goto err0;
	}

	cdev_init(priv_data->cdev, &fops);
	priv_data->cdev->owner = THIS_MODULE;
	priv_data->cdev->ops = &fops;
	cdev_add(priv_data->cdev, priv_data->dev, 1);

	/* add class */
	priv_data->cls = class_create(THIS_MODULE, priv_data->name);
	if(!priv_data->cls){
		pr_err("class create err.\n");
		goto err1;
	}
	device_create(priv_data->cls, NULL, priv_data->dev, NULL, priv_data->name);	

	res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	priv_data->base = (struct buzzer_gpio *)devm_ioremap(dev, res0->start, resource_size(res0));
	if(!priv_data->base){
		pr_err("ioremap base err.\n");
		goto err2;
	}

	res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	priv_data->timer = (struct s5pv210_timer *)devm_ioremap(dev, res1->start, resource_size(res1));
	if(!priv_data->timer){
		pr_err("ioremap base err.\n");
		goto err2;
	}

	return 0;
err2:
	device_destroy(priv_data->cls, priv_data->dev);
	class_destroy(priv_data->cls);
err1:
	cdev_del(priv_data->cdev);
err0:
	unregister_chrdev_region(priv_data->dev, 1);
	return -EINVAL;	
}

static int buzzer_pwm_drv_remove(struct platform_device *pdev)
{
	pr_info("%s called .\n", __func__);
	device_destroy(priv_data->cls, priv_data->dev);
	class_destroy(priv_data->cls);

	cdev_del(priv_data->cdev);
	unregister_chrdev_region(priv_data->dev, 1);
	return 0;
}

const struct of_device_id buzzer_ids[] = 
{
	{.compatible = "smart210,buzzer"},
	{}
};

static struct platform_driver buzzer_pwm_drv = {
	.driver = {
		.name = "smart210,buzzer",
		.of_match_table = buzzer_ids,
	},
	.probe = buzzer_pwm_drv_probe,
	.remove = buzzer_pwm_drv_remove,
};
static int __init buzzer_platform_drv_init(void)
{
	int ret = 0;
	pr_info("%s called .\n", __func__);

	ret = platform_driver_register(&buzzer_pwm_drv);
	if(ret){
		pr_err("platform driver register err.\n");
	}
	
	return ret;
}

static void __exit buzzer_platform_drv_exit(void)
{
	pr_info("%s called .\n", __func__);

	platform_driver_unregister(&buzzer_pwm_drv);
}

module_init(buzzer_platform_drv_init);
module_exit(buzzer_platform_drv_exit);
MODULE_LICENSE("GPL");

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值