tiny4412 驱动 (12)设备树之beep(pwm)

本文介绍了一个基于 Linux 4.19.27 的 Beep PWM 驱动实现细节,包括设备树配置、驱动源码分析及工作原理说明。该驱动通过 PWM 控制蜂鸣器发声。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内核版本linux-4.19.27

不同前面设备树之beep,只是控制gpio电平来驱动beep,这里使用pwm

先看设备树

beep-pwm {
                compatible = "tiny4412,beep-pwm";
                reg = <0x139D0000 0x14>;
                pinctrl-names = "pwm_pin";
                pinctrl-0 = <&pwm_pin>;
                clocks = <&clock CLK_PWM>;
                clock-names = "timers";
        };



&pinctrl_0 {	
	pwm_pin: pwm_pin {
	                samsung,pins = "gpd0-0";
	                samsung,pin-function = <2>;
	                samsung,pin-pud = <0>;
	                samsung,pin-drv = <0>;
	        };
	...
};

在没有设备树之前,采用clk_get(NULL, “timers”)等获取时钟。在采用设备树时候,节点添加如下属性即可。

 clocks = <&clock CLK_PWM>;
 clock-names = "timers";

reg属性会被自动展开为以前platform_device的IORESOURCE,兼容以前的 
platform_get_resource API。 
timers是我们取的名字。 
CLK_PWM在include/dt-bindings/clock/exynos4.h定义:

 

&clock引用arch/arm/boot/dts/exynos4412.dtsi上节点 

直接看驱动:

/*
 * beep-pwm-dt driver for tiny4412 on linux-4.19.27
*/
#include <linux/module.h>
	
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
	
#include <linux/platform_device.h>
#include <linux/slab.h>       //
#include <linux/io.h>
	
#include <linux/gfp.h>
#include <linux/cdev.h>
	
#include <linux/uaccess.h> /* copy_from_usr, */
#include <linux/interrupt.h>

#include <linux/clk.h>
	
struct pri_data
{
	struct resource *res;
	struct pinctrl *pinctrl;
	struct pinctrl_state *pstate;
	struct clk *base_clk;
};

static struct pri_data *dt_data = NULL;


#define DEV_NAME		"beep-pwm-dt"

struct pri_dev
{	
	dev_t dev;
	int major;
	char name[32];
	struct class *class;
	struct cdev *cdev;
};


static struct pri_dev *dev = NULL;


/*
* @ Device Tree :


beep-pwm {
                compatible = "tiny4412,beep-pwm";
                reg = <0x139D0000 0x14>;
                pinctrl-names = "pwm_pin";
                pinctrl-0 = <&pwm_pin>;
                clocks = <&clock CLK_PWM>;
                clock-names = "timers";
        };



&pinctrl_0 {	
	pwm_pin: pwm_pin {
	                samsung,pins = "gpd0-0";
	                samsung,pin-function = <2>;
	                samsung,pin-pud = <0>;
	                samsung,pin-drv = <0>;
	        };
	...
};

*/


struct TIMER_BASE{
    unsigned int TCFG0;         
    unsigned int TCFG1;          
    unsigned int TCON;       
    unsigned int TCNTB0;         
    unsigned int TCMPB0;         
};

volatile static struct TIMER_BASE * timer = NULL;


#define BEPP_IN_FREQ 100000  
static void beep_set_freq(unsigned long arg)  
{   
    printk("ioctl %d\n",(unsigned int)arg);
    timer->TCNTB0 = BEPP_IN_FREQ;
    timer->TCMPB0 = BEPP_IN_FREQ / arg;
    timer->TCON   = (timer->TCON  &~(0x0f << 0)) | (0x06 << 0);
    timer->TCON = (timer->TCON &~(0xff))| 0x0d;
} 

static void beep_on(void)
{
    printk("beep on\n");
    timer->TCON = (timer->TCON &~(0xff))| 0x0d;
    printk("%x\n",timer->TCON);
}


static void beep_off(void)
{
    timer->TCON = timer->TCON & ~(0x01);
}




static int beep_open (struct inode *inode, struct file *file)
{
	printk(KERN_INFO "open \n");	
	
	return 0;
}

static int beep_close (struct inode *inode, struct file *file)
{
	printk(KERN_INFO "close \n");	
	
	return 0;
}

static ssize_t beep_write (struct file *file, const char __user *usrbuf, size_t len, loff_t *off)
{
	char cmd,opt;
	char rev_buf[8] = {0};
	
	printk(KERN_INFO "write \n");		

	if(copy_from_user(rev_buf,usrbuf,8))
		return -EFAULT;

	cmd = rev_buf[0];
	opt = rev_buf[1];

	printk(KERN_NOTICE "cmd : %d opt : %d \n", cmd, opt);

	if(cmd == 0)
	{
		// off
		beep_off();	
	}
	else if(cmd == 1)
	{
		// on
		beep_on();
	}
	else
	{
		// do nothing.
	}
	return 0;
}


const struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = beep_open,
	.release = beep_close,
	.write = beep_write,
};

static int chrdev_setup(void)
{
	int ret = 0;
	dev = kmalloc(sizeof(struct pri_dev),GFP_KERNEL);
	if(!dev)
		return -ENOMEM;

	strcpy(dev->name, DEV_NAME);

	ret = alloc_chrdev_region(&dev->dev, 0, 1, dev->name);
	dev->major = MAJOR(dev->dev);
	
	if(ret < 0)
	{
		kfree(dev);
		return ret;
	}

	dev->cdev = cdev_alloc();
	if(!dev->cdev)
	{
		unregister_chrdev_region(dev->dev,1);
		kfree(dev);
		return -EFAULT;
	}
	cdev_init(dev->cdev,&fops);
	dev->cdev->owner = THIS_MODULE;
	dev->cdev->ops = &fops;
	cdev_add(dev->cdev,dev->dev,1);

	dev->class = class_create(THIS_MODULE,dev->name);
	ret = PTR_ERR(dev->class);
	if (IS_ERR(dev->class))
	{
		cdev_del(dev->cdev);
		unregister_chrdev_region(dev->dev,1);
		kfree(dev);
		return -EFAULT;
	}

	device_create(dev->class,NULL,dev->dev,NULL,dev->name,dev);

	return 0;
}

static void chrdev_setdown(void)
{
	device_destroy(dev->class, dev->dev);
	class_destroy(dev->class);
	cdev_del(dev->cdev);
	unregister_chrdev_region(dev->dev,1);
	kfree(dev);
}



static int beep_pwm_probe(struct platform_device *pdev)
{
	int ret = 0;

	struct device *dev = &pdev->dev;
	
	pr_info("%s called .\n", __func__);

	ret = chrdev_setup();
	if(ret){
		pr_err("chrdev setup err.\n");
		return -EINVAL;
	}

	dt_data = devm_kmalloc(dev,sizeof(struct pri_data),  GFP_KERNEL);
	if(!dt_data){
		pr_err("no memory .\n");
		goto err;
	}

	dt_data->pinctrl = devm_pinctrl_get(dev);
	if(!dt_data->pinctrl){
		pr_err("get pinctrl err.\n");
		goto err1;
	}

	dt_data->pstate = pinctrl_lookup_state(dt_data->pinctrl , "pwm_pin");
	if(!dt_data->pstate){
		pr_err("get pinctrl state err.\n");
		goto err1;
	}

	/* select pinctrl state */
	pinctrl_select_state(dt_data->pinctrl , dt_data->pstate);

	dt_data->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if(!dt_data->res){
		pr_err("get resource err.\n");
		goto err1;
	}

	/* get clk */
	dt_data->base_clk = devm_clk_get(&pdev->dev, "timers");
	if (IS_ERR(dt_data->base_clk)) {
		pr_err("get base clk err.\n");
		goto err1;
	 }

	/* enable clk */
	ret = clk_prepare_enable(dt_data->base_clk);
	if (ret < 0) {
        dev_err(dev, "failed to enable base clock ret : %d \n", ret);
        goto err1;
    }

	/* ioremap */
	timer = devm_ioremap_resource(&pdev->dev, dt_data->res);
	if(!timer){
		pr_err("ioremap err.\n");
		goto err1;
	}

	/* timer setup */
	timer->TCFG0  = (timer->TCFG0 &~(0xff << 0)) | (0xfa << 0);
	timer->TCFG1  = (timer->TCFG1 &~(0x0f << 0)) | (0x02 << 0);
	timer->TCNTB0 = 100000;
	timer->TCMPB0 = 90000;
	timer->TCON   = (timer->TCON  &~(0x0f << 0)) | (0x06 << 0);
	printk("timer :TCFG0:%08x TCFG1:%08x TCNTB0:%08x TCMPB0:%08x TCON:%08x\n",timer->TCFG0,timer->TCFG1,
		timer->TCNTB0,timer->TCMPB0,timer->TCON);
	
	
	return 0;

err1:	
	devm_kfree(dev,dt_data);
err:
	chrdev_setdown();
	return -EINVAL;
}

static int beep_pwm_remove(struct platform_device *pdev)
{
	pr_info("%s called .\n", __func__);
	
	chrdev_setdown();
	
	return 0;
}

static const struct of_device_id tiny4412_beep_match_tbl[] = 
{
	{.compatible = "tiny4412,beep-pwm"},
	{},
};


static struct platform_driver tiny4412_beep_pwm_dt_drv = 
{
	.probe = beep_pwm_probe,
	.remove = beep_pwm_remove,
	.driver = 
	{
		.owner = THIS_MODULE,
		.name = "beep-pwm-dt",
		.of_match_table = tiny4412_beep_match_tbl,
	}
};


static int __init beep_pwm_dt_init(void)
{
	int ret = 0;

	ret = platform_driver_register(&tiny4412_beep_pwm_dt_drv);
	if(ret)
		printk(KERN_ERR "platform register  failed: %d\n", ret);

	return ret;
}

static void __exit beep_pwm_dt_exit(void)
{
	platform_driver_unregister(&tiny4412_beep_pwm_dt_drv);
}

module_init(beep_pwm_dt_init);
module_exit(beep_pwm_dt_exit);
MODULE_LICENSE("GPL");

由于没有设置频率,默认以1HZ来驱动

[root@tiny4412 ]#./driver_test_tool /dev/beep-pwm-dt -o 1        # on
[  364.356548] open 
cmd : 1 0 0 0 0 0 0 0 
[  364.362535] write 
[  364.362577] cmd : 1 opt : 0 
[  364.362616] beep on
[  364.362649] 50000d
[  364.362695] close 
[root@tiny4412 ]# ./driver_test_tool /dev/beep-pwm-dt -o 0       #off
[  371.712235] open 
cmd : 0 0 0 0 0 0 0 0 
[  371.712394] write 
[  371.712430] cmd : 0 opt : 0 
[  371.712480] close 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值