1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ICS MK712 touchscreen controller driver 4 * 5 * Copyright (c) 1999-2002 Transmeta Corporation 6 * Copyright (c) 2005 Rick Koch <n1gp@hotmail.com> 7 * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> 8 */ 9 10 11 /* 12 * This driver supports the ICS MicroClock MK712 TouchScreen controller, 13 * found in Gateway AOL Connected Touchpad computers. 14 * 15 * Documentation for ICS MK712 can be found at: 16 * https://www.idt.com/general-parts/mk712-touch-screen-controller 17 */ 18 19 /* 20 * 1999-12-18: original version, Daniel Quinlan 21 * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll 22 * to use queue_empty, Nathan Laredo 23 * 1999-12-20: improved random point rejection, Nathan Laredo 24 * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed 25 * queue code, added module options, other fixes, Daniel Quinlan 26 * 2002-03-15: Clean up for kernel merge <alan@redhat.com> 27 * Fixed multi open race, fixed memory checks, fixed resource 28 * allocation, fixed close/powerdown bug, switched to new init 29 * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch 30 * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik 31 * 32 */ 33 34 #include <linux/module.h> 35 #include <linux/kernel.h> 36 #include <linux/init.h> 37 #include <linux/errno.h> 38 #include <linux/delay.h> 39 #include <linux/ioport.h> 40 #include <linux/interrupt.h> 41 #include <linux/input.h> 42 #include <asm/io.h> 43 44 MODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>"); 45 MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver"); 46 MODULE_LICENSE("GPL"); 47 48 static unsigned int mk712_io = 0x260; /* Also 0x200, 0x208, 0x300 */ 49 module_param_hw_named(io, mk712_io, uint, ioport, 0); 50 MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller"); 51 52 static unsigned int mk712_irq = 10; /* Also 12, 14, 15 */ 53 module_param_hw_named(irq, mk712_irq, uint, irq, 0); 54 MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller"); 55 56 /* eight 8-bit registers */ 57 #define MK712_STATUS 0 58 #define MK712_X 2 59 #define MK712_Y 4 60 #define MK712_CONTROL 6 61 #define MK712_RATE 7 62 63 /* status */ 64 #define MK712_STATUS_TOUCH 0x10 65 #define MK712_CONVERSION_COMPLETE 0x80 66 67 /* control */ 68 #define MK712_ENABLE_INT 0x01 69 #define MK712_INT_ON_CONVERSION_COMPLETE 0x02 70 #define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS 0x04 71 #define MK712_ENABLE_PERIODIC_CONVERSIONS 0x10 72 #define MK712_READ_ONE_POINT 0x20 73 #define MK712_POWERUP 0x40 74 75 static struct input_dev *mk712_dev; 76 static DEFINE_SPINLOCK(mk712_lock); 77 78 static irqreturn_t mk712_interrupt(int irq, void *dev_id) 79 { 80 unsigned char status; 81 static int debounce = 1; 82 static unsigned short last_x; 83 static unsigned short last_y; 84 85 spin_lock(&mk712_lock); 86 87 status = inb(mk712_io + MK712_STATUS); 88 89 if (~status & MK712_CONVERSION_COMPLETE) { 90 debounce = 1; 91 goto end; 92 } 93 94 if (~status & MK712_STATUS_TOUCH) { 95 debounce = 1; 96 input_report_key(mk712_dev, BTN_TOUCH, 0); 97 goto end; 98 } 99 100 if (debounce) { 101 debounce = 0; 102 goto end; 103 } 104 105 input_report_key(mk712_dev, BTN_TOUCH, 1); 106 input_report_abs(mk712_dev, ABS_X, last_x); 107 input_report_abs(mk712_dev, ABS_Y, last_y); 108 109 end: 110 last_x = inw(mk712_io + MK712_X) & 0x0fff; 111 last_y = inw(mk712_io + MK712_Y) & 0x0fff; 112 input_sync(mk712_dev); 113 spin_unlock(&mk712_lock); 114 return IRQ_HANDLED; 115 } 116 117 static int mk712_open(struct input_dev *dev) 118 { 119 unsigned long flags; 120 121 spin_lock_irqsave(&mk712_lock, flags); 122 123 outb(0, mk712_io + MK712_CONTROL); /* Reset */ 124 125 outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE | 126 MK712_INT_ON_CHANGE_IN_TOUCH_STATUS | 127 MK712_ENABLE_PERIODIC_CONVERSIONS | 128 MK712_POWERUP, mk712_io + MK712_CONTROL); 129 130 outb(10, mk712_io + MK712_RATE); /* 187 points per second */ 131 132 spin_unlock_irqrestore(&mk712_lock, flags); 133 134 return 0; 135 } 136 137 static void mk712_close(struct input_dev *dev) 138 { 139 unsigned long flags; 140 141 spin_lock_irqsave(&mk712_lock, flags); 142 143 outb(0, mk712_io + MK712_CONTROL); 144 145 spin_unlock_irqrestore(&mk712_lock, flags); 146 } 147 148 static int __init mk712_init(void) 149 { 150 int err; 151 152 if (!request_region(mk712_io, 8, "mk712")) { 153 printk(KERN_WARNING "mk712: unable to get IO region\n"); 154 return -ENODEV; 155 } 156 157 outb(0, mk712_io + MK712_CONTROL); 158 159 if ((inw(mk712_io + MK712_X) & 0xf000) || /* Sanity check */ 160 (inw(mk712_io + MK712_Y) & 0xf000) || 161 (inw(mk712_io + MK712_STATUS) & 0xf333)) { 162 printk(KERN_WARNING "mk712: device not present\n"); 163 err = -ENODEV; 164 goto fail1; 165 } 166 167 mk712_dev = input_allocate_device(); 168 if (!mk712_dev) { 169 printk(KERN_ERR "mk712: not enough memory\n"); 170 err = -ENOMEM; 171 goto fail1; 172 } 173 174 mk712_dev->name = "ICS MicroClock MK712 TouchScreen"; 175 mk712_dev->phys = "isa0260/input0"; 176 mk712_dev->id.bustype = BUS_ISA; 177 mk712_dev->id.vendor = 0x0005; 178 mk712_dev->id.product = 0x0001; 179 mk712_dev->id.version = 0x0100; 180 181 mk712_dev->open = mk712_open; 182 mk712_dev->close = mk712_close; 183 184 mk712_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 185 mk712_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 186 input_set_abs_params(mk712_dev, ABS_X, 0, 0xfff, 88, 0); 187 input_set_abs_params(mk712_dev, ABS_Y, 0, 0xfff, 88, 0); 188 189 if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) { 190 printk(KERN_WARNING "mk712: unable to get IRQ\n"); 191 err = -EBUSY; 192 goto fail1; 193 } 194 195 err = input_register_device(mk712_dev); 196 if (err) 197 goto fail2; 198 199 return 0; 200 201 fail2: free_irq(mk712_irq, mk712_dev); 202 fail1: input_free_device(mk712_dev); 203 release_region(mk712_io, 8); 204 return err; 205 } 206 207 static void __exit mk712_exit(void) 208 { 209 input_unregister_device(mk712_dev); 210 free_irq(mk712_irq, mk712_dev); 211 release_region(mk712_io, 8); 212 } 213 214 module_init(mk712_init); 215 module_exit(mk712_exit); 216