1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Copyright (c) 1999-2001 Vojtech Pavlik
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Based on the work of:
61da177e4SLinus Torvalds * Teemu Rantanen Derrick Cole
71da177e4SLinus Torvalds * Peter Cervasio Christoph Niemann
81da177e4SLinus Torvalds * Philip Blundell Russell King
91da177e4SLinus Torvalds * Bob Harris
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds /*
131da177e4SLinus Torvalds * Inport (ATI XL and Microsoft) busmouse driver for Linux
141da177e4SLinus Torvalds */
151da177e4SLinus Torvalds
161da177e4SLinus Torvalds #include <linux/module.h>
171da177e4SLinus Torvalds #include <linux/ioport.h>
181da177e4SLinus Torvalds #include <linux/init.h>
191da177e4SLinus Torvalds #include <linux/interrupt.h>
201da177e4SLinus Torvalds #include <linux/input.h>
211da177e4SLinus Torvalds
221da177e4SLinus Torvalds #include <asm/io.h>
231da177e4SLinus Torvalds #include <asm/irq.h>
241da177e4SLinus Torvalds
251da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
261da177e4SLinus Torvalds MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver");
271da177e4SLinus Torvalds MODULE_LICENSE("GPL");
281da177e4SLinus Torvalds
291da177e4SLinus Torvalds #define INPORT_BASE 0x23c
301da177e4SLinus Torvalds #define INPORT_EXTENT 4
311da177e4SLinus Torvalds
321da177e4SLinus Torvalds #define INPORT_CONTROL_PORT INPORT_BASE + 0
331da177e4SLinus Torvalds #define INPORT_DATA_PORT INPORT_BASE + 1
341da177e4SLinus Torvalds #define INPORT_SIGNATURE_PORT INPORT_BASE + 2
351da177e4SLinus Torvalds
361da177e4SLinus Torvalds #define INPORT_REG_BTNS 0x00
371da177e4SLinus Torvalds #define INPORT_REG_X 0x01
381da177e4SLinus Torvalds #define INPORT_REG_Y 0x02
391da177e4SLinus Torvalds #define INPORT_REG_MODE 0x07
401da177e4SLinus Torvalds #define INPORT_RESET 0x80
411da177e4SLinus Torvalds
428370a643SRobert P. J. Day #ifdef CONFIG_MOUSE_ATIXL
431da177e4SLinus Torvalds #define INPORT_NAME "ATI XL Mouse"
441da177e4SLinus Torvalds #define INPORT_VENDOR 0x0002
451da177e4SLinus Torvalds #define INPORT_SPEED_30HZ 0x01
461da177e4SLinus Torvalds #define INPORT_SPEED_50HZ 0x02
471da177e4SLinus Torvalds #define INPORT_SPEED_100HZ 0x03
481da177e4SLinus Torvalds #define INPORT_SPEED_200HZ 0x04
491da177e4SLinus Torvalds #define INPORT_MODE_BASE INPORT_SPEED_100HZ
501da177e4SLinus Torvalds #define INPORT_MODE_IRQ 0x08
511da177e4SLinus Torvalds #else
521da177e4SLinus Torvalds #define INPORT_NAME "Microsoft InPort Mouse"
531da177e4SLinus Torvalds #define INPORT_VENDOR 0x0001
541da177e4SLinus Torvalds #define INPORT_MODE_BASE 0x10
551da177e4SLinus Torvalds #define INPORT_MODE_IRQ 0x01
561da177e4SLinus Torvalds #endif
571da177e4SLinus Torvalds #define INPORT_MODE_HOLD 0x20
581da177e4SLinus Torvalds
591da177e4SLinus Torvalds #define INPORT_IRQ 5
601da177e4SLinus Torvalds
611da177e4SLinus Torvalds static int inport_irq = INPORT_IRQ;
62f6b12d04SDavid Howells module_param_hw_named(irq, inport_irq, uint, irq, 0);
631da177e4SLinus Torvalds MODULE_PARM_DESC(irq, "IRQ number (5=default)");
641da177e4SLinus Torvalds
652e5b636bSDmitry Torokhov static struct input_dev *inport_dev;
662e5b636bSDmitry Torokhov
inport_interrupt(int irq,void * dev_id)677d12e780SDavid Howells static irqreturn_t inport_interrupt(int irq, void *dev_id)
682e5b636bSDmitry Torokhov {
692e5b636bSDmitry Torokhov unsigned char buttons;
702e5b636bSDmitry Torokhov
712e5b636bSDmitry Torokhov outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
722e5b636bSDmitry Torokhov outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
732e5b636bSDmitry Torokhov
742e5b636bSDmitry Torokhov outb(INPORT_REG_X, INPORT_CONTROL_PORT);
752e5b636bSDmitry Torokhov input_report_rel(inport_dev, REL_X, inb(INPORT_DATA_PORT));
762e5b636bSDmitry Torokhov
772e5b636bSDmitry Torokhov outb(INPORT_REG_Y, INPORT_CONTROL_PORT);
782e5b636bSDmitry Torokhov input_report_rel(inport_dev, REL_Y, inb(INPORT_DATA_PORT));
792e5b636bSDmitry Torokhov
802e5b636bSDmitry Torokhov outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT);
812e5b636bSDmitry Torokhov buttons = inb(INPORT_DATA_PORT);
822e5b636bSDmitry Torokhov
832e5b636bSDmitry Torokhov input_report_key(inport_dev, BTN_MIDDLE, buttons & 1);
842e5b636bSDmitry Torokhov input_report_key(inport_dev, BTN_LEFT, buttons & 2);
852e5b636bSDmitry Torokhov input_report_key(inport_dev, BTN_RIGHT, buttons & 4);
862e5b636bSDmitry Torokhov
872e5b636bSDmitry Torokhov outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
882e5b636bSDmitry Torokhov outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
892e5b636bSDmitry Torokhov
902e5b636bSDmitry Torokhov input_sync(inport_dev);
912e5b636bSDmitry Torokhov return IRQ_HANDLED;
922e5b636bSDmitry Torokhov }
931da177e4SLinus Torvalds
inport_open(struct input_dev * dev)941da177e4SLinus Torvalds static int inport_open(struct input_dev *dev)
951da177e4SLinus Torvalds {
961da177e4SLinus Torvalds if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL))
971da177e4SLinus Torvalds return -EBUSY;
981da177e4SLinus Torvalds outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
991da177e4SLinus Torvalds outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
1001da177e4SLinus Torvalds
1011da177e4SLinus Torvalds return 0;
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds
inport_close(struct input_dev * dev)1041da177e4SLinus Torvalds static void inport_close(struct input_dev *dev)
1051da177e4SLinus Torvalds {
1061da177e4SLinus Torvalds outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
1071da177e4SLinus Torvalds outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
1081da177e4SLinus Torvalds free_irq(inport_irq, NULL);
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds
inport_init(void)1111da177e4SLinus Torvalds static int __init inport_init(void)
1121da177e4SLinus Torvalds {
1131da177e4SLinus Torvalds unsigned char a, b, c;
11472155615SDmitry Torokhov int err;
1151da177e4SLinus Torvalds
1161da177e4SLinus Torvalds if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) {
1171da177e4SLinus Torvalds printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE);
1181da177e4SLinus Torvalds return -EBUSY;
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds
1211da177e4SLinus Torvalds a = inb(INPORT_SIGNATURE_PORT);
1221da177e4SLinus Torvalds b = inb(INPORT_SIGNATURE_PORT);
1231da177e4SLinus Torvalds c = inb(INPORT_SIGNATURE_PORT);
1242e5b636bSDmitry Torokhov if (a == b || a != c) {
1252a0f9c4cSHelge Deller printk(KERN_INFO "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE);
12672155615SDmitry Torokhov err = -ENODEV;
12772155615SDmitry Torokhov goto err_release_region;
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds
13072155615SDmitry Torokhov inport_dev = input_allocate_device();
13172155615SDmitry Torokhov if (!inport_dev) {
1322e5b636bSDmitry Torokhov printk(KERN_ERR "inport.c: Not enough memory for input device\n");
13372155615SDmitry Torokhov err = -ENOMEM;
13472155615SDmitry Torokhov goto err_release_region;
1352e5b636bSDmitry Torokhov }
1362e5b636bSDmitry Torokhov
1372e5b636bSDmitry Torokhov inport_dev->name = INPORT_NAME;
1382e5b636bSDmitry Torokhov inport_dev->phys = "isa023c/input0";
1392e5b636bSDmitry Torokhov inport_dev->id.bustype = BUS_ISA;
1402e5b636bSDmitry Torokhov inport_dev->id.vendor = INPORT_VENDOR;
1412e5b636bSDmitry Torokhov inport_dev->id.product = 0x0001;
1422e5b636bSDmitry Torokhov inport_dev->id.version = 0x0100;
1432e5b636bSDmitry Torokhov
1447b19ada2SJiri Slaby inport_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
1457b19ada2SJiri Slaby inport_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
1467b19ada2SJiri Slaby BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
1477b19ada2SJiri Slaby inport_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
1482e5b636bSDmitry Torokhov
1492e5b636bSDmitry Torokhov inport_dev->open = inport_open;
1502e5b636bSDmitry Torokhov inport_dev->close = inport_close;
1512e5b636bSDmitry Torokhov
1521da177e4SLinus Torvalds outb(INPORT_RESET, INPORT_CONTROL_PORT);
1531da177e4SLinus Torvalds outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
1541da177e4SLinus Torvalds outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
1551da177e4SLinus Torvalds
15672155615SDmitry Torokhov err = input_register_device(inport_dev);
15772155615SDmitry Torokhov if (err)
15872155615SDmitry Torokhov goto err_free_dev;
1591da177e4SLinus Torvalds
1601da177e4SLinus Torvalds return 0;
16172155615SDmitry Torokhov
16272155615SDmitry Torokhov err_free_dev:
16372155615SDmitry Torokhov input_free_device(inport_dev);
16472155615SDmitry Torokhov err_release_region:
16572155615SDmitry Torokhov release_region(INPORT_BASE, INPORT_EXTENT);
16672155615SDmitry Torokhov
16772155615SDmitry Torokhov return err;
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds
inport_exit(void)1701da177e4SLinus Torvalds static void __exit inport_exit(void)
1711da177e4SLinus Torvalds {
1722e5b636bSDmitry Torokhov input_unregister_device(inport_dev);
1731da177e4SLinus Torvalds release_region(INPORT_BASE, INPORT_EXTENT);
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds
1761da177e4SLinus Torvalds module_init(inport_init);
1771da177e4SLinus Torvalds module_exit(inport_exit);
178