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