|
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.
|