mirror of
https://github.com/torvalds/linux.git
synced 2024-12-24 20:01:55 +00:00
7b19ada2ed
get rid of input BIT* duplicate defines use newly global defined macros for input layer. Also remove includes of input.h from non-input sources only for BIT macro definiton. Define the macro temporarily in local manner, all those local definitons will be removed further in this patchset (to not break bisecting). BIT macro will be globally defined (1<<x) Signed-off-by: Jiri Slaby <jirislaby@gmail.com> Cc: <dtor@mail.ru> Acked-by: Jiri Kosina <jkosina@suse.cz> Cc: <lenb@kernel.org> Acked-by: Marcel Holtmann <marcel@holtmann.org> Cc: <perex@suse.cz> Acked-by: Mauro Carvalho Chehab <mchehab@infradead.org> Cc: <vernux@us.ibm.com> Cc: <malattia@linux.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
374 lines
8.2 KiB
C
374 lines
8.2 KiB
C
/*
|
|
* linux/drivers/hil/hilkbd.c
|
|
*
|
|
* Copyright (C) 1998 Philip Blundell <philb@gnu.org>
|
|
* Copyright (C) 1999 Matthew Wilcox <willy@bofh.ai>
|
|
* Copyright (C) 1999-2007 Helge Deller <deller@gmx.de>
|
|
*
|
|
* Very basic HP Human Interface Loop (HIL) driver.
|
|
* This driver handles the keyboard on HP300 (m68k) and on some
|
|
* HP700 (parisc) series machines.
|
|
*
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License version 2. See the file COPYING in the main directory of this
|
|
* archive for more details.
|
|
*/
|
|
|
|
#include <linux/pci_ids.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/module.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/input.h>
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/hil.h>
|
|
#include <linux/io.h>
|
|
#include <linux/spinlock.h>
|
|
#include <asm/irq.h>
|
|
#ifdef CONFIG_HP300
|
|
#include <asm/hwtest.h>
|
|
#endif
|
|
|
|
|
|
MODULE_AUTHOR("Philip Blundell, Matthew Wilcox, Helge Deller");
|
|
MODULE_DESCRIPTION("HIL keyboard driver (basic functionality)");
|
|
MODULE_LICENSE("GPL v2");
|
|
|
|
|
|
#if defined(CONFIG_PARISC)
|
|
|
|
#include <asm/io.h>
|
|
#include <asm/hardware.h>
|
|
#include <asm/parisc-device.h>
|
|
static unsigned long hil_base; /* HPA for the HIL device */
|
|
static unsigned int hil_irq;
|
|
#define HILBASE hil_base /* HPPA (parisc) port address */
|
|
#define HIL_DATA 0x800
|
|
#define HIL_CMD 0x801
|
|
#define HIL_IRQ hil_irq
|
|
#define hil_readb(p) gsc_readb(p)
|
|
#define hil_writeb(v,p) gsc_writeb((v),(p))
|
|
|
|
#elif defined(CONFIG_HP300)
|
|
|
|
#define HILBASE 0xf0428000UL /* HP300 (m68k) port address */
|
|
#define HIL_DATA 0x1
|
|
#define HIL_CMD 0x3
|
|
#define HIL_IRQ 2
|
|
#define hil_readb(p) readb(p)
|
|
#define hil_writeb(v,p) writeb((v),(p))
|
|
|
|
#else
|
|
#error "HIL is not supported on this platform"
|
|
#endif
|
|
|
|
|
|
|
|
/* HIL helper functions */
|
|
|
|
#define hil_busy() (hil_readb(HILBASE + HIL_CMD) & HIL_BUSY)
|
|
#define hil_data_available() (hil_readb(HILBASE + HIL_CMD) & HIL_DATA_RDY)
|
|
#define hil_status() (hil_readb(HILBASE + HIL_CMD))
|
|
#define hil_command(x) do { hil_writeb((x), HILBASE + HIL_CMD); } while (0)
|
|
#define hil_read_data() (hil_readb(HILBASE + HIL_DATA))
|
|
#define hil_write_data(x) do { hil_writeb((x), HILBASE + HIL_DATA); } while (0)
|
|
|
|
/* HIL constants */
|
|
|
|
#define HIL_BUSY 0x02
|
|
#define HIL_DATA_RDY 0x01
|
|
|
|
#define HIL_SETARD 0xA0 /* set auto-repeat delay */
|
|
#define HIL_SETARR 0xA2 /* set auto-repeat rate */
|
|
#define HIL_SETTONE 0xA3 /* set tone generator */
|
|
#define HIL_CNMT 0xB2 /* clear nmi */
|
|
#define HIL_INTON 0x5C /* Turn on interrupts. */
|
|
#define HIL_INTOFF 0x5D /* Turn off interrupts. */
|
|
|
|
#define HIL_READKBDSADR 0xF9
|
|
#define HIL_WRITEKBDSADR 0xE9
|
|
|
|
static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly =
|
|
{ HIL_KEYCODES_SET1 };
|
|
|
|
/* HIL structure */
|
|
static struct {
|
|
struct input_dev *dev;
|
|
|
|
unsigned int curdev;
|
|
|
|
unsigned char s;
|
|
unsigned char c;
|
|
int valid;
|
|
|
|
unsigned char data[16];
|
|
unsigned int ptr;
|
|
spinlock_t lock;
|
|
|
|
void *dev_id; /* native bus device */
|
|
} hil_dev;
|
|
|
|
|
|
static void poll_finished(void)
|
|
{
|
|
int down;
|
|
int key;
|
|
unsigned char scode;
|
|
|
|
switch (hil_dev.data[0]) {
|
|
case 0x40:
|
|
down = (hil_dev.data[1] & 1) == 0;
|
|
scode = hil_dev.data[1] >> 1;
|
|
key = hphilkeyb_keycode[scode];
|
|
input_report_key(hil_dev.dev, key, down);
|
|
break;
|
|
}
|
|
hil_dev.curdev = 0;
|
|
}
|
|
|
|
|
|
static inline void handle_status(unsigned char s, unsigned char c)
|
|
{
|
|
if (c & 0x8) {
|
|
/* End of block */
|
|
if (c & 0x10)
|
|
poll_finished();
|
|
} else {
|
|
if (c & 0x10) {
|
|
if (hil_dev.curdev)
|
|
poll_finished(); /* just in case */
|
|
hil_dev.curdev = c & 7;
|
|
hil_dev.ptr = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static inline void handle_data(unsigned char s, unsigned char c)
|
|
{
|
|
if (hil_dev.curdev) {
|
|
hil_dev.data[hil_dev.ptr++] = c;
|
|
hil_dev.ptr &= 15;
|
|
}
|
|
}
|
|
|
|
|
|
/* handle HIL interrupts */
|
|
static irqreturn_t hil_interrupt(int irq, void *handle)
|
|
{
|
|
unsigned char s, c;
|
|
|
|
s = hil_status();
|
|
c = hil_read_data();
|
|
|
|
switch (s >> 4) {
|
|
case 0x5:
|
|
handle_status(s, c);
|
|
break;
|
|
case 0x6:
|
|
handle_data(s, c);
|
|
break;
|
|
case 0x4:
|
|
hil_dev.s = s;
|
|
hil_dev.c = c;
|
|
mb();
|
|
hil_dev.valid = 1;
|
|
break;
|
|
}
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
|
|
/* send a command to the HIL */
|
|
static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&hil_dev.lock, flags);
|
|
while (hil_busy())
|
|
/* wait */;
|
|
hil_command(cmd);
|
|
while (len--) {
|
|
while (hil_busy())
|
|
/* wait */;
|
|
hil_write_data(*(data++));
|
|
}
|
|
spin_unlock_irqrestore(&hil_dev.lock, flags);
|
|
}
|
|
|
|
|
|
/* initialise HIL */
|
|
static int __init
|
|
hil_keyb_init(void)
|
|
{
|
|
unsigned char c;
|
|
unsigned int i, kbid;
|
|
wait_queue_head_t hil_wait;
|
|
int err;
|
|
|
|
if (hil_dev.dev) {
|
|
return -ENODEV; /* already initialized */
|
|
}
|
|
|
|
spin_lock_init(&hil_dev.lock);
|
|
hil_dev.dev = input_allocate_device();
|
|
if (!hil_dev.dev)
|
|
return -ENOMEM;
|
|
|
|
#if defined(CONFIG_HP300)
|
|
if (!hwreg_present((void *)(HILBASE + HIL_DATA))) {
|
|
printk(KERN_ERR "HIL: hardware register was not found\n");
|
|
err = -ENODEV;
|
|
goto err1;
|
|
}
|
|
if (!request_region(HILBASE + HIL_DATA, 2, "hil")) {
|
|
printk(KERN_ERR "HIL: IOPORT region already used\n");
|
|
err = -EIO;
|
|
goto err1;
|
|
}
|
|
#endif
|
|
|
|
err = request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id);
|
|
if (err) {
|
|
printk(KERN_ERR "HIL: Can't get IRQ\n");
|
|
goto err2;
|
|
}
|
|
|
|
/* Turn on interrupts */
|
|
hil_do(HIL_INTON, NULL, 0);
|
|
|
|
/* Look for keyboards */
|
|
hil_dev.valid = 0; /* clear any pending data */
|
|
hil_do(HIL_READKBDSADR, NULL, 0);
|
|
|
|
init_waitqueue_head(&hil_wait);
|
|
wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3*HZ);
|
|
if (!hil_dev.valid) {
|
|
printk(KERN_WARNING "HIL: timed out, assuming no keyboard present\n");
|
|
}
|
|
|
|
c = hil_dev.c;
|
|
hil_dev.valid = 0;
|
|
if (c == 0) {
|
|
kbid = -1;
|
|
printk(KERN_WARNING "HIL: no keyboard present\n");
|
|
} else {
|
|
kbid = ffz(~c);
|
|
printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid);
|
|
}
|
|
|
|
/* set it to raw mode */
|
|
c = 0;
|
|
hil_do(HIL_WRITEKBDSADR, &c, 1);
|
|
|
|
for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
|
|
if (hphilkeyb_keycode[i] != KEY_RESERVED)
|
|
set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit);
|
|
|
|
hil_dev.dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
|
|
hil_dev.dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
|
|
BIT_MASK(LED_SCROLLL);
|
|
hil_dev.dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
|
|
hil_dev.dev->keycodesize= sizeof(hphilkeyb_keycode[0]);
|
|
hil_dev.dev->keycode = hphilkeyb_keycode;
|
|
hil_dev.dev->name = "HIL keyboard";
|
|
hil_dev.dev->phys = "hpkbd/input0";
|
|
|
|
hil_dev.dev->id.bustype = BUS_HIL;
|
|
hil_dev.dev->id.vendor = PCI_VENDOR_ID_HP;
|
|
hil_dev.dev->id.product = 0x0001;
|
|
hil_dev.dev->id.version = 0x0010;
|
|
|
|
err = input_register_device(hil_dev.dev);
|
|
if (err) {
|
|
printk(KERN_ERR "HIL: Can't register device\n");
|
|
goto err3;
|
|
}
|
|
printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
|
|
hil_dev.dev->name, kbid, HILBASE, HIL_IRQ);
|
|
|
|
return 0;
|
|
|
|
err3:
|
|
hil_do(HIL_INTOFF, NULL, 0);
|
|
disable_irq(HIL_IRQ);
|
|
free_irq(HIL_IRQ, hil_dev.dev_id);
|
|
err2:
|
|
#if defined(CONFIG_HP300)
|
|
release_region(HILBASE + HIL_DATA, 2);
|
|
err1:
|
|
#endif
|
|
input_free_device(hil_dev.dev);
|
|
hil_dev.dev = NULL;
|
|
return err;
|
|
}
|
|
|
|
|
|
#if defined(CONFIG_PARISC)
|
|
static int __init
|
|
hil_init_chip(struct parisc_device *dev)
|
|
{
|
|
if (!dev->irq) {
|
|
printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%08lx\n", dev->hpa.start);
|
|
return -ENODEV;
|
|
}
|
|
|
|
hil_base = dev->hpa.start;
|
|
hil_irq = dev->irq;
|
|
hil_dev.dev_id = dev;
|
|
|
|
printk(KERN_INFO "Found HIL bus at 0x%08lx, IRQ %d\n", hil_base, hil_irq);
|
|
|
|
return hil_keyb_init();
|
|
}
|
|
|
|
static struct parisc_device_id hil_tbl[] = {
|
|
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 },
|
|
{ 0, }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(parisc, hil_tbl);
|
|
|
|
static struct parisc_driver hil_driver = {
|
|
.name = "hil",
|
|
.id_table = hil_tbl,
|
|
.probe = hil_init_chip,
|
|
};
|
|
#endif /* CONFIG_PARISC */
|
|
|
|
|
|
static int __init hil_init(void)
|
|
{
|
|
#if defined(CONFIG_PARISC)
|
|
return register_parisc_driver(&hil_driver);
|
|
#else
|
|
return hil_keyb_init();
|
|
#endif
|
|
}
|
|
|
|
|
|
static void __exit hil_exit(void)
|
|
{
|
|
if (HIL_IRQ) {
|
|
disable_irq(HIL_IRQ);
|
|
free_irq(HIL_IRQ, hil_dev.dev_id);
|
|
}
|
|
|
|
/* Turn off interrupts */
|
|
hil_do(HIL_INTOFF, NULL, 0);
|
|
|
|
input_unregister_device(hil_dev.dev);
|
|
|
|
hil_dev.dev = NULL;
|
|
|
|
#if defined(CONFIG_PARISC)
|
|
unregister_parisc_driver(&hil_driver);
|
|
#else
|
|
release_region(HILBASE+HIL_DATA, 2);
|
|
#endif
|
|
}
|
|
|
|
module_init(hil_init);
|
|
module_exit(hil_exit);
|