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 25 MODULE_AUTHOR("Vojtech Pavlik, Russell King"); 26 MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver"); 27 MODULE_LICENSE("GPL"); 28 MODULE_ALIAS("platform:kart"); 29 30 struct rpckbd_data { 31 int tx_irq; 32 int rx_irq; 33 }; 34 35 static 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 45 static 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 60 static irqreturn_t rpckbd_tx(int irq, void *dev_id) 61 { 62 return IRQ_HANDLED; 63 } 64 65 static 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 88 static 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 */ 100 static 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 139 static 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 150 static struct platform_driver rpckbd_driver = { 151 .probe = rpckbd_probe, 152 .remove = rpckbd_remove, 153 .driver = { 154 .name = "kart", 155 }, 156 }; 157 module_platform_driver(rpckbd_driver); 158