From: <hol...@us...> - 2007-01-10 11:24:22
|
Revision: 747 http://svn.sourceforge.net/hackndev/?rev=747&view=rev Author: holger_bocklet Date: 2007-01-10 03:24:16 -0800 (Wed, 10 Jan 2007) Log Message: ----------- ptc_ucb1400 fix hang with apm-battery Modified Paths: -------------- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c 2007-01-08 11:43:37 UTC (rev 746) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.c 2007-01-10 11:24:16 UTC (rev 747) @@ -30,6 +30,8 @@ #include <linux/device.h> #include <linux/string.h> #include <linux/ctype.h> +#include <linux/time.h> +#include <linux/mutex.h> #include <asm/delay.h> #include <asm/arch/hardware.h> @@ -61,22 +63,31 @@ ac97_t *ac97; struct input_dev *idev; struct task_struct *thread; - wait_queue_head_t pen_irq_wait; - wait_queue_head_t adc_irq_wait; + //wait_queue_head_t pen_irq_wait; + //wait_queue_head_t adc_irq_wait; int pen_irq; int adc_irq; struct workqueue_struct *palmtc_ucb1400_workqueue; struct work_struct palmtc_ucb1400_work; struct completion thr_init; struct completion thr_exit; - struct semaphore adc_sem; + struct completion pen_irq_wait; + struct completion adc_irq_wait; + struct mutex adc_mutex; + struct mutex ucb_reg_mutex; u8 last_status; }; #ifdef CONFIG_APM +#define APM_MIN_INTERVAL 500 struct ucb1400 *ucb_static_copy; + struct mutex apm_mutex; + struct apm_power_info info_cache; + unsigned long cache_tstamp; #endif +//static struct timeval t; + struct ts_reading { u16 p; u16 x; @@ -89,7 +100,7 @@ // Pen sampling frequency (Hz) while touched. // more is not possible cause jiffies are at 100Hz in mach-pxa (why ???) -static int cont_rate = 100; +static int cont_rate = 80; module_param(cont_rate, int, 0); MODULE_PARM_DESC(cont_rate, "Sampling rate while pen down (Hz). Not more than ~100Hz on T|C"); @@ -120,17 +131,22 @@ * can only be called from process context, not interrupt * context. */ -void ucb1400_adc_enable(struct ucb1400 *ucb) +int ucb1400_adc_enable(struct ucb1400 *ucb) { u16 val=0; - down(&ucb->adc_sem); + + if (unlikely(mutex_lock_interruptible(&ucb->adc_mutex))) + return -ERESTARTSYS; ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); - val=ucb1400_reg_read(ucb, UCB_CSR2); - ucb1400_reg_write(ucb, UCB_CSR2, val | UCB_ADC_FILTER_ENA); - + mutex_lock(&ucb->ucb_reg_mutex); val=ucb1400_reg_read(ucb, UCB_IE_RIS); ucb1400_reg_write(ucb, UCB_IE_RIS, val | UCB_IE_ADC); + val=ucb1400_reg_read(ucb, UCB_IE_FAL); + ucb1400_reg_write(ucb, UCB_IE_FAL, val | UCB_IE_ADC); + mutex_unlock(&ucb->ucb_reg_mutex); + + return 0; } @@ -143,11 +159,17 @@ void ucb1400_adc_disable(struct ucb1400 *ucb) { u16 val=0; + + mutex_lock(&ucb->ucb_reg_mutex); val &= ~UCB_ADC_ENA; ucb1400_reg_write(ucb, UCB_ADC_CR, val); val=ucb1400_reg_read(ucb, UCB_IE_RIS); ucb1400_reg_write(ucb, UCB_IE_RIS, val & (~UCB_IE_ADC)); - up(&ucb->adc_sem); + val=ucb1400_reg_read(ucb, UCB_IE_FAL); + ucb1400_reg_write(ucb, UCB_IE_FAL, val & (~UCB_IE_ADC)); + mutex_unlock(&ucb->ucb_reg_mutex); + + mutex_unlock(&ucb->adc_mutex); } @@ -161,9 +183,10 @@ * This function currently sleeps waiting for the conversion to * complete (2 frames max without sync). */ -unsigned int ucb1400_adc_read(struct ucb1400 *ucb, int adc_channel) +unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel) { unsigned int val; + //unsigned long tstamp, sleep_time; adc_channel |= UCB_ADC_ENA; @@ -171,15 +194,38 @@ ucb1400_reg_write(ucb, UCB_ADC_CR, adc_channel | UCB_ADC_START); ucb->adc_irq=FALSE; + val = ucb1400_reg_read(ucb, UCB_ADC_DATA); + if ( !(val & UCB_ADC_DATA_VALID)) { + wait_for_completion_interruptible_timeout(&ucb->adc_irq_wait,UCB_ADC_TIMEOUT); + val = ucb1400_reg_read(ucb, UCB_ADC_DATA); + +/* ucb->adc_irq=FALSE; wait_event_interruptible( ucb->adc_irq_wait, ucb->adc_irq); - ucb->adc_irq=FALSE; + sleep_time=UCB_ADC_TIMEOUT; + tstamp=jiffies; + wait_event_interruptible_timeout( ucb->adc_irq_wait, (ucb->adc_irq) || + (((unsigned long)((long)jiffies - (long)tstamp)) >= sleep_time) , sleep_time); +*/ + if (likely(ucb->adc_irq)) { + ucb->adc_irq=FALSE; + } else { + //do_gettimeofday(&t); + //DBG("adc read timeout: %ld %ld\n",t.tv_sec,t.tv_usec); + printk(KERN_ERR "%s: read error (timeout on adc%1u), value:0x%x\n", + __FUNCTION__,(adc_channel&0x0c)>>2, val); + + } - val = ucb1400_reg_read(ucb, UCB_ADC_DATA); - + if (unlikely ( !(val & UCB_ADC_DATA_VALID) ) ){ + printk(KERN_ERR "%s: read error (data invalid on adc%1u), value:0x%x\n", + __FUNCTION__,(adc_channel&0x0c)>>2, val); + } + } return val & UCB_ADC_DATA_MASK; } + // create sysfs-entries for adc+gpio #define UCB1400_SYSFS_ATTR_SHOW(name,input); \ static ssize_t ucb1400_sysfs_##name##_show(struct device *dev, struct device_attribute *attr, char *buf) \ @@ -220,24 +266,27 @@ if (!(isdigit(buf[1]))) return -EINVAL; io_nr=simple_strtol(buf+1, NULL, 10); + + mutex_lock(&ucb->ucb_reg_mutex); val=ucb1400_reg_read(ucb, UCB_IO_DIR); ucb1400_reg_write(ucb, UCB_IO_DIR, val | (1 << io_nr)); // set output val=ucb1400_reg_read(ucb, UCB_IO_DATA); ucb1400_reg_write(ucb, UCB_IO_DATA, val & (~(1 << io_nr))); // unset bit + mutex_unlock(&ucb->ucb_reg_mutex); } else { if (strlen(buf)>2) return -EINVAL; if (!(isdigit(buf[0]))) return -EINVAL; io_nr=simple_strtol(buf, NULL, 10); + mutex_lock(&ucb->ucb_reg_mutex); val=ucb1400_reg_read(ucb, UCB_IO_DIR); ucb1400_reg_write(ucb, UCB_IO_DIR, val | (1 << io_nr)); // set output val=ucb1400_reg_read(ucb, UCB_IO_DATA); ucb1400_reg_write(ucb, UCB_IO_DATA, val | (1 << io_nr)); // set bit + mutex_unlock(&ucb->ucb_reg_mutex); } - val1=ucb1400_reg_read(ucb, UCB_IO_DIR); - val2=ucb1400_reg_read(ucb, UCB_IO_DATA); return 2; } static DEVICE_ATTR(gpio, 0664, ucb1400_sysfs_io_show, ucb1400_sysfs_io_store); @@ -246,8 +295,26 @@ static void palmtc_get_power_status(struct apm_power_info *info) { struct ucb1400 *ucb=ucb_static_copy; - u16 battery=0; + u16 battery, a,b,c; + int i; + + if (mutex_lock_interruptible(&apm_mutex)) + return; + //do_gettimeofday(&t); + //DBG("apm-call sem-down: %ld %ld\n",t.tv_sec,t.tv_usec); + + // if last call with hardware-read is < 5 Sek just return cached info + if ( ((unsigned long)((long)jiffies - (long)cache_tstamp) ) < APM_MIN_INTERVAL ) { + info->ac_line_status = info_cache.ac_line_status; + info->battery_status = info_cache.battery_status; + info->battery_flag = info_cache.battery_flag; + info->battery_life = info_cache.battery_life; + info->time = info_cache.time; + mutex_unlock(&apm_mutex); + return; + } + //DBG("apm-call read\n"); info->battery_flag = 0; battery=ucb1400_reg_read(ucb, UCB_IO_DATA); if (battery & UCB_IO_0) { @@ -258,11 +325,22 @@ info->ac_line_status = APM_AC_OFFLINE; } - while ( || - ucb1400_adc_enable(ucb); + if (ucb1400_adc_enable(ucb)) + return; + battery=ucb1400_adc_read(ucb, UCB_ADC_INP_AD0); + for (i=0;(i<3);i++) { + a=ucb1400_adc_read(ucb, UCB_ADC_INP_AD0); + b=ucb1400_adc_read(ucb, UCB_ADC_INP_AD0); + c=ucb1400_adc_read(ucb, UCB_ADC_INP_AD0); + if ((a==b) && (b==c)) { + battery=c; + break; + } else { + battery=(a+b+c+battery)/4; + } + } ucb1400_adc_disable(ucb); - DBG("batt: %u\n",battery); if (battery > UCB_BATT_HIGH) { info->battery_flag |= APM_BATTERY_FLAG_HIGH; @@ -282,8 +360,16 @@ if (info->battery_life>100) info->battery_life=100; info->time = info->battery_life * 6; - //info->battery_life = ((info->time * 100) / UCB_BATT_LIFE); info->units = APM_UNITS_MINS; + + info_cache.ac_line_status=info->ac_line_status; + info_cache.battery_status=info->battery_status; + info_cache.battery_flag=info->battery_flag; + info_cache.battery_life=info->battery_life; + info_cache.time=info->time; + cache_tstamp=jiffies; + + mutex_unlock(&apm_mutex); } #endif @@ -316,6 +402,8 @@ u16 val; // clear remaining ints + mutex_lock(&ucb->ucb_reg_mutex); + val=ucb1400_reg_read(ucb, UCB_IE_STATUS); ucb1400_reg_write(ucb, UCB_IE_CLEAR, val); @@ -331,6 +419,7 @@ val=ucb1400_reg_read(ucb, UCB_IE_RIS); ucb1400_reg_write(ucb, UCB_IE_RIS, val & (~UCB_IE_TSPX) & (~UCB_IE_TSMX)); //ucb1400_reg_write(ucb, UCB_IE_RIS, val & (~UCB_IE_TSPX)); + //DBG("ts irq-off"); } else { @@ -354,6 +443,8 @@ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | UCB_TS_CR_MODE_INT); } + mutex_unlock(&ucb->ucb_reg_mutex); + //DBG("ts irqcontrol: fal: 0x%x ris: 0x%x\n",ucb1400_reg_read(ucb, UCB_IE_FAL),ucb1400_reg_read(ucb, UCB_IE_RIS)); } @@ -420,15 +511,18 @@ } -static void ucb1400_ts_take_reading(struct ucb1400 *ucb, struct ts_reading *ts) +static int ucb1400_ts_take_reading(struct ucb1400 *ucb, struct ts_reading *ts) { - ucb1400_adc_enable(ucb); + //ucb1400_adc_enable(ucb); + if (ucb1400_adc_enable(ucb)) + return -ERESTARTSYS; ts->x = ucb1400_ts_read_xpos(ucb); ts->y = ucb1400_ts_read_ypos(ucb); ts->p = ucb1400_ts_read_pressure(ucb); ucb1400_adc_disable(ucb); + return 0; } // ISR for ac97, shared with pxa2xx-ac97, and later with ucb1400-gpio and ucb1400-adc @@ -438,7 +532,7 @@ struct ucb1400 *ucb = (struct ucb1400 *) devid; status=GSR; - if (status & GSR_GSCI) { + if (likely(status & GSR_GSCI)) { GSR&=GSR_GSCI; queue_work(ucb->palmtc_ucb1400_workqueue, &ucb->palmtc_ucb1400_work); @@ -455,28 +549,33 @@ struct ucb1400 *ucb = (struct ucb1400 *) data; u16 val,val1,val2; - // check which irq + //mutex_trylock(&ucb->ucb_reg_mutex); don't: this delays irqs ! + // check which irq and clear val=ucb1400_reg_read(ucb, UCB_IE_STATUS); + ucb1400_reg_write(ucb, UCB_IE_CLEAR, val); val1=ucb1400_reg_read(ucb, UCB_IE_EXTRA); - // clear irqs - ucb1400_reg_write(ucb, UCB_IE_CLEAR, val); ucb1400_reg_write(ucb, UCB_IE_EXTRA, val1); + //mutex_unlock(&ucb->ucb_reg_mutex); - if (val & UCB_IE_TSPX) { + //if ((val & UCB_IE_ADC) && (ucb->adc_irq==FALSE)) { + if ((val & UCB_IE_ADC) ) { + ucb->adc_irq=TRUE; + complete(&ucb->adc_irq_wait); + //wake_up_interruptible(&ucb->adc_irq_wait); + } + //if ((val & UCB_IE_TSPX) && (ucb->pen_irq==FALSE)) { + if ((val & UCB_IE_TSPX)) { ucb->pen_irq=TRUE; - //DBG("irq_wq: TS\n"); - wake_up_interruptible(&ucb->pen_irq_wait); + complete(&ucb->pen_irq_wait); + //do_gettimeofday(&t); + //DBG("irq_wq_TS: %ld %ld\n",t.tv_sec,t.tv_usec); + //wake_up_interruptible(&ucb->pen_irq_wait); } - if (val & UCB_IE_ADC) { - ucb->adc_irq=TRUE; - //DBG("irq_wq: ADC\n"); - wake_up_interruptible(&ucb->adc_irq_wait); - } if (val & UCB_IE_IO) { //show it val1=ucb1400_reg_read(ucb, UCB_IO_DIR); val2=ucb1400_reg_read(ucb, UCB_IO_DATA); - //DBG("gpio_irq: irq: 0x%u dir: %u data: %u\n",val,val1,val2); + DBG("gpio_irq: irq: 0x%u dir: %u data: %u\n",val,val1,val2); } } @@ -488,7 +587,7 @@ { unsigned long sleep_time; int pen_was_down=FALSE; - unsigned long tstamp; // timestamp (jiffies) + //unsigned long tstamp; // timestamp (jiffies) struct ts_reading ts_values; struct ucb1400 *ucb = (struct ucb1400 *) data; @@ -512,21 +611,27 @@ /* touch reader loop */ while (ucb->thread) { ucb->pen_irq=FALSE; - wait_event_interruptible( ucb->pen_irq_wait, ucb->pen_irq); + //do_gettimeofday(&t); + //DBG("kthread-1: %ld %ld\n",t.tv_sec,t.tv_usec); + //wait_event_interruptible( ucb->pen_irq_wait, ucb->pen_irq); + wait_for_completion_interruptible(&ucb->pen_irq_wait); if (ucb->thread) { // are we still alive? do { ucb1400_ts_mode_int(ucb, 0); // int off / clear int - ucb1400_ts_take_reading(ucb, &ts_values); + if (! (ucb1400_ts_take_reading(ucb, &ts_values))) { + ucb1400_ts_mode_int(ucb, UCB_IE_RIS); //int on rising edge=wait for penup ucb1400_ts_report_event(ucb, ts_values); pen_was_down=TRUE; //DBG("kthread x: %d y: %d p: %d\n",ts_values.x,ts_values.y,ts_values.p); - tstamp=jiffies; + //tstamp=jiffies; // This marco needs a TRUE condition to return, Timeout alone without TRUE does NOT return, // so make up a TRUE when timeout. A bit bloated though .... ucb->pen_irq=FALSE; - wait_event_interruptible_timeout( ucb->pen_irq_wait, (ucb->pen_irq) || - (((unsigned long)((long)jiffies - (long)tstamp)) >= sleep_time) , sleep_time); + wait_for_completion_interruptible_timeout(&ucb->pen_irq_wait,sleep_time); + //wait_event_interruptible_timeout( ucb->pen_irq_wait, (ucb->pen_irq) || + // (((unsigned long)((long)jiffies - (long)tstamp)) >= sleep_time) , sleep_time); + } } while(! ucb->pen_irq); if (ucb->thread) @@ -596,9 +701,14 @@ ucb1400_reg_write(ucb, UCB_IE_EXTRA, val); // start reader-thread for touchscreen + ucb->pen_irq=FALSE; + ucb->adc_irq=FALSE; init_completion(&ucb->thr_init); init_completion(&ucb->thr_exit); - init_waitqueue_head(&ucb->pen_irq_wait); + init_completion(&ucb->pen_irq_wait); + mutex_init(&ucb->ucb_reg_mutex); + //init_MUTEX(&ucb->ucb_reg_sem); + //init_waitqueue_head(&ucb->pen_irq_wait); //ucb1400_ts_mode_int(ucb, 0); ret = kernel_thread(ucb1400_ts_read_thread, ucb, CLONE_KERNEL); if (ret > 0) { @@ -614,8 +724,14 @@ INIT_WORK(&ucb->palmtc_ucb1400_work, palmtc_ucb1400_irq_queuework, ucb); //adc - init_waitqueue_head(&ucb->adc_irq_wait); - sema_init(&ucb->adc_sem, 1); + val=ucb1400_reg_read(ucb, UCB_CSR2); + ucb1400_reg_write(ucb, UCB_CSR2, val | UCB_ADC_FILTER_ENA); + + //init_waitqueue_head(&ucb->adc_irq_wait); + init_completion(&ucb->adc_irq_wait); + mutex_init(&ucb->adc_mutex); + //init_MUTEX(&ucb->adc_sem); + //sema_init(&ucb->adc_sem, 1); // get our share of AC97-Bus interrupt err = request_irq(IRQ_AC97, ucb1400_irq, SA_SHIRQ, "ucb1400", ucb); @@ -653,8 +769,18 @@ device_create_file(dev, &dev_attr_gpio); #ifdef CONFIG_APM + //sema_init(&apm_sem, 1); + info_cache.ac_line_status=APM_AC_UNKNOWN; + info_cache.battery_status=APM_BATTERY_STATUS_UNKNOWN; + info_cache.battery_flag=APM_BATTERY_FLAG_UNKNOWN; + info_cache.battery_life=-1; + info_cache.time=-1; + info_cache.units=APM_UNITS_MINS; + cache_tstamp=0; + //init_MUTEX(&apm_sem); + mutex_init(&apm_mutex); + ucb_static_copy=ucb; //sorry for this, apm-power-function doesnt have a parm to pass dev... apm_get_power_status = palmtc_get_power_status; - ucb_static_copy=ucb; //sorry for this, apm-power-function doesnt have a parm to pass dev... #endif @@ -664,7 +790,9 @@ if (ucb->thread) { ucb->thread = NULL; ucb->pen_irq = TRUE; - wake_up_interruptible(&ucb->pen_irq_wait); + complete_all(&ucb->pen_irq_wait); + complete_all(&ucb->adc_irq_wait); + //wake_up_interruptible(&ucb->pen_irq_wait); wait_for_completion(&ucb->thr_exit); } rollback2: @@ -690,6 +818,9 @@ #ifdef CONFIG_APM apm_get_power_status = NULL;; #endif + ucb->adc_irq = TRUE; + complete_all(&ucb->adc_irq_wait); + //wake_up_interruptible(&ucb->adc_irq_wait); // disable "GPIO"-int in pxa-ac97 if we found it disabled if ((ucb->last_status & GCR_GIE) == 0) @@ -700,14 +831,12 @@ ucb1400_reg_write(ucb, UCB_CSR1, val); } - ucb->adc_irq = TRUE; - wake_up_interruptible(&ucb->adc_irq_wait); - //stop kthread if (ucb->thread) { ucb->thread = NULL; ucb->pen_irq = TRUE; - wake_up_interruptible(&ucb->pen_irq_wait); + complete_all(&ucb->pen_irq_wait); + //wake_up_interruptible(&ucb->pen_irq_wait); wait_for_completion(&ucb->thr_exit); } Modified: linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h =================================================================== --- linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h 2007-01-08 11:43:37 UTC (rev 746) +++ linux4palm/linux/trunk/arch/arm/mach-pxa/palmtc/ptc_ucb1400.h 2007-01-10 11:24:16 UTC (rev 747) @@ -43,7 +43,7 @@ #define UCB_IE_TSMX (1 << 13) #define UCB_IE_TCLIP (1 << 14) #define UCB_IE_ACLIP (1 << 15) -#define UCB_IE_IO (0x1FF) +#define UCB_IE_IO (0x3FF) #define UCB_TC_B_VOICE_ENA (1 << 3) #define UCB_TC_B_CLIP (1 << 4) @@ -89,8 +89,8 @@ #define UCB_ADC_FILTER_ENA (1 << 12) -#define UCB_ADC_DATA_MASK 0x3ff -#define UCB_ADC_DATA_VALID (1 << 15) +#define UCB_ADC_DATA_MASK 0x3ff +#define UCB_ADC_DATA_VALID (1 << 15) #define UCB_ID_1400 0x4304 @@ -122,3 +122,4 @@ #define UCB_BATT_CRITICAL 480 #define UCB_BATT_LIFE 600 // battery duration in minutes +#define UCB_ADC_TIMEOUT 100 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |