18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Xilinx XPS PS/2 device driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (c) 2005 MontaVista Software, Inc.
68c2ecf20Sopenharmony_ci * (c) 2008 Xilinx, Inc.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/serio.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/list.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci#include <linux/of_address.h>
188c2ecf20Sopenharmony_ci#include <linux/of_device.h>
198c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
208c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define DRIVER_NAME		"xilinx_ps2"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* Register offsets for the xps2 device */
258c2ecf20Sopenharmony_ci#define XPS2_SRST_OFFSET	0x00000000 /* Software Reset register */
268c2ecf20Sopenharmony_ci#define XPS2_STATUS_OFFSET	0x00000004 /* Status register */
278c2ecf20Sopenharmony_ci#define XPS2_RX_DATA_OFFSET	0x00000008 /* Receive Data register */
288c2ecf20Sopenharmony_ci#define XPS2_TX_DATA_OFFSET	0x0000000C /* Transmit Data register */
298c2ecf20Sopenharmony_ci#define XPS2_GIER_OFFSET	0x0000002C /* Global Interrupt Enable reg */
308c2ecf20Sopenharmony_ci#define XPS2_IPISR_OFFSET	0x00000030 /* Interrupt Status register */
318c2ecf20Sopenharmony_ci#define XPS2_IPIER_OFFSET	0x00000038 /* Interrupt Enable register */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* Reset Register Bit Definitions */
348c2ecf20Sopenharmony_ci#define XPS2_SRST_RESET		0x0000000A /* Software Reset  */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* Status Register Bit Positions */
378c2ecf20Sopenharmony_ci#define XPS2_STATUS_RX_FULL	0x00000001 /* Receive Full  */
388c2ecf20Sopenharmony_ci#define XPS2_STATUS_TX_FULL	0x00000002 /* Transmit Full  */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * Bit definitions for ISR/IER registers. Both the registers have the same bit
428c2ecf20Sopenharmony_ci * definitions and are only defined once.
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ci#define XPS2_IPIXR_WDT_TOUT	0x00000001 /* Watchdog Timeout Interrupt */
458c2ecf20Sopenharmony_ci#define XPS2_IPIXR_TX_NOACK	0x00000002 /* Transmit No ACK Interrupt */
468c2ecf20Sopenharmony_ci#define XPS2_IPIXR_TX_ACK	0x00000004 /* Transmit ACK (Data) Interrupt */
478c2ecf20Sopenharmony_ci#define XPS2_IPIXR_RX_OVF	0x00000008 /* Receive Overflow Interrupt */
488c2ecf20Sopenharmony_ci#define XPS2_IPIXR_RX_ERR	0x00000010 /* Receive Error Interrupt */
498c2ecf20Sopenharmony_ci#define XPS2_IPIXR_RX_FULL	0x00000020 /* Receive Data Interrupt */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* Mask for all the Transmit Interrupts */
528c2ecf20Sopenharmony_ci#define XPS2_IPIXR_TX_ALL	(XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_TX_ACK)
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* Mask for all the Receive Interrupts */
558c2ecf20Sopenharmony_ci#define XPS2_IPIXR_RX_ALL	(XPS2_IPIXR_RX_OVF | XPS2_IPIXR_RX_ERR |  \
568c2ecf20Sopenharmony_ci				 XPS2_IPIXR_RX_FULL)
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* Mask for all the Interrupts */
598c2ecf20Sopenharmony_ci#define XPS2_IPIXR_ALL		(XPS2_IPIXR_TX_ALL | XPS2_IPIXR_RX_ALL |  \
608c2ecf20Sopenharmony_ci				 XPS2_IPIXR_WDT_TOUT)
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Global Interrupt Enable mask */
638c2ecf20Sopenharmony_ci#define XPS2_GIER_GIE_MASK	0x80000000
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistruct xps2data {
668c2ecf20Sopenharmony_ci	int irq;
678c2ecf20Sopenharmony_ci	spinlock_t lock;
688c2ecf20Sopenharmony_ci	void __iomem *base_address;	/* virt. address of control registers */
698c2ecf20Sopenharmony_ci	unsigned int flags;
708c2ecf20Sopenharmony_ci	struct serio *serio;		/* serio */
718c2ecf20Sopenharmony_ci	struct device *dev;
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/************************************/
758c2ecf20Sopenharmony_ci/* XPS PS/2 data transmission calls */
768c2ecf20Sopenharmony_ci/************************************/
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/**
798c2ecf20Sopenharmony_ci * xps2_recv() - attempts to receive a byte from the PS/2 port.
808c2ecf20Sopenharmony_ci * @drvdata:	pointer to ps2 device private data structure
818c2ecf20Sopenharmony_ci * @byte:	address where the read data will be copied
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci * If there is any data available in the PS/2 receiver, this functions reads
848c2ecf20Sopenharmony_ci * the data, otherwise it returns error.
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_cistatic int xps2_recv(struct xps2data *drvdata, u8 *byte)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	u32 sr;
898c2ecf20Sopenharmony_ci	int status = -1;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* If there is data available in the PS/2 receiver, read it */
928c2ecf20Sopenharmony_ci	sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET);
938c2ecf20Sopenharmony_ci	if (sr & XPS2_STATUS_RX_FULL) {
948c2ecf20Sopenharmony_ci		*byte = in_be32(drvdata->base_address + XPS2_RX_DATA_OFFSET);
958c2ecf20Sopenharmony_ci		status = 0;
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return status;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/*********************/
1028c2ecf20Sopenharmony_ci/* Interrupt handler */
1038c2ecf20Sopenharmony_ci/*********************/
1048c2ecf20Sopenharmony_cistatic irqreturn_t xps2_interrupt(int irq, void *dev_id)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct xps2data *drvdata = dev_id;
1078c2ecf20Sopenharmony_ci	u32 intr_sr;
1088c2ecf20Sopenharmony_ci	u8 c;
1098c2ecf20Sopenharmony_ci	int status;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* Get the PS/2 interrupts and clear them */
1128c2ecf20Sopenharmony_ci	intr_sr = in_be32(drvdata->base_address + XPS2_IPISR_OFFSET);
1138c2ecf20Sopenharmony_ci	out_be32(drvdata->base_address + XPS2_IPISR_OFFSET, intr_sr);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* Check which interrupt is active */
1168c2ecf20Sopenharmony_ci	if (intr_sr & XPS2_IPIXR_RX_OVF)
1178c2ecf20Sopenharmony_ci		dev_warn(drvdata->dev, "receive overrun error\n");
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (intr_sr & XPS2_IPIXR_RX_ERR)
1208c2ecf20Sopenharmony_ci		drvdata->flags |= SERIO_PARITY;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (intr_sr & (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_WDT_TOUT))
1238c2ecf20Sopenharmony_ci		drvdata->flags |= SERIO_TIMEOUT;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (intr_sr & XPS2_IPIXR_RX_FULL) {
1268c2ecf20Sopenharmony_ci		status = xps2_recv(drvdata, &c);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		/* Error, if a byte is not received */
1298c2ecf20Sopenharmony_ci		if (status) {
1308c2ecf20Sopenharmony_ci			dev_err(drvdata->dev,
1318c2ecf20Sopenharmony_ci				"wrong rcvd byte count (%d)\n", status);
1328c2ecf20Sopenharmony_ci		} else {
1338c2ecf20Sopenharmony_ci			serio_interrupt(drvdata->serio, c, drvdata->flags);
1348c2ecf20Sopenharmony_ci			drvdata->flags = 0;
1358c2ecf20Sopenharmony_ci		}
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci/*******************/
1428c2ecf20Sopenharmony_ci/* serio callbacks */
1438c2ecf20Sopenharmony_ci/*******************/
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/**
1468c2ecf20Sopenharmony_ci * sxps2_write() - sends a byte out through the PS/2 port.
1478c2ecf20Sopenharmony_ci * @pserio:	pointer to the serio structure of the PS/2 port
1488c2ecf20Sopenharmony_ci * @c:		data that needs to be written to the PS/2 port
1498c2ecf20Sopenharmony_ci *
1508c2ecf20Sopenharmony_ci * This function checks if the PS/2 transmitter is empty and sends a byte.
1518c2ecf20Sopenharmony_ci * Otherwise it returns error. Transmission fails only when nothing is connected
1528c2ecf20Sopenharmony_ci * to the PS/2 port. Thats why, we do not try to resend the data in case of a
1538c2ecf20Sopenharmony_ci * failure.
1548c2ecf20Sopenharmony_ci */
1558c2ecf20Sopenharmony_cistatic int sxps2_write(struct serio *pserio, unsigned char c)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct xps2data *drvdata = pserio->port_data;
1588c2ecf20Sopenharmony_ci	unsigned long flags;
1598c2ecf20Sopenharmony_ci	u32 sr;
1608c2ecf20Sopenharmony_ci	int status = -1;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	spin_lock_irqsave(&drvdata->lock, flags);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* If the PS/2 transmitter is empty send a byte of data */
1658c2ecf20Sopenharmony_ci	sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET);
1668c2ecf20Sopenharmony_ci	if (!(sr & XPS2_STATUS_TX_FULL)) {
1678c2ecf20Sopenharmony_ci		out_be32(drvdata->base_address + XPS2_TX_DATA_OFFSET, c);
1688c2ecf20Sopenharmony_ci		status = 0;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&drvdata->lock, flags);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return status;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/**
1778c2ecf20Sopenharmony_ci * sxps2_open() - called when a port is opened by the higher layer.
1788c2ecf20Sopenharmony_ci * @pserio:	pointer to the serio structure of the PS/2 device
1798c2ecf20Sopenharmony_ci *
1808c2ecf20Sopenharmony_ci * This function requests irq and enables interrupts for the PS/2 device.
1818c2ecf20Sopenharmony_ci */
1828c2ecf20Sopenharmony_cistatic int sxps2_open(struct serio *pserio)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct xps2data *drvdata = pserio->port_data;
1858c2ecf20Sopenharmony_ci	int error;
1868c2ecf20Sopenharmony_ci	u8 c;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	error = request_irq(drvdata->irq, &xps2_interrupt, 0,
1898c2ecf20Sopenharmony_ci				DRIVER_NAME, drvdata);
1908c2ecf20Sopenharmony_ci	if (error) {
1918c2ecf20Sopenharmony_ci		dev_err(drvdata->dev,
1928c2ecf20Sopenharmony_ci			"Couldn't allocate interrupt %d\n", drvdata->irq);
1938c2ecf20Sopenharmony_ci		return error;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* start reception by enabling the interrupts */
1978c2ecf20Sopenharmony_ci	out_be32(drvdata->base_address + XPS2_GIER_OFFSET, XPS2_GIER_GIE_MASK);
1988c2ecf20Sopenharmony_ci	out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, XPS2_IPIXR_RX_ALL);
1998c2ecf20Sopenharmony_ci	(void)xps2_recv(drvdata, &c);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return 0;		/* success */
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/**
2058c2ecf20Sopenharmony_ci * sxps2_close() - frees the interrupt.
2068c2ecf20Sopenharmony_ci * @pserio:	pointer to the serio structure of the PS/2 device
2078c2ecf20Sopenharmony_ci *
2088c2ecf20Sopenharmony_ci * This function frees the irq and disables interrupts for the PS/2 device.
2098c2ecf20Sopenharmony_ci */
2108c2ecf20Sopenharmony_cistatic void sxps2_close(struct serio *pserio)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct xps2data *drvdata = pserio->port_data;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* Disable the PS2 interrupts */
2158c2ecf20Sopenharmony_ci	out_be32(drvdata->base_address + XPS2_GIER_OFFSET, 0x00);
2168c2ecf20Sopenharmony_ci	out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0x00);
2178c2ecf20Sopenharmony_ci	free_irq(drvdata->irq, drvdata);
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci/**
2218c2ecf20Sopenharmony_ci * xps2_of_probe - probe method for the PS/2 device.
2228c2ecf20Sopenharmony_ci * @of_dev:	pointer to OF device structure
2238c2ecf20Sopenharmony_ci * @match:	pointer to the structure used for matching a device
2248c2ecf20Sopenharmony_ci *
2258c2ecf20Sopenharmony_ci * This function probes the PS/2 device in the device tree.
2268c2ecf20Sopenharmony_ci * It initializes the driver data structure and the hardware.
2278c2ecf20Sopenharmony_ci * It returns 0, if the driver is bound to the PS/2 device, or a negative
2288c2ecf20Sopenharmony_ci * value if there is an error.
2298c2ecf20Sopenharmony_ci */
2308c2ecf20Sopenharmony_cistatic int xps2_of_probe(struct platform_device *ofdev)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct resource r_mem; /* IO mem resources */
2338c2ecf20Sopenharmony_ci	struct xps2data *drvdata;
2348c2ecf20Sopenharmony_ci	struct serio *serio;
2358c2ecf20Sopenharmony_ci	struct device *dev = &ofdev->dev;
2368c2ecf20Sopenharmony_ci	resource_size_t remap_size, phys_addr;
2378c2ecf20Sopenharmony_ci	unsigned int irq;
2388c2ecf20Sopenharmony_ci	int error;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	dev_info(dev, "Device Tree Probing \'%pOFn\'\n", dev->of_node);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* Get iospace for the device */
2438c2ecf20Sopenharmony_ci	error = of_address_to_resource(dev->of_node, 0, &r_mem);
2448c2ecf20Sopenharmony_ci	if (error) {
2458c2ecf20Sopenharmony_ci		dev_err(dev, "invalid address\n");
2468c2ecf20Sopenharmony_ci		return error;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/* Get IRQ for the device */
2508c2ecf20Sopenharmony_ci	irq = irq_of_parse_and_map(dev->of_node, 0);
2518c2ecf20Sopenharmony_ci	if (!irq) {
2528c2ecf20Sopenharmony_ci		dev_err(dev, "no IRQ found\n");
2538c2ecf20Sopenharmony_ci		return -ENODEV;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	drvdata = kzalloc(sizeof(struct xps2data), GFP_KERNEL);
2578c2ecf20Sopenharmony_ci	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
2588c2ecf20Sopenharmony_ci	if (!drvdata || !serio) {
2598c2ecf20Sopenharmony_ci		error = -ENOMEM;
2608c2ecf20Sopenharmony_ci		goto failed1;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	spin_lock_init(&drvdata->lock);
2648c2ecf20Sopenharmony_ci	drvdata->irq = irq;
2658c2ecf20Sopenharmony_ci	drvdata->serio = serio;
2668c2ecf20Sopenharmony_ci	drvdata->dev = dev;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	phys_addr = r_mem.start;
2698c2ecf20Sopenharmony_ci	remap_size = resource_size(&r_mem);
2708c2ecf20Sopenharmony_ci	if (!request_mem_region(phys_addr, remap_size, DRIVER_NAME)) {
2718c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't lock memory region at 0x%08llX\n",
2728c2ecf20Sopenharmony_ci			(unsigned long long)phys_addr);
2738c2ecf20Sopenharmony_ci		error = -EBUSY;
2748c2ecf20Sopenharmony_ci		goto failed1;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* Fill in configuration data and add them to the list */
2788c2ecf20Sopenharmony_ci	drvdata->base_address = ioremap(phys_addr, remap_size);
2798c2ecf20Sopenharmony_ci	if (drvdata->base_address == NULL) {
2808c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't ioremap memory at 0x%08llX\n",
2818c2ecf20Sopenharmony_ci			(unsigned long long)phys_addr);
2828c2ecf20Sopenharmony_ci		error = -EFAULT;
2838c2ecf20Sopenharmony_ci		goto failed2;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/* Disable all the interrupts, just in case */
2878c2ecf20Sopenharmony_ci	out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/*
2908c2ecf20Sopenharmony_ci	 * Reset the PS2 device and abort any current transaction,
2918c2ecf20Sopenharmony_ci	 * to make sure we have the PS2 in a good state.
2928c2ecf20Sopenharmony_ci	 */
2938c2ecf20Sopenharmony_ci	out_be32(drvdata->base_address + XPS2_SRST_OFFSET, XPS2_SRST_RESET);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	dev_info(dev, "Xilinx PS2 at 0x%08llX mapped to 0x%p, irq=%d\n",
2968c2ecf20Sopenharmony_ci		 (unsigned long long)phys_addr, drvdata->base_address,
2978c2ecf20Sopenharmony_ci		 drvdata->irq);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	serio->id.type = SERIO_8042;
3008c2ecf20Sopenharmony_ci	serio->write = sxps2_write;
3018c2ecf20Sopenharmony_ci	serio->open = sxps2_open;
3028c2ecf20Sopenharmony_ci	serio->close = sxps2_close;
3038c2ecf20Sopenharmony_ci	serio->port_data = drvdata;
3048c2ecf20Sopenharmony_ci	serio->dev.parent = dev;
3058c2ecf20Sopenharmony_ci	snprintf(serio->name, sizeof(serio->name),
3068c2ecf20Sopenharmony_ci		 "Xilinx XPS PS/2 at %08llX", (unsigned long long)phys_addr);
3078c2ecf20Sopenharmony_ci	snprintf(serio->phys, sizeof(serio->phys),
3088c2ecf20Sopenharmony_ci		 "xilinxps2/serio at %08llX", (unsigned long long)phys_addr);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	serio_register_port(serio);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	platform_set_drvdata(ofdev, drvdata);
3138c2ecf20Sopenharmony_ci	return 0;		/* success */
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cifailed2:
3168c2ecf20Sopenharmony_ci	release_mem_region(phys_addr, remap_size);
3178c2ecf20Sopenharmony_cifailed1:
3188c2ecf20Sopenharmony_ci	kfree(serio);
3198c2ecf20Sopenharmony_ci	kfree(drvdata);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return error;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/**
3258c2ecf20Sopenharmony_ci * xps2_of_remove - unbinds the driver from the PS/2 device.
3268c2ecf20Sopenharmony_ci * @of_dev:	pointer to OF device structure
3278c2ecf20Sopenharmony_ci *
3288c2ecf20Sopenharmony_ci * This function is called if a device is physically removed from the system or
3298c2ecf20Sopenharmony_ci * if the driver module is being unloaded. It frees any resources allocated to
3308c2ecf20Sopenharmony_ci * the device.
3318c2ecf20Sopenharmony_ci */
3328c2ecf20Sopenharmony_cistatic int xps2_of_remove(struct platform_device *of_dev)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	struct xps2data *drvdata = platform_get_drvdata(of_dev);
3358c2ecf20Sopenharmony_ci	struct resource r_mem; /* IO mem resources */
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	serio_unregister_port(drvdata->serio);
3388c2ecf20Sopenharmony_ci	iounmap(drvdata->base_address);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* Get iospace of the device */
3418c2ecf20Sopenharmony_ci	if (of_address_to_resource(of_dev->dev.of_node, 0, &r_mem))
3428c2ecf20Sopenharmony_ci		dev_err(drvdata->dev, "invalid address\n");
3438c2ecf20Sopenharmony_ci	else
3448c2ecf20Sopenharmony_ci		release_mem_region(r_mem.start, resource_size(&r_mem));
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	kfree(drvdata);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return 0;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci/* Match table for of_platform binding */
3528c2ecf20Sopenharmony_cistatic const struct of_device_id xps2_of_match[] = {
3538c2ecf20Sopenharmony_ci	{ .compatible = "xlnx,xps-ps2-1.00.a", },
3548c2ecf20Sopenharmony_ci	{ /* end of list */ },
3558c2ecf20Sopenharmony_ci};
3568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, xps2_of_match);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic struct platform_driver xps2_of_driver = {
3598c2ecf20Sopenharmony_ci	.driver = {
3608c2ecf20Sopenharmony_ci		.name = DRIVER_NAME,
3618c2ecf20Sopenharmony_ci		.of_match_table = xps2_of_match,
3628c2ecf20Sopenharmony_ci	},
3638c2ecf20Sopenharmony_ci	.probe		= xps2_of_probe,
3648c2ecf20Sopenharmony_ci	.remove		= xps2_of_remove,
3658c2ecf20Sopenharmony_ci};
3668c2ecf20Sopenharmony_cimodule_platform_driver(xps2_of_driver);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Xilinx, Inc.");
3698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xilinx XPS PS/2 driver");
3708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3718c2ecf20Sopenharmony_ci
372