全志A20 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);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值