18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/drivers/input/serio/sa1111ps2.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2002 Russell King
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/input.h>
108c2ecf20Sopenharmony_ci#include <linux/serio.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/ioport.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/device.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/io.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <asm/hardware/sa1111.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define PS2CR		0x0000
248c2ecf20Sopenharmony_ci#define PS2STAT		0x0004
258c2ecf20Sopenharmony_ci#define PS2DATA		0x0008
268c2ecf20Sopenharmony_ci#define PS2CLKDIV	0x000c
278c2ecf20Sopenharmony_ci#define PS2PRECNT	0x0010
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define PS2CR_ENA	0x08
308c2ecf20Sopenharmony_ci#define PS2CR_FKD	0x02
318c2ecf20Sopenharmony_ci#define PS2CR_FKC	0x01
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define PS2STAT_STP	0x0100
348c2ecf20Sopenharmony_ci#define PS2STAT_TXE	0x0080
358c2ecf20Sopenharmony_ci#define PS2STAT_TXB	0x0040
368c2ecf20Sopenharmony_ci#define PS2STAT_RXF	0x0020
378c2ecf20Sopenharmony_ci#define PS2STAT_RXB	0x0010
388c2ecf20Sopenharmony_ci#define PS2STAT_ENA	0x0008
398c2ecf20Sopenharmony_ci#define PS2STAT_RXP	0x0004
408c2ecf20Sopenharmony_ci#define PS2STAT_KBD	0x0002
418c2ecf20Sopenharmony_ci#define PS2STAT_KBC	0x0001
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct ps2if {
448c2ecf20Sopenharmony_ci	struct serio		*io;
458c2ecf20Sopenharmony_ci	struct sa1111_dev	*dev;
468c2ecf20Sopenharmony_ci	void __iomem		*base;
478c2ecf20Sopenharmony_ci	int			rx_irq;
488c2ecf20Sopenharmony_ci	int			tx_irq;
498c2ecf20Sopenharmony_ci	unsigned int		open;
508c2ecf20Sopenharmony_ci	spinlock_t		lock;
518c2ecf20Sopenharmony_ci	unsigned int		head;
528c2ecf20Sopenharmony_ci	unsigned int		tail;
538c2ecf20Sopenharmony_ci	unsigned char		buf[4];
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * Read all bytes waiting in the PS2 port.  There should be
588c2ecf20Sopenharmony_ci * at the most one, but we loop for safety.  If there was a
598c2ecf20Sopenharmony_ci * framing error, we have to manually clear the status.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_cistatic irqreturn_t ps2_rxint(int irq, void *dev_id)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct ps2if *ps2if = dev_id;
648c2ecf20Sopenharmony_ci	unsigned int scancode, flag, status;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	status = readl_relaxed(ps2if->base + PS2STAT);
678c2ecf20Sopenharmony_ci	while (status & PS2STAT_RXF) {
688c2ecf20Sopenharmony_ci		if (status & PS2STAT_STP)
698c2ecf20Sopenharmony_ci			writel_relaxed(PS2STAT_STP, ps2if->base + PS2STAT);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) |
728c2ecf20Sopenharmony_ci		       (status & PS2STAT_RXP ? 0 : SERIO_PARITY);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci		scancode = readl_relaxed(ps2if->base + PS2DATA) & 0xff;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci		if (hweight8(scancode) & 1)
778c2ecf20Sopenharmony_ci			flag ^= SERIO_PARITY;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		serio_interrupt(ps2if->io, scancode, flag);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci		status = readl_relaxed(ps2if->base + PS2STAT);
828c2ecf20Sopenharmony_ci        }
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci        return IRQ_HANDLED;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/*
888c2ecf20Sopenharmony_ci * Completion of ps2 write
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_cistatic irqreturn_t ps2_txint(int irq, void *dev_id)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct ps2if *ps2if = dev_id;
938c2ecf20Sopenharmony_ci	unsigned int status;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	spin_lock(&ps2if->lock);
968c2ecf20Sopenharmony_ci	status = readl_relaxed(ps2if->base + PS2STAT);
978c2ecf20Sopenharmony_ci	if (ps2if->head == ps2if->tail) {
988c2ecf20Sopenharmony_ci		disable_irq_nosync(irq);
998c2ecf20Sopenharmony_ci		/* done */
1008c2ecf20Sopenharmony_ci	} else if (status & PS2STAT_TXE) {
1018c2ecf20Sopenharmony_ci		writel_relaxed(ps2if->buf[ps2if->tail], ps2if->base + PS2DATA);
1028c2ecf20Sopenharmony_ci		ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1);
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci	spin_unlock(&ps2if->lock);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * Write a byte to the PS2 port.  We have to wait for the
1118c2ecf20Sopenharmony_ci * port to indicate that the transmitter is empty.
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cistatic int ps2_write(struct serio *io, unsigned char val)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct ps2if *ps2if = io->port_data;
1168c2ecf20Sopenharmony_ci	unsigned long flags;
1178c2ecf20Sopenharmony_ci	unsigned int head;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ps2if->lock, flags);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/*
1228c2ecf20Sopenharmony_ci	 * If the TX register is empty, we can go straight out.
1238c2ecf20Sopenharmony_ci	 */
1248c2ecf20Sopenharmony_ci	if (readl_relaxed(ps2if->base + PS2STAT) & PS2STAT_TXE) {
1258c2ecf20Sopenharmony_ci		writel_relaxed(val, ps2if->base + PS2DATA);
1268c2ecf20Sopenharmony_ci	} else {
1278c2ecf20Sopenharmony_ci		if (ps2if->head == ps2if->tail)
1288c2ecf20Sopenharmony_ci			enable_irq(ps2if->tx_irq);
1298c2ecf20Sopenharmony_ci		head = (ps2if->head + 1) & (sizeof(ps2if->buf) - 1);
1308c2ecf20Sopenharmony_ci		if (head != ps2if->tail) {
1318c2ecf20Sopenharmony_ci			ps2if->buf[ps2if->head] = val;
1328c2ecf20Sopenharmony_ci			ps2if->head = head;
1338c2ecf20Sopenharmony_ci		}
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ps2if->lock, flags);
1378c2ecf20Sopenharmony_ci	return 0;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int ps2_open(struct serio *io)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct ps2if *ps2if = io->port_data;
1438c2ecf20Sopenharmony_ci	int ret;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	ret = sa1111_enable_device(ps2if->dev);
1468c2ecf20Sopenharmony_ci	if (ret)
1478c2ecf20Sopenharmony_ci		return ret;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	ret = request_irq(ps2if->rx_irq, ps2_rxint, 0,
1508c2ecf20Sopenharmony_ci			  SA1111_DRIVER_NAME(ps2if->dev), ps2if);
1518c2ecf20Sopenharmony_ci	if (ret) {
1528c2ecf20Sopenharmony_ci		printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
1538c2ecf20Sopenharmony_ci			ps2if->rx_irq, ret);
1548c2ecf20Sopenharmony_ci		sa1111_disable_device(ps2if->dev);
1558c2ecf20Sopenharmony_ci		return ret;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	ret = request_irq(ps2if->tx_irq, ps2_txint, 0,
1598c2ecf20Sopenharmony_ci			  SA1111_DRIVER_NAME(ps2if->dev), ps2if);
1608c2ecf20Sopenharmony_ci	if (ret) {
1618c2ecf20Sopenharmony_ci		printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
1628c2ecf20Sopenharmony_ci			ps2if->tx_irq, ret);
1638c2ecf20Sopenharmony_ci		free_irq(ps2if->rx_irq, ps2if);
1648c2ecf20Sopenharmony_ci		sa1111_disable_device(ps2if->dev);
1658c2ecf20Sopenharmony_ci		return ret;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	ps2if->open = 1;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	enable_irq_wake(ps2if->rx_irq);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	writel_relaxed(PS2CR_ENA, ps2if->base + PS2CR);
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void ps2_close(struct serio *io)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct ps2if *ps2if = io->port_data;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	writel_relaxed(0, ps2if->base + PS2CR);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	disable_irq_wake(ps2if->rx_irq);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	ps2if->open = 0;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	free_irq(ps2if->tx_irq, ps2if);
1878c2ecf20Sopenharmony_ci	free_irq(ps2if->rx_irq, ps2if);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	sa1111_disable_device(ps2if->dev);
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/*
1938c2ecf20Sopenharmony_ci * Clear the input buffer.
1948c2ecf20Sopenharmony_ci */
1958c2ecf20Sopenharmony_cistatic void ps2_clear_input(struct ps2if *ps2if)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	int maxread = 100;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	while (maxread--) {
2008c2ecf20Sopenharmony_ci		if ((readl_relaxed(ps2if->base + PS2DATA) & 0xff) == 0xff)
2018c2ecf20Sopenharmony_ci			break;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic unsigned int ps2_test_one(struct ps2if *ps2if,
2068c2ecf20Sopenharmony_ci					   unsigned int mask)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	unsigned int val;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	writel_relaxed(PS2CR_ENA | mask, ps2if->base + PS2CR);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	udelay(10);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	val = readl_relaxed(ps2if->base + PS2STAT);
2158c2ecf20Sopenharmony_ci	return val & (PS2STAT_KBC | PS2STAT_KBD);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/*
2198c2ecf20Sopenharmony_ci * Test the keyboard interface.  We basically check to make sure that
2208c2ecf20Sopenharmony_ci * we can drive each line to the keyboard independently of each other.
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_cistatic int ps2_test(struct ps2if *ps2if)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	unsigned int stat;
2258c2ecf20Sopenharmony_ci	int ret = 0;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	stat = ps2_test_one(ps2if, PS2CR_FKC);
2288c2ecf20Sopenharmony_ci	if (stat != PS2STAT_KBD) {
2298c2ecf20Sopenharmony_ci		printk("PS/2 interface test failed[1]: %02x\n", stat);
2308c2ecf20Sopenharmony_ci		ret = -ENODEV;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	stat = ps2_test_one(ps2if, 0);
2348c2ecf20Sopenharmony_ci	if (stat != (PS2STAT_KBC | PS2STAT_KBD)) {
2358c2ecf20Sopenharmony_ci		printk("PS/2 interface test failed[2]: %02x\n", stat);
2368c2ecf20Sopenharmony_ci		ret = -ENODEV;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	stat = ps2_test_one(ps2if, PS2CR_FKD);
2408c2ecf20Sopenharmony_ci	if (stat != PS2STAT_KBC) {
2418c2ecf20Sopenharmony_ci		printk("PS/2 interface test failed[3]: %02x\n", stat);
2428c2ecf20Sopenharmony_ci		ret = -ENODEV;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	writel_relaxed(0, ps2if->base + PS2CR);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return ret;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/*
2518c2ecf20Sopenharmony_ci * Add one device to this driver.
2528c2ecf20Sopenharmony_ci */
2538c2ecf20Sopenharmony_cistatic int ps2_probe(struct sa1111_dev *dev)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct ps2if *ps2if;
2568c2ecf20Sopenharmony_ci	struct serio *serio;
2578c2ecf20Sopenharmony_ci	int ret;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL);
2608c2ecf20Sopenharmony_ci	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
2618c2ecf20Sopenharmony_ci	if (!ps2if || !serio) {
2628c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2638c2ecf20Sopenharmony_ci		goto free;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	serio->id.type		= SERIO_8042;
2678c2ecf20Sopenharmony_ci	serio->write		= ps2_write;
2688c2ecf20Sopenharmony_ci	serio->open		= ps2_open;
2698c2ecf20Sopenharmony_ci	serio->close		= ps2_close;
2708c2ecf20Sopenharmony_ci	strlcpy(serio->name, dev_name(&dev->dev), sizeof(serio->name));
2718c2ecf20Sopenharmony_ci	strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
2728c2ecf20Sopenharmony_ci	serio->port_data	= ps2if;
2738c2ecf20Sopenharmony_ci	serio->dev.parent	= &dev->dev;
2748c2ecf20Sopenharmony_ci	ps2if->io		= serio;
2758c2ecf20Sopenharmony_ci	ps2if->dev		= dev;
2768c2ecf20Sopenharmony_ci	sa1111_set_drvdata(dev, ps2if);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	spin_lock_init(&ps2if->lock);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	ps2if->rx_irq = sa1111_get_irq(dev, 0);
2818c2ecf20Sopenharmony_ci	if (ps2if->rx_irq <= 0) {
2828c2ecf20Sopenharmony_ci		ret = ps2if->rx_irq ? : -ENXIO;
2838c2ecf20Sopenharmony_ci		goto free;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	ps2if->tx_irq = sa1111_get_irq(dev, 1);
2878c2ecf20Sopenharmony_ci	if (ps2if->tx_irq <= 0) {
2888c2ecf20Sopenharmony_ci		ret = ps2if->tx_irq ? : -ENXIO;
2898c2ecf20Sopenharmony_ci		goto free;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/*
2938c2ecf20Sopenharmony_ci	 * Request the physical region for this PS2 port.
2948c2ecf20Sopenharmony_ci	 */
2958c2ecf20Sopenharmony_ci	if (!request_mem_region(dev->res.start,
2968c2ecf20Sopenharmony_ci				dev->res.end - dev->res.start + 1,
2978c2ecf20Sopenharmony_ci				SA1111_DRIVER_NAME(dev))) {
2988c2ecf20Sopenharmony_ci		ret = -EBUSY;
2998c2ecf20Sopenharmony_ci		goto free;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/*
3038c2ecf20Sopenharmony_ci	 * Our parent device has already mapped the region.
3048c2ecf20Sopenharmony_ci	 */
3058c2ecf20Sopenharmony_ci	ps2if->base = dev->mapbase;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	sa1111_enable_device(ps2if->dev);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* Incoming clock is 8MHz */
3108c2ecf20Sopenharmony_ci	writel_relaxed(0, ps2if->base + PS2CLKDIV);
3118c2ecf20Sopenharmony_ci	writel_relaxed(127, ps2if->base + PS2PRECNT);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/*
3148c2ecf20Sopenharmony_ci	 * Flush any pending input.
3158c2ecf20Sopenharmony_ci	 */
3168c2ecf20Sopenharmony_ci	ps2_clear_input(ps2if);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/*
3198c2ecf20Sopenharmony_ci	 * Test the keyboard interface.
3208c2ecf20Sopenharmony_ci	 */
3218c2ecf20Sopenharmony_ci	ret = ps2_test(ps2if);
3228c2ecf20Sopenharmony_ci	if (ret)
3238c2ecf20Sopenharmony_ci		goto out;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/*
3268c2ecf20Sopenharmony_ci	 * Flush any pending input.
3278c2ecf20Sopenharmony_ci	 */
3288c2ecf20Sopenharmony_ci	ps2_clear_input(ps2if);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	sa1111_disable_device(ps2if->dev);
3318c2ecf20Sopenharmony_ci	serio_register_port(ps2if->io);
3328c2ecf20Sopenharmony_ci	return 0;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci out:
3358c2ecf20Sopenharmony_ci	sa1111_disable_device(ps2if->dev);
3368c2ecf20Sopenharmony_ci	release_mem_region(dev->res.start, resource_size(&dev->res));
3378c2ecf20Sopenharmony_ci free:
3388c2ecf20Sopenharmony_ci	sa1111_set_drvdata(dev, NULL);
3398c2ecf20Sopenharmony_ci	kfree(ps2if);
3408c2ecf20Sopenharmony_ci	kfree(serio);
3418c2ecf20Sopenharmony_ci	return ret;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci/*
3458c2ecf20Sopenharmony_ci * Remove one device from this driver.
3468c2ecf20Sopenharmony_ci */
3478c2ecf20Sopenharmony_cistatic int ps2_remove(struct sa1111_dev *dev)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct ps2if *ps2if = sa1111_get_drvdata(dev);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	serio_unregister_port(ps2if->io);
3528c2ecf20Sopenharmony_ci	release_mem_region(dev->res.start, resource_size(&dev->res));
3538c2ecf20Sopenharmony_ci	sa1111_set_drvdata(dev, NULL);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	kfree(ps2if);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return 0;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci/*
3618c2ecf20Sopenharmony_ci * Our device driver structure
3628c2ecf20Sopenharmony_ci */
3638c2ecf20Sopenharmony_cistatic struct sa1111_driver ps2_driver = {
3648c2ecf20Sopenharmony_ci	.drv = {
3658c2ecf20Sopenharmony_ci		.name	= "sa1111-ps2",
3668c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
3678c2ecf20Sopenharmony_ci	},
3688c2ecf20Sopenharmony_ci	.devid		= SA1111_DEVID_PS2,
3698c2ecf20Sopenharmony_ci	.probe		= ps2_probe,
3708c2ecf20Sopenharmony_ci	.remove		= ps2_remove,
3718c2ecf20Sopenharmony_ci};
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic int __init ps2_init(void)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	return sa1111_driver_register(&ps2_driver);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic void __exit ps2_exit(void)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	sa1111_driver_unregister(&ps2_driver);
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cimodule_init(ps2_init);
3848c2ecf20Sopenharmony_cimodule_exit(ps2_exit);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
3878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SA1111 PS2 controller driver");
3888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
389