^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (c) 2000-2001 Vojtech Pavlik
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Based on the work of:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Alan Cox Robin O'Leary
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * IBM PC110 touchpad driver for Linux
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/ioport.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/input.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/interrupt.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/pci.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <asm/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <asm/irq.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) MODULE_DESCRIPTION("IBM PC110 touchpad driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #define PC110PAD_OFF 0x30
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #define PC110PAD_ON 0x38
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) static int pc110pad_irq = 10;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) static int pc110pad_io = 0x15e0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) static struct input_dev *pc110pad_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) static int pc110pad_data[3];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) static int pc110pad_count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) static irqreturn_t pc110pad_interrupt(int irq, void *ptr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) int value = inb_p(pc110pad_io);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) int handshake = inb_p(pc110pad_io + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) outb(handshake | 1, pc110pad_io + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) udelay(2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) outb(handshake & ~1, pc110pad_io + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) udelay(2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) inb_p(0x64);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) pc110pad_data[pc110pad_count++] = value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) if (pc110pad_count < 3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) return IRQ_HANDLED;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) input_report_key(pc110pad_dev, BTN_TOUCH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) pc110pad_data[0] & 0x01);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) input_report_abs(pc110pad_dev, ABS_X,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) input_report_abs(pc110pad_dev, ABS_Y,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) input_sync(pc110pad_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) pc110pad_count = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) return IRQ_HANDLED;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) static void pc110pad_close(struct input_dev *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) outb(PC110PAD_OFF, pc110pad_io + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) static int pc110pad_open(struct input_dev *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) pc110pad_interrupt(0, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) pc110pad_interrupt(0, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) pc110pad_interrupt(0, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) outb(PC110PAD_ON, pc110pad_io + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) pc110pad_count = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) * We try to avoid enabling the hardware if it's not
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) * there, but we don't know how to test. But we do know
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) * that the PC110 is not a PCI system. So if we find any
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) * PCI devices in the machine, we don't have a PC110.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) static int __init pc110pad_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) if (!no_pci_devices())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) if (!request_region(pc110pad_io, 4, "pc110pad")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) pc110pad_io, pc110pad_io + 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) outb(PC110PAD_OFF, pc110pad_io + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) err = -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) goto err_release_region;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) pc110pad_dev = input_allocate_device();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) if (!pc110pad_dev) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) printk(KERN_ERR "pc110pad: Not enough memory.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) err = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) goto err_free_irq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) pc110pad_dev->name = "IBM PC110 TouchPad";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) pc110pad_dev->phys = "isa15e0/input0";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) pc110pad_dev->id.bustype = BUS_ISA;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) pc110pad_dev->id.vendor = 0x0003;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) pc110pad_dev->id.product = 0x0001;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) pc110pad_dev->id.version = 0x0100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) pc110pad_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) pc110pad_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) pc110pad_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) input_abs_set_max(pc110pad_dev, ABS_X, 0x1ff);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) input_abs_set_max(pc110pad_dev, ABS_Y, 0x0ff);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) pc110pad_dev->open = pc110pad_open;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) pc110pad_dev->close = pc110pad_close;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) err = input_register_device(pc110pad_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) goto err_free_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) err_free_dev:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) input_free_device(pc110pad_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) err_free_irq:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) free_irq(pc110pad_irq, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) err_release_region:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) release_region(pc110pad_io, 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) static void __exit pc110pad_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) outb(PC110PAD_OFF, pc110pad_io + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) free_irq(pc110pad_irq, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) input_unregister_device(pc110pad_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) release_region(pc110pad_io, 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) module_init(pc110pad_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) module_exit(pc110pad_exit);