From: Leblanc f. <fle...@us...> - 2002-03-07 09:15:19
|
Update of /cvsroot/linux-mips/linux/drivers/char In directory usw-pr-cvs1:/tmp/cvs-serv3157/drivers/char Modified Files: Makefile misc.c Added Files: buttons.c e105btns.c gpiobtns.c tpanel.c Log Message: Adds more VR stuff for Cassiopeia E15 Support. --- NEW FILE: buttons.c --- /* * VR41xx button input driver * * Note that the way this driver does atomic access to buffers is not * SMP-safe. VR41xx CPUs don't support SMP anyway. * * Copyright (c) 2000 Michael Klar <wyl...@in...> * Copyright (c) 2001 François Leblanc <fra...@ce...> * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/sched.h> #include <linux/signal.h> #include <linux/errno.h> #include <linux/poll.h> #include <linux/miscdevice.h> #include <linux/init.h> #ifdef CONFIG_BUTTONS_DIRECT_POWEROFF #include <linux/reboot.h> #endif #ifdef CONFIG_PM_SUSPEND_WAKEUP #include <asm/power.h> #endif #include <linux/buttons.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/irq.h> #include <asm/vr41xx.h> #define VRBUTTONS_VERSION "0.1.1" extern unsigned short gpio_btn_map[]; extern void open_gpio_buttons(void); extern void close_gpio_buttons(void); extern unsigned short e105_btn_map[]; extern void open_e105_buttons(void); extern void close_e105_buttons(void); // BUFFSIZE must be power of 2 #define BUFFSIZE 16 struct buttons_instance { struct buttons_instance *next; struct file *owner; struct fasync_struct *fasyncptr; wait_queue_head_t wait; unsigned char head; unsigned char tail; unsigned char buffer[BUFFSIZE]; }; // Implicit NULL initializers: static struct buttons_instance *buttons_head; void add_button_data(unsigned char data) { struct buttons_instance *btnptr = buttons_head; while (btnptr) { // MFK *** maybe implement a mask, so not all buttons have to go to all openers // otherwise, might as well make the wake_up and kill_fasync global instead of per-open btnptr->buffer[btnptr->head++] = data; btnptr->head &= BUFFSIZE - 1; if (btnptr->head == btnptr->tail) { btnptr->tail++; btnptr->tail &= BUFFSIZE - 1; btnptr->buffer[btnptr->tail] |= 0x40; } wake_up_interruptible(&btnptr->wait); if (btnptr->fasyncptr) kill_fasync(&btnptr->fasyncptr, SIGIO, POLL_IN); btnptr = btnptr->next; } // printk("button 0x%02x\n", data); } static void power_btn_interrupt(int irq, void *dev_id, struct pt_regs *regs) { add_button_data(0xbf); *VR41XX_PMUINTREG = 1; #ifdef CONFIG_BUTTONS_DIRECT_POWEROFF if(!buttons_head) machine_halt(); #endif } static loff_t llseek_buttons(struct file * file, loff_t offset, int origin) { return -ESPIPE; } /* * Read data format (subject to change): * unsigned short: bit 15: press/release 1 = pressed, 0 = released * bit 14: data lost flag 1 = data lost (but this data still valid) * bit 13: reserved * bit 12: reserved * bits 0-11: button code */ static ssize_t read_buttons(struct file * file, char * buffer, size_t count, loff_t *ppos) { struct buttons_instance *btnptr = file->private_data; unsigned char bufdat; unsigned short data; size_t retcnt = 0; if (count < sizeof(data)) return -EINVAL; cli(); while (btnptr->head == btnptr->tail) { if (file->f_flags & O_NONBLOCK) { sti(); return -EAGAIN; } interruptible_sleep_on(&btnptr->wait); // sleep_on will sti for us if (signal_pending(current)) return -ERESTARTSYS; cli(); } do { bufdat = btnptr->buffer[btnptr->tail++]; btnptr->tail &= BUFFSIZE - 1; sti(); data = (bufdat & 0xc0) << 8; #ifdef CONFIG_VR41XX_GPIO_BUTTONS if ((bufdat & 0x3f) < 0x20) data |= gpio_btn_map[bufdat & 0x1f]; else #endif #ifdef CONFIG_VR41XX_E105_BUTTONS if ((bufdat & 0x3f) < 0x28) data |= e105_btn_map[bufdat & 0x07]; else #endif if ((bufdat & 0x3f) == 0x3f) data |= BTN_POWER; if(put_user(data, (short*)(buffer + retcnt))) return -EFAULT; retcnt += sizeof(data); if (retcnt > count - sizeof(data)) return retcnt; cli(); } while (btnptr->head != btnptr->tail); sti(); return retcnt; } static unsigned int poll_buttons(struct file *file, poll_table * wait) { struct buttons_instance *btnptr = file->private_data; int comp; poll_wait(file, &btnptr->wait, wait); cli(); comp = (btnptr->head != btnptr->tail); sti(); if (comp) return POLLIN | POLLRDNORM; return 0; } static int ioctl_buttons(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { // Lots of good stuff to be added here return -EINVAL; } static int open_buttons(struct inode * inode, struct file * file) { struct buttons_instance *prev = NULL, *btnptr = buttons_head; while (btnptr) { // sanity check: make sure same open hasn't already happened if (btnptr->owner == file) return -EINVAL; prev = btnptr; btnptr = btnptr->next; } btnptr = kmalloc(sizeof(struct buttons_instance), GFP_KERNEL); if (!btnptr) return -ENOMEM; memset(btnptr, 0, sizeof(struct buttons_instance)); btnptr->owner = file; init_waitqueue_head(&btnptr->wait); // on first open, request IRQs and set to level trigger, active high // enabling IRQs before buttons_head is assigned causes any pending // ints to not fill buffer with data, which is probably good if (!prev) { #ifdef CONFIG_VR41XX_GPIO_BUTTONS open_gpio_buttons(); #endif #ifdef CONFIG_VR41XX_E105_BUTTONS open_e105_buttons(); #endif buttons_head = btnptr; } else { prev->next = btnptr; } file->private_data = btnptr; return 0; } static int fasync_buttons(int fd, struct file * file, int on) { struct buttons_instance *btnptr = file->private_data; int retval; retval = fasync_helper(fd, file, on, &btnptr->fasyncptr); if (retval < 0) return retval; return 0; } static int release_buttons(struct inode * inode, struct file * file) { struct buttons_instance *prev = NULL, *btnptr = buttons_head; fasync_buttons(-1, file, 0); while (btnptr) { if ((btnptr->owner == file) || (btnptr == file->private_data)) break; prev = btnptr; btnptr = btnptr->next; } if (btnptr) { if (!prev) buttons_head = btnptr->next; else prev->next = btnptr->next; kfree(btnptr); } else { // shouldn't ever happen if (file->private_data) kfree(file->private_data); } // free IRQs on last close if (!buttons_head) { #ifdef CONFIG_VR41XX_GPIO_BUTTONS close_gpio_buttons(); #endif #ifdef CONFIG_VR41XX_E105_BUTTONS close_e105_buttons(); #endif } file->private_data = NULL; return 0; } struct file_operations vr41xx_buttons_fops = { llseek: llseek_buttons, read: read_buttons, poll: poll_buttons, ioctl: ioctl_buttons, open: open_buttons, release: release_buttons, fasync: fasync_buttons, }; static struct miscdevice vr41xx_buttons = { VR41XX_BUTTONS_MINOR, "vrbuttons", &vr41xx_buttons_fops }; int __init vr41xx_buttons_init(void) { int retval; printk (KERN_INFO"VR41xx button input driver version "VRBUTTONS_VERSION"\n"); #ifdef CONFIG_VR41XX_E105_BUTTONS printk (KERN_INFO "E105 buttons mapping configured\n"); #endif retval = misc_register(&vr41xx_buttons); if (retval < 0) return retval; // always have power buton active, other buttons active only on first open if (request_irq(VR41XX_IRQ_POWER, power_btn_interrupt, SA_SAMPLE_RANDOM | SA_INTERRUPT, "vrbuttons", NULL)) printk(KERN_ERR "vrbuttons: Unable to get power button IRQ.\n"); return 0; } --- NEW FILE: e105btns.c --- /* $Id: e105btns.c,v 1.1 2002/03/07 09:15:16 fleblanc Exp $ * * Casio E-10x/50x input button (sub)driver * * Original driver by Robert Coie, modified and integrated into main VR41xx * buttons driver by Michael Klar. * * Copyright (c) 1999, Robert Coie <ra...@in...> * Copyright (c) 2000, Michael Klar, mf...@po... * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> #include <linux/ioport.h> #include <linux/buttons.h> #include <asm/io.h> #include <asm/vr41xx.h> extern void add_button_data(unsigned char); #define E105_BUTTONS_GPIO 9 #define E105_BUTTONS_IRQ VR41XX_IRQ_GPIO(E105_BUTTONS_GPIO) #define E105_BUTTONS_MASK (1 << E105_BUTTONS_GPIO) #define E105_BUTTONS_PORT 0xA018 #define E105_BUTTONS_DEV_ID ((void *) 0x45357878) /* 'E5xx' */ const unsigned short e105_btn_map[] = { BTN_UP, BTN_DOWN, BTN_ACTION, BTN_EXIT, BTN_NORTH, BTN_SOUTH, BTN_EAST, BTN_WEST }; struct e105_buttons_device_t { struct timer_list timer; u8 status; volatile char bounce; }; static struct e105_buttons_device_t buttons; // this is only called from int handler if no timer active, so not reentrant // static void process_e105_buttons(void) { u8 new_status, diff_status; int i; diff_status = buttons.status; buttons.status = new_status = inb(E105_BUTTONS_PORT); diff_status ^= new_status; if (diff_status == 0) return; for (i = 0; i < 8; i++) if (diff_status & (1 << i)) add_button_data((new_status & (1 << i) ? 0 : 0x80) | (i + 0x20)); } static void e105_button_timer(unsigned long data) { // if bouncing, just unmark bounce and go on, will handle data // next timer tick if (buttons.bounce) { buttons.bounce = 0; } else { process_e105_buttons(); if (!buttons.status) { unsigned long flags; // handle potential race with int handler: save_and_cli(flags); if (!buttons.bounce) { buttons.timer.data = 0; restore_flags(flags); return; } restore_flags(flags); } } buttons.timer.expires = jiffies + (HZ + 99)/100; add_timer(&buttons.timer); } static void e105_buttons_interrupt(int irq, void * dev_id, struct pt_regs * regs) { // flip active level for interrupt to opposite of current pin state if (*VR41XX_GIUPIODL & E105_BUTTONS_MASK) *VR41XX_GIUINTALSELL &= ~E105_BUTTONS_MASK; else *VR41XX_GIUINTALSELL |= E105_BUTTONS_MASK; // if timer not already active, activate it // this is only case for first button press if multiple buttons // pressed simulateously if (!buttons.timer.data) { buttons.timer.expires = jiffies + (HZ + 99)/100 + 1; buttons.timer.data = 1; add_timer(&buttons.timer); process_e105_buttons(); } // otherwise just mark as (still) bouncing buttons.bounce = 1; } void open_e105_buttons(void) { if (check_region(E105_BUTTONS_PORT, 1)) return; init_timer(&buttons.timer); buttons.timer.function = &e105_button_timer; // see README.GIU for explaination of why cli/sti cli(); *VR41XX_GIUINTTYPL &= ~E105_BUTTONS_MASK; *VR41XX_GIUINTALSELL |= E105_BUTTONS_MASK; sti(); if (request_irq(E105_BUTTONS_IRQ, e105_buttons_interrupt, SA_SHIRQ | SA_SAMPLE_RANDOM | SA_INTERRUPT, "E105buttons", E105_BUTTONS_DEV_ID)); return; request_region(E105_BUTTONS_PORT, 1, "E105buttons"); } void close_e105_buttons(void) { free_irq(E105_BUTTONS_IRQ, E105_BUTTONS_DEV_ID); del_timer_sync(&buttons.timer); buttons.status = 0; buttons.bounce = 0; release_region(E105_BUTTONS_PORT, 1); } --- NEW FILE: gpiobtns.c --- /* $Id: gpiobtns.c,v 1.1 2002/03/07 09:15:16 fleblanc Exp $ * * GPIO button functions for VR41xx button input driver * * Note that the way this driver does atomic access to buffers is not * SMP-safe. VR41xx CPUs don't support SMP anyway. * * Copyright (c) 2000 Michael Klar <wyl...@in...> * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> #include <linux/timer.h> #include <linux/buttons.h> #include <asm/vr41xx-platdep.h> extern void add_button_data(unsigned char); #ifdef CONFIG_CPU_VR4181 typedef u16 btn_mask_t; #define NUM_GPIO_IRQS 16 #else #ifdef CONFIG_HAS_VRC4173 typedef u64 btn_mask_t; #define NUM_GPIO_IRQS 52 #else typedef u32 btn_mask_t; #define NUM_GPIO_IRQS 32 #endif #endif #ifdef GPIO_BTN_MAP unsigned short gpio_btn_map[NUM_GPIO_IRQS] = GPIO_BTN_MAP; #else #warning "No GPIO button map defined, using default blank map" // Implicit 0 initializer: unsigned short gpio_btn_map[NUM_GPIO_IRQS]; #endif struct gpiobtns_device { struct timer_list btn_timer; btn_mask_t btn_timer_data; btn_mask_t btn_state; btn_mask_t btn_bounce; btn_mask_t pending_bits; unsigned char press_low; }; #ifdef GPIO_BTN_PRESS_LOW static struct gpiobtns_device gpiob = { { }, 0, 0, 0, 0, 0x80 }; #else static struct gpiobtns_device gpiob; #endif #ifdef CONFIG_HAS_VRC4173 extern void vrc4173_init_buttons(void); extern void vrc4173_close_buttons(void); extern btn_mask_t vrc4173_gpio_state(void); #endif static void debounce_timer_event(unsigned long data) { btn_mask_t curstate, same, mask; int i; long flags; // This is fairly nasty, but we need to make sure no button interrupt // happens while we're tweaking the debounce bits save_and_cli(flags); // reread data rather than trust passed arg, since int handler may // have added bits mask = gpiob.btn_timer_data; #ifdef CONFIG_CPU_VR4181 curstate = *VR41XX_GPDATLREG; #else curstate = *VR41XX_GIUPIODL | ((btn_mask_t)*VR41XX_GIUPIODH << 16); #ifdef CONFIG_HAS_VRC4173 curstate |= vrc4173_gpio_state(); #endif #endif gpiob.btn_state ^= mask; same = (curstate ^ gpiob.btn_state) & mask; gpiob.btn_bounce &= ~(same ^ mask); // if state is same as before transition detected, we either missed a // transition, or button switched state for real during debounce period if (same) for (i = 0; i < NUM_GPIO_IRQS; i++) if (same & ((btn_mask_t)1 << i)) add_button_data((((gpiob.btn_state & ((btn_mask_t)1 << i)) ? 0 : 0x80) ^ gpiob.press_low) | i); gpiob.btn_timer_data = gpiob.pending_bits | same; gpiob.pending_bits = 0; if (gpiob.btn_timer_data) { gpiob.btn_timer.expires = jiffies + (HZ + 99)/100; add_timer(&gpiob.btn_timer); } restore_flags(flags); } void gpio_handle_btn_interrupt(int gpio) { btn_mask_t gpiobit; // shouldn't get a non-button irq, but just in case... if (!gpio_btn_map[gpio]) return; gpiobit = (btn_mask_t)1 << gpio; if (gpiob.btn_bounce & gpiobit) return; // add data as opposite of previous state, rather than current pin state add_button_data((((gpiob.btn_state & gpiobit) ? 0 : 0x80) ^ gpiob.press_low)| gpio); gpiob.btn_bounce |= gpiobit; // debounce for at least 10ms past end of current timer tick // if timer event not active yet, just activate it for this bit if (!gpiob.btn_timer_data) { gpiob.btn_timer.expires = jiffies + (HZ + 99)/100 + 1; gpiob.btn_timer_data = gpiobit; add_timer(&gpiob.btn_timer); return; } // if timer event already active at right time, add this bit if (time_after_eq(gpiob.btn_timer.expires, jiffies + (HZ + 99)/100 + 1)) { gpiob.btn_timer_data |= gpiobit; return; } // otherwise, wait until the timer event to (re)activate for this bit gpiob.pending_bits |= gpiobit; } static void gpio_btn_interrupt(int irq, void *dev_id, struct pt_regs *regs) { unsigned short bit; int gpio = irq - 40; // flip active level for interrupt to opposite of current pin state #ifdef CONFIG_CPU_VR4181 bit = 1 << ((gpio & 7) * 2); if (*VR41XX_GPDATLREG & (1 << gpio)) *(gpio < 8 ? VR41XX_GPINTTYPL : VR41XX_GPINTTYPH) &= ~bit; else *(gpio < 8 ? VR41XX_GPINTTYPL : VR41XX_GPINTTYPH) |= bit; #else bit = 1 << (gpio & 15); if (*(gpio < 16 ? VR41XX_GIUPIODL : VR41XX_GIUPIODH) & bit) *(gpio < 16 ? VR41XX_GIUINTALSELL : VR41XX_GIUINTALSELH) &= ~bit; else *(gpio < 16 ? VR41XX_GIUINTALSELL : VR41XX_GIUINTALSELH) |= bit; #endif gpio_handle_btn_interrupt(gpio); } void open_gpio_buttons(void) { int i; unsigned short bits; init_timer(&gpiob.btn_timer); gpiob.btn_timer.function = &debounce_timer_event; for (i = 0; i < NUM_GPIO_IRQS; i++) { if (!(gpio_btn_map[i])) continue; #ifdef CONFIG_CPU_VR4181 bits = 0x0003 << ((i & 7) * 2); if (*(i < 8 ? VR41XX_GPMD0REG : VR41XX_GPMD1REG) & bits) { gpio_btn_map[i] = 0; continue; } // see README.GIU for explanation of why cli/sti cli(); *(i < 8 ? VR41XX_GPINTTYPL : VR41XX_GPINTTYPH) |= bits; #else #ifdef CONFIG_HAS_VRC4173 if (i >= 32) break; #endif bits = 1 << (i & 15); if (*(i < 16 ? VR41XX_GIUIOSELL : VR41XX_GIUIOSELH) & bits) { gpio_btn_map[i] = 0; continue; } // see README.GIU for explanation of why cli/sti cli(); *(i < 16 ? VR41XX_GIUINTTYPL : VR41XX_GIUINTTYPH) &= ~bits; *(i < 16 ? VR41XX_GIUINTALSELL : VR41XX_GIUINTALSELH) |= bits; *(i < 16 ? VR41XX_GIUINTHTSELL : VR41XX_GIUINTHTSELH) |= bits; #endif sti(); if (request_irq(i + 40, gpio_btn_interrupt, SA_SAMPLE_RANDOM | SA_INTERRUPT, "vrbuttons", NULL)) gpio_btn_map[i] = 0; } #ifdef CONFIG_HAS_VRC4173 vrc4173_init_buttons(); #endif } void close_gpio_buttons(void) { int i; for (i = 0; i < NUM_GPIO_IRQS; i++) if (gpio_btn_map[i]) free_irq(i + 40, NULL); gpiob.pending_bits = 0; del_timer(&gpiob.btn_timer); gpiob.btn_state = 0; gpiob.btn_bounce = 0; #ifdef CONFIG_HAS_VRC4173 vrc4173_close_buttons(); #endif } --- NEW FILE: tpanel.c --- /* * VR41xx Touch Panel Driver * * Copyright (c) 1999,2000 Michael Klar, mf...@po... * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ // This is used to enable the /proc/piuinfo interface to return // diagnostic info on PIU state and events: // #define DEBUG_TIMING_PARAMS #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/poll.h> #include <linux/miscdevice.h> #include <linux/random.h> #include <linux/init.h> #include <linux/pm.h> #ifdef DEBUG_TIMING_PARAMS #include <linux/proc_fs.h> #endif #include <asm/uaccess.h> #include <asm/system.h> #include <asm/irq.h> #include <asm/vr41xx.h> #include <linux/tpanel.h> #define TPANEL_VERSION "0.1.0" // BUFFSIZE can be increased, but it must be a power of 2 #define BUFFSIZE 128 struct tpanel_status { unsigned int buffer[BUFFSIZE]; unsigned char head; unsigned char tail; unsigned char lostflag; unsigned char lastcontact; wait_queue_head_t wait; struct fasync_struct *fasyncptr; int active; #ifdef DEBUG_TIMING_PARAMS unsigned short orig_scanint; unsigned short orig_settletime; unsigned int dropped_count; unsigned int overflow_count; unsigned int incompletescan_count; unsigned int dualpagevalid_count; unsigned int unhandledint_count; #endif }; static struct tpanel_status tpanel; static void tpanel_add_data(unsigned int data1, unsigned int data2) { unsigned int errcnt; tpanel.buffer[tpanel.head++] = data1 | ((unsigned int)tpanel.lostflag << 28); tpanel.buffer[tpanel.head++] = data2; tpanel.head &= (BUFFSIZE - 1); if (tpanel.head == tpanel.tail) { errcnt = tpanel.buffer[tpanel.tail] & 0xff; if (!(tpanel.buffer[tpanel.tail] & 0x80000000)) { errcnt++; if (!(errcnt & 0xff)) errcnt = 0xff; } errcnt |= tpanel.buffer[tpanel.tail] & 0x10000000; tpanel.tail += 2; tpanel.tail &= (BUFFSIZE - 1); tpanel.buffer[tpanel.tail] = (tpanel.buffer[tpanel.tail] & 0xffffff00) | 0x20000000 | errcnt; #ifdef DEBUG_TIMING_PARAMS tpanel.dropped_count++; #endif } tpanel.lostflag = 0; } static void tpanel_interrupt(int irq, void *dev_id, struct pt_regs *regs) { unsigned int cschange = 0, data1 = 0, data2 = 0; unsigned short intreg, pendown; *VR41XX_PIUCNTREG = 0x0326; // sometimes bits get reset in this reg barrier(); // so we just reinitialize it while ((intreg = (*VR41XX_PIUINTREG & 0x807d)) || cschange) { pendown = *VR41XX_PIUCNTREG & 0x2000; if (tpanel.lastcontact != (pendown >> 13)) { cschange = 1; #ifdef CONFIG_CPU_VR4181 // VR4181 has extra clock mask bits, but they apply to AIU also // MFK: so add in coordination with AIU... // these extra bits only have to be on for actual scanning // not for wait-for-pen-touch if (pendown) { *VR41XX_CMUCLKMSK |= VR41XX_CMUCLKMSK_MSKADUPCLK | VR41XX_CMUCLKMSK_MSKADU18M; } else { *VR41XX_CMUCLKMSK &= ~(VR41XX_CMUCLKMSK_MSKADUPCLK | VR41XX_CMUCLKMSK_MSKADU18M); } #endif } tpanel.lastcontact = pendown >> 13; if (intreg & VR41XX_PIUINTREG_PENCHG) { *VR41XX_PIUINTREG = VR41XX_PIUINTREG_PENCHG; } // PADDLOST interrupt: datapoint lost, just report it // (after processing data and pen-down) if (((intreg & (VR41XX_PIUINTREG_PADDLOST | VR41XX_PIUINTREG_PADPAGE0 | VR41XX_PIUINTREG_PADPAGE1)) == VR41XX_PIUINTREG_PADDLOST) && !(cschange && pendown)) { *VR41XX_PIUINTREG = VR41XX_PIUINTREG_PADDLOST; barrier(); *VR41XX_PIUCNTREG = 0x0022; barrier(); pendown = *VR41XX_PIUCNTREG & 0x2000; if (tpanel.lastcontact != (pendown >> 13)) cschange = 1; tpanel.lastcontact = pendown >> 13; if (pendown) { *VR41XX_PIUCNTREG = 0x0062; barrier(); *VR41XX_PIUCNTREG = 0x0066; // force manual scan } else { *VR41XX_PIUCNTREG = 0x0322; barrier(); *VR41XX_PIUCNTREG = 0x0326; // resume autoscan } tpanel.lostflag = 1; #ifdef DEBUG_TIMING_PARAMS if ((intreg & (VR41XX_PIUINTREG_PADPAGE1 | VR41XX_PIUINTREG_PADPAGE0)) == (VR41XX_PIUINTREG_PADPAGE1 | VR41XX_PIUINTREG_PADPAGE0)) tpanel.overflow_count++; else tpanel.incompletescan_count++; #endif } // Panel contact state change interrupt // If pen-up, do data ints first if (cschange && (pendown || !(intreg & (VR41XX_PIUINTREG_PADPAGE0 | VR41XX_PIUINTREG_PADPAGE1)))) { data1 = (pendown | 0x4000) << 17; tpanel_add_data(data1, 0); cschange = 0; } // PADCMD and PADADP shouldn't happen if (intreg & (VR41XX_PIUINTREG_PADCMD | VR41XX_PIUINTREG_PADADP)) { *VR41XX_PIUINTREG = VR41XX_PIUINTREG_PADCMD | VR41XX_PIUINTREG_PADADP; #ifdef DEBUG_TIMING_PARAMS tpanel.unhandledint_count++; #endif } // Data buffer interrupt if (intreg & VR41XX_PIUINTREG_PADPAGE0) { #ifdef DEBUG_TIMING_PARAMS if (intreg & VR41XX_PIUINTREG_PADPAGE1) tpanel.dualpagevalid_count++; #endif if ((intreg & (VR41XX_PIUINTREG_PADPAGE1 | VR41XX_PIUINTREG_OVP)) != (VR41XX_PIUINTREG_PADPAGE1 | VR41XX_PIUINTREG_OVP)) { data1 = 0x40000000 | ((*VR41XX_PIUPB01REG & 0x3ff) << 18) | ((*VR41XX_PIUPB00REG & 0x3ff) << 8); data2 = ( *VR41XX_PIUPB03REG << 20) | ((*VR41XX_PIUPB02REG & 0x3ff) << 10) | ( *VR41XX_PIUPB04REG & 0x3ff); tpanel_add_data(data1, data2); *VR41XX_PIUINTREG = VR41XX_PIUINTREG_PADPAGE0; } } if (intreg & VR41XX_PIUINTREG_PADPAGE1) { data1 = 0x40000000 | ((*VR41XX_PIUPB11REG & 0x3ff) << 18) | ((*VR41XX_PIUPB10REG & 0x3ff) << 8); data2 = ( *VR41XX_PIUPB13REG << 20) | ((*VR41XX_PIUPB12REG & 0x3ff) << 10) | ( *VR41XX_PIUPB14REG & 0x3ff); tpanel_add_data(data1, data2); *VR41XX_PIUINTREG = VR41XX_PIUINTREG_PADPAGE1; } } if (data1) { add_mouse_randomness(data1); if (data2) add_mouse_randomness(data2); wake_up_interruptible(&tpanel.wait); if (tpanel.fasyncptr) kill_fasync(&tpanel.fasyncptr, SIGIO, POLL_IN); } } static int fasync_tpanel(int fd, struct file *filp, int on) { int retval; retval = fasync_helper(fd, filp, on, &tpanel.fasyncptr); if (retval < 0) return retval; return 0; } static int close_tpanel(struct inode * inode, struct file * file) { fasync_tpanel(-1, file, 0); if (--tpanel.active) return 0; // set for standby *VR41XX_MPIUINTREG = 0; *VR41XX_PIUCNTREG = 0; free_irq(VR41XX_IRQ_PIU, NULL); cli(); *VR41XX_CMUCLKMSK &= ~VR41XX_CMUCLKMSK_MSKPIU; #ifdef CONFIG_CPU_VR4181 // MFK: coordinate with AIU driver *VR41XX_CMUCLKMSK &= ~VR41XX_CMUCLKMSK_MSKADUPCLK | VR41XX_CMUCLKMSK_MSKADU18M; #endif sti(); MOD_DEC_USE_COUNT; return 0; } static int open_tpanel(struct inode * inode, struct file * file) { if (tpanel.active++) return 0; cli(); *VR41XX_CMUCLKMSK |= VR41XX_CMUCLKMSK_MSKPIU; sti(); *VR41XX_MPIUINTREG = 0; barrier(); tpanel.tail = tpanel.head; tpanel.lastcontact = 0; // set up for autoscan *VR41XX_PIUCNTREG = 0x0322; // autoscan, sequence suspended *VR41XX_PIUSIVLREG = 333; // set interval to .01 sec default *VR41XX_PIUSTBLREG = 16; barrier(); *VR41XX_MPIUINTREG = 0x007d; *VR41XX_PIUINTREG = 0x007d; // clear any pending ints barrier(); if (request_irq(VR41XX_IRQ_PIU, tpanel_interrupt, SA_SHIRQ, "vr41xxtpanel", NULL)) { tpanel.active--; return -EBUSY; } MOD_INC_USE_COUNT; barrier(); *VR41XX_PIUCNTREG = 0x0326; // autoscan, sequence enabled return 0; } /* * Read touch panel data. * * Data format: * unsigned short status: bit 15 = xyz data valid (0 means contact state only) * bit 14 = pen contact state (1 means contact) * bit 13 = soft data lost flag: if this is 1, this data * is valid (if bit 15 is 1), but data was lost * between this point and the previous point * due to a buffer overrun in the driver * bit 12 = hard data lost flag: if this is 1, this point * is valid (if bit 15 is 1), but data was lost * between this point and the previous point * due to hardware error * bits 11-8 = reserved * bits 7-0 = count of how many data packet lost, if hard * or soft error is falgged * unsigned short x+ raw data (if status:15 = 1) * unsigned short x- raw data (if status:15 = 1) * unsigned short y+ raw data (if status:15 = 1) * unsigned short y- raw data (if status:15 = 1) * unsigned short z (pressure) raw data (if status:15 = 1) * * x+, x-, y+, and y- are limited to range 0-1023 for this hardware. Each +/- * pair is somewhat redundant: the + value can be used as is, but using the * difference (eg. (x+) - (x-)) will produce a more accurate result. Further * manipulation and/or statistical analysis may be required for best accuracy. * * No calibration is done on the driver side, that is expected to be done on * the user side. */ static ssize_t read_tpanel(struct file * file, char * buffer, size_t count, loff_t *ppos) { unsigned int data1, data2; size_t retcnt = 0; if (count < 12) return -EINVAL; // We need to access the circular buffer atomic to // anything else that will read or write it cli(); while (tpanel.head == tpanel.tail) { if (file->f_flags & O_NONBLOCK) { sti(); return -EAGAIN; } interruptible_sleep_on(&tpanel.wait); // sleep_on will sti for us if (signal_pending(current)) return -ERESTARTSYS; cli(); } do { data1 = tpanel.buffer[tpanel.tail++]; data2 = tpanel.buffer[tpanel.tail++]; tpanel.tail &= BUFFSIZE - 1; sti(); if (!access_ok(VERIFY_WRITE, buffer + retcnt, 12)) return -EFAULT; __put_user((((data1 >> 16) & 0xf000) | (data1 & 0x00ff)) ^ 0x8000, (short*)(buffer + retcnt)); __put_user((data1 >> 18) & 0x03ff, (short*)(buffer + retcnt + 2)); __put_user((data1 >> 8) & 0x03ff, (short*)(buffer + retcnt + 4)); __put_user((data2 >> 20) & 0x03ff, (short*)(buffer + retcnt + 6)); __put_user((data2 >> 10) & 0x03ff, (short*)(buffer + retcnt + 8)); __put_user(data2 & 0x03ff, (short*)(buffer + retcnt + 10)); retcnt += 12; if (retcnt > count - 12) return retcnt; cli(); } while (tpanel.head != tpanel.tail); sti(); return retcnt; } static unsigned int poll_tpanel(struct file *file, poll_table * wait) { int comp; poll_wait(file, &tpanel.wait, wait); cli(); comp = (tpanel.head != tpanel.tail); sti(); if (comp) return POLLIN | POLLRDNORM; return 0; } static int ioctl_tpanel(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { struct scanparam *sparm; unsigned int i1, i2; sparm = (struct scanparam *)arg; switch (cmd) { case TPGETSCANPARM: i1 = *VR41XX_PIUSIVLREG * 30; // PIUSIVLREG is in 30us units if (put_user(i1, &sparm->interval)) return -EFAULT; i2 = *VR41XX_PIUSTBLREG * 30; // PIUSTBLREG is in 30us units if (put_user(i2, &sparm->settletime)) return -EFAULT; return 0; case TPSETSCANPARM: if (get_user(i1, &sparm->interval)) return -EFAULT; i1 = (i1 + 15) / 30; if (i1 > 0x07ff) return -EINVAL; if (get_user(i2, &sparm->settletime)) return -EFAULT; i2 = (i2 + 15) / 30; if (i2 > 0x003f) return -EINVAL; *VR41XX_PIUSIVLREG = (unsigned short)i1; *VR41XX_PIUSTBLREG = (unsigned short)i2; __put_user(i1 * 30, &sparm->interval); __put_user(i2 * 30, &sparm->settletime); return 0; default: return -EINVAL; } } struct file_operations vr41xx_tpanel_fops = { read: read_tpanel, poll: poll_tpanel, ioctl: ioctl_tpanel, open: open_tpanel, release: close_tpanel, fasync: fasync_tpanel, }; static struct miscdevice vr41xx_tpanel = { VR41XX_TPANEL_MINOR, "vr41xxtpanel", &vr41xx_tpanel_fops }; #ifdef DEBUG_TIMING_PARAMS static int get_piuinfo(char *page, char **start, off_t off, int count, int *eof, void *data) { int len; // return lots of nifty diagnostic info // buffer is PAGE_SIZE long, we're OK for all page sizes VR41xx supports len = sprintf(page, "piucntreg: 0x%04hx\n" "piusivlreg: 0x%04hx\n" "piustblreg: 0x%04hx\n" "orig piusivlreg: 0x%04hx\n" "orig piustblreg: 0x%04hx\n" "dropped data count: %d\n" "page overflow count: %d\n" "bad scan count: %d\n" "dual page count: %d\n" "unhandled int count: %d\n", *VR41XX_PIUCNTREG, *VR41XX_PIUSIVLREG, *VR41XX_PIUSTBLREG, tpanel.orig_scanint, tpanel.orig_settletime, tpanel.dropped_count, tpanel.overflow_count, tpanel.incompletescan_count, tpanel.dualpagevalid_count, tpanel.unhandledint_count); if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len > count) len = count; if (len < 0) len = 0; return len; } #endif #ifdef CONFIG_PM static int pm_tpanel_request(struct pm_dev *dev, pm_request_t rqst, void *data) { static unsigned short piu_state[4]; switch (rqst) { case PM_SUSPEND: piu_state[0] = *VR41XX_PIUCNTREG; piu_state[1] = *VR41XX_PIUSIVLREG; piu_state[2] = *VR41XX_PIUSTBLREG; piu_state[3] = *VR41XX_MPIUINTREG; break; case PM_RESUME: *VR41XX_PIUCNTREG = piu_state[0]; *VR41XX_PIUSIVLREG = piu_state[1]; *VR41XX_PIUSTBLREG = piu_state[2]; *VR41XX_PIUINTREG = ~0; barrier(); *VR41XX_MPIUINTREG = piu_state[3]; break; } return 0; } #endif int __init vr41xx_tpanel_init(void) { #ifdef DEBUG_TIMING_PARAMS *VR41XX_CMUCLKMSK |= VR41XX_CMUCLKMSK_MSKPIU; barrier(); tpanel.orig_scanint = *VR41XX_PIUSIVLREG; tpanel.orig_settletime = *VR41XX_PIUSTBLREG; tpanel.dropped_count = 0; tpanel.overflow_count = 0; tpanel.incompletescan_count = 0; tpanel.dualpagevalid_count = 0; tpanel.unhandledint_count = 0; create_proc_read_entry("piuinfo", 0, NULL, get_piuinfo, NULL); #endif // power down outputs and reset the PIU *VR41XX_PIUCNTREG = VR41XX_PIUCNTREG_PIUPWR; tpanel.active = 0; tpanel.head = tpanel.tail = 0; init_waitqueue_head(&tpanel.wait); tpanel.fasyncptr = NULL; printk(KERN_INFO "VR41xx touch panel initialized, using IRQ %d version "TPANEL_VERSION"\n", VR41XX_IRQ_PIU); misc_register(&vr41xx_tpanel); #ifdef CONFIG_PM pm_register(PM_ISA_DEV, PM_SYS_UNKNOWN, pm_tpanel_request); #endif // clocks remained masked until the touch panel device is opened *VR41XX_CMUCLKMSK &= ~VR41XX_CMUCLKMSK_MSKPIU; #ifdef CONFIG_CPU_VR4181 // MFK: coordinate with AIU *VR41XX_CMUCLKMSK &= ~VR41XX_CMUCLKMSK_MSKADUPCLK | VR41XX_CMUCLKMSK_MSKADU18M; #endif return 0; } #ifdef MODULE int init_module(void) { return vr41xx_tpanel_init(); } void cleanup_module(void) { misc_deregister(&vr41xx_tpanel); } #endif Index: Makefile =================================================================== RCS file: /cvsroot/linux-mips/linux/drivers/char/Makefile,v retrieving revision 1.26 retrieving revision 1.27 diff -u -d -r1.26 -r1.27 --- Makefile 26 Feb 2002 17:46:07 -0000 1.26 +++ Makefile 7 Mar 2002 09:15:16 -0000 1.27 @@ -258,6 +258,10 @@ obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_VR41XX_WDT) += vr41xxwdt.o +obj-$(CONFIG_BUTTONS) += buttons.o +obj-$(CONFIG_VR41XX_GPIO_BUTTONS) += gpiobtns.o +obj-$(CONFIG_VR41XX_E105_BUTTONS) += e105btns.o +obj-$(CONFIG_TOUCH_PANEL) += tpanel.o subdir-$(CONFIG_MWAVE) += mwave ifeq ($(CONFIG_MWAVE),y) Index: misc.c =================================================================== RCS file: /cvsroot/linux-mips/linux/drivers/char/misc.c,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- misc.c 6 Nov 2001 09:10:21 -0000 1.5 +++ misc.c 7 Mar 2002 09:15:16 -0000 1.6 @@ -66,6 +66,8 @@ static unsigned char misc_minors[DYNAMIC_MINORS / 8]; extern int psaux_init(void); +extern int vr41xx_tpanel_init(void); +extern int vr41xx_buttons_init(void); #ifdef CONFIG_SGI_NEWPORT_GFX extern void gfx_register(void); #endif @@ -253,6 +255,16 @@ int __init misc_init(void) { create_proc_read_entry("misc", 0, 0, misc_read_proc, NULL); +#ifdef CONFIG_TOUCH_PANEL +#if defined(CONFIG_CPU_VR41XX) && !defined(CONFIG_HAS_VRC4173) + vr41xx_tpanel_init(); +#endif +#endif +#ifdef CONFIG_BUTTONS +#ifdef CONFIG_CPU_VR41XX + vr41xx_buttons_init(); +#endif +#endif #ifdef CONFIG_MVME16x rtc_MK48T08_init(); #endif |