1 /* 2 * $Id: pc110pad.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $ 3 * 4 * Copyright (c) 2000-2001 Vojtech Pavlik 5 * 6 * Based on the work of: 7 * Alan Cox Robin O'Leary 8 */ 9 10 /* 11 * IBM PC110 touchpad driver for Linux 12 */ 13 14 /* 15 * This program is free software; you can redistribute it and/or modify 16 * it under the terms of the GNU General Public License as published by 17 * the Free Software Foundation; either version 2 of the License, or 18 * (at your option) any later version. 19 * 20 * This program is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * GNU General Public License for more details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with this program; if not, write to the Free Software 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 * 29 * Should you need to contact me, the author, you can do so either by 30 * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: 31 * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic 32 */ 33 34 #include <linux/module.h> 35 #include <linux/kernel.h> 36 #include <linux/errno.h> 37 #include <linux/ioport.h> 38 #include <linux/input.h> 39 #include <linux/init.h> 40 #include <linux/interrupt.h> 41 #include <linux/pci.h> 42 #include <linux/delay.h> 43 44 #include <asm/io.h> 45 #include <asm/irq.h> 46 47 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 48 MODULE_DESCRIPTION("IBM PC110 touchpad driver"); 49 MODULE_LICENSE("GPL"); 50 51 #define PC110PAD_OFF 0x30 52 #define PC110PAD_ON 0x38 53 54 static int pc110pad_irq = 10; 55 static int pc110pad_io = 0x15e0; 56 57 static struct input_dev *pc110pad_dev; 58 static int pc110pad_data[3]; 59 static int pc110pad_count; 60 61 static irqreturn_t pc110pad_interrupt(int irq, void *ptr) 62 { 63 int value = inb_p(pc110pad_io); 64 int handshake = inb_p(pc110pad_io + 2); 65 66 outb(handshake | 1, pc110pad_io + 2); 67 udelay(2); 68 outb(handshake & ~1, pc110pad_io + 2); 69 udelay(2); 70 inb_p(0x64); 71 72 pc110pad_data[pc110pad_count++] = value; 73 74 if (pc110pad_count < 3) 75 return IRQ_HANDLED; 76 77 input_report_key(pc110pad_dev, BTN_TOUCH, 78 pc110pad_data[0] & 0x01); 79 input_report_abs(pc110pad_dev, ABS_X, 80 pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100)); 81 input_report_abs(pc110pad_dev, ABS_Y, 82 pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80)); 83 input_sync(pc110pad_dev); 84 85 pc110pad_count = 0; 86 return IRQ_HANDLED; 87 } 88 89 static void pc110pad_close(struct input_dev *dev) 90 { 91 outb(PC110PAD_OFF, pc110pad_io + 2); 92 } 93 94 static int pc110pad_open(struct input_dev *dev) 95 { 96 pc110pad_interrupt(0, NULL); 97 pc110pad_interrupt(0, NULL); 98 pc110pad_interrupt(0, NULL); 99 outb(PC110PAD_ON, pc110pad_io + 2); 100 pc110pad_count = 0; 101 102 return 0; 103 } 104 105 /* 106 * We try to avoid enabling the hardware if it's not 107 * there, but we don't know how to test. But we do know 108 * that the PC110 is not a PCI system. So if we find any 109 * PCI devices in the machine, we don't have a PC110. 110 */ 111 static int __init pc110pad_init(void) 112 { 113 struct pci_dev *dev; 114 int err; 115 116 dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); 117 if (dev) { 118 pci_dev_put(dev); 119 return -ENODEV; 120 } 121 122 if (!request_region(pc110pad_io, 4, "pc110pad")) { 123 printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n", 124 pc110pad_io, pc110pad_io + 4); 125 return -EBUSY; 126 } 127 128 outb(PC110PAD_OFF, pc110pad_io + 2); 129 130 if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) { 131 printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq); 132 err = -EBUSY; 133 goto err_release_region; 134 } 135 136 pc110pad_dev = input_allocate_device(); 137 if (!pc110pad_dev) { 138 printk(KERN_ERR "pc110pad: Not enough memory.\n"); 139 err = -ENOMEM; 140 goto err_free_irq; 141 } 142 143 pc110pad_dev->name = "IBM PC110 TouchPad"; 144 pc110pad_dev->phys = "isa15e0/input0"; 145 pc110pad_dev->id.bustype = BUS_ISA; 146 pc110pad_dev->id.vendor = 0x0003; 147 pc110pad_dev->id.product = 0x0001; 148 pc110pad_dev->id.version = 0x0100; 149 150 pc110pad_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 151 pc110pad_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y); 152 pc110pad_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 153 154 pc110pad_dev->absmax[ABS_X] = 0x1ff; 155 pc110pad_dev->absmax[ABS_Y] = 0x0ff; 156 157 pc110pad_dev->open = pc110pad_open; 158 pc110pad_dev->close = pc110pad_close; 159 160 err = input_register_device(pc110pad_dev); 161 if (err) 162 goto err_free_dev; 163 164 return 0; 165 166 err_free_dev: 167 input_free_device(pc110pad_dev); 168 err_free_irq: 169 free_irq(pc110pad_irq, NULL); 170 err_release_region: 171 release_region(pc110pad_io, 4); 172 173 return err; 174 } 175 176 static void __exit pc110pad_exit(void) 177 { 178 outb(PC110PAD_OFF, pc110pad_io + 2); 179 free_irq(pc110pad_irq, NULL); 180 input_unregister_device(pc110pad_dev); 181 release_region(pc110pad_io, 4); 182 } 183 184 module_init(pc110pad_init); 185 module_exit(pc110pad_exit); 186