ckfwq/linux-3.0.4/drivers/spi/adc/adc-dev.c

177 lines
4.0 KiB
C

/*
* GSC3280 SoC adc Controller Driver
*
* Copyright (C) 2013 BLX IC Design Corp.,Ltd.
* Author: Davied, apple_guet@126.com
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/sched.h>
#include <linux/adc-core.h>
#ifdef CONFIG_ADC_DEV_DEBUG
#define DBG(msg...) do { \
printk(KERN_INFO msg); \
} while (0)
#else
#define DBG(msg...) do { } while(0)
#endif
#define ADC_DEV_MAX 16 /* 16 ADCs should be enough for everyone... */
#define ADC_DEV_BUSY 0
#define ADC_DEV_IOC_MAGIC 'a'
#define ADC_DEV_IOC_MAXNR 2
#define ADC_DEV_CON_PBAT _IOR(ADC_DEV_IOC_MAGIC, 0, int)
#define ADC_DEV_CON_CHX _IOWR(ADC_DEV_IOC_MAGIC, 1, int)
static dev_t adc_devt;
static int adc_dev_open(struct inode *inode, struct file *file)
{
int err;
struct adc_core_dev *adc = container_of(inode->i_cdev, struct adc_core_dev, char_dev);
if (test_and_set_bit_lock(ADC_DEV_BUSY, &adc->flags))
return -EBUSY;
file->private_data = adc;
err = adc->ops->open ? adc->ops->open(adc->dev.parent) : 0;
if (err == 0) {
return 0;
}
/* something has gone wrong */
clear_bit_unlock(ADC_DEV_BUSY, &adc->flags);
return err;
}
static long adc_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int err = 0, channel = 0;
unsigned short adc_cmd = 0;
struct adc_core_dev *adc = file->private_data;
void __user *argp = (void __user *)arg;
int __user *p = argp;
err = mutex_lock_interruptible(&adc->ops_lock);
if (err)
return err;
if ((_IOC_TYPE(cmd) != ADC_DEV_IOC_MAGIC) || (_IOC_NR(cmd) > ADC_DEV_IOC_MAXNR)) {
err = -ENOTTY;
goto exit;
}
switch(cmd) {
case ADC_DEV_CON_PBAT:
adc_cmd = CMD_AD_CON_PBAT;
break;
case ADC_DEV_CON_CHX:
if (get_user(channel, p)) {
err = -EFAULT;
goto exit;
}
switch (channel) {
case 0:
adc_cmd = CMD_AD_CON_CH0;
break;
case 1:
adc_cmd = CMD_AD_CON_CH1;
break;
case 2:
adc_cmd = CMD_AD_CON_CH2;
break;
case 3:
adc_cmd = CMD_AD_CON_CH3;
break;
}
break;
default:
err = -ENOTTY;
goto exit;
}
DBG("adc_cmd = 0x%x\n", adc_cmd);
put_user(adc->ops->convert(adc_cmd), p);
exit:
mutex_unlock(&adc->ops_lock);
return err;
}
static int adc_dev_release(struct inode *inode, struct file *file)
{
struct adc_core_dev *adc = file->private_data;
if (adc->ops->release)
adc->ops->release(adc->dev.parent);
clear_bit_unlock(ADC_DEV_BUSY, &adc->flags);
return 0;
}
static const struct file_operations adc_dev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.unlocked_ioctl = adc_dev_ioctl,
.open = adc_dev_open,
.release = adc_dev_release,
};
void adc_dev_prepare(struct adc_core_dev *adc)
{
if (!adc_devt) {
DBG("!!!!!!adc_devt = 0!!!!!!\n");
return;
}
if (adc->id >= ADC_DEV_MAX) {
DBG("!!!!!!adc dev prepare error,id too many!!!!!!\n");
pr_debug("%s: too many ADC devices\n", adc->name);
return;
}
adc->dev.devt = MKDEV(MAJOR(adc_devt), adc->id);
cdev_init(&adc->char_dev, &adc_dev_fops);
adc->char_dev.owner = adc->owner;
}
void adc_dev_add_device(struct adc_core_dev *adc)
{
if (cdev_add(&adc->char_dev, adc->dev.devt, 1)) {
DBG("!!!!!!cdev add error.!!!!!!\n");
printk(KERN_WARNING "%s: failed to add char device %d:%d\n",
adc->name, MAJOR(adc_devt), adc->id);
}
else {
pr_debug("%s: dev (%d:%d)\n", adc->name, MAJOR(adc_devt), adc->id);
}
}
void adc_dev_del_device(struct adc_core_dev *adc)
{
if (adc->dev.devt)
cdev_del(&adc->char_dev);
}
void __init adc_dev_init(void)
{
int err;
err = alloc_chrdev_region(&adc_devt, 0, ADC_DEV_MAX, "adc");
if (err < 0) {
DBG("!!!!!!alloc chrdev region error!!!!!!\n");
printk(KERN_ERR "%s: failed to allocate char dev region\n", __FILE__);
}
}
void __exit adc_dev_exit(void)
{
if (adc_devt)
unregister_chrdev_region(adc_devt, ADC_DEV_MAX);
}