11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * SGI O2 MACE PS2 controller driver for linux 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 2002 Vivien Chappelier 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License version 2 as 81da177e4SLinus Torvalds * published by the Free Software Foundation 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds #include <linux/module.h> 111da177e4SLinus Torvalds #include <linux/init.h> 121da177e4SLinus Torvalds #include <linux/serio.h> 131da177e4SLinus Torvalds #include <linux/errno.h> 141da177e4SLinus Torvalds #include <linux/interrupt.h> 151da177e4SLinus Torvalds #include <linux/ioport.h> 161da177e4SLinus Torvalds #include <linux/delay.h> 17d052d1beSRussell King #include <linux/platform_device.h> 181da177e4SLinus Torvalds #include <linux/slab.h> 191da177e4SLinus Torvalds #include <linux/spinlock.h> 201da177e4SLinus Torvalds #include <linux/err.h> 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <asm/io.h> 231da177e4SLinus Torvalds #include <asm/irq.h> 241da177e4SLinus Torvalds #include <asm/ip32/mace.h> 251da177e4SLinus Torvalds #include <asm/ip32/ip32_ints.h> 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org"); 281da177e4SLinus Torvalds MODULE_DESCRIPTION("SGI O2 MACE PS2 controller driver"); 291da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #define MACE_PS2_TIMEOUT 10000 /* in 50us unit */ 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds #define PS2_STATUS_CLOCK_SIGNAL BIT(0) /* external clock signal */ 341da177e4SLinus Torvalds #define PS2_STATUS_CLOCK_INHIBIT BIT(1) /* clken output signal */ 351da177e4SLinus Torvalds #define PS2_STATUS_TX_INPROGRESS BIT(2) /* transmission in progress */ 361da177e4SLinus Torvalds #define PS2_STATUS_TX_EMPTY BIT(3) /* empty transmit buffer */ 371da177e4SLinus Torvalds #define PS2_STATUS_RX_FULL BIT(4) /* full receive buffer */ 381da177e4SLinus Torvalds #define PS2_STATUS_RX_INPROGRESS BIT(5) /* reception in progress */ 391da177e4SLinus Torvalds #define PS2_STATUS_ERROR_PARITY BIT(6) /* parity error */ 401da177e4SLinus Torvalds #define PS2_STATUS_ERROR_FRAMING BIT(7) /* framing error */ 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds #define PS2_CONTROL_TX_CLOCK_DISABLE BIT(0) /* inhibit clock signal after TX */ 431da177e4SLinus Torvalds #define PS2_CONTROL_TX_ENABLE BIT(1) /* transmit enable */ 441da177e4SLinus Torvalds #define PS2_CONTROL_TX_INT_ENABLE BIT(2) /* enable transmit interrupt */ 451da177e4SLinus Torvalds #define PS2_CONTROL_RX_INT_ENABLE BIT(3) /* enable receive interrupt */ 461da177e4SLinus Torvalds #define PS2_CONTROL_RX_CLOCK_ENABLE BIT(4) /* pause reception if set to 0 */ 471da177e4SLinus Torvalds #define PS2_CONTROL_RESET BIT(5) /* reset */ 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds struct maceps2_data { 501da177e4SLinus Torvalds struct mace_ps2port *port; 511da177e4SLinus Torvalds int irq; 521da177e4SLinus Torvalds }; 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds static struct maceps2_data port_data[2]; 551da177e4SLinus Torvalds static struct serio *maceps2_port[2]; 561da177e4SLinus Torvalds static struct platform_device *maceps2_device; 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds static int maceps2_write(struct serio *dev, unsigned char val) 591da177e4SLinus Torvalds { 601da177e4SLinus Torvalds struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port; 611da177e4SLinus Torvalds unsigned int timeout = MACE_PS2_TIMEOUT; 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds do { 641da177e4SLinus Torvalds if (port->status & PS2_STATUS_TX_EMPTY) { 651da177e4SLinus Torvalds port->tx = val; 661da177e4SLinus Torvalds return 0; 671da177e4SLinus Torvalds } 681da177e4SLinus Torvalds udelay(50); 691da177e4SLinus Torvalds } while (timeout--); 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds return -1; 721da177e4SLinus Torvalds } 731da177e4SLinus Torvalds 747d12e780SDavid Howells static irqreturn_t maceps2_interrupt(int irq, void *dev_id) 751da177e4SLinus Torvalds { 761da177e4SLinus Torvalds struct serio *dev = dev_id; 771da177e4SLinus Torvalds struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port; 781da177e4SLinus Torvalds unsigned long byte; 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds if (port->status & PS2_STATUS_RX_FULL) { 811da177e4SLinus Torvalds byte = port->rx; 827d12e780SDavid Howells serio_interrupt(dev, byte & 0xff, 0); 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds return IRQ_HANDLED; 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds static int maceps2_open(struct serio *dev) 891da177e4SLinus Torvalds { 901da177e4SLinus Torvalds struct maceps2_data *data = (struct maceps2_data *)dev->port_data; 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds if (request_irq(data->irq, maceps2_interrupt, 0, "PS2 port", dev)) { 931da177e4SLinus Torvalds printk(KERN_ERR "Could not allocate PS/2 IRQ\n"); 941da177e4SLinus Torvalds return -EBUSY; 951da177e4SLinus Torvalds } 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds /* Reset port */ 981da177e4SLinus Torvalds data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET; 991da177e4SLinus Torvalds udelay(100); 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds /* Enable interrupts */ 1021da177e4SLinus Torvalds data->port->control = PS2_CONTROL_RX_CLOCK_ENABLE | 1031da177e4SLinus Torvalds PS2_CONTROL_TX_ENABLE | 1041da177e4SLinus Torvalds PS2_CONTROL_RX_INT_ENABLE; 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds return 0; 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds static void maceps2_close(struct serio *dev) 1101da177e4SLinus Torvalds { 1111da177e4SLinus Torvalds struct maceps2_data *data = (struct maceps2_data *)dev->port_data; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET; 1141da177e4SLinus Torvalds udelay(100); 1151da177e4SLinus Torvalds free_irq(data->irq, dev); 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds 1199d6c2502SDmitry Torokhov static struct serio * __devinit maceps2_allocate_port(int idx) 1201da177e4SLinus Torvalds { 1211da177e4SLinus Torvalds struct serio *serio; 1221da177e4SLinus Torvalds 1239d6c2502SDmitry Torokhov serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 1241da177e4SLinus Torvalds if (serio) { 1251da177e4SLinus Torvalds serio->id.type = SERIO_8042; 1261da177e4SLinus Torvalds serio->write = maceps2_write; 1271da177e4SLinus Torvalds serio->open = maceps2_open; 1281da177e4SLinus Torvalds serio->close = maceps2_close; 1291da177e4SLinus Torvalds snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx); 1301da177e4SLinus Torvalds snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx); 1311da177e4SLinus Torvalds serio->port_data = &port_data[idx]; 1321da177e4SLinus Torvalds serio->dev.parent = &maceps2_device->dev; 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds return serio; 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1389d6c2502SDmitry Torokhov static int __devinit maceps2_probe(struct platform_device *dev) 1391da177e4SLinus Torvalds { 1401da177e4SLinus Torvalds maceps2_port[0] = maceps2_allocate_port(0); 1411da177e4SLinus Torvalds maceps2_port[1] = maceps2_allocate_port(1); 1421da177e4SLinus Torvalds if (!maceps2_port[0] || !maceps2_port[1]) { 1431da177e4SLinus Torvalds kfree(maceps2_port[0]); 1441da177e4SLinus Torvalds kfree(maceps2_port[1]); 1451da177e4SLinus Torvalds return -ENOMEM; 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds serio_register_port(maceps2_port[0]); 1491da177e4SLinus Torvalds serio_register_port(maceps2_port[1]); 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds return 0; 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds 1549d6c2502SDmitry Torokhov static int __devexit maceps2_remove(struct platform_device *dev) 1551da177e4SLinus Torvalds { 1561da177e4SLinus Torvalds serio_unregister_port(maceps2_port[0]); 1571da177e4SLinus Torvalds serio_unregister_port(maceps2_port[1]); 1589d6c2502SDmitry Torokhov 1599d6c2502SDmitry Torokhov return 0; 1609d6c2502SDmitry Torokhov } 1619d6c2502SDmitry Torokhov 1629d6c2502SDmitry Torokhov static struct platform_driver maceps2_driver = { 1639d6c2502SDmitry Torokhov .driver = { 1649d6c2502SDmitry Torokhov .name = "maceps2", 1659d6c2502SDmitry Torokhov .owner = THIS_MODULE, 1669d6c2502SDmitry Torokhov }, 1679d6c2502SDmitry Torokhov .probe = maceps2_probe, 1681cb0aa88SBill Pemberton .remove = maceps2_remove, 1699d6c2502SDmitry Torokhov }; 1709d6c2502SDmitry Torokhov 1719d6c2502SDmitry Torokhov static int __init maceps2_init(void) 1729d6c2502SDmitry Torokhov { 1739d6c2502SDmitry Torokhov int error; 1749d6c2502SDmitry Torokhov 1759d6c2502SDmitry Torokhov error = platform_driver_register(&maceps2_driver); 1769d6c2502SDmitry Torokhov if (error) 1779d6c2502SDmitry Torokhov return error; 1789d6c2502SDmitry Torokhov 1799d6c2502SDmitry Torokhov maceps2_device = platform_device_alloc("maceps2", -1); 1809d6c2502SDmitry Torokhov if (!maceps2_device) { 1819d6c2502SDmitry Torokhov error = -ENOMEM; 1829d6c2502SDmitry Torokhov goto err_unregister_driver; 1839d6c2502SDmitry Torokhov } 1849d6c2502SDmitry Torokhov 1859d6c2502SDmitry Torokhov port_data[0].port = &mace->perif.ps2.keyb; 1869d6c2502SDmitry Torokhov port_data[0].irq = MACEISA_KEYB_IRQ; 1879d6c2502SDmitry Torokhov port_data[1].port = &mace->perif.ps2.mouse; 1889d6c2502SDmitry Torokhov port_data[1].irq = MACEISA_MOUSE_IRQ; 1899d6c2502SDmitry Torokhov 1909d6c2502SDmitry Torokhov error = platform_device_add(maceps2_device); 1919d6c2502SDmitry Torokhov if (error) 1929d6c2502SDmitry Torokhov goto err_free_device; 1939d6c2502SDmitry Torokhov 1949d6c2502SDmitry Torokhov return 0; 1959d6c2502SDmitry Torokhov 1969d6c2502SDmitry Torokhov err_free_device: 1979d6c2502SDmitry Torokhov platform_device_put(maceps2_device); 1989d6c2502SDmitry Torokhov err_unregister_driver: 1999d6c2502SDmitry Torokhov platform_driver_unregister(&maceps2_driver); 2009d6c2502SDmitry Torokhov return error; 2019d6c2502SDmitry Torokhov } 2029d6c2502SDmitry Torokhov 2039d6c2502SDmitry Torokhov static void __exit maceps2_exit(void) 2049d6c2502SDmitry Torokhov { 2051da177e4SLinus Torvalds platform_device_unregister(maceps2_device); 2069d6c2502SDmitry Torokhov platform_driver_unregister(&maceps2_driver); 2071da177e4SLinus Torvalds } 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds module_init(maceps2_init); 2101da177e4SLinus Torvalds module_exit(maceps2_exit); 211