linux/drivers/input/serio/maceps2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * SGI O2 MACE PS2 controller driver for linux
   4 *
   5 * Copyright (C) 2002 Vivien Chappelier
   6 */
   7#include <linux/module.h>
   8#include <linux/init.h>
   9#include <linux/serio.h>
  10#include <linux/errno.h>
  11#include <linux/interrupt.h>
  12#include <linux/ioport.h>
  13#include <linux/delay.h>
  14#include <linux/platform_device.h>
  15#include <linux/slab.h>
  16#include <linux/spinlock.h>
  17#include <linux/err.h>
  18
  19#include <asm/io.h>
  20#include <asm/irq.h>
  21#include <asm/ip32/mace.h>
  22#include <asm/ip32/ip32_ints.h>
  23
  24MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org");
  25MODULE_DESCRIPTION("SGI O2 MACE PS2 controller driver");
  26MODULE_LICENSE("GPL");
  27
  28#define MACE_PS2_TIMEOUT 10000 /* in 50us unit */
  29
  30#define PS2_STATUS_CLOCK_SIGNAL  BIT(0) /* external clock signal */
  31#define PS2_STATUS_CLOCK_INHIBIT BIT(1) /* clken output signal */
  32#define PS2_STATUS_TX_INPROGRESS BIT(2) /* transmission in progress */
  33#define PS2_STATUS_TX_EMPTY      BIT(3) /* empty transmit buffer */
  34#define PS2_STATUS_RX_FULL       BIT(4) /* full receive buffer */
  35#define PS2_STATUS_RX_INPROGRESS BIT(5) /* reception in progress */
  36#define PS2_STATUS_ERROR_PARITY  BIT(6) /* parity error */
  37#define PS2_STATUS_ERROR_FRAMING BIT(7) /* framing error */
  38
  39#define PS2_CONTROL_TX_CLOCK_DISABLE BIT(0) /* inhibit clock signal after TX */
  40#define PS2_CONTROL_TX_ENABLE        BIT(1) /* transmit enable */
  41#define PS2_CONTROL_TX_INT_ENABLE    BIT(2) /* enable transmit interrupt */
  42#define PS2_CONTROL_RX_INT_ENABLE    BIT(3) /* enable receive interrupt */
  43#define PS2_CONTROL_RX_CLOCK_ENABLE  BIT(4) /* pause reception if set to 0 */
  44#define PS2_CONTROL_RESET            BIT(5) /* reset */
  45
  46struct maceps2_data {
  47        struct mace_ps2port *port;
  48        int irq;
  49};
  50
  51static struct maceps2_data port_data[2];
  52static struct serio *maceps2_port[2];
  53static struct platform_device *maceps2_device;
  54
  55static int maceps2_write(struct serio *dev, unsigned char val)
  56{
  57        struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
  58        unsigned int timeout = MACE_PS2_TIMEOUT;
  59
  60        do {
  61                if (port->status & PS2_STATUS_TX_EMPTY) {
  62                        port->tx = val;
  63                        return 0;
  64                }
  65                udelay(50);
  66        } while (timeout--);
  67
  68        return -1;
  69}
  70
  71static irqreturn_t maceps2_interrupt(int irq, void *dev_id)
  72{
  73        struct serio *dev = dev_id;
  74        struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
  75        unsigned long byte;
  76
  77        if (port->status & PS2_STATUS_RX_FULL) {
  78                byte = port->rx;
  79                serio_interrupt(dev, byte & 0xff, 0);
  80        }
  81
  82        return IRQ_HANDLED;
  83}
  84
  85static int maceps2_open(struct serio *dev)
  86{
  87        struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
  88
  89        if (request_irq(data->irq, maceps2_interrupt, 0, "PS2 port", dev)) {
  90                printk(KERN_ERR "Could not allocate PS/2 IRQ\n");
  91                return -EBUSY;
  92        }
  93
  94        /* Reset port */
  95        data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET;
  96        udelay(100);
  97
  98        /* Enable interrupts */
  99        data->port->control = PS2_CONTROL_RX_CLOCK_ENABLE |
 100                              PS2_CONTROL_TX_ENABLE |
 101                              PS2_CONTROL_RX_INT_ENABLE;
 102
 103        return 0;
 104}
 105
 106static void maceps2_close(struct serio *dev)
 107{
 108        struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
 109
 110        data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET;
 111        udelay(100);
 112        free_irq(data->irq, dev);
 113}
 114
 115
 116static struct serio *maceps2_allocate_port(int idx)
 117{
 118        struct serio *serio;
 119
 120        serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
 121        if (serio) {
 122                serio->id.type          = SERIO_8042;
 123                serio->write            = maceps2_write;
 124                serio->open             = maceps2_open;
 125                serio->close            = maceps2_close;
 126                snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx);
 127                snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx);
 128                serio->port_data        = &port_data[idx];
 129                serio->dev.parent       = &maceps2_device->dev;
 130        }
 131
 132        return serio;
 133}
 134
 135static int maceps2_probe(struct platform_device *dev)
 136{
 137        maceps2_port[0] = maceps2_allocate_port(0);
 138        maceps2_port[1] = maceps2_allocate_port(1);
 139        if (!maceps2_port[0] || !maceps2_port[1]) {
 140                kfree(maceps2_port[0]);
 141                kfree(maceps2_port[1]);
 142                return -ENOMEM;
 143        }
 144
 145        serio_register_port(maceps2_port[0]);
 146        serio_register_port(maceps2_port[1]);
 147
 148        return 0;
 149}
 150
 151static int maceps2_remove(struct platform_device *dev)
 152{
 153        serio_unregister_port(maceps2_port[0]);
 154        serio_unregister_port(maceps2_port[1]);
 155
 156        return 0;
 157}
 158
 159static struct platform_driver maceps2_driver = {
 160        .driver         = {
 161                .name   = "maceps2",
 162        },
 163        .probe          = maceps2_probe,
 164        .remove         = maceps2_remove,
 165};
 166
 167static int __init maceps2_init(void)
 168{
 169        int error;
 170
 171        error = platform_driver_register(&maceps2_driver);
 172        if (error)
 173                return error;
 174
 175        maceps2_device = platform_device_alloc("maceps2", -1);
 176        if (!maceps2_device) {
 177                error = -ENOMEM;
 178                goto err_unregister_driver;
 179        }
 180
 181        port_data[0].port = &mace->perif.ps2.keyb;
 182        port_data[0].irq  = MACEISA_KEYB_IRQ;
 183        port_data[1].port = &mace->perif.ps2.mouse;
 184        port_data[1].irq  = MACEISA_MOUSE_IRQ;
 185
 186        error = platform_device_add(maceps2_device);
 187        if (error)
 188                goto err_free_device;
 189
 190        return 0;
 191
 192 err_free_device:
 193        platform_device_put(maceps2_device);
 194 err_unregister_driver:
 195        platform_driver_unregister(&maceps2_driver);
 196        return error;
 197}
 198
 199static void __exit maceps2_exit(void)
 200{
 201        platform_device_unregister(maceps2_device);
 202        platform_driver_unregister(&maceps2_driver);
 203}
 204
 205module_init(maceps2_init);
 206module_exit(maceps2_exit);
 207