11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  linux/drivers/input/serio/sa1111ps2.c
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (C) 2002 Russell King
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/input.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serio.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/device.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/hardware/sa1111.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
264f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2CR		0x0000
274f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2STAT		0x0004
284f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2DATA		0x0008
294f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2CLKDIV	0x000c
304f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2PRECNT	0x0010
314f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King
324f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2CR_ENA	0x08
334f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2CR_FKD	0x02
344f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2CR_FKC	0x01
354f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King
364f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2STAT_STP	0x0100
374f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2STAT_TXE	0x0080
384f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2STAT_TXB	0x0040
394f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2STAT_RXF	0x0020
404f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2STAT_RXB	0x0010
414f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2STAT_ENA	0x0008
424f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2STAT_RXP	0x0004
434f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2STAT_KBD	0x0002
444f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King#define PS2STAT_KBC	0x0001
454f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct ps2if {
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct serio		*io;
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sa1111_dev	*dev;
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem		*base;
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int		open;
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spinlock_t		lock;
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int		head;
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int		tail;
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char		buf[4];
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read all bytes waiting in the PS2 port.  There should be
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * at the most one, but we loop for safety.  If there was a
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * framing error, we have to manually clear the status.
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
627d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t ps2_rxint(int irq, void *dev_id)
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ps2if *ps2if = dev_id;
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int scancode, flag, status;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
674f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King	status = sa1111_readl(ps2if->base + PS2STAT);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (status & PS2STAT_RXF) {
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (status & PS2STAT_STP)
704f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King			sa1111_writel(PS2STAT_STP, ps2if->base + PS2STAT);
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) |
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       (status & PS2STAT_RXP ? 0 : SERIO_PARITY);
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
754f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King		scancode = sa1111_readl(ps2if->base + PS2DATA) & 0xff;
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (hweight8(scancode) & 1)
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			flag ^= SERIO_PARITY;
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
807d12e780e003f93433d49ce78cfedf4b4c52adc5David Howells		serio_interrupt(ps2if->io, scancode, flag);
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
824f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King		status = sa1111_readl(ps2if->base + PS2STAT);
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        return IRQ_HANDLED;
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Completion of ps2 write
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
917d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t ps2_txint(int irq, void *dev_id)
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ps2if *ps2if = dev_id;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int status;
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock(&ps2if->lock);
974f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King	status = sa1111_readl(ps2if->base + PS2STAT);
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ps2if->head == ps2if->tail) {
99e4bd3e591c8cc52ccf7a0b27f33aa7a5a19058d7Ben Nizette		disable_irq_nosync(irq);
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* done */
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (status & PS2STAT_TXE) {
1024f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King		sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + PS2DATA);
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1);
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock(&ps2if->lock);
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Write a byte to the PS2 port.  We have to wait for the
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * port to indicate that the transmitter is empty.
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ps2_write(struct serio *io, unsigned char val)
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ps2if *ps2if = io->port_data;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int head;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&ps2if->lock, flags);
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * If the TX register is empty, we can go straight out.
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1254f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King	if (sa1111_readl(ps2if->base + PS2STAT) & PS2STAT_TXE) {
1264f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King		sa1111_writel(val, ps2if->base + PS2DATA);
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ps2if->head == ps2if->tail)
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			enable_irq(ps2if->dev->irq[1]);
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		head = (ps2if->head + 1) & (sizeof(ps2if->buf) - 1);
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (head != ps2if->tail) {
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ps2if->buf[ps2if->head] = val;
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ps2if->head = head;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&ps2if->lock, flags);
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ps2_open(struct serio *io)
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ps2if *ps2if = io->port_data;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
146ae99ddbc976572194e8a68cb9ca1e27805ce30c7Russell King	ret = sa1111_enable_device(ps2if->dev);
147ae99ddbc976572194e8a68cb9ca1e27805ce30c7Russell King	if (ret)
148ae99ddbc976572194e8a68cb9ca1e27805ce30c7Russell King		return ret;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = request_irq(ps2if->dev->irq[0], ps2_rxint, 0,
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			  SA1111_DRIVER_NAME(ps2if->dev), ps2if);
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret) {
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ps2if->dev->irq[0], ret);
155ae99ddbc976572194e8a68cb9ca1e27805ce30c7Russell King		sa1111_disable_device(ps2if->dev);
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = request_irq(ps2if->dev->irq[1], ps2_txint, 0,
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			  SA1111_DRIVER_NAME(ps2if->dev), ps2if);
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret) {
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ps2if->dev->irq[1], ret);
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_irq(ps2if->dev->irq[0], ps2if);
165ae99ddbc976572194e8a68cb9ca1e27805ce30c7Russell King		sa1111_disable_device(ps2if->dev);
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2if->open = 1;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	enable_irq_wake(ps2if->dev->irq[0]);
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1734f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King	sa1111_writel(PS2CR_ENA, ps2if->base + PS2CR);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void ps2_close(struct serio *io)
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ps2if *ps2if = io->port_data;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1814f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King	sa1111_writel(0, ps2if->base + PS2CR);
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	disable_irq_wake(ps2if->dev->irq[0]);
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2if->open = 0;
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(ps2if->dev->irq[1], ps2if);
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(ps2if->dev->irq[0], ps2if);
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sa1111_disable_device(ps2if->dev);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Clear the input buffer.
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1965298cc4cc753bbe4c530b41341834f6ef3344d0dBill Pembertonstatic void ps2_clear_input(struct ps2if *ps2if)
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int maxread = 100;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (maxread--) {
2014f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King		if ((sa1111_readl(ps2if->base + PS2DATA) & 0xff) == 0xff)
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2065298cc4cc753bbe4c530b41341834f6ef3344d0dBill Pembertonstatic unsigned int ps2_test_one(struct ps2if *ps2if,
207010c33cc7907239ffc8f49f09ccb3dc6d84a0369Dmitry Torokhov					   unsigned int mask)
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int val;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2114f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King	sa1111_writel(PS2CR_ENA | mask, ps2if->base + PS2CR);
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	udelay(2);
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2154f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King	val = sa1111_readl(ps2if->base + PS2STAT);
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return val & (PS2STAT_KBC | PS2STAT_KBD);
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Test the keyboard interface.  We basically check to make sure that
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * we can drive each line to the keyboard independently of each other.
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2235298cc4cc753bbe4c530b41341834f6ef3344d0dBill Pembertonstatic int ps2_test(struct ps2if *ps2if)
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int stat;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = 0;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	stat = ps2_test_one(ps2if, PS2CR_FKC);
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (stat != PS2STAT_KBD) {
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("PS/2 interface test failed[1]: %02x\n", stat);
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -ENODEV;
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	stat = ps2_test_one(ps2if, 0);
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (stat != (PS2STAT_KBC | PS2STAT_KBD)) {
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("PS/2 interface test failed[2]: %02x\n", stat);
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -ENODEV;
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	stat = ps2_test_one(ps2if, PS2CR_FKD);
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (stat != PS2STAT_KBC) {
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("PS/2 interface test failed[3]: %02x\n", stat);
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -ENODEV;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2464f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King	sa1111_writel(0, ps2if->base + PS2CR);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Add one device to this driver.
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2545298cc4cc753bbe4c530b41341834f6ef3344d0dBill Pembertonstatic int ps2_probe(struct sa1111_dev *dev)
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ps2if *ps2if;
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct serio *serio;
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
260dd00cc486ab1c17049a535413d1751ef3482141cYoann Padioleau	ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL);
261dd00cc486ab1c17049a535413d1751ef3482141cYoann Padioleau	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ps2if || !serio) {
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -ENOMEM;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free;
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio->id.type		= SERIO_8042;
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio->write		= ps2_write;
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio->open		= ps2_open;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio->close		= ps2_close;
2724e8718a1f960db0c48427f4583f89f4cb62f2480Kay Sievers	strlcpy(serio->name, dev_name(&dev->dev), sizeof(serio->name));
2734e8718a1f960db0c48427f4583f89f4cb62f2480Kay Sievers	strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio->port_data	= ps2if;
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio->dev.parent	= &dev->dev;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2if->io		= serio;
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2if->dev		= dev;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sa1111_set_drvdata(dev, ps2if);
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&ps2if->lock);
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Request the physical region for this PS2 port.
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_mem_region(dev->res.start,
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dev->res.end - dev->res.start + 1,
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				SA1111_DRIVER_NAME(dev))) {
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -EBUSY;
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free;
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Our parent device has already mapped the region.
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2if->base = dev->mapbase;
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sa1111_enable_device(ps2if->dev);
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Incoming clock is 8MHz */
3004f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King	sa1111_writel(0, ps2if->base + PS2CLKDIV);
3014f8d9cae15b5b5c89ec17c8168215aa06a5c9b2cRussell King	sa1111_writel(127, ps2if->base + PS2PRECNT);
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Flush any pending input.
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2_clear_input(ps2if);
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Test the keyboard interface.
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = ps2_test(ps2if);
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret)
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Flush any pending input.
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2_clear_input(ps2if);
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sa1111_disable_device(ps2if->dev);
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_register_port(ps2if->io);
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds out:
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sa1111_disable_device(ps2if->dev);
32628f65c11f2ffb3957259dece647a24f8ad2e241bJoe Perches	release_mem_region(dev->res.start, resource_size(&dev->res));
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free:
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sa1111_set_drvdata(dev, NULL);
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(ps2if);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(serio);
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Remove one device from this driver.
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
337e2619cf78e19476bfd7ceaefa9eff0847529346eBill Pembertonstatic int ps2_remove(struct sa1111_dev *dev)
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ps2if *ps2if = sa1111_get_drvdata(dev);
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_unregister_port(ps2if->io);
34228f65c11f2ffb3957259dece647a24f8ad2e241bJoe Perches	release_mem_region(dev->res.start, resource_size(&dev->res));
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sa1111_set_drvdata(dev, NULL);
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(ps2if);
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Our device driver structure
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct sa1111_driver ps2_driver = {
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.drv = {
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.name	= "sa1111-ps2",
3561ebcd7654e4e391a36945c937c125995c737c446Russell King		.owner	= THIS_MODULE,
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.devid		= SA1111_DEVID_PS2,
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe		= ps2_probe,
3601cb0aa88179b7a71c240529e9d781d7bbb43d2e8Bill Pemberton	.remove		= ps2_remove,
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init ps2_init(void)
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return sa1111_driver_register(&ps2_driver);
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit ps2_exit(void)
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sa1111_driver_unregister(&ps2_driver);
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(ps2_init);
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(ps2_exit);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("SA1111 PS2 controller driver");
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
379