mk712.c (5688B)
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 44MODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>"); 45MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver"); 46MODULE_LICENSE("GPL"); 47 48static unsigned int mk712_io = 0x260; /* Also 0x200, 0x208, 0x300 */ 49module_param_hw_named(io, mk712_io, uint, ioport, 0); 50MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller"); 51 52static unsigned int mk712_irq = 10; /* Also 12, 14, 15 */ 53module_param_hw_named(irq, mk712_irq, uint, irq, 0); 54MODULE_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 75static struct input_dev *mk712_dev; 76static DEFINE_SPINLOCK(mk712_lock); 77 78static 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 117static 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 137static 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 148static 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 207static 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 214module_init(mk712_init); 215module_exit(mk712_exit);