ad7888 linux driver /* ADCCONVERT.c : Generate ADC signals from SPI ports, as the A/D control signals. Author: Aaron Fu 2008-10-14 */ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/slab.h> /* kmalloc */ #include <linux/fs.h> /* struect file inode */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/proc_fs.h> /* proc_fs */ #include <linux/fcntl.h> /* O_ACCMODE */ #include <linux/seq_file.h> /* seq_file */ #include <linux/cdev.h> /* register char device */ #include <asm/system.h> /* cli(), *_flags */ #include <asm/uaccess.h> /* copy_*_user */ #include <asm/delay.h> /* udelay */ #include <asm/io.h> /* ioremap...*/ #include "adconvert.h" /* local definitions */ int adc_major = ADC_MAJOR; int adc_minor = 0; int adc_nr_devs = ADC_NR_DEVS; static int adcopen; struct cdev *adc_cdev; module_param( adc_major, int, S_IRUGO ); module_param( adc_minor, int, S_IRUGO ); module_param( adc_nr_devs, int, S_IRUGO ); MODULE_LICENSE( "Aaron Fu" ); MODULE_DESCRIPTION( "Iconic Shanghai Co.,LTD." ); MODULE_AUTHOR( "GPL" ); void adc_address_map(void){ r_SPSTA0 = ioremap( rSPSTA0, 0x00000004 ); r_SPPRE0 = ioremap( rSPPRE0, 0x00000004 ); // rSPPRE0 r_SPCON0 = ioremap( rSPCON0, 0x00000004 ); // rSPCON0 r_SPTDAT0 = ioremap( rSPTDAT0, 0x00000004 ); // rSPTDAT0 r_SPRDAT0 = ioremap( rSPRDAT0, 0x00000004 ); r_GPECON = ioremap( rGPECON, 0x00000004 ); //rGPECON r_GPEUP = ioremap( rGPEUP, 0x00000004 ); // rGPEUP r_GPHCON = ioremap( rGPHCON, 0x00000004 ); // rGPHCON r_GPHUP = ioremap( rGPHUP, 0x00000004 ); // rGPHUP r_GPHDAT = ioremap( rGPHDAT, 0x00000004 ); //rGPHDAT } void adc_cancel_add_map( void ){ iounmap( r_SPSTA0 ); iounmap( r_SPPRE0 ); iounmap( r_SPCON0 ); iounmap( r_SPTDAT0 ); iounmap( r_SPRDAT0 ); iounmap( r_GPECON ); iounmap( r_GPEUP ); iounmap( r_GPHCON ); iounmap( r_GPHUP ); iounmap( r_GPHDAT ); } static void adc_spi_init( void ){ int i; /* rSPPRE0 = 0x32; rSPCON0 = 0x1e; for( i = 0; i < 10; i++ ) rSPTDAT0 = 0xff; rGPECON |= 0x0a800000; rGPECON &= ( ~0x05400000 ); rGPEUP |= 0x3800; //GPH5----->CS rGPHCON |= 0x0400; rGPHCON &= ( ~0x0800 ); rGPHUP &= ( ~0x20 ); rGPHDAT |= 0x20;*/ iowrite32( ioread32( r_SPSTA0 ) | 0x01, r_SPSTA0 ); iowrite32( 0x4, r_SPPRE0 ); iowrite32( 0x1e, r_SPCON0 ); for( i = 0; i < 10; i++ ){ iowrite32( 0xff, r_SPTDAT0 ); } //for( i = 0; i < 10; i++ ){ iowrite32( 0x00, r_SPRDAT0 ); //} iowrite32( ioread32( r_GPECON ) | 0x0a800000, r_GPECON ); iowrite32( ioread32( r_GPECON ) & ( ~0x05400000 ), r_GPECON ); iowrite32( ioread32( r_GPEUP ) | 0x3800, r_GPEUP ); iowrite32( ioread32( r_GPHCON ) | 0x0400, r_GPHCON ); iowrite32( ioread32( r_GPHCON ) & ( ~0x0800 ), r_GPHCON ); iowrite32( ioread32( r_GPHUP ) & ( ~0x20 ), r_GPHUP ); iowrite32( ioread32( r_GPHDAT ) | ( 0x20 ), r_GPHDAT ); } static struct file_operations adc_fops = { /* driver info */ .owner = THIS_MODULE, .open = adc_open, .read = adc_read, .write = adc_write, .release = adc_release, }; static ssize_t adc_write( struct file *file, const char __user *buf, size_t count, loff_t *offset ){ int ret = 0; int i = 0; printk( "write count value = %d ", count ); dbuf = kmalloc( count * sizeof( unsigned char ), GFP_KERNEL); if ( dbuf == NULL ){ printk( KERN_WARNING "write: dbuf alloc memory fail " ); return -1; } memset( dbuf, 0, count *sizeof( unsigned char ) ); copy_from_user( dbuf, buf, count ); printk( "************** write bufx = %x ******************* ", buf ); printk( "************* write bufs = %s ******************* ", buf ); for( i = 0; i < count; i++ ){ ADCTxdata[i] = dbuf[i]; printk( "write adctx value [%d] = %c ", i, ADCTxdata[i] ); } kfree( dbuf ); return ret; } static ssize_t adc_read( struct file *file, char __user *buf, size_t count, loff_t *offset ){ int i = 0; int ret = 0; iowrite32( ioread32( r_SPCON0 ) | 0x1, r_SPCON0 ); adc_convert(); adc_convert(); printk( " " ); printk( "read count value = %d ", count ); dbuf = kmalloc( count * sizeof( unsigned char ), GFP_KERNEL ); if ( dbuf == NULL ){ printk( KERN_WARNING "read: dbuf alloc memory fail " ); return -1; } memset( dbuf, 0, count * sizeof( unsigned char ) ); for( i = 0; i < count; i++ ){ dbuf[i] = ADCRxdata[i]; printk( "read adctx value [%d] = %c ", i, ADCTxdata[i] ); } printk( " " ); copy_to_user( buf, dbuf, count); printk( "************** read bufx = %x ************************ ", buf ); printk( "************** read bufs = %s ************************ ", buf ); kfree( dbuf ); return ret; } void adc_convert( void ){ /*rGPHDAT &= ( ~0x20 ); adc_tx_data( ADCTxdata[0] ); // ADCRXdata[0] = rSPRDATO; ADCRxdata[0] = r_SPRDAT0; adc_tx_data( 0xff ); // ADCRXdata[1] = rSPRDATO; ADCRxdata[1] = r_SPRDAT0; rGPHDAT |= 0x20;*/ iowrite32( ioread32( r_GPHDAT ) & ( 0x20 ), r_GPHDAT ); adc_tx_data( ADCTxdata[0] ); ADCRxdata[0] = r_SPRDAT0; adc_tx_data( 0xff ); ADCRxdata[1] = r_SPRDAT0; iowrite32( ioread32( r_GPHDAT ) | ( 0x20 ), r_GPHDAT ); } static void adc_tx_data( unsigned char data ){ iowrite32( ioread32( r_SPSTA0 ) | 0x01, r_SPSTA0 ); spi_poll_done(); //rSPTDAT0 = data; iowrite32( data, r_SPTDAT0 ); spi_poll_done(); } void spi_poll_done( void ){ //while ( !( rSPSTA0 & 0x01 ) ) while ( !( ioread32( r_SPSTA0 ) & 0x01 ) ) ; } int adc_release( struct inode *inode, struct file *filp ){ adcopen--; module_put( THIS_MODULE ); return 0 ; } int adc_init_module( void ){ int result; dev_t dev = 0; if ( adc_major ){ dev = MKDEV( adc_major, adc_minor ); result = register_chrdev_region( dev, adc_nr_devs, "adc" ); }else{ result = alloc_chrdev_region( &dev, adc_minor, adc_nr_devs, "adc" ); adc_major = MAJOR( dev ); } adc_cdev = cdev_alloc(); if ( adc_cdev == NULL ){ return -1; } if ( result < 0 ){ printk( KERN_WARNING "adc: can't get major %d ", adc_major ); return result; } printk( KERN_INFO "adc: this major is %d ", adc_major ); adcopen = 0; adc_cdev->ops = &adc_fops; cdev_init( adc_cdev, &adc_fops ); cdev_add( adc_cdev, dev, 1 ); adc_address_map(); adc_spi_init(); printk( KERN_INFO "*** adc_init() finished *** " ); return 0; } static int adc_open( struct inode *inode, struct file *filp ){ if ( adcopen == 0 ) adcopen++; else{ printk( KERN_ALERT "Another process open the char device " ); return -1; } try_module_get( THIS_MODULE ); return 0; } void adc_cleanup_module( void ){ dev_t devno; adc_cancel_add_map(); devno = MKDEV( adc_major, adc_minor ); unregister_chrdev_region( devno, adc_nr_devs ); cdev_del( adc_cdev ); } module_init( adc_init_module ); module_exit( adc_cleanup_module );