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");