linux/drivers/input/serio/q40kbd.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Copyright (c) 2000-2001 Vojtech Pavlik
   4 *
   5 *  Based on the work of:
   6 *      Richard Zidlicky <Richard.Zidlicky@stud.informatik.uni-erlangen.de>
   7 */
   8
   9/*
  10 * Q40 PS/2 keyboard controller driver for Linux/m68k
  11 */
  12
  13/*
  14 */
  15
  16#include <linux/module.h>
  17#include <linux/serio.h>
  18#include <linux/interrupt.h>
  19#include <linux/err.h>
  20#include <linux/bitops.h>
  21#include <linux/platform_device.h>
  22#include <linux/slab.h>
  23
  24#include <asm/io.h>
  25#include <linux/uaccess.h>
  26#include <asm/q40_master.h>
  27#include <asm/irq.h>
  28#include <asm/q40ints.h>
  29
  30#define DRV_NAME        "q40kbd"
  31
  32MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
  33MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver");
  34MODULE_LICENSE("GPL");
  35MODULE_ALIAS("platform:" DRV_NAME);
  36
  37struct q40kbd {
  38        struct serio *port;
  39        spinlock_t lock;
  40};
  41
  42static irqreturn_t q40kbd_interrupt(int irq, void *dev_id)
  43{
  44        struct q40kbd *q40kbd = dev_id;
  45        unsigned long flags;
  46
  47        spin_lock_irqsave(&q40kbd->lock, flags);
  48
  49        if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))
  50                serio_interrupt(q40kbd->port, master_inb(KEYCODE_REG), 0);
  51
  52        master_outb(-1, KEYBOARD_UNLOCK_REG);
  53
  54        spin_unlock_irqrestore(&q40kbd->lock, flags);
  55
  56        return IRQ_HANDLED;
  57}
  58
  59/*
  60 * q40kbd_flush() flushes all data that may be in the keyboard buffers
  61 */
  62
  63static void q40kbd_flush(struct q40kbd *q40kbd)
  64{
  65        int maxread = 100;
  66        unsigned long flags;
  67
  68        spin_lock_irqsave(&q40kbd->lock, flags);
  69
  70        while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)))
  71                master_inb(KEYCODE_REG);
  72
  73        spin_unlock_irqrestore(&q40kbd->lock, flags);
  74}
  75
  76static void q40kbd_stop(void)
  77{
  78        master_outb(0, KEY_IRQ_ENABLE_REG);
  79        master_outb(-1, KEYBOARD_UNLOCK_REG);
  80}
  81
  82/*
  83 * q40kbd_open() is called when a port is open by the higher layer.
  84 * It allocates the interrupt and enables in in the chip.
  85 */
  86
  87static int q40kbd_open(struct serio *port)
  88{
  89        struct q40kbd *q40kbd = port->port_data;
  90
  91        q40kbd_flush(q40kbd);
  92
  93        /* off we go */
  94        master_outb(-1, KEYBOARD_UNLOCK_REG);
  95        master_outb(1, KEY_IRQ_ENABLE_REG);
  96
  97        return 0;
  98}
  99
 100static void q40kbd_close(struct serio *port)
 101{
 102        struct q40kbd *q40kbd = port->port_data;
 103
 104        q40kbd_stop();
 105        q40kbd_flush(q40kbd);
 106}
 107
 108static int q40kbd_probe(struct platform_device *pdev)
 109{
 110        struct q40kbd *q40kbd;
 111        struct serio *port;
 112        int error;
 113
 114        q40kbd = kzalloc(sizeof(struct q40kbd), GFP_KERNEL);
 115        port = kzalloc(sizeof(struct serio), GFP_KERNEL);
 116        if (!q40kbd || !port) {
 117                error = -ENOMEM;
 118                goto err_free_mem;
 119        }
 120
 121        q40kbd->port = port;
 122        spin_lock_init(&q40kbd->lock);
 123
 124        port->id.type = SERIO_8042;
 125        port->open = q40kbd_open;
 126        port->close = q40kbd_close;
 127        port->port_data = q40kbd;
 128        port->dev.parent = &pdev->dev;
 129        strlcpy(port->name, "Q40 Kbd Port", sizeof(port->name));
 130        strlcpy(port->phys, "Q40", sizeof(port->phys));
 131
 132        q40kbd_stop();
 133
 134        error = request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0,
 135                            DRV_NAME, q40kbd);
 136        if (error) {
 137                dev_err(&pdev->dev, "Can't get irq %d.\n", Q40_IRQ_KEYBOARD);
 138                goto err_free_mem;
 139        }
 140
 141        serio_register_port(q40kbd->port);
 142
 143        platform_set_drvdata(pdev, q40kbd);
 144        printk(KERN_INFO "serio: Q40 kbd registered\n");
 145
 146        return 0;
 147
 148err_free_mem:
 149        kfree(port);
 150        kfree(q40kbd);
 151        return error;
 152}
 153
 154static int q40kbd_remove(struct platform_device *pdev)
 155{
 156        struct q40kbd *q40kbd = platform_get_drvdata(pdev);
 157
 158        /*
 159         * q40kbd_close() will be called as part of unregistering
 160         * and will ensure that IRQ is turned off, so it is safe
 161         * to unregister port first and free IRQ later.
 162         */
 163        serio_unregister_port(q40kbd->port);
 164        free_irq(Q40_IRQ_KEYBOARD, q40kbd);
 165        kfree(q40kbd);
 166
 167        return 0;
 168}
 169
 170static struct platform_driver q40kbd_driver = {
 171        .driver         = {
 172                .name   = "q40kbd",
 173        },
 174        .remove         = q40kbd_remove,
 175};
 176
 177module_platform_driver_probe(q40kbd_driver, q40kbd_probe);
 178