162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * drivers/input/serio/gscps2.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2004-2006 Helge Deller <deller@gmx.de>
562306a36Sopenharmony_ci * Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
662306a36Sopenharmony_ci * Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c
962306a36Sopenharmony_ci *	Copyright (c) 1999 Alex deVries <alex@onefishtwo.ca>
1062306a36Sopenharmony_ci *	Copyright (c) 1999-2000 Philipp Rumpf <prumpf@tux.org>
1162306a36Sopenharmony_ci *	Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
1262306a36Sopenharmony_ci *	Copyright (c) 2000-2001 Thomas Marteau <marteaut@esiee.fr>
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * HP GSC PS/2 port driver, found in PA/RISC Workstations
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
1762306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
1862306a36Sopenharmony_ci * for more details.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * TODO:
2162306a36Sopenharmony_ci * - Dino testing (did HP ever shipped a machine on which this port
2262306a36Sopenharmony_ci *                 was usable/enabled ?)
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/init.h>
2662306a36Sopenharmony_ci#include <linux/module.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/serio.h>
2962306a36Sopenharmony_ci#include <linux/input.h>
3062306a36Sopenharmony_ci#include <linux/interrupt.h>
3162306a36Sopenharmony_ci#include <linux/spinlock.h>
3262306a36Sopenharmony_ci#include <linux/delay.h>
3362306a36Sopenharmony_ci#include <linux/ioport.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <asm/irq.h>
3662306a36Sopenharmony_ci#include <asm/io.h>
3762306a36Sopenharmony_ci#include <asm/parisc-device.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciMODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>, Thibaut Varene <varenet@parisc-linux.org>, Helge Deller <deller@gmx.de>");
4062306a36Sopenharmony_ciMODULE_DESCRIPTION("HP GSC PS2 port driver");
4162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define PFX "gscps2.c: "
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * Driver constants
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* various constants */
5062306a36Sopenharmony_ci#define ENABLE			1
5162306a36Sopenharmony_ci#define DISABLE			0
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define GSC_DINO_OFFSET		0x0800	/* offset for DINO controller versus LASI one */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* PS/2 IO port offsets */
5662306a36Sopenharmony_ci#define GSC_ID			0x00	/* device ID offset (see: GSC_ID_XXX) */
5762306a36Sopenharmony_ci#define GSC_RESET		0x00	/* reset port offset */
5862306a36Sopenharmony_ci#define GSC_RCVDATA		0x04	/* receive port offset */
5962306a36Sopenharmony_ci#define GSC_XMTDATA		0x04	/* transmit port offset */
6062306a36Sopenharmony_ci#define GSC_CONTROL		0x08	/* see: Control register bits */
6162306a36Sopenharmony_ci#define GSC_STATUS		0x0C	/* see: Status register bits */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* Control register bits */
6462306a36Sopenharmony_ci#define GSC_CTRL_ENBL		0x01	/* enable interface */
6562306a36Sopenharmony_ci#define GSC_CTRL_LPBXR		0x02	/* loopback operation */
6662306a36Sopenharmony_ci#define GSC_CTRL_DIAG		0x20	/* directly control clock/data line */
6762306a36Sopenharmony_ci#define GSC_CTRL_DATDIR		0x40	/* data line direct control */
6862306a36Sopenharmony_ci#define GSC_CTRL_CLKDIR		0x80	/* clock line direct control */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Status register bits */
7162306a36Sopenharmony_ci#define GSC_STAT_RBNE		0x01	/* Receive Buffer Not Empty */
7262306a36Sopenharmony_ci#define GSC_STAT_TBNE		0x02	/* Transmit Buffer Not Empty */
7362306a36Sopenharmony_ci#define GSC_STAT_TERR		0x04	/* Timeout Error */
7462306a36Sopenharmony_ci#define GSC_STAT_PERR		0x08	/* Parity Error */
7562306a36Sopenharmony_ci#define GSC_STAT_CMPINTR	0x10	/* Composite Interrupt = irq on any port */
7662306a36Sopenharmony_ci#define GSC_STAT_DATSHD		0x40	/* Data Line Shadow */
7762306a36Sopenharmony_ci#define GSC_STAT_CLKSHD		0x80	/* Clock Line Shadow */
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/* IDs returned by GSC_ID port register */
8062306a36Sopenharmony_ci#define GSC_ID_KEYBOARD		0	/* device ID values */
8162306a36Sopenharmony_ci#define GSC_ID_MOUSE		1
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic irqreturn_t gscps2_interrupt(int irq, void *dev);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define BUFFER_SIZE 0x0f
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* GSC PS/2 port device struct */
8962306a36Sopenharmony_cistruct gscps2port {
9062306a36Sopenharmony_ci	struct list_head node;
9162306a36Sopenharmony_ci	struct parisc_device *padev;
9262306a36Sopenharmony_ci	struct serio *port;
9362306a36Sopenharmony_ci	spinlock_t lock;
9462306a36Sopenharmony_ci	char __iomem *addr;
9562306a36Sopenharmony_ci	u8 act, append; /* position in buffer[] */
9662306a36Sopenharmony_ci	struct {
9762306a36Sopenharmony_ci		u8 data;
9862306a36Sopenharmony_ci		u8 str;
9962306a36Sopenharmony_ci	} buffer[BUFFER_SIZE+1];
10062306a36Sopenharmony_ci	int id;
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/*
10462306a36Sopenharmony_ci * Various HW level routines
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci#define gscps2_readb_input(x)		readb((x)+GSC_RCVDATA)
10862306a36Sopenharmony_ci#define gscps2_readb_control(x)		readb((x)+GSC_CONTROL)
10962306a36Sopenharmony_ci#define gscps2_readb_status(x)		readb((x)+GSC_STATUS)
11062306a36Sopenharmony_ci#define gscps2_writeb_control(x, y)	writeb((x), (y)+GSC_CONTROL)
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/*
11462306a36Sopenharmony_ci * wait_TBE() - wait for Transmit Buffer Empty
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic int wait_TBE(char __iomem *addr)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	int timeout = 25000; /* device is expected to react within 250 msec */
12062306a36Sopenharmony_ci	while (gscps2_readb_status(addr) & GSC_STAT_TBNE) {
12162306a36Sopenharmony_ci		if (!--timeout)
12262306a36Sopenharmony_ci			return 0;	/* This should not happen */
12362306a36Sopenharmony_ci		udelay(10);
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	return 1;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * gscps2_flush() - flush the receive buffer
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void gscps2_flush(struct gscps2port *ps2port)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE)
13662306a36Sopenharmony_ci		gscps2_readb_input(ps2port->addr);
13762306a36Sopenharmony_ci	ps2port->act = ps2port->append = 0;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/*
14162306a36Sopenharmony_ci * gscps2_writeb_output() - write a byte to the port
14262306a36Sopenharmony_ci *
14362306a36Sopenharmony_ci * returns 1 on success, 0 on error
14462306a36Sopenharmony_ci */
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	unsigned long flags;
14962306a36Sopenharmony_ci	char __iomem *addr = ps2port->addr;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (!wait_TBE(addr)) {
15262306a36Sopenharmony_ci		printk(KERN_DEBUG PFX "timeout - could not write byte %#x\n", data);
15362306a36Sopenharmony_ci		return 0;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	while (gscps2_readb_status(addr) & GSC_STAT_RBNE)
15762306a36Sopenharmony_ci		/* wait */;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	spin_lock_irqsave(&ps2port->lock, flags);
16062306a36Sopenharmony_ci	writeb(data, addr+GSC_XMTDATA);
16162306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps2port->lock, flags);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* this is ugly, but due to timing of the port it seems to be necessary. */
16462306a36Sopenharmony_ci	mdelay(6);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* make sure any received data is returned as fast as possible */
16762306a36Sopenharmony_ci	/* this is important e.g. when we set the LEDs on the keyboard */
16862306a36Sopenharmony_ci	gscps2_interrupt(0, NULL);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return 1;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/*
17562306a36Sopenharmony_ci * gscps2_enable() - enables or disables the port
17662306a36Sopenharmony_ci */
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic void gscps2_enable(struct gscps2port *ps2port, int enable)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	unsigned long flags;
18162306a36Sopenharmony_ci	u8 data;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* now enable/disable the port */
18462306a36Sopenharmony_ci	spin_lock_irqsave(&ps2port->lock, flags);
18562306a36Sopenharmony_ci	gscps2_flush(ps2port);
18662306a36Sopenharmony_ci	data = gscps2_readb_control(ps2port->addr);
18762306a36Sopenharmony_ci	if (enable)
18862306a36Sopenharmony_ci		data |= GSC_CTRL_ENBL;
18962306a36Sopenharmony_ci	else
19062306a36Sopenharmony_ci		data &= ~GSC_CTRL_ENBL;
19162306a36Sopenharmony_ci	gscps2_writeb_control(data, ps2port->addr);
19262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps2port->lock, flags);
19362306a36Sopenharmony_ci	wait_TBE(ps2port->addr);
19462306a36Sopenharmony_ci	gscps2_flush(ps2port);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/*
19862306a36Sopenharmony_ci * gscps2_reset() - resets the PS/2 port
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void gscps2_reset(struct gscps2port *ps2port)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	unsigned long flags;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* reset the interface */
20662306a36Sopenharmony_ci	spin_lock_irqsave(&ps2port->lock, flags);
20762306a36Sopenharmony_ci	gscps2_flush(ps2port);
20862306a36Sopenharmony_ci	writeb(0xff, ps2port->addr + GSC_RESET);
20962306a36Sopenharmony_ci	gscps2_flush(ps2port);
21062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ps2port->lock, flags);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic LIST_HEAD(ps2port_list);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/**
21662306a36Sopenharmony_ci * gscps2_interrupt() - Interruption service routine
21762306a36Sopenharmony_ci *
21862306a36Sopenharmony_ci * This function reads received PS/2 bytes and processes them on
21962306a36Sopenharmony_ci * all interfaces.
22062306a36Sopenharmony_ci * The problematic part here is, that the keyboard and mouse PS/2 port
22162306a36Sopenharmony_ci * share the same interrupt and it's not possible to send data if any
22262306a36Sopenharmony_ci * one of them holds input data. To solve this problem we try to receive
22362306a36Sopenharmony_ci * the data as fast as possible and handle the reporting to the upper layer
22462306a36Sopenharmony_ci * later.
22562306a36Sopenharmony_ci */
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic irqreturn_t gscps2_interrupt(int irq, void *dev)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct gscps2port *ps2port;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	list_for_each_entry(ps2port, &ps2port_list, node) {
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	  unsigned long flags;
23462306a36Sopenharmony_ci	  spin_lock_irqsave(&ps2port->lock, flags);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	  while ( (ps2port->buffer[ps2port->append].str =
23762306a36Sopenharmony_ci		   gscps2_readb_status(ps2port->addr)) & GSC_STAT_RBNE ) {
23862306a36Sopenharmony_ci		ps2port->buffer[ps2port->append].data =
23962306a36Sopenharmony_ci				gscps2_readb_input(ps2port->addr);
24062306a36Sopenharmony_ci		ps2port->append = ((ps2port->append+1) & BUFFER_SIZE);
24162306a36Sopenharmony_ci	  }
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	  spin_unlock_irqrestore(&ps2port->lock, flags);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	} /* list_for_each_entry */
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* all data was read from the ports - now report the data to upper layer */
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	list_for_each_entry(ps2port, &ps2port_list, node) {
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	  while (ps2port->act != ps2port->append) {
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	    unsigned int rxflags;
25462306a36Sopenharmony_ci	    u8 data, status;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	    /* Did new data arrived while we read existing data ?
25762306a36Sopenharmony_ci	       If yes, exit now and let the new irq handler start over again */
25862306a36Sopenharmony_ci	    if (gscps2_readb_status(ps2port->addr) & GSC_STAT_CMPINTR)
25962306a36Sopenharmony_ci		return IRQ_HANDLED;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	    status = ps2port->buffer[ps2port->act].str;
26262306a36Sopenharmony_ci	    data   = ps2port->buffer[ps2port->act].data;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	    ps2port->act = ((ps2port->act+1) & BUFFER_SIZE);
26562306a36Sopenharmony_ci	    rxflags =	((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) |
26662306a36Sopenharmony_ci			((status & GSC_STAT_PERR) ? SERIO_PARITY  : 0 );
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	    serio_interrupt(ps2port->port, data, rxflags);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	  } /* while() */
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	} /* list_for_each_entry */
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return IRQ_HANDLED;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/*
27962306a36Sopenharmony_ci * gscps2_write() - send a byte out through the aux interface.
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic int gscps2_write(struct serio *port, unsigned char data)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct gscps2port *ps2port = port->port_data;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (!gscps2_writeb_output(ps2port, data)) {
28762306a36Sopenharmony_ci		printk(KERN_DEBUG PFX "sending byte %#x failed.\n", data);
28862306a36Sopenharmony_ci		return -1;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/*
29462306a36Sopenharmony_ci * gscps2_open() is called when a port is opened by the higher layer.
29562306a36Sopenharmony_ci * It resets and enables the port.
29662306a36Sopenharmony_ci */
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int gscps2_open(struct serio *port)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct gscps2port *ps2port = port->port_data;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	gscps2_reset(ps2port);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* enable it */
30562306a36Sopenharmony_ci	gscps2_enable(ps2port, ENABLE);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	gscps2_interrupt(0, NULL);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return 0;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/*
31362306a36Sopenharmony_ci * gscps2_close() disables the port
31462306a36Sopenharmony_ci */
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic void gscps2_close(struct serio *port)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct gscps2port *ps2port = port->port_data;
31962306a36Sopenharmony_ci	gscps2_enable(ps2port, DISABLE);
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci/**
32362306a36Sopenharmony_ci * gscps2_probe() - Probes PS2 devices
32462306a36Sopenharmony_ci * @return: success/error report
32562306a36Sopenharmony_ci */
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic int __init gscps2_probe(struct parisc_device *dev)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct gscps2port *ps2port;
33062306a36Sopenharmony_ci	struct serio *serio;
33162306a36Sopenharmony_ci	unsigned long hpa = dev->hpa.start;
33262306a36Sopenharmony_ci	int ret;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (!dev->irq)
33562306a36Sopenharmony_ci		return -ENODEV;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Offset for DINO PS/2. Works with LASI even */
33862306a36Sopenharmony_ci	if (dev->id.sversion == 0x96)
33962306a36Sopenharmony_ci		hpa += GSC_DINO_OFFSET;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	ps2port = kzalloc(sizeof(struct gscps2port), GFP_KERNEL);
34262306a36Sopenharmony_ci	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
34362306a36Sopenharmony_ci	if (!ps2port || !serio) {
34462306a36Sopenharmony_ci		ret = -ENOMEM;
34562306a36Sopenharmony_ci		goto fail_nomem;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	dev_set_drvdata(&dev->dev, ps2port);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	ps2port->port = serio;
35162306a36Sopenharmony_ci	ps2port->padev = dev;
35262306a36Sopenharmony_ci	ps2port->addr = ioremap(hpa, GSC_STATUS + 4);
35362306a36Sopenharmony_ci	if (!ps2port->addr) {
35462306a36Sopenharmony_ci		ret = -ENOMEM;
35562306a36Sopenharmony_ci		goto fail_nomem;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	spin_lock_init(&ps2port->lock);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	gscps2_reset(ps2port);
36062306a36Sopenharmony_ci	ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	snprintf(serio->name, sizeof(serio->name), "gsc-ps2-%s",
36362306a36Sopenharmony_ci		 (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse");
36462306a36Sopenharmony_ci	strscpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
36562306a36Sopenharmony_ci	serio->id.type		= SERIO_8042;
36662306a36Sopenharmony_ci	serio->write		= gscps2_write;
36762306a36Sopenharmony_ci	serio->open		= gscps2_open;
36862306a36Sopenharmony_ci	serio->close		= gscps2_close;
36962306a36Sopenharmony_ci	serio->port_data	= ps2port;
37062306a36Sopenharmony_ci	serio->dev.parent	= &dev->dev;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	ret = -EBUSY;
37362306a36Sopenharmony_ci	if (request_irq(dev->irq, gscps2_interrupt, IRQF_SHARED, ps2port->port->name, ps2port))
37462306a36Sopenharmony_ci		goto fail_miserably;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (ps2port->id != GSC_ID_KEYBOARD && ps2port->id != GSC_ID_MOUSE) {
37762306a36Sopenharmony_ci		printk(KERN_WARNING PFX "Unsupported PS/2 port at 0x%08lx (id=%d) ignored\n",
37862306a36Sopenharmony_ci				hpa, ps2port->id);
37962306a36Sopenharmony_ci		ret = -ENODEV;
38062306a36Sopenharmony_ci		goto fail;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci#if 0
38462306a36Sopenharmony_ci	if (!request_mem_region(hpa, GSC_STATUS + 4, ps2port->port.name))
38562306a36Sopenharmony_ci		goto fail;
38662306a36Sopenharmony_ci#endif
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	pr_info("serio: %s port at 0x%08lx irq %d @ %s\n",
38962306a36Sopenharmony_ci		ps2port->port->name,
39062306a36Sopenharmony_ci		hpa,
39162306a36Sopenharmony_ci		ps2port->padev->irq,
39262306a36Sopenharmony_ci		ps2port->port->phys);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	serio_register_port(ps2port->port);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	list_add_tail(&ps2port->node, &ps2port_list);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cifail:
40162306a36Sopenharmony_ci	free_irq(dev->irq, ps2port);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cifail_miserably:
40462306a36Sopenharmony_ci	iounmap(ps2port->addr);
40562306a36Sopenharmony_ci	release_mem_region(dev->hpa.start, GSC_STATUS + 4);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cifail_nomem:
40862306a36Sopenharmony_ci	kfree(ps2port);
40962306a36Sopenharmony_ci	kfree(serio);
41062306a36Sopenharmony_ci	return ret;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci/**
41462306a36Sopenharmony_ci * gscps2_remove() - Removes PS2 devices
41562306a36Sopenharmony_ci * @return: success/error report
41662306a36Sopenharmony_ci */
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic void __exit gscps2_remove(struct parisc_device *dev)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct gscps2port *ps2port = dev_get_drvdata(&dev->dev);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	serio_unregister_port(ps2port->port);
42362306a36Sopenharmony_ci	free_irq(dev->irq, ps2port);
42462306a36Sopenharmony_ci	gscps2_flush(ps2port);
42562306a36Sopenharmony_ci	list_del(&ps2port->node);
42662306a36Sopenharmony_ci	iounmap(ps2port->addr);
42762306a36Sopenharmony_ci#if 0
42862306a36Sopenharmony_ci	release_mem_region(dev->hpa, GSC_STATUS + 4);
42962306a36Sopenharmony_ci#endif
43062306a36Sopenharmony_ci	dev_set_drvdata(&dev->dev, NULL);
43162306a36Sopenharmony_ci	kfree(ps2port);
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic const struct parisc_device_id gscps2_device_tbl[] __initconst = {
43662306a36Sopenharmony_ci	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00084 }, /* LASI PS/2 */
43762306a36Sopenharmony_ci#ifdef DINO_TESTED
43862306a36Sopenharmony_ci	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00096 }, /* DINO PS/2 */
43962306a36Sopenharmony_ci#endif
44062306a36Sopenharmony_ci	{ 0, }	/* 0 terminated list */
44162306a36Sopenharmony_ci};
44262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic struct parisc_driver parisc_ps2_driver __refdata = {
44562306a36Sopenharmony_ci	.name		= "gsc_ps2",
44662306a36Sopenharmony_ci	.id_table	= gscps2_device_tbl,
44762306a36Sopenharmony_ci	.probe		= gscps2_probe,
44862306a36Sopenharmony_ci	.remove		= __exit_p(gscps2_remove),
44962306a36Sopenharmony_ci};
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic int __init gscps2_init(void)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	register_parisc_driver(&parisc_ps2_driver);
45462306a36Sopenharmony_ci	return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic void __exit gscps2_exit(void)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	unregister_parisc_driver(&parisc_ps2_driver);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cimodule_init(gscps2_init);
46462306a36Sopenharmony_cimodule_exit(gscps2_exit);
46562306a36Sopenharmony_ci
466