1/* 2 * ATI XL Bus Mouse Driver for Linux 3 * by Bob Harris (rth@sparta.com) 4 * 5 * Uses VFS interface for linux 0.98 (01OCT92) 6 * 7 * Modified by Chris Colohan (colohan@eecg.toronto.edu) 8 * Modularised 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk> 9 * 10 * Converted to use new generic busmouse code. 5 Apr 1998 11 * Russell King <rmk@arm.uk.linux.org> 12 * 13 * version 0.3a 14 */ 15 16#include <linux/module.h> 17 18#include <linux/kernel.h> 19#include <linux/sched.h> 20#include <linux/signal.h> 21#include <linux/errno.h> 22#include <linux/miscdevice.h> 23#include <linux/random.h> 24#include <linux/poll.h> 25#include <linux/init.h> 26#include <linux/ioport.h> 27 28#include <asm/io.h> 29#include <asm/uaccess.h> 30#include <asm/system.h> 31#include <asm/irq.h> 32 33#include "busmouse.h" 34 35#define ATIXL_MOUSE_IRQ 5 /* H/W interrupt # set up on ATIXL board */ 36#define ATIXL_BUSMOUSE 3 /* Minor device # (mknod c 10 3 /dev/bm) */ 37 38/* ATI XL Inport Busmouse Definitions */ 39 40#define ATIXL_MSE_DATA_PORT 0x23d 41#define ATIXL_MSE_SIGNATURE_PORT 0x23e 42#define ATIXL_MSE_CONTROL_PORT 0x23c 43 44#define ATIXL_MSE_READ_BUTTONS 0x00 45#define ATIXL_MSE_READ_X 0x01 46#define ATIXL_MSE_READ_Y 0x02 47 48/* Some nice ATI XL macros */ 49 50/* Select IR7, HOLD UPDATES (INT ENABLED), save X,Y */ 51#define ATIXL_MSE_DISABLE_UPDATE() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \ 52 outb( (0x20 | inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); } 53 54/* Select IR7, Enable updates (INT ENABLED) */ 55#define ATIXL_MSE_ENABLE_UPDATE() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \ 56 outb( (0xdf & inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); } 57 58/* Select IR7 - Mode Register, NO INTERRUPTS */ 59#define ATIXL_MSE_INT_OFF() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \ 60 outb( (0xe7 & inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); } 61 62/* Select IR7 - Mode Register, DATA INTERRUPTS ENABLED */ 63#define ATIXL_MSE_INT_ON() { outb( 0x07, ATIXL_MSE_CONTROL_PORT ); \ 64 outb( (0x08 | inb( ATIXL_MSE_DATA_PORT )), ATIXL_MSE_DATA_PORT ); } 65 66/* Same general mouse structure */ 67 68static int msedev; 69 70static void mouse_interrupt(int irq, void *dev_id, struct pt_regs * regs) 71{ 72 char dx, dy, buttons; 73 74 ATIXL_MSE_DISABLE_UPDATE(); /* Note that interrupts are still enabled */ 75 outb(ATIXL_MSE_READ_X, ATIXL_MSE_CONTROL_PORT); /* Select IR1 - X movement */ 76 dx = inb( ATIXL_MSE_DATA_PORT); 77 outb(ATIXL_MSE_READ_Y, ATIXL_MSE_CONTROL_PORT); /* Select IR2 - Y movement */ 78 dy = inb( ATIXL_MSE_DATA_PORT); 79 outb(ATIXL_MSE_READ_BUTTONS, ATIXL_MSE_CONTROL_PORT); /* Select IR0 - Button Status */ 80 buttons = inb( ATIXL_MSE_DATA_PORT); 81 busmouse_add_movementbuttons(msedev, dx, -dy, buttons); 82 ATIXL_MSE_ENABLE_UPDATE(); 83} 84 85static int release_mouse(struct inode * inode, struct file * file) 86{ 87 ATIXL_MSE_INT_OFF(); /* Interrupts are really shut down here */ 88 free_irq(ATIXL_MOUSE_IRQ, NULL); 89 return 0; 90} 91 92static int open_mouse(struct inode * inode, struct file * file) 93{ 94 if (request_irq(ATIXL_MOUSE_IRQ, mouse_interrupt, 0, "ATIXL mouse", NULL)) 95 return -EBUSY; 96 ATIXL_MSE_INT_ON(); /* Interrupts are really enabled here */ 97 return 0; 98} 99 100static struct busmouse atixlmouse = { 101 ATIXL_BUSMOUSE, "atixl", THIS_MODULE, open_mouse, release_mouse, 0 102}; 103 104static int __init atixl_busmouse_init(void) 105{ 106 unsigned char a,b,c; 107 108 /* 109 * We must request the resource and claim it atomically 110 * nowdays. We can throw it away on error. Otherwise we 111 * may race another module load of the same I/O 112 */ 113 114 if (!request_region(ATIXL_MSE_DATA_PORT, 3, "atixlmouse")) 115 return -EIO; 116 117 a = inb( ATIXL_MSE_SIGNATURE_PORT ); /* Get signature */ 118 b = inb( ATIXL_MSE_SIGNATURE_PORT ); 119 c = inb( ATIXL_MSE_SIGNATURE_PORT ); 120 if (( a != b ) && ( a == c )) 121 printk(KERN_INFO "\nATI Inport "); 122 else 123 { 124 release_region(ATIXL_MSE_DATA_PORT,3); 125 return -EIO; 126 } 127 outb(0x80, ATIXL_MSE_CONTROL_PORT); /* Reset the Inport device */ 128 outb(0x07, ATIXL_MSE_CONTROL_PORT); /* Select Internal Register 7 */ 129 outb(0x0a, ATIXL_MSE_DATA_PORT); /* Data Interrupts 8+, 1=30hz, 2=50hz, 3=100hz, 4=200hz rate */ 130 131 msedev = register_busmouse(&atixlmouse); 132 if (msedev < 0) 133 { 134 printk("Bus mouse initialisation error.\n"); 135 release_region(ATIXL_MSE_DATA_PORT,3); /* Was missing */ 136 } 137 else 138 printk("Bus mouse detected and installed.\n"); 139 return msedev < 0 ? msedev : 0; 140} 141 142static void __exit atixl_cleanup (void) 143{ 144 release_region(ATIXL_MSE_DATA_PORT, 3); 145 unregister_busmouse(msedev); 146} 147 148module_init(atixl_busmouse_init); 149module_exit(atixl_cleanup); 150 151MODULE_LICENSE("GPL"); 152