?? ads8344_driver.c
字號:
/*----------------------------------------------------------------------------*//* adc.c, V1.0 2007/07/21 Jesse Jiang * * linux/drivers/char/adc.c - AD converter driver main entity * * Author: Jesse Jiang <jiangxiaogen@sina.com> * Copyright (C) 2007~2008 * * * Updates history: * ----------------------------------------------------------- * 2007-07-21 Jesse Jiang v1.0 initial * 2007-12-03 Jesse Jiang fix the bug on queue management * * Hardware: MCU: Samsung S3C44B0X * For analog converter: Burr-Brown ADS8344 * For digital count : ExtInt2~7 * For online check : AIN0~5 * For A/D type check : PC10~15 * * OS: uClinux version 2.4.20 * * Analog Input: * ADS8344 S3C44B0X , I/O Config * BUSY ----> PF6 , Input, Busy ind in low level * /CS ----> PE7 , Ouput, Enable by low level * DIN ----> PF5 , SIOTxD * DOUT ----> PF7 , SIORxD * DCLK ----> PF8 , SIOCLK * Digital Count: * Channel0~5 ----> ExtInt2~7 * * Online Check: * Channel0~5 ----> AIN0~5 * * A/D Type Check: * SIF6_CS ----> PC15 , Output * SIF6_A2 ----> PC10 , Output * SIF6_A1 ----> PC11 , Output * SIF6_A0 ----> PC14 , Output * * TXD1 ----> PC12 , TxD1 * RXD1 ----> PC13 , RxD1 * * PC15(CS) PC14(A0) PC11(A1) PC10(A2) * Chan. Select0: 1 0 0 0 * 1: 1 0 0 1 * 2: 1 0 1 0 * 3: 1 0 1 1 * 4: 1 1 0 0 * 5: 1 1 0 1 * * General work flow: * 1. Create a kerner thread in adc_open * 2. Poll 6-channel internal ADC to see whether there * exists any external A/D converter connected * 3. If yes, send enquiry command to get external A/D * converter types * 4. If yes, check ExtInt to get the count for digital * A/D, and check ADS8344 to get A/D conversion * result. * *//*----------------------------------------------------------------------------*/#ifndef __KERNEL__#define __KERNEL__#endif#include <linux/kernel.h> /* We're doing kernel work */#include <linux/module.h> /* Specifically, a module */#include <linux/interrupt.h> /* We want interrupts */#include <linux/miscdevice.h> /* for misc_register() and misc_deregister() */#include <linux/fs.h> /* for struct 'file_operations' */#include <linux/timer.h> /* for timeout interrupts */#include <linux/param.h> /* for HZ. HZ = 100 and the timer step is 1/100 */#include <linux/sched.h> /* for jiffies definition. jiffies is incremented * once for each clock tick; thus it's incremented * HZ times per secondes.*/#include <linux/mm.h> /* for verify_area */#include <linux/slab.h> /* for kmalloc */#include <linux/init.h>#include <linux/errno.h>#include <linux/smp_lock.h>#include <linux/nsyadc.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/system.h>#include <asm/irq.h>#include <asm/arch/irqs.h>#include <asm/arch/s3c44b0x.h>/*----------------------------------------------------------------------------*/static const char* __file__ = __FILE__;/*----------------------------------------------------------------------------*//* * Kernel compatibility. * Kernel > 2.2.3, include/linux/version.h contain a macro for KERNEL_VERSION */#include <linux/version.h>#ifndef KERNEL_VERSION#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))#endif/* * Conditional compilation. LINUX_VERSION_CODE is the code of this version * (as per KERNEL_VERSION) */#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))#include <asm/uaccess.h> /* for put_user */#include <linux/poll.h> /* for polling fnct */#endif/* * Should move to linux\include\msicdevice.h */#define NSY_ADC_MINOR 18/*----------------------------------------------------------------------------*//* ADC main entity------------------------------------------------------------*//* The item length in buffer queue */#define DATA_LENGTH sizeof(struct adc_conv_info)/* Size of the buffer for the event queue */#define AD_BUF_SIZE (256*DATA_LENGTH) /* 256*DATA_LENGTH, Modify for queue bug fix *//* signal to quit adc poll & sample threads */#define ADC_THREAD_SHUTDOWN (sigmask(SIGKILL)) /* |sigmask(SIGINT)|sigmask(SIGTERM))*/ /*----------------------------------------------------------------------------*//* Digital A/D conversion-----------------------------------------------------*//* Interrupt for digital sampling */#define CHA_IRQ_0 (S3C44B0X_INTERRUPT_EINT2)#define CHA_IRQ_1 (S3C44B0X_INTERRUPT_EINT3)#define CHA_IRQ_2345 (S3C44B0X_INTERRUPT_EINT4567)#define CLEAR_SIO_IRQ { outl(0x00000010, S3C44B0X_I_ISPC); }#define CLEAR_CHA0_IRQ { outl(0x00800000, S3C44B0X_I_ISPC); }#define CLEAR_CHA1_IRQ { outl(0x00400000, S3C44B0X_I_ISPC); }#define CLEAR_CHA2345_IRQ { outb(0x0f, S3C44B0X_EXTINPND);\ outl(0x00200000, S3C44B0X_I_ISPC);\ } /*----------------------------------------------------------------------------*//* Analog A/D conversion------------------------------------------------------*/#define MAX_CONV_TIME (50) /* Maximum conversion time for analog ADC:ms *//* SIO baudrate = MCLK/2/(Prescaler+1) */#define SIO_PRESCALER (119)#define SIO_IVTCNT (1)/* SIO control bits. */#define SIOCONT_BIT_CLK (1<<7)#define SIOCONT_BIT_DIR (1<<6)#define SIOCONT_BIT_TXRX (1<<5)#define SIOCONT_BIT_EDGE (1<<4)#define SIOCONT_BIT_START (1<<3)#define SIOCONT_BIT_OPER (1<<2)#define SIOCONT_BIT_MODE1 (1<<1)#define SIOCONT_BIT_MODE0 (1<<0)/* SIO initial definition * Internal clock * MSB first * Transmit/Recevie * Falling edge * No action * Non hand-shaking * Interrupt mode */#define SIOCONT_INIT ( SIOCONT_BIT_CLK *0 | \ SIOCONT_BIT_DIR *0 | \ SIOCONT_BIT_TXRX | \ SIOCONT_BIT_EDGE | \ SIOCONT_BIT_START *0 | \ SIOCONT_BIT_OPER *0 | \ SIOCONT_BIT_MODE1 *0 | \ SIOCONT_BIT_MODE0 )#define SIOCONT_DISABLE ((inl(S3C44B0X_SIOCON)&0xFFFFFFFC)|0x00)#define SIOCONT_ENABLE ((inl(S3C44B0X_SIOCON)&0xFFFFFFFC)|0x01)#define SIOCONT_START ((inl(S3C44B0X_SIOCON)&0xFFFFFFF7)|0x08)#define SIOCONT_STOP ((inl(S3C44B0X_SIOCON)&0xFFFFFFF7)|0x00)/* SIO transfer definitions */#define WRITE_SIODATA(x) {outl(x, S3C44B0X_SIODAT);}#define SET_SIOCON(x) {outl(x, S3C44B0X_SIOCON);}#define SET_SIOSBRDR(x) {outl(x, S3C44B0X_SBRDR); }#define SET_SIOIVTCNT(x) {outl(x, S3C44B0X_ITVCNT); }#define SIODATA (inb(S3C44B0X_SIODAT))#define SIO_INIT { SET_SIOCON(SIOCONT_INIT); } /* init SIO */#define SIO_ENABLE { SET_SIOCON(SIOCONT_ENABLE); } /* enable SIO */#define SIO_DISABLE { SET_SIOCON(SIOCONT_DISABLE); } /* disable SIO */#define SIO_START { SET_SIOCON(SIOCONT_START); } /* start SIO transfer */#define SIO_STOP { SET_SIOCON(SIOCONT_STOP); } /* stop SIO transfer *//* ADS8344 control bits */#define ADS8344_START_BIT (1<<7)#define ADS8344_A2 (1<<6)#define ADS8344_A1 (1<<5)#define ADS8344_A0 (1<<4)#define ADS8344_NOP (1<<3) #define ADS8344_SER_DFR (1<<2) /* High: Single mode, * Low: differential mode */#define ADS8344_PD1 (1<<1) #define ADS8344_PD0 (1<<0) /* AD control bytes */#define AD_CTRL_CHA0 ( ADS8344_START_BIT | \ ADS8344_A2 | \ ADS8344_A1 | \ ADS8344_A0 | \ ADS8344_NOP *0| \ ADS8344_SER_DFR | \ ADS8344_PD1 *1| \ ADS8344_PD0 *1 ) #define AD_CTRL_CHA1 ( ADS8344_START_BIT | \ ADS8344_A2 *1| \ ADS8344_A1 *1| \ ADS8344_A0 *0| \ ADS8344_NOP *1| \ ADS8344_SER_DFR | \ ADS8344_PD1 *1| \ ADS8344_PD0 *0 ) #define AD_CTRL_CHA2 ( ADS8344_START_BIT | \ ADS8344_A2 *0| \ ADS8344_A1 *0| \ ADS8344_A0 *1| \ ADS8344_NOP *1| \ ADS8344_SER_DFR | \ ADS8344_PD1 *0| \ ADS8344_PD0 *1 ) #define AD_CTRL_CHA3 ( ADS8344_START_BIT | \ ADS8344_A2 *1| \ ADS8344_A1 *1| \ ADS8344_A0 *0| \ ADS8344_NOP *1| \ ADS8344_SER_DFR | \ ADS8344_PD1 *1| \ ADS8344_PD0 *1 ) #define AD_CTRL_CHA4 ( ADS8344_START_BIT | \ ADS8344_A2 *0| \ ADS8344_A1 *1| \ ADS8344_A0 *0| \ ADS8344_NOP *1| \ ADS8344_SER_DFR | \ ADS8344_PD1 *1| \ ADS8344_PD0 *1 ) #define AD_CTRL_CHA5 ( ADS8344_START_BIT | \ ADS8344_A2 *1| \ ADS8344_A1 *1| \ ADS8344_A0 *0| \ ADS8344_NOP *0| \ ADS8344_SER_DFR | \ ADS8344_PD1 *1| \ ADS8344_PD0 *1 )/* * Generate clock to fall pull BUSY signal. * No start bit to avoid generating a BUSY at end of the transfer. */#define SIODATA_NULL ( ADS8344_START_BIT *0| \ ADS8344_A2 *1| \ ADS8344_A1 *1| \ ADS8344_A0 *1| \ ADS8344_NOP *1| \ ADS8344_SER_DFR *1| \ ADS8344_PD1 *1| \ ADS8344_PD0 *1 )/* ADS8344 A/D conversion states. */#define AD_CONV_ERROR (-1)#define AD_CONV_IDLE (0)#define AD_CONV_ST0 (1)#define AD_CONV_ST1 (2)#define AD_CONV_ST2 (3)#define AD_CONV_ST3 (4)#define AD_CONV_END (5)/* ADS8344 conversion value bits */#define BIT15_BIT9 (0)#define BIT8_BIT1 (1)#define BIT0_BIT0 (2)/* Digital Sensor conversion state */#define AD_DIGIT_BEGIN (0)#define AD_DIGIT_RUNNING (1)#define AD_DIGIT_END (2)#define EXT_AD_SELECT { ads8344_select(); }#define EXT_AD_DESELECT { ads8344_deselect(); }#define TS_7843_DESELECT { ads7843_deselect(); }/*----------------------------------------------------------------------------*//* predefinitions ------------------------------------------------------------*/static void handle_sio_irq(int irq, void *dev_id, struct pt_regs *regs);static void handle_cha0_irq(int irq, void *dev_id, struct pt_regs *regs);static void handle_cha1_irq(int irq, void *dev_id, struct pt_regs *regs);static void handle_cha2345_irq(int irq, void *dev_id, struct pt_regs *regs);static int adc_sample_thread(void *ctrl);static void ad_conv_state_shift(int new_state);static void put_in_queue(char *in, int len);static void release_SIO_transfer(void);static void set_SIO_transfer(void);static void sio_send_ad_ctrl(int channel);static ssize_t adc_read (struct file *file, char *buff, size_t len, loff_t *offset);static unsigned int adc_poll(struct file *file, poll_table *table);static int adc_ioctl(struct inode *inode, struct file *file,unsigned int cmd,unsigned long arg);static int adc_open(struct inode *inode, struct file *file);static int adc_release(struct inode *inode, struct file *file);static int adc_fasync(int inode, struct file *file, int mode);/*----------------------------------------------------------------------------*//* structure -----------------------------------------------------------------*/struct adc_queue { unsigned long head; unsigned long tail; unsigned long count; /* Add for queue bug fix */ spinlock_t lock; wait_queue_head_t proc_list; struct fasync_struct *fasync; unsigned char buf[AD_BUF_SIZE];};struct adc_digit_data{ int ad_count_dur; /* Unit: us for duration */ struct timeval first_int; struct timeval second_int; };struct ctrl_info_t { struct task_struct *monitor_thread; int sample_ms;};/*----------------------------------------------------------------------------*//* Variables -----------------------------------------------------------------*//* thread variables */struct ctrl_info_t sample_ctrl;static volatile char sample_shutdown = 0;/* adc drv parameters. */static struct adc_drv_params current_params;/* adc buffer queue */static struct adc_queue *queue;static volatile int device_open = 0; /* number of open device to prevent concurrent * access to the same device */static volatile int have_ad_data = 0;spinlock_t have_dat_lock = SPIN_LOCK_UNLOCKED;/* A/D conversion info: a item variable in the queue. */static struct adc_conv_info adc_queue_node; /* ADS8344 conversion state */static volatile int ad_conv_state;spinlock_t conv_state_lock = SPIN_LOCK_UNLOCKED;/* ADS8344 conversion value */static volatile unsigned short ad_conv_val = 0;/* digital sample variable: to storage sampling data */static struct adc_digit_data adc_digit_sample[6];static volatile int ad_digit_st[6]; static int s3c44b0_adc_psr = 128; //0xF; /* default value */static spinlock_t digit_lock = SPIN_LOCK_UNLOCKED;/*----------------------------------------------------------------------------*//* internal support functions ------------------------------------------------*/ /* * nsyadc_sleep: Delay, unit in ms */static void nsyadc_sleep(int delay){ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ*delay/1000);}/*----------------------------------------------------------------------------*//* Init & release functions --------------------------------------------------*//* * Set default values for the params of the driver. */static void init_adc_settings(void) { int i; current_params.version_req = ADC_VERSION; current_params.sample_ms = 20; for (i = 0; i < 6; i++) { current_params.cha[i].snr_online = SNR_IS_OFF; current_params.cha[i].snr_type = SNR_TYPE_UNKNOWN; current_params.cha[i].snr_mode = SNR_MODE_UNKNOWN; current_params.cha[i].snr_id = SNR_ID_UNKNOWN; current_params.cha[i].snr_oper = SNR_OPER_UNKNOWN; } current_params.deglitch_on = 1; current_params.event_queue_on = 1; adc_queue_node.snr_type = SNR_TYPE_UNKNOWN; adc_queue_node.snr_value = SNR_VAL_INVALID; do_gettimeofday(&adc_queue_node.ad_timeval); for (i = 0; i < 6; i++) { ad_digit_st[i] = AD_DIGIT_BEGIN; adc_digit_sample[i].ad_count_dur = 0; adc_digit_sample[i].first_int.tv_sec = 0; adc_digit_sample[i].first_int.tv_usec = 0; adc_digit_sample[i].second_int.tv_sec = 0; adc_digit_sample[i].second_int.tv_usec = 0; } ad_conv_state = AD_CONV_IDLE;}/* * to select chip ADS8344 */static void ads8344_select (void){ int val32; /* Set PE7 to low level */ val32 = inl(S3C44B0X_PDATE); outl((val32&0xFFFFFF18)|0x00000000, S3C44B0X_PDATE); /* GPE7: (ADS8344-/CS) low*/}/* * to deselect chip ADS8344 */static void ads8344_deselect (void){ int val32; /* Set PE7 to high level */ val32 = inl(S3C44B0X_PDATE); outl((val32&0xFFFFFF18)|0x00000048, S3C44B0X_PDATE); /* GPE7: (ADS8344-/CS) high*/}/* * to deselece chip 7843 */static void ads7843_deselect (void){ int i, val32; /* Set PE5 to high level */ val32 = inl(S3C44B0X_PDATF) |0xef; outl(val32, S3C44B0X_PDATF); /* GPE5: (ADS7843-/CS) high*/ for (i = 0; i < 500; i++); /* Wait for ADS7843 to be stable */} static void init_adc_gpio(void) { unsigned int val32; /* initialize s3c44b0 ADC */ val32 = inl(S3C44B0X_CLKCON) | (1 << 12); outl(val32, S3C44B0X_CLKCON); outl(s3c44b0_adc_psr, S3C44B0X_ADCPSR); /* config prescaler */ val32 = inl(S3C44B0X_ADCCON) | 0x20; outl(val32, S3C44B0X_ADCCON); /* Sleep ADC */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -