2440上的触摸屏驱动移植

2019-07-12 15:16发布

嵌入式Linux之我行,主要讲述和总结了本人在学习嵌入式linux中的每个步骤。一为总结经验,二希望能给想入门嵌入式Linux的朋友提供方便。如有错误之处,谢请指正。 一、移植环境
  • 主  机:VMWare--Fedora 9
  • 开发板:Mini2440--64MB Nand
  • 编译器:arm-linux-gcc-4.3.2
二、移植步骤 1. 准备驱动源码。因为linux-2.6.30.4内核中没有提供合适的ADC驱动和触摸屏驱动,所以这里就直接用友善提供的驱动   s3c24xx-adc.h # ifndef _S3C2410_ADC_H_
# define _S3C2410_ADC_H_

# define ADC_WRITE( ch, prescale)     ( ( ch) < < 16| ( prescale) )

# define ADC_WRITE_GETCH( data)     ( ( ( data) > > 16) & 0x7)
# define ADC_WRITE_GETPRE( data)     ( ( data) & 0xff)

# endif /* _S3C2410_ADC_H_ */

mini2440_adc.c # include < linux/ errno . h>
# include < linux/ kernel. h>
# include < linux/ module. h>
# include < linux/ slab. h>
# include < linux/ input. h>
# include < linux/ init. h>
# include < linux/ serio. h>
# include < linux/ delay. h>
# include < linux/ clk. h>
# include < asm / io. h>
# include < asm / irq. h>
# include < asm / uaccess. h>
# include < mach/ regs- clock . h>
# include < plat/ regs- timer. h>
    
# include < plat/ regs- adc. h>
# include < mach/ regs- gpio. h>
# include < linux/ cdev. h>
# include < linux/ miscdevice. h>

# include "s3c24xx-adc.h"

# undef DEBUG
//#define DEBUG

# ifdef DEBUG
# define DPRINTK( x. . . ) { printk( __FUNCTION__ "(%d): " , __LINE__ ) ; printk( # # x) ; }
# else
# define DPRINTK( x. . . ) ( void ) ( 0)
# endif

# define DEVICE_NAME    "adc"

static void __iomem * base_addr;

typedef struct {
    wait_queue_head_t wait;
    int channel;
    int prescale;
} ADC_DEV;

DECLARE_MUTEX( ADC_LOCK) ;
static int OwnADC = 0;

static ADC_DEV adcdev;
static volatile int ev_adc = 0;
static int adc_data;

static struct clk    * adc_clock;

# define ADCCON ( * ( volatile unsigned long * ) ( base_addr + S3C2410_ADCCON) )     
//ADC control

# define ADCTSC ( * ( volatile unsigned long * ) ( base_addr + S3C2410_ADCTSC) )     
//ADC touch screen control

# define ADCDLY ( * ( volatile unsigned long * ) ( base_addr + S3C2410_ADCDLY) )     
//ADC start or Interval Delay

# define ADCDAT0 ( * ( volatile unsigned long * ) ( base_addr + S3C2410_ADCDAT0) )     
//ADC conversion data 0

# define ADCDAT1 ( * ( volatile unsigned long * ) ( base_addr + S3C2410_ADCDAT1) )     
//ADC conversion data 1

# define ADCUPDN ( * ( volatile unsigned long * ) ( base_addr + 0x14) )     
//Stylus Up/Down interrupt status

# define PRESCALE_DIS ( 0 < < 14)
# define PRESCALE_EN ( 1 < < 14)
# define PRSCVL( x) ( ( x) < < 6)
# define ADC_INPUT( x) ( ( x) < < 3)
# define ADC_START ( 1 < < 0)
# define ADC_ENDCVT ( 1 < < 15)

# define START_ADC_AIN( ch, prescale) /
    do { /
        ADCCON = PRESCALE_EN | PRSCVL( prescale) | ADC_INPUT( ( ch) ) ; /
        ADCCON | = ADC_START; /
    } while ( 0)

static irqreturn_t adcdone_int_handler( int irq, void * dev_id)
{
    if ( OwnADC) {
        adc_data = ADCDAT0 & 0x3ff;

        ev_adc = 1;
        wake_up_interruptible( & adcdev. wait) ;
    }

    return IRQ_HANDLED;
}

static ssize_t s3c2410_adc_read( struct file * filp, char * buffer, size_t count , loff_t * ppos)
{
    char str[ 20] ;
    int value;
    size_t len;
    if ( down_trylock( & ADC_LOCK) = = 0) {
        OwnADC = 1;
        START_ADC_AIN( adcdev. channel, adcdev. prescale) ;
        wait_event_interruptible( adcdev. wait, ev_adc) ;

        ev_adc = 0;

        DPRINTK( "AIN[%d] = 0x%04x, %d/n" , adcdev. channel, adc_data, ADCCON & 0x80 ? 1: 0) ;

        value = adc_data;
        sprintf ( str, "%5d" , adc_data) ;
        copy_to_user( buffer, ( char * ) & adc_data, sizeof ( adc_data) ) ;

        OwnADC = 0;
        up( & ADC_LOCK) ;
    } else {
        value = - 1;
    }

    len = sprintf ( str, "%d/n" , value) ;
    if ( count > = len) {
        int r = copy_to_user( buffer, str, len) ;
        return r ? r : len;
    } else {
        return - EINVAL;
    }
}

static int s3c2410_adc_open( struct inode * inode, struct file * filp)
{
    init_waitqueue_head( & ( adcdev. wait) ) ;

    adcdev. channel= 0;
    adcdev. prescale= 0xff;

    DPRINTK( "adc opened/n" ) ;
    return 0;
}

static int s3c2410_adc_release( struct inode * inode, struct file * filp)
{
    DPRINTK( "adc closed/n" ) ;
    return 0;
}

static struct file_operations dev_fops = {
    owner:     THIS_MODULE,
    open :     s3c2410_adc_open,
    read :     s3c2410_adc_read,     
    release:     s3c2410_adc_release,
} ;

static struct miscdevice misc = {
    . minor = MISC_DYNAMIC_MINOR,
    . name = DEVICE_NAME,
    . fops = & dev_fops,
} ;

static int __init dev_init( void )
{
    int ret;

    base_addr= ioremap( S3C2410_PA_ADC, 0x20) ;
    if ( base_addr = = NULL ) {
        printk( KERN_ERR "Failed to remap register block/n" ) ;
        return - ENOMEM;
    }

    adc_clock = clk_get( NULL , "adc" ) ;
    if ( ! adc_clock) {
        printk( KERN_ERR "failed to get adc clock source/n" ) ;
        return - ENOENT;
    }
    clk_enable( adc_clock) ;
    
    /* normal ADC */
    ADCTSC = 0;

    ret = request_irq( IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, & adcdev) ;
    if ( ret) {
        iounmap( base_addr) ;
        return ret;
    }

    ret = misc_register( & misc) ;

    printk ( DEVICE_NAME"/tinitialized/n" ) ;
    return ret;
}

static void __exit dev_exit( void )
{
    free_irq( IRQ_ADC, & adcdev) ;
    iounmap( base_addr) ;

    if ( adc_clock) {
        clk_disable( adc_clock) ;
        clk_put( adc_clock) ;
        adc_clock = NULL ;
    }

    misc_deregister( & misc) ;
}

EXPORT_SYMBOL( ADC_LOCK) ;
module_init( dev_init) ;
module_exit( dev_exit) ;
MODULE_LICENSE( "GPL" ) ;
MODULE_AUTHOR( "FriendlyARM Inc." ) ;

s3c2410_ts.c # include < linux/ errno . h>
# include < linux/ kernel. h>
# include < linux/ module. h>
# include < linux/ slab. h>
# include < linux/ input. h>
# include < linux/ init. h>
# include < linux/ serio. h>
# include < linux/ delay. h>
# include < linux/ platform_device. h>
# include < linux/ clk. h>
# include < asm / io. h>
# include < asm / irq. h>

# include < plat/ regs- adc. h>
# include < mach/ regs- gpio. h>

/* For ts.dev.id.version */
# define S3C2410TSVERSION    0x0101

# define WAIT4INT( x) ( ( ( x) < < 8) | /
         S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | /
         S3C2410_ADCTSC_XY_PST( 3) )

# define AUTOPST     ( S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | /
         S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST( 0) )

static char * s3c2410ts_name = "s3c2410 TouchScreen" ;

static     struct input_dev * dev;
static     long xp;
static     long yp;
static     int count ;

extern struct semaphore ADC_LOCK;
static int OwnADC = 0;

static void __iomem * base_addr;

static inline void s3c2410_ts_connect( void )
{
    s3c2410_gpio_cfgpin( S3C2410_GPG12, S3C2410_GPG12_XMON) ;
    s3c2410_gpio_cfgpin( S3C2410_GPG13, S3C2410_GPG13_nXPON) ;
    s3c2410_gpio_cfgpin( S3C2410_GPG14, S3C2410_GPG14_YMON) ;
    s3c2410_gpio_cfgpin( S3C2410_GPG15, S3C2410_GPG15_nYPON) ;
}

static void touch_timer_fire( unsigned long data)
{
      unsigned long data0;
      unsigned long data1;
    int updown;

      data0 = ioread32( base_addr+ S3C2410_ADCDAT0) ;
      data1 = ioread32( base_addr+ S3C2410_ADCDAT1) ;

     updown = ( ! ( data0 & S3C2410_ADCDAT0_UPDOWN) ) & & ( ! ( data1 & S3C2410_ADCDAT0_UPDOWN) ) ;

     if ( updown) {
         if ( count ! = 0) {
            long tmp;
                                                                                                 
            tmp = xp;
            xp = yp;
            yp = tmp;
                                                                                                 
            xp > > = 2;
            yp > > = 2;

# ifdef CONFIG_TOUCHSCREEN_MY2440_DEBUG
            struct timeval tv;
            do_gettimeofday( & tv) ;
            printk( KERN_DEBUG "T: %06d, X: %03ld, Y: %03ld/n" , ( int ) tv. tv_usec, xp, yp) ;
# endif

             input_report_abs( dev, ABS_X, xp) ;
             input_report_abs( dev, ABS_Y, yp) ;

             input_report_key( dev, BTN_TOUCH, 1) ;
             input_report_abs( dev, ABS_PRESSURE, 1) ;
             input_sync( dev) ;
         }

         xp = 0;
         yp = 0;
         count = 0;

         iowrite32( S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+ S3C2410_ADCTSC) ;
         iowrite32( ioread32( base_addr+ S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+ S3C2410_ADCCON) ;
     } else {
         count = 0;

         input_report_key( dev, BTN_TOUCH, 0) ;
         input_report_abs( dev, ABS_PRESSURE, 0) ;
         input_sync( dev) ;

         iowrite32( WAIT4INT( 0) , base_addr+ S3C2410_ADCTSC) ;
        if ( OwnADC) {
            OwnADC = 0;
            up( & ADC_LOCK) ;
        }
     }
}

static struct timer_list touch_timer =
        TIMER_INITIALIZER( touch_timer_fire, 0, 0) ;

static irqreturn_t stylus_updown( int irq, void * dev_id)
{
    unsigned long data0;
    unsigned long data1;
    int updown;

    if ( down_trylock( & ADC_LOCK) = = 0) {
        OwnADC = 1;
        data0 = ioread32( base_addr+ S3C2410_ADCDAT0) ;
        data1 = ioread32( base_addr+ S3C2410_ADCDAT1) ;

        updown = ( ! ( data0 & S3C2410_ADCDAT0_UPDOWN) ) & & ( ! ( data1 & S3C2410_ADCDAT0_UPDOWN) ) ;

        if ( updown) {
            touch_timer_fire( 0) ;
        } else {
            OwnADC = 0;
            up( & ADC_LOCK) ;
        }
    }

    return IRQ_HANDLED;
}

static irqreturn_t stylus_action( int irq, void * dev_id)
{
    unsigned long data0;
    unsigned long data1;

    if ( OwnADC) {
        data0 = ioread32( base_addr+ S3C2410_ADCDAT0) ;
        data1 = ioread32( base_addr+ S3C2410_ADCDAT1) ;

        xp + = data0 & S3C2410_ADCDAT0_XPDATA_MASK;
        yp + = data1 & S3C2410_ADCDAT1_YPDATA_MASK;
        count + + ;

     if ( count < ( 1< < 2) ) {
            iowrite32( S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+ S3C2410_ADCTSC) ;
            iowrite32( ioread32( base_addr+ S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+ S3C2410_ADCCON) ;
        } else {
            mod_timer( & touch_timer, jiffies+ 1) ;
            iowrite32( WAIT4INT( 1) , base_addr+ S3C2410_ADCTSC) ;
        }
    }

    return IRQ_HANDLED;
}

static struct clk    * adc_clock;

static int __init s3c2410ts_init( void )
{
    struct input_dev * input_dev;

    adc_clock = clk_get( NULL , "adc" ) ;
    if ( ! adc_clock) {
        printk( KERN_ERR "failed to get adc clock source/n" ) ;
        return - ENOENT;
    }
    clk_enable( adc_clock) ;

    base_addr= ioremap( S3C2410_PA_ADC, 0x20) ;
    if ( base_addr = = NULL ) {
        printk( KERN_ERR "Failed to remap register block/n" ) ;
        return - ENOMEM;
    }

    /* Configure GPIOs */
    s3c2410_ts_connect( ) ;

    iowrite32( S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL( 0xFF) , /
         base_addr+ S3C2410_ADCCON)