18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ICS MK712 touchscreen controller driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 1999-2002 Transmeta Corporation 68c2ecf20Sopenharmony_ci * Copyright (c) 2005 Rick Koch <n1gp@hotmail.com> 78c2ecf20Sopenharmony_ci * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * This driver supports the ICS MicroClock MK712 TouchScreen controller, 138c2ecf20Sopenharmony_ci * found in Gateway AOL Connected Touchpad computers. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Documentation for ICS MK712 can be found at: 168c2ecf20Sopenharmony_ci * https://www.idt.com/general-parts/mk712-touch-screen-controller 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * 1999-12-18: original version, Daniel Quinlan 218c2ecf20Sopenharmony_ci * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll 228c2ecf20Sopenharmony_ci * to use queue_empty, Nathan Laredo 238c2ecf20Sopenharmony_ci * 1999-12-20: improved random point rejection, Nathan Laredo 248c2ecf20Sopenharmony_ci * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed 258c2ecf20Sopenharmony_ci * queue code, added module options, other fixes, Daniel Quinlan 268c2ecf20Sopenharmony_ci * 2002-03-15: Clean up for kernel merge <alan@redhat.com> 278c2ecf20Sopenharmony_ci * Fixed multi open race, fixed memory checks, fixed resource 288c2ecf20Sopenharmony_ci * allocation, fixed close/powerdown bug, switched to new init 298c2ecf20Sopenharmony_ci * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch 308c2ecf20Sopenharmony_ci * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/module.h> 358c2ecf20Sopenharmony_ci#include <linux/kernel.h> 368c2ecf20Sopenharmony_ci#include <linux/init.h> 378c2ecf20Sopenharmony_ci#include <linux/errno.h> 388c2ecf20Sopenharmony_ci#include <linux/delay.h> 398c2ecf20Sopenharmony_ci#include <linux/ioport.h> 408c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 418c2ecf20Sopenharmony_ci#include <linux/input.h> 428c2ecf20Sopenharmony_ci#include <asm/io.h> 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>"); 458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver"); 468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic unsigned int mk712_io = 0x260; /* Also 0x200, 0x208, 0x300 */ 498c2ecf20Sopenharmony_cimodule_param_hw_named(io, mk712_io, uint, ioport, 0); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller"); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic unsigned int mk712_irq = 10; /* Also 12, 14, 15 */ 538c2ecf20Sopenharmony_cimodule_param_hw_named(irq, mk712_irq, uint, irq, 0); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller"); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* eight 8-bit registers */ 578c2ecf20Sopenharmony_ci#define MK712_STATUS 0 588c2ecf20Sopenharmony_ci#define MK712_X 2 598c2ecf20Sopenharmony_ci#define MK712_Y 4 608c2ecf20Sopenharmony_ci#define MK712_CONTROL 6 618c2ecf20Sopenharmony_ci#define MK712_RATE 7 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* status */ 648c2ecf20Sopenharmony_ci#define MK712_STATUS_TOUCH 0x10 658c2ecf20Sopenharmony_ci#define MK712_CONVERSION_COMPLETE 0x80 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* control */ 688c2ecf20Sopenharmony_ci#define MK712_ENABLE_INT 0x01 698c2ecf20Sopenharmony_ci#define MK712_INT_ON_CONVERSION_COMPLETE 0x02 708c2ecf20Sopenharmony_ci#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS 0x04 718c2ecf20Sopenharmony_ci#define MK712_ENABLE_PERIODIC_CONVERSIONS 0x10 728c2ecf20Sopenharmony_ci#define MK712_READ_ONE_POINT 0x20 738c2ecf20Sopenharmony_ci#define MK712_POWERUP 0x40 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic struct input_dev *mk712_dev; 768c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(mk712_lock); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic irqreturn_t mk712_interrupt(int irq, void *dev_id) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci unsigned char status; 818c2ecf20Sopenharmony_ci static int debounce = 1; 828c2ecf20Sopenharmony_ci static unsigned short last_x; 838c2ecf20Sopenharmony_ci static unsigned short last_y; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci spin_lock(&mk712_lock); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci status = inb(mk712_io + MK712_STATUS); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (~status & MK712_CONVERSION_COMPLETE) { 908c2ecf20Sopenharmony_ci debounce = 1; 918c2ecf20Sopenharmony_ci goto end; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (~status & MK712_STATUS_TOUCH) { 958c2ecf20Sopenharmony_ci debounce = 1; 968c2ecf20Sopenharmony_ci input_report_key(mk712_dev, BTN_TOUCH, 0); 978c2ecf20Sopenharmony_ci goto end; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (debounce) { 1018c2ecf20Sopenharmony_ci debounce = 0; 1028c2ecf20Sopenharmony_ci goto end; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci input_report_key(mk712_dev, BTN_TOUCH, 1); 1068c2ecf20Sopenharmony_ci input_report_abs(mk712_dev, ABS_X, last_x); 1078c2ecf20Sopenharmony_ci input_report_abs(mk712_dev, ABS_Y, last_y); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci end: 1108c2ecf20Sopenharmony_ci last_x = inw(mk712_io + MK712_X) & 0x0fff; 1118c2ecf20Sopenharmony_ci last_y = inw(mk712_io + MK712_Y) & 0x0fff; 1128c2ecf20Sopenharmony_ci input_sync(mk712_dev); 1138c2ecf20Sopenharmony_ci spin_unlock(&mk712_lock); 1148c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int mk712_open(struct input_dev *dev) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci unsigned long flags; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci spin_lock_irqsave(&mk712_lock, flags); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci outb(0, mk712_io + MK712_CONTROL); /* Reset */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE | 1268c2ecf20Sopenharmony_ci MK712_INT_ON_CHANGE_IN_TOUCH_STATUS | 1278c2ecf20Sopenharmony_ci MK712_ENABLE_PERIODIC_CONVERSIONS | 1288c2ecf20Sopenharmony_ci MK712_POWERUP, mk712_io + MK712_CONTROL); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci outb(10, mk712_io + MK712_RATE); /* 187 points per second */ 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mk712_lock, flags); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void mk712_close(struct input_dev *dev) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci unsigned long flags; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci spin_lock_irqsave(&mk712_lock, flags); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci outb(0, mk712_io + MK712_CONTROL); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mk712_lock, flags); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int __init mk712_init(void) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int err; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (!request_region(mk712_io, 8, "mk712")) { 1538c2ecf20Sopenharmony_ci printk(KERN_WARNING "mk712: unable to get IO region\n"); 1548c2ecf20Sopenharmony_ci return -ENODEV; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci outb(0, mk712_io + MK712_CONTROL); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if ((inw(mk712_io + MK712_X) & 0xf000) || /* Sanity check */ 1608c2ecf20Sopenharmony_ci (inw(mk712_io + MK712_Y) & 0xf000) || 1618c2ecf20Sopenharmony_ci (inw(mk712_io + MK712_STATUS) & 0xf333)) { 1628c2ecf20Sopenharmony_ci printk(KERN_WARNING "mk712: device not present\n"); 1638c2ecf20Sopenharmony_ci err = -ENODEV; 1648c2ecf20Sopenharmony_ci goto fail1; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci mk712_dev = input_allocate_device(); 1688c2ecf20Sopenharmony_ci if (!mk712_dev) { 1698c2ecf20Sopenharmony_ci printk(KERN_ERR "mk712: not enough memory\n"); 1708c2ecf20Sopenharmony_ci err = -ENOMEM; 1718c2ecf20Sopenharmony_ci goto fail1; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci mk712_dev->name = "ICS MicroClock MK712 TouchScreen"; 1758c2ecf20Sopenharmony_ci mk712_dev->phys = "isa0260/input0"; 1768c2ecf20Sopenharmony_ci mk712_dev->id.bustype = BUS_ISA; 1778c2ecf20Sopenharmony_ci mk712_dev->id.vendor = 0x0005; 1788c2ecf20Sopenharmony_ci mk712_dev->id.product = 0x0001; 1798c2ecf20Sopenharmony_ci mk712_dev->id.version = 0x0100; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci mk712_dev->open = mk712_open; 1828c2ecf20Sopenharmony_ci mk712_dev->close = mk712_close; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci mk712_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 1858c2ecf20Sopenharmony_ci mk712_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 1868c2ecf20Sopenharmony_ci input_set_abs_params(mk712_dev, ABS_X, 0, 0xfff, 88, 0); 1878c2ecf20Sopenharmony_ci input_set_abs_params(mk712_dev, ABS_Y, 0, 0xfff, 88, 0); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) { 1908c2ecf20Sopenharmony_ci printk(KERN_WARNING "mk712: unable to get IRQ\n"); 1918c2ecf20Sopenharmony_ci err = -EBUSY; 1928c2ecf20Sopenharmony_ci goto fail1; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci err = input_register_device(mk712_dev); 1968c2ecf20Sopenharmony_ci if (err) 1978c2ecf20Sopenharmony_ci goto fail2; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci fail2: free_irq(mk712_irq, mk712_dev); 2028c2ecf20Sopenharmony_ci fail1: input_free_device(mk712_dev); 2038c2ecf20Sopenharmony_ci release_region(mk712_io, 8); 2048c2ecf20Sopenharmony_ci return err; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void __exit mk712_exit(void) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci input_unregister_device(mk712_dev); 2108c2ecf20Sopenharmony_ci free_irq(mk712_irq, mk712_dev); 2118c2ecf20Sopenharmony_ci release_region(mk712_io, 8); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cimodule_init(mk712_init); 2158c2ecf20Sopenharmony_cimodule_exit(mk712_exit); 216