#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/gpio.h>
#include <plat/regs-adc.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/clk.h>
#include <linux/wait.h>
#include <mach/regs-clock.h>
#include <plat/regs-timer.h>
#include <linux/cdev.h>
#define DEVICE_NAME "adc6126"
//#define base_addr __va(0x28000000)
static unsigned char c_AD_CONTROL = 0x02;
static void __iomem *base_addr;
#define ADC6126_PA_ADC 0x28000000
#define ADC6126_CON (0x04)
#define ADC6126_DATA (0x04)
#define ADC6126_STUT (0x08)
#define ADC6126CON (*(volatile unsigned int*)(base_addr + ADC6126_CON))
#define ADC6126DATA (*(volatile unsigned int*)(base_addr + ADC6126_DATA))
#define ADC6126STUTS (*(volatile unsigned int*)(base_addr + ADC6126_STUT))
//ioctl(fd,cmd,arg)的第二个参数
#define IO_CTL_MCONA 0
#define IO_CTL_SHDNA 1
#define IO_CTL_CONA 2
#define IO_CTL_CSA 3
#define IO_CTL_STAT 4
volatile unsigned int adcvalue[8];
static unsigned int num_value=0;
static volatile int ev_adc = 0;
static DECLARE_WAIT_QUEUE_HEAD(adc_waitq);
struct adc6126_irq_desc {
int irq;
int pin;
int pin_setting;
int number;
char *name;
};
static struct adc6126_irq_desc adc_irq[] =
{{IRQ_EINT3 , S3C2410_GPF(3) , S3C2410_GPF3_EINT3 , 0, "ADC6126"}
};
static void setCS(void)
{
c_AD_CONTROL |= (1 <<3);
ADC6126CON = c_AD_CONTROL;//(1 <<3);
// outb(c_AD_CONTROL,ADC6126CON);
}
static void clsCS(void)
{
c_AD_CONTROL &= ~(1 <<3);
ADC6126CON = c_AD_CONTROL;
// outb(c_AD_CONTROL,ADC6126CON);
}
static void init_11046(unsigned char data)
{
unsigned char set_data = data ;
setCS();
udelay(2);
ADC6126STUTS = (0X00 | set_data);
// outb((0X00 | data),ADC6126CON);
udelay(2);
clsCS();
}
static irqreturn_t adc6126_interrupt(int irq, void *dev_id)
{
struct adc6126_irq_desc *adc_irq = (struct adc6126_irq_desc *)dev_id;
int down;
unsigned char i;
int data;
// adc_irq = (struct adc6126_irq_desc *)dev_id;
// udelay(0);
down = s3c2410_gpio_getpin(S3C2410_GPF(3));
printk("pin %d:%d\n",adc_irq->pin,down);
if (down == 0 )
{ // Changed
// printk("pin %d:%d\n",S3C2410_GPF(3),down);
setCS();
num_value = 0 ;
for(i=0;i<8;i++){
// ndelay(1);
data = ADC6126DATA ;
// data = inw(ADC6126DATA);
data = data & 0xffff;
adcvalue[num_value+i] = data;
}
// ndelay(1);
clsCS();
ev_adc = 1;
wake_up_interruptible(&adc_waitq);
}
return IRQ_RETVAL(IRQ_HANDLED);
}
// ------------------- READ ------------------------
static int adc6126_read (struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
// int len;
unsigned long err=-EINVAL;
if (!ev_adc) {
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
else
wait_event_interruptible(adc_waitq, ev_adc);
}
ev_adc = 0;
num_value = 0 ;
if(count <0)
return -EINVAL;
err=copy_to_user(buff,(const char *)adcvalue,min(sizeof(adcvalue),count));
// memset((void *)adcvalue,0,sizeof(adcvalue));
//}
return err ? -EINVAL:min(sizeof(adcvalue),count);
}
// ------------------- IOCTL -----------------------
static int adc6126_ioctl (struct inode * inode ,struct file * file, unsigned int cmd, unsigned long data)
{
switch (cmd)
{
//c_AD_CONTROL BIT: 7..4 .3 .2 .1 .0
// defined undefined /cs CONA SHDNA MCONA
//effect Bit 1:can R/W 1:auto10KHz 1:shutPOWER whenCONA=0,
// whenMCONA=0 0:POWER ON conclk=MCONA,1:effect
//ADC_STUTAS (DB): 7..4 .3 .2 .1 .0
// defined undefined INT/EXT REF Data Format Reserved CONVST Mode
//effect Bit 0:INT,1:EXT 0:OFFSET,1:two'scomplement 0:control,1:acquistion whenCONA=0,conclk=MCONA,1:effect
case IO_CTL_MCONA : {
if(data == 0) {
c_AD_CONTROL &= ~(1 <<0);
// ADC6126CON = c_AD_CONTROL;
// outb(c_AD_CONTROL,ADC6126CON);
}
else{
c_AD_CONTROL |= (1 <<0);
// ADC6126CON = c_AD_CONTROL;
// outb(c_AD_CONTROL,ADC6126CON);
}
ADC6126CON = c_AD_CONTROL;
break;}
case IO_CTL_SHDNA : {
if(data == 0) {
c_AD_CONTROL &= ~(1 <<1);
// ADC6126CON = c_AD_CONTROL;
// outb(c_AD_CONTROL,ADC6126CON);
}
else{
c_AD_CONTROL |= (1 <<1);
// ADC6126CON = c_AD_CONTROL;
// outb(c_AD_CONTROL,ADC6126CON);
}
ADC6126CON = c_AD_CONTROL;
break;}
case IO_CTL_CONA : {
if(data == 0) {
c_AD_CONTROL &= ~(1 <<2);
// ADC6126CON = c_AD_CONTROL;
// outb(c_AD_CONTROL,ADC6126CON);
}
else{
c_AD_CONTROL |= (1 <<2);
// ADC6126CON = c_AD_CONTROL;
// outb(c_AD_CONTROL,ADC6126CON);
}
ADC6126CON = c_AD_CONTROL;
break;}
case IO_CTL_CSA : {
if(data == 0) {
c_AD_CONTROL &= ~(1 <<3);
// ADC6126CON = c_AD_CONTROL;
// outb(c_AD_CONTROL,ADC6126CON);
}
else{
c_AD_CONTROL |= (1 <<3);
// ADC6126CON = c_AD_CONTROL;
// outb(c_AD_CONTROL,ADC6126CON);
}
ADC6126CON = c_AD_CONTROL;
break;}
case IO_CTL_STAT : {
ADC6126STUTS = (unsigned char)(data &0x0f);
// outb((unsigned char)(data &0x0f),ADC6126STUTS);
break;}
default :
{
// printk ("ADC control : no cmd run [ --kernel-- ]\n");
return (-EINVAL);}
}
return 0;
}
// ------------------- OPEN ------------------------
static int adc6126_open (struct inode * inode ,struct file * file)
{
int err = 0;
c_AD_CONTROL = 0x00;
c_AD_CONTROL = ( c_AD_CONTROL & 0x04); //SHDNA=0 : POWER ON
ADC6126CON = c_AD_CONTROL ;
// outb(c_AD_CONTROL,ADC6126CON);
init_11046(8);
s3c2410_gpio_cfgpin(adc_irq[0].pin,adc_irq[0].pin_setting);
set_irq_type(adc_irq[0].irq, IRQ_TYPE_EDGE_FALLING);
// memset((void *)adcvalue,0,sizeof(adcvalue));
if (adc_irq[0].irq < 0) {
return -EINVAL;
}
err = request_irq(adc_irq[0].irq, adc6126_interrupt, IRQ_TYPE_EDGE_BOTH,
adc_irq[0].name, (void *)&adc_irq[0]);
// ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev);
if (err) {
return -EBUSY;
}
ev_adc = 1;
return 0;
}
// ------------------- RELEASE/CLOSE ---------------
static int adc6126_release (struct inode * inode ,struct file * file)
{
c_AD_CONTROL = 0x00;
c_AD_CONTROL = ( c_AD_CONTROL | 0x02);
ADC6126CON = c_AD_CONTROL ;
// outb(c_AD_CONTROL,ADC6126CON);
if (adc_irq[0].irq < 0) {
return 0;
}
disable_irq(adc_irq[0].irq);
free_irq(adc_irq[0].irq, (void *)&adc_irq[0]);
// printk( "adc6126 closed\n");
return 0;
}
// -------------------------------------------------
/*
static unsigned int adc6126_poll( struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &adc_waitq, wait);
if (ev_adc)
mask |= POLLIN | POLLRDNORM;
return mask;
}
*/
//struct file_operations adc6126_ctl_ops
static struct file_operations dev_fops={
.owner = THIS_MODULE,
.open = adc6126_open,
.read = adc6126_read,
// .write = adc6126_write,
.ioctl = adc6126_ioctl,
.release = adc6126_release,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init S3C2410_6126_CTL_init(void)
{
int ret = -ENODEV;
// ret = misc_register(&misc);
base_addr=ioremap(ADC6126_PA_ADC,0x10);
if (base_addr == NULL) {
// printk(KERN_ERR "Failed to remap register block\n");
return -ENOMEM;
}
// printk("init start...1...\n");
c_AD_CONTROL=0x00;
// printk("init start...2...\n");
ADC6126CON =c_AD_CONTROL;// c_AD_CONTROL=0x02;
// outb(c_AD_CONTROL,ADC6126CON);
// printk("--------------------------------------------\n\n");
ret = misc_register(&misc);
if( ret < 0 )
{
// printk (" S3C2410: init_module failed with %d\n", ret);
return ret;
}
else
{
// printk("S3C2410 6126_driver register success!!! \n");
}
return 0;
}
static void __exit cleanup_6126_ctl(void)
{
if(adc_irq[0].irq > 0){
disable_irq(adc_irq[0].irq);
free_irq(adc_irq[0].irq, (void *)&adc_irq[0]);
}
misc_deregister(&misc);
}
module_init(S3C2410_6126_CTL_init);
module_exit(cleanup_6126_ctl);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Atangxb");