rpckbd.c (3416B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2000-2001 Vojtech Pavlik 4 * Copyright (c) 2002 Russell King 5 */ 6 7/* 8 * Acorn RiscPC PS/2 keyboard controller driver for Linux/ARM 9 */ 10 11/* 12 */ 13 14#include <linux/module.h> 15#include <linux/interrupt.h> 16#include <linux/serio.h> 17#include <linux/err.h> 18#include <linux/platform_device.h> 19#include <linux/io.h> 20#include <linux/slab.h> 21 22#include <mach/hardware.h> 23#include <asm/hardware/iomd.h> 24 25MODULE_AUTHOR("Vojtech Pavlik, Russell King"); 26MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver"); 27MODULE_LICENSE("GPL"); 28MODULE_ALIAS("platform:kart"); 29 30struct rpckbd_data { 31 int tx_irq; 32 int rx_irq; 33}; 34 35static int rpckbd_write(struct serio *port, unsigned char val) 36{ 37 while (!(iomd_readb(IOMD_KCTRL) & (1 << 7))) 38 cpu_relax(); 39 40 iomd_writeb(val, IOMD_KARTTX); 41 42 return 0; 43} 44 45static irqreturn_t rpckbd_rx(int irq, void *dev_id) 46{ 47 struct serio *port = dev_id; 48 unsigned int byte; 49 int handled = IRQ_NONE; 50 51 while (iomd_readb(IOMD_KCTRL) & (1 << 5)) { 52 byte = iomd_readb(IOMD_KARTRX); 53 54 serio_interrupt(port, byte, 0); 55 handled = IRQ_HANDLED; 56 } 57 return handled; 58} 59 60static irqreturn_t rpckbd_tx(int irq, void *dev_id) 61{ 62 return IRQ_HANDLED; 63} 64 65static int rpckbd_open(struct serio *port) 66{ 67 struct rpckbd_data *rpckbd = port->port_data; 68 69 /* Reset the keyboard state machine. */ 70 iomd_writeb(0, IOMD_KCTRL); 71 iomd_writeb(8, IOMD_KCTRL); 72 iomd_readb(IOMD_KARTRX); 73 74 if (request_irq(rpckbd->rx_irq, rpckbd_rx, 0, "rpckbd", port) != 0) { 75 printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ\n"); 76 return -EBUSY; 77 } 78 79 if (request_irq(rpckbd->tx_irq, rpckbd_tx, 0, "rpckbd", port) != 0) { 80 printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ\n"); 81 free_irq(rpckbd->rx_irq, port); 82 return -EBUSY; 83 } 84 85 return 0; 86} 87 88static void rpckbd_close(struct serio *port) 89{ 90 struct rpckbd_data *rpckbd = port->port_data; 91 92 free_irq(rpckbd->rx_irq, port); 93 free_irq(rpckbd->tx_irq, port); 94} 95 96/* 97 * Allocate and initialize serio structure for subsequent registration 98 * with serio core. 99 */ 100static int rpckbd_probe(struct platform_device *dev) 101{ 102 struct rpckbd_data *rpckbd; 103 struct serio *serio; 104 int tx_irq, rx_irq; 105 106 rx_irq = platform_get_irq(dev, 0); 107 if (rx_irq <= 0) 108 return rx_irq < 0 ? rx_irq : -ENXIO; 109 110 tx_irq = platform_get_irq(dev, 1); 111 if (tx_irq <= 0) 112 return tx_irq < 0 ? tx_irq : -ENXIO; 113 114 serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 115 rpckbd = kzalloc(sizeof(*rpckbd), GFP_KERNEL); 116 if (!serio || !rpckbd) { 117 kfree(rpckbd); 118 kfree(serio); 119 return -ENOMEM; 120 } 121 122 rpckbd->rx_irq = rx_irq; 123 rpckbd->tx_irq = tx_irq; 124 125 serio->id.type = SERIO_8042; 126 serio->write = rpckbd_write; 127 serio->open = rpckbd_open; 128 serio->close = rpckbd_close; 129 serio->dev.parent = &dev->dev; 130 serio->port_data = rpckbd; 131 strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name)); 132 strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys)); 133 134 platform_set_drvdata(dev, serio); 135 serio_register_port(serio); 136 return 0; 137} 138 139static int rpckbd_remove(struct platform_device *dev) 140{ 141 struct serio *serio = platform_get_drvdata(dev); 142 struct rpckbd_data *rpckbd = serio->port_data; 143 144 serio_unregister_port(serio); 145 kfree(rpckbd); 146 147 return 0; 148} 149 150static struct platform_driver rpckbd_driver = { 151 .probe = rpckbd_probe, 152 .remove = rpckbd_remove, 153 .driver = { 154 .name = "kart", 155 }, 156}; 157module_platform_driver(rpckbd_driver);