18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * drivers/input/serio/gscps2.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2004-2006 Helge Deller <deller@gmx.de> 58c2ecf20Sopenharmony_ci * Copyright (c) 2002 Laurent Canet <canetl@esiee.fr> 68c2ecf20Sopenharmony_ci * Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c 98c2ecf20Sopenharmony_ci * Copyright (c) 1999 Alex deVries <alex@onefishtwo.ca> 108c2ecf20Sopenharmony_ci * Copyright (c) 1999-2000 Philipp Rumpf <prumpf@tux.org> 118c2ecf20Sopenharmony_ci * Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr> 128c2ecf20Sopenharmony_ci * Copyright (c) 2000-2001 Thomas Marteau <marteaut@esiee.fr> 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * HP GSC PS/2 port driver, found in PA/RISC Workstations 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 178c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 188c2ecf20Sopenharmony_ci * for more details. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * TODO: 218c2ecf20Sopenharmony_ci * - Dino testing (did HP ever shipped a machine on which this port 228c2ecf20Sopenharmony_ci * was usable/enabled ?) 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/init.h> 268c2ecf20Sopenharmony_ci#include <linux/module.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/serio.h> 298c2ecf20Sopenharmony_ci#include <linux/input.h> 308c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 318c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 328c2ecf20Sopenharmony_ci#include <linux/delay.h> 338c2ecf20Sopenharmony_ci#include <linux/ioport.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <asm/irq.h> 368c2ecf20Sopenharmony_ci#include <asm/io.h> 378c2ecf20Sopenharmony_ci#include <asm/parisc-device.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>, Thibaut Varene <varenet@parisc-linux.org>, Helge Deller <deller@gmx.de>"); 408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("HP GSC PS2 port driver"); 418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define PFX "gscps2.c: " 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * Driver constants 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* various constants */ 508c2ecf20Sopenharmony_ci#define ENABLE 1 518c2ecf20Sopenharmony_ci#define DISABLE 0 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define GSC_DINO_OFFSET 0x0800 /* offset for DINO controller versus LASI one */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* PS/2 IO port offsets */ 568c2ecf20Sopenharmony_ci#define GSC_ID 0x00 /* device ID offset (see: GSC_ID_XXX) */ 578c2ecf20Sopenharmony_ci#define GSC_RESET 0x00 /* reset port offset */ 588c2ecf20Sopenharmony_ci#define GSC_RCVDATA 0x04 /* receive port offset */ 598c2ecf20Sopenharmony_ci#define GSC_XMTDATA 0x04 /* transmit port offset */ 608c2ecf20Sopenharmony_ci#define GSC_CONTROL 0x08 /* see: Control register bits */ 618c2ecf20Sopenharmony_ci#define GSC_STATUS 0x0C /* see: Status register bits */ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* Control register bits */ 648c2ecf20Sopenharmony_ci#define GSC_CTRL_ENBL 0x01 /* enable interface */ 658c2ecf20Sopenharmony_ci#define GSC_CTRL_LPBXR 0x02 /* loopback operation */ 668c2ecf20Sopenharmony_ci#define GSC_CTRL_DIAG 0x20 /* directly control clock/data line */ 678c2ecf20Sopenharmony_ci#define GSC_CTRL_DATDIR 0x40 /* data line direct control */ 688c2ecf20Sopenharmony_ci#define GSC_CTRL_CLKDIR 0x80 /* clock line direct control */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Status register bits */ 718c2ecf20Sopenharmony_ci#define GSC_STAT_RBNE 0x01 /* Receive Buffer Not Empty */ 728c2ecf20Sopenharmony_ci#define GSC_STAT_TBNE 0x02 /* Transmit Buffer Not Empty */ 738c2ecf20Sopenharmony_ci#define GSC_STAT_TERR 0x04 /* Timeout Error */ 748c2ecf20Sopenharmony_ci#define GSC_STAT_PERR 0x08 /* Parity Error */ 758c2ecf20Sopenharmony_ci#define GSC_STAT_CMPINTR 0x10 /* Composite Interrupt = irq on any port */ 768c2ecf20Sopenharmony_ci#define GSC_STAT_DATSHD 0x40 /* Data Line Shadow */ 778c2ecf20Sopenharmony_ci#define GSC_STAT_CLKSHD 0x80 /* Clock Line Shadow */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* IDs returned by GSC_ID port register */ 808c2ecf20Sopenharmony_ci#define GSC_ID_KEYBOARD 0 /* device ID values */ 818c2ecf20Sopenharmony_ci#define GSC_ID_MOUSE 1 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic irqreturn_t gscps2_interrupt(int irq, void *dev); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define BUFFER_SIZE 0x0f 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* GSC PS/2 port device struct */ 898c2ecf20Sopenharmony_cistruct gscps2port { 908c2ecf20Sopenharmony_ci struct list_head node; 918c2ecf20Sopenharmony_ci struct parisc_device *padev; 928c2ecf20Sopenharmony_ci struct serio *port; 938c2ecf20Sopenharmony_ci spinlock_t lock; 948c2ecf20Sopenharmony_ci char __iomem *addr; 958c2ecf20Sopenharmony_ci u8 act, append; /* position in buffer[] */ 968c2ecf20Sopenharmony_ci struct { 978c2ecf20Sopenharmony_ci u8 data; 988c2ecf20Sopenharmony_ci u8 str; 998c2ecf20Sopenharmony_ci } buffer[BUFFER_SIZE+1]; 1008c2ecf20Sopenharmony_ci int id; 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* 1048c2ecf20Sopenharmony_ci * Various HW level routines 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define gscps2_readb_input(x) readb((x)+GSC_RCVDATA) 1088c2ecf20Sopenharmony_ci#define gscps2_readb_control(x) readb((x)+GSC_CONTROL) 1098c2ecf20Sopenharmony_ci#define gscps2_readb_status(x) readb((x)+GSC_STATUS) 1108c2ecf20Sopenharmony_ci#define gscps2_writeb_control(x, y) writeb((x), (y)+GSC_CONTROL) 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* 1148c2ecf20Sopenharmony_ci * wait_TBE() - wait for Transmit Buffer Empty 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int wait_TBE(char __iomem *addr) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int timeout = 25000; /* device is expected to react within 250 msec */ 1208c2ecf20Sopenharmony_ci while (gscps2_readb_status(addr) & GSC_STAT_TBNE) { 1218c2ecf20Sopenharmony_ci if (!--timeout) 1228c2ecf20Sopenharmony_ci return 0; /* This should not happen */ 1238c2ecf20Sopenharmony_ci udelay(10); 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci return 1; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * gscps2_flush() - flush the receive buffer 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void gscps2_flush(struct gscps2port *ps2port) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE) 1368c2ecf20Sopenharmony_ci gscps2_readb_input(ps2port->addr); 1378c2ecf20Sopenharmony_ci ps2port->act = ps2port->append = 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* 1418c2ecf20Sopenharmony_ci * gscps2_writeb_output() - write a byte to the port 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * returns 1 on success, 0 on error 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci unsigned long flags; 1498c2ecf20Sopenharmony_ci char __iomem *addr = ps2port->addr; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (!wait_TBE(addr)) { 1528c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "timeout - could not write byte %#x\n", data); 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci while (gscps2_readb_status(addr) & GSC_STAT_RBNE) 1578c2ecf20Sopenharmony_ci /* wait */; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci spin_lock_irqsave(&ps2port->lock, flags); 1608c2ecf20Sopenharmony_ci writeb(data, addr+GSC_XMTDATA); 1618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ps2port->lock, flags); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* this is ugly, but due to timing of the port it seems to be necessary. */ 1648c2ecf20Sopenharmony_ci mdelay(6); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* make sure any received data is returned as fast as possible */ 1678c2ecf20Sopenharmony_ci /* this is important e.g. when we set the LEDs on the keyboard */ 1688c2ecf20Sopenharmony_ci gscps2_interrupt(0, NULL); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 1; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* 1758c2ecf20Sopenharmony_ci * gscps2_enable() - enables or disables the port 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void gscps2_enable(struct gscps2port *ps2port, int enable) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci unsigned long flags; 1818c2ecf20Sopenharmony_ci u8 data; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* now enable/disable the port */ 1848c2ecf20Sopenharmony_ci spin_lock_irqsave(&ps2port->lock, flags); 1858c2ecf20Sopenharmony_ci gscps2_flush(ps2port); 1868c2ecf20Sopenharmony_ci data = gscps2_readb_control(ps2port->addr); 1878c2ecf20Sopenharmony_ci if (enable) 1888c2ecf20Sopenharmony_ci data |= GSC_CTRL_ENBL; 1898c2ecf20Sopenharmony_ci else 1908c2ecf20Sopenharmony_ci data &= ~GSC_CTRL_ENBL; 1918c2ecf20Sopenharmony_ci gscps2_writeb_control(data, ps2port->addr); 1928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ps2port->lock, flags); 1938c2ecf20Sopenharmony_ci wait_TBE(ps2port->addr); 1948c2ecf20Sopenharmony_ci gscps2_flush(ps2port); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* 1988c2ecf20Sopenharmony_ci * gscps2_reset() - resets the PS/2 port 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic void gscps2_reset(struct gscps2port *ps2port) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci unsigned long flags; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* reset the interface */ 2068c2ecf20Sopenharmony_ci spin_lock_irqsave(&ps2port->lock, flags); 2078c2ecf20Sopenharmony_ci gscps2_flush(ps2port); 2088c2ecf20Sopenharmony_ci writeb(0xff, ps2port->addr + GSC_RESET); 2098c2ecf20Sopenharmony_ci gscps2_flush(ps2port); 2108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ps2port->lock, flags); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic LIST_HEAD(ps2port_list); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/** 2168c2ecf20Sopenharmony_ci * gscps2_interrupt() - Interruption service routine 2178c2ecf20Sopenharmony_ci * 2188c2ecf20Sopenharmony_ci * This function reads received PS/2 bytes and processes them on 2198c2ecf20Sopenharmony_ci * all interfaces. 2208c2ecf20Sopenharmony_ci * The problematic part here is, that the keyboard and mouse PS/2 port 2218c2ecf20Sopenharmony_ci * share the same interrupt and it's not possible to send data if any 2228c2ecf20Sopenharmony_ci * one of them holds input data. To solve this problem we try to receive 2238c2ecf20Sopenharmony_ci * the data as fast as possible and handle the reporting to the upper layer 2248c2ecf20Sopenharmony_ci * later. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic irqreturn_t gscps2_interrupt(int irq, void *dev) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct gscps2port *ps2port; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci list_for_each_entry(ps2port, &ps2port_list, node) { 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci unsigned long flags; 2348c2ecf20Sopenharmony_ci spin_lock_irqsave(&ps2port->lock, flags); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci while ( (ps2port->buffer[ps2port->append].str = 2378c2ecf20Sopenharmony_ci gscps2_readb_status(ps2port->addr)) & GSC_STAT_RBNE ) { 2388c2ecf20Sopenharmony_ci ps2port->buffer[ps2port->append].data = 2398c2ecf20Sopenharmony_ci gscps2_readb_input(ps2port->addr); 2408c2ecf20Sopenharmony_ci ps2port->append = ((ps2port->append+1) & BUFFER_SIZE); 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ps2port->lock, flags); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci } /* list_for_each_entry */ 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* all data was read from the ports - now report the data to upper layer */ 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci list_for_each_entry(ps2port, &ps2port_list, node) { 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci while (ps2port->act != ps2port->append) { 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci unsigned int rxflags; 2548c2ecf20Sopenharmony_ci u8 data, status; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Did new data arrived while we read existing data ? 2578c2ecf20Sopenharmony_ci If yes, exit now and let the new irq handler start over again */ 2588c2ecf20Sopenharmony_ci if (gscps2_readb_status(ps2port->addr) & GSC_STAT_CMPINTR) 2598c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci status = ps2port->buffer[ps2port->act].str; 2628c2ecf20Sopenharmony_ci data = ps2port->buffer[ps2port->act].data; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ps2port->act = ((ps2port->act+1) & BUFFER_SIZE); 2658c2ecf20Sopenharmony_ci rxflags = ((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) | 2668c2ecf20Sopenharmony_ci ((status & GSC_STAT_PERR) ? SERIO_PARITY : 0 ); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci serio_interrupt(ps2port->port, data, rxflags); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci } /* while() */ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci } /* list_for_each_entry */ 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/* 2798c2ecf20Sopenharmony_ci * gscps2_write() - send a byte out through the aux interface. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int gscps2_write(struct serio *port, unsigned char data) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct gscps2port *ps2port = port->port_data; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (!gscps2_writeb_output(ps2port, data)) { 2878c2ecf20Sopenharmony_ci printk(KERN_DEBUG PFX "sending byte %#x failed.\n", data); 2888c2ecf20Sopenharmony_ci return -1; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* 2948c2ecf20Sopenharmony_ci * gscps2_open() is called when a port is opened by the higher layer. 2958c2ecf20Sopenharmony_ci * It resets and enables the port. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int gscps2_open(struct serio *port) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct gscps2port *ps2port = port->port_data; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci gscps2_reset(ps2port); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* enable it */ 3058c2ecf20Sopenharmony_ci gscps2_enable(ps2port, ENABLE); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci gscps2_interrupt(0, NULL); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* 3138c2ecf20Sopenharmony_ci * gscps2_close() disables the port 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic void gscps2_close(struct serio *port) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct gscps2port *ps2port = port->port_data; 3198c2ecf20Sopenharmony_ci gscps2_enable(ps2port, DISABLE); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/** 3238c2ecf20Sopenharmony_ci * gscps2_probe() - Probes PS2 devices 3248c2ecf20Sopenharmony_ci * @return: success/error report 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int __init gscps2_probe(struct parisc_device *dev) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct gscps2port *ps2port; 3308c2ecf20Sopenharmony_ci struct serio *serio; 3318c2ecf20Sopenharmony_ci unsigned long hpa = dev->hpa.start; 3328c2ecf20Sopenharmony_ci int ret; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!dev->irq) 3358c2ecf20Sopenharmony_ci return -ENODEV; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Offset for DINO PS/2. Works with LASI even */ 3388c2ecf20Sopenharmony_ci if (dev->id.sversion == 0x96) 3398c2ecf20Sopenharmony_ci hpa += GSC_DINO_OFFSET; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ps2port = kzalloc(sizeof(struct gscps2port), GFP_KERNEL); 3428c2ecf20Sopenharmony_ci serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 3438c2ecf20Sopenharmony_ci if (!ps2port || !serio) { 3448c2ecf20Sopenharmony_ci ret = -ENOMEM; 3458c2ecf20Sopenharmony_ci goto fail_nomem; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, ps2port); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci ps2port->port = serio; 3518c2ecf20Sopenharmony_ci ps2port->padev = dev; 3528c2ecf20Sopenharmony_ci ps2port->addr = ioremap(hpa, GSC_STATUS + 4); 3538c2ecf20Sopenharmony_ci if (!ps2port->addr) { 3548c2ecf20Sopenharmony_ci ret = -ENOMEM; 3558c2ecf20Sopenharmony_ci goto fail_nomem; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci spin_lock_init(&ps2port->lock); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci gscps2_reset(ps2port); 3608c2ecf20Sopenharmony_ci ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci snprintf(serio->name, sizeof(serio->name), "gsc-ps2-%s", 3638c2ecf20Sopenharmony_ci (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse"); 3648c2ecf20Sopenharmony_ci strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys)); 3658c2ecf20Sopenharmony_ci serio->id.type = SERIO_8042; 3668c2ecf20Sopenharmony_ci serio->write = gscps2_write; 3678c2ecf20Sopenharmony_ci serio->open = gscps2_open; 3688c2ecf20Sopenharmony_ci serio->close = gscps2_close; 3698c2ecf20Sopenharmony_ci serio->port_data = ps2port; 3708c2ecf20Sopenharmony_ci serio->dev.parent = &dev->dev; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ret = -EBUSY; 3738c2ecf20Sopenharmony_ci if (request_irq(dev->irq, gscps2_interrupt, IRQF_SHARED, ps2port->port->name, ps2port)) 3748c2ecf20Sopenharmony_ci goto fail_miserably; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (ps2port->id != GSC_ID_KEYBOARD && ps2port->id != GSC_ID_MOUSE) { 3778c2ecf20Sopenharmony_ci printk(KERN_WARNING PFX "Unsupported PS/2 port at 0x%08lx (id=%d) ignored\n", 3788c2ecf20Sopenharmony_ci hpa, ps2port->id); 3798c2ecf20Sopenharmony_ci ret = -ENODEV; 3808c2ecf20Sopenharmony_ci goto fail; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci#if 0 3848c2ecf20Sopenharmony_ci if (!request_mem_region(hpa, GSC_STATUS + 4, ps2port->port.name)) 3858c2ecf20Sopenharmony_ci goto fail; 3868c2ecf20Sopenharmony_ci#endif 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci pr_info("serio: %s port at 0x%08lx irq %d @ %s\n", 3898c2ecf20Sopenharmony_ci ps2port->port->name, 3908c2ecf20Sopenharmony_ci hpa, 3918c2ecf20Sopenharmony_ci ps2port->padev->irq, 3928c2ecf20Sopenharmony_ci ps2port->port->phys); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci serio_register_port(ps2port->port); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci list_add_tail(&ps2port->node, &ps2port_list); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cifail: 4018c2ecf20Sopenharmony_ci free_irq(dev->irq, ps2port); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cifail_miserably: 4048c2ecf20Sopenharmony_ci iounmap(ps2port->addr); 4058c2ecf20Sopenharmony_ci release_mem_region(dev->hpa.start, GSC_STATUS + 4); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cifail_nomem: 4088c2ecf20Sopenharmony_ci kfree(ps2port); 4098c2ecf20Sopenharmony_ci kfree(serio); 4108c2ecf20Sopenharmony_ci return ret; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/** 4148c2ecf20Sopenharmony_ci * gscps2_remove() - Removes PS2 devices 4158c2ecf20Sopenharmony_ci * @return: success/error report 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic int __exit gscps2_remove(struct parisc_device *dev) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct gscps2port *ps2port = dev_get_drvdata(&dev->dev); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci serio_unregister_port(ps2port->port); 4238c2ecf20Sopenharmony_ci free_irq(dev->irq, ps2port); 4248c2ecf20Sopenharmony_ci gscps2_flush(ps2port); 4258c2ecf20Sopenharmony_ci list_del(&ps2port->node); 4268c2ecf20Sopenharmony_ci iounmap(ps2port->addr); 4278c2ecf20Sopenharmony_ci#if 0 4288c2ecf20Sopenharmony_ci release_mem_region(dev->hpa, GSC_STATUS + 4); 4298c2ecf20Sopenharmony_ci#endif 4308c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, NULL); 4318c2ecf20Sopenharmony_ci kfree(ps2port); 4328c2ecf20Sopenharmony_ci return 0; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic const struct parisc_device_id gscps2_device_tbl[] __initconst = { 4378c2ecf20Sopenharmony_ci { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00084 }, /* LASI PS/2 */ 4388c2ecf20Sopenharmony_ci#ifdef DINO_TESTED 4398c2ecf20Sopenharmony_ci { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00096 }, /* DINO PS/2 */ 4408c2ecf20Sopenharmony_ci#endif 4418c2ecf20Sopenharmony_ci { 0, } /* 0 terminated list */ 4428c2ecf20Sopenharmony_ci}; 4438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(parisc, gscps2_device_tbl); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic struct parisc_driver parisc_ps2_driver __refdata = { 4468c2ecf20Sopenharmony_ci .name = "gsc_ps2", 4478c2ecf20Sopenharmony_ci .id_table = gscps2_device_tbl, 4488c2ecf20Sopenharmony_ci .probe = gscps2_probe, 4498c2ecf20Sopenharmony_ci .remove = __exit_p(gscps2_remove), 4508c2ecf20Sopenharmony_ci}; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int __init gscps2_init(void) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci register_parisc_driver(&parisc_ps2_driver); 4558c2ecf20Sopenharmony_ci return 0; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic void __exit gscps2_exit(void) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci unregister_parisc_driver(&parisc_ps2_driver); 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cimodule_init(gscps2_init); 4658c2ecf20Sopenharmony_cimodule_exit(gscps2_exit); 4668c2ecf20Sopenharmony_ci 467