全志A20 keyadc驱动程序及toolbox测试代码

原创 2013年12月04日 06:24:45

全志A20 keyadc驱动程序及toolbox测试代码

//TODO:文件分类, 功能

一,


//TODO:程序的分解

二,


//TODO:程序的调试和测试

三,


附录:

//TODO:keyadc驱动程序

/*
 *  linux/drivers/char/keyadc.c
 *
 *  Copyright (C) 2013 Jack Chen, chwenj@gmail.com
 *
 *      - Base on Allwinner A20 K70 platform;
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of this archive
 * for more details.
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/uaccess.h>

/****************************************************************/
#define MAGIC '8'
#define KEYADC_SET		_IOW(MAGIC, 0, unsigned long)
#define KEYADC_GET		_IOR(MAGIC, 1, unsigned long)

#define KEYADC_VOLUP		(0x11)
#define KEYADC_VOLDOWN		(0x12)
#define KEYADC_HOME		(0x13)
#define KEYADC_BACK		(0x14)

#define KEY_IRQNO		(AW_IRQ_LRADC)

#define INPUTNAME		"keyadc_input"	
#define PLATFORMNAME		"platform_keyadc"
#define DEVNAME			"keyadc"
#define WORKQUEUENAME		"keyadc_workqueue"
#define IRQNAME			"keyadc_irq"

//#define DEBUGMASK 
#ifdef DEBUGMASK
	#define dprintk(fmt, arg...) printk(fmt, ## arg)
#else
	#define dprintk(fmt, arg...)
#endif

/****************************************************************/
static struct sun7i_lradc_regs {
	#define LRADC_BASE_ADDR         (0xf1c22800) 
	#define LRADC_CTRL		(0x00)
	#define LRADC_INTC		(0x04)
	#define LRADC_INTS		(0x08)
	#define LRADC_DATA0		(0x0c)
	#define LRADC_DATA1		(0x10)
 			
//regs 
	/*LRADC_CTRL*/
	u32 ctrl;
	/*LRADC_INTC*/
	u32 intc;
	/*LRADC_INTS*/
	u32 ints;
	/*LRADC_DATA0*/
	u32 data0;
	/*LRADC_DATA1*/
	u32 data1;
//bits
	#define  FIRST_CONCERT_DLY	(2<<24)
	#define  CHAN			(0x3)
	#define  ADC_CHAN_SELECT	(CHAN<<22)
	#define  LRADC_KEY_MODE		(0)
	#define  KEY_MODE_SELECT	(LRADC_KEY_MODE<<12)
	#define  LEVELB_VOL		(0<<4)

	#define  LRADC_HOLD_EN		(1<<6)

	#define  LRADC_SAMPLE_32HZ	(3<<2)
	#define  LRADC_SAMPLE_62HZ	(2<<2)
	#define  LRADC_SAMPLE_125HZ	(1<<2)
	#define  LRADC_SAMPLE_250HZ	(0<<2)

	#define  LRADC_EN		(1<<0)

	#define  LRADC_ADC1_UP_EN	(1<<12)
	#define  LRADC_ADC1_DOWN_EN	(1<<9)
	#define  LRADC_ADC1_DATA_EN	(1<<8)

	#define  LRADC_ADC0_UP_EN	(1<<4)
	#define  LRADC_ADC0_DOWN_EN	(1<<1)
	#define  LRADC_ADC0_DATA_EN	(1<<0)

	#define  LRADC_ADC1_UPPEND	(1<<12)
	#define  LRADC_ADC1_DOWNPEND	(1<<9)
	#define  LRADC_ADC1_DATAPEND	(1<<8)

	#define  LRADC_ADC0_UPPEND 	(1<<4)
	#define  LRADC_ADC0_DOWNPEND	(1<<1)
	#define  LRADC_ADC0_DATAPEND	(1<<0)
} __attribute__((packed));

static struct keyadc_dev{
	dev_t devid;
	struct cdev chrdev;
	struct class *class;
	struct input_dev *input;

	struct workqueue_struct *keyadc_workqueue;
	struct work_struct keyadc_work;

	unsigned int keycode;
	volatile struct sun7i_lradc_regs *regs;
};	

static struct keyadc_dev keyadc_pdata = {
	.regs = NULL,
};

static struct platform_device keyadc_device = {
	.name	= PLATFORMNAME,
	.id	= -1,
	.dev	= {
		.platform_data = &keyadc_pdata,
	}		
};

/****************************************************************/
static int keyadc_open(struct inode *inode, struct file *filp)
{
	struct keyadc_dev *pdata;	
	
	dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);

	pdata = container_of(inode->i_cdev, struct keyadc_dev, chrdev);
	filp->private_data = pdata;

	return 0;
}

static int keyadc_release(struct inode *inode, struct file *filp)
{
	dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);

	filp->private_data = NULL;

	return 0;
}

static long keyadc_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	long ret;
	void __user *uarg;
	unsigned long karg;

	struct keyadc_dev *pdata = filp->private_data;

	dprintk("=====[%s(%d)]:cmd=%d\n", __FUNCTION__, __LINE__, cmd);

	if (KEYADC_SET == cmd) {
		uarg = (void __user *)arg;
		copy_from_user(&karg, uarg, sizeof(unsigned long));
	
		switch (karg) {
		case KEYADC_VOLUP:
			pdata->keycode = KEY_VOLUMEUP;
			break;
		case KEYADC_VOLDOWN:
			pdata->keycode = KEY_VOLUMEDOWN;
			break;
		case KEYADC_HOME:
			pdata->keycode = KEY_HOME;
			break;
		case KEYADC_BACK:
			pdata->keycode = KEY_BACK;
			break;
		default:
			dprintk("this is defalt\n");
			break;
		}
	}
	if (KEYADC_GET == cmd){
		karg = pdata->keycode;
		dprintk("send keycode to usr space: pdata->keycode=0x%x, karg=0x%x\n", pdata->keycode, karg);
		ret = copy_to_user(arg, &karg, sizeof(unsigned long));
		if(0 == ret){
			dprintk("copy_to_user successed!\n");
		}
	}

	return 0;
}

static struct file_operations keyadc_fops = {
	.owner		= THIS_MODULE,
	.open		= keyadc_open,
	.release	= keyadc_release,
	.unlocked_ioctl	= keyadc_unlocked_ioctl,
};

static void keyadc_do_work(struct work_struct *work)
{
	struct keyadc_dev *pdata;

	dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);

	pdata = (struct keyadc_dev *)container_of(work, struct keyadc_dev, keyadc_work);		
	
	if ((KEY_VOLUMEUP == pdata->keycode)	||
	    (KEY_VOLUMEDOWN == pdata->keycode)	||
	    (KEY_HOME == pdata->keycode)	||
	    (KEY_BACK == pdata->keycode)) {
		input_report_key(pdata->input, pdata->keycode, 1);
		input_report_key(pdata->input, pdata->keycode, 0);
		input_sync(pdata->input);
	} else {
		dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "No Keycode to Report!");
	}
}

static irqreturn_t key_interrupt(int irq, void *pvoid)
{
	dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);

	unsigned int reg_val;
	struct keyadc_dev *pdata	= (struct keyadc_dev *)pvoid;

	reg_val  = readl(&pdata->regs->ints);
	
	queue_work(pdata->keyadc_workqueue, &pdata->keyadc_work);

	writel(reg_val, &pdata->regs->ints);

	return IRQ_HANDLED;
}

static int keyadc_probe(struct platform_device *pdev)
{
	dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);
	
	int ret;
	struct cdev *pchrdev;

	struct keyadc_dev *pdata = pdev->dev.platform_data;
	pdata->regs = (struct sun7i_lradc_regs *)LRADC_BASE_ADDR;

	alloc_chrdev_region(&pdata->devid, 0, 1, DEVNAME);
	pchrdev = cdev_alloc();
	pdata->chrdev = *pchrdev;
	cdev_init(&pdata->chrdev, &keyadc_fops);
	pdata->chrdev.owner = THIS_MODULE;
	ret = cdev_add(&pdata->chrdev, pdata->devid, 1);
	if (ret) {
		dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to cdev_add!");
		goto fail0;
	}

 	pdata->input = input_allocate_device();	
	if (!pdata->input) {
		dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to input_allocate_device");
		goto fail1;
	}
	pdata->input->name = INPUTNAME;
	set_bit(EV_KEY,		pdata->input->evbit);
	set_bit(KEY_VOLUMEDOWN,	pdata->input->keybit);
	set_bit(KEY_VOLUMEUP,	pdata->input->keybit);
	set_bit(KEY_HOME,	pdata->input->keybit);
	set_bit(KEY_BACK,	pdata->input->keybit);
	ret = input_register_device(pdata->input);
	if (ret) {
		dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to input_register_device");
		goto fail2;
	}

	pdata->class = class_create(THIS_MODULE, DEVNAME);
	if (IS_ERR(pdata->class)) {
		dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to class_create");
		goto fail3;
	}
	device_create(pdata->class, NULL, pdata->devid, NULL, DEVNAME);
	
	writel(LRADC_ADC0_DOWN_EN|LRADC_ADC0_UP_EN|LRADC_ADC0_DATA_EN, &pdata->regs->intc);
	writel(FIRST_CONCERT_DLY|LEVELB_VOL|KEY_MODE_SELECT|LRADC_HOLD_EN|ADC_CHAN_SELECT|LRADC_SAMPLE_125HZ|LRADC_EN, &pdata->regs->ctrl);

	if (request_irq(KEY_IRQNO, key_interrupt, 0, IRQNAME, pdata)) {
		dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to request_irq");
		goto fail3;
	}

	pdata->keyadc_workqueue = create_singlethread_workqueue(WORKQUEUENAME);	
	if (NULL == pdata->keyadc_workqueue) {
		dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to create_singlethread_workqueue");
		goto fail4;
	}	
	INIT_WORK(&pdata->keyadc_work, keyadc_do_work);

	return 0;

fail4:
	destroy_workqueue(pdata->keyadc_workqueue);	
fail3:
	free_irq(KEY_IRQNO, pdata);
	device_destroy(pdata->class, pdata->devid);
	class_destroy(pdata->class);
fail2:
	input_unregister_device(pdata->input);
fail1:
	input_free_device(pdata->input);
fail0:
	cdev_del(&pdata->chrdev);
	kfree(pdata);

	return ret;
}

static int keyadc_remove(struct platform_device *pdev)
{
	dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);

	struct keyadc_dev *pdata = pdev->dev.platform_data;

	destroy_workqueue(pdata->keyadc_workqueue);
	device_destroy(pdata->class, pdata->devid);		
	class_destroy(pdata->class);
	input_free_device(pdata->input);
	cdev_del(&pdata->chrdev);
	free_irq(KEY_IRQNO, pdata);		
	//kfree(pdata);
	return 0;
}


static struct platform_driver keyadc_driver = {
	.probe	= keyadc_probe,
	.remove	= keyadc_remove,
	.driver	= {
		.name	= PLATFORMNAME,
		.owner	= THIS_MODULE,
	}
};

static int __init keyadc_init(void)
{
	int ret;

	dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);
	
	ret = platform_device_register(&keyadc_device);
	if (ret < 0) {
		dprintk("ERROR:Fail to platform_device_register!\n");
		return ret;
	}

	ret = platform_driver_register(&keyadc_driver);	
	if (ret < 0) {
		dprintk("ERROR:Fail to platform_driver_register!\n");
		return ret;
	}

	return 0;
}

static void __exit keyadc_exit(void)
{
	dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);

	platform_device_unregister(&keyadc_device);
	platform_driver_unregister(&keyadc_driver);
}

MODULE_AUTHOR("Jack Chen, chwenj@gmail.com");
MODULE_LICENSE("GPL");

module_init(keyadc_init);
module_exit(keyadc_exit);


相关文章推荐

全志平台TP驱动

全志各平台的TP驱动移植可以参照全志A20平台CTP模块开发说明文档V2.0-20130628.pdf其中全志平台编译环境的搭建可以参照此为全志A31编译环境搭建...

全志R16平台Android下添加自定义按键

Board:全志R16 SDK:Android KitKat、linux-3.41.先在linux-3.4/inlcude/linux/input/h添加自定义按键#define SOCHIP_E...
  • tsb151
  • tsb151
  • 2016年12月19日 21:26
  • 1508

ADC按键驱动

ADC是有通道的概念的,每一个GPIO可以被复用一个ADC按键,公司芯片有8个ADC通道。 至于为什么要用ADC按键,因为按键可以通过形成一个阵列的方式,每一个开关按键分到不同的电压,通过ADC就能...

全志平台boot框架中增加设备驱动过程分析

在boot启动阶段,大家都知道他的主要目的就是引导uboot,uboot在引导内核,从而让整个系统运作起来。全志的boot阶段,对应平板这一块,它会驱动LCD,显示一些开机LOGO,这个过程很快,也就...

A20平台物理按键适配总结

android源码对应如下: external/kernel-headers/original/linux/input.h device/softwinner/wing-clover/conf...

全志A20 GPIO 总结文档

个人编写的GPIO驱动以及相应的GPIO测试文档
  • chwenj
  • chwenj
  • 2014年12月27日 08:44
  • 6299

33 全志GPIO口的脚本配置及超声波测距模块的linux驱动

linux内核有gpiolib标准的gpio操作接口. 但这套接口只能配置输入,输出,获取或设置IO口的电平. 但GPIO口是多功能, 还有上/下拉功能. 全志的GPIO额外的配置在script.b...
  • jklinux
  • jklinux
  • 2017年06月19日 10:50
  • 645

Linux下I2C驱动分析(一)

最近在做一个基于全志A33芯片的android移植时发现嵌入式设备很多都用到了I2C总线通信,比如说摄像头,G-sensor,触摸屏等,为此我觉得很好的理解I2C设备驱动在今后的嵌入式开发中是非常有好...

Android下led控制(下)--Linux驱动部分--script与gpio(全志)

前面写了三篇关于全志CQA83T下Android控制led的博文,但是还是有很多东西可以学习,可以写写作为学习记录。如果看源码,绕不过script这个东西,这个不是像其他系统脚本一样,这里的scrip...

基于全志芯片的GPIO及底层(C/Python)编程

目标环境:搭载A20芯片的核心板Cubieboard2,使用522扩展板,用TF卡搭载cb2-dvk-sdcard-ubuntu-desktop-lcd-ctp-lvds-1024x600-v1.1....
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:全志A20 keyadc驱动程序及toolbox测试代码
举报原因:
原因补充:

(最多只允许输入30个字)