summaryrefslogtreecommitdiffstats
path: root/uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c
diff options
context:
space:
mode:
Diffstat (limited to 'uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c')
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c809
1 files changed, 809 insertions, 0 deletions
diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c b/uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c
new file mode 100644
index 0000000..17ac0c0
--- /dev/null
+++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c
@@ -0,0 +1,809 @@
+/****************************************************************************
+ * *
+ * Copyright (c) 2004 - 2006 Winbond Electronics Corp. All rights reserved. *
+ * *
+ ***************************************************************************/
+
+/****************************************************************************
+ *
+ * FILENAME
+ * linux-2.4.x/drivers/char/w90n745_ps2.c
+ *
+ * VERSION
+ * 1.0
+ *
+ * DESCRIPTION
+ * This file is the winbond ps2 keyboard driver
+ *
+ * HISTORY
+ * 1/9/2005 Ver 1.0 Created by PC34 MCLi
+ *
+ * REMARK
+ * None
+ **************************************************************************/
+
+#include <linux/config.h>
+
+
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/signal.h>
+#include <linux/init.h>
+#include <linux/kbd_ll.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/smp_lock.h>
+#include <linux/kd.h>
+#include <linux/pm.h>
+
+#undef KBD_DATA_REG
+#undef KBD_STATUS_REG
+#undef KBD_CNTL_REG
+
+#include "keyboard.h"
+#include "w90n745_ps2.h"
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include <asm/io.h>
+
+/* Some configuration switches are present in the include file... */
+
+
+
+/* Simple translation table for the SysRq keys */
+
+static void kbd_write_command_w(int data);
+static void kbd_write_output_w(int data);
+#ifndef kbd_controller_present
+#define kbd_controller_present() 1
+#endif
+static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
+static unsigned char handle_kbd_event(void);
+
+/* used only by send_data - set by keyboard_interrupt */
+static volatile unsigned char reply_expected;
+static volatile unsigned char acknowledge;
+static volatile unsigned char resend;
+
+/*
+ * Wait for keyboard controller input buffer to drain.
+ *
+ * Don't use 'jiffies' so that we don't depend on
+ * interrupts..
+ *
+ * Quote from PS/2 System Reference Manual:
+ *
+ * "Address hex 0060 and address hex 0064 should be written only when
+ * the input-buffer-full bit and output-buffer-full bit in the
+ * Controller Status register are set 0."
+ */
+
+unsigned char IsExt = 0;
+unsigned char IsLED = 0;
+unsigned char CapsLock = 0;
+unsigned char gvLEDCode = 0; /* bit 2 ~ 0 : Capslock, 1:NumLock, 0:ScrollLock */
+
+int IsLEDKeyPress(unsigned int code)
+{
+ int value = 0;
+
+ if (code == 0x77)
+ {
+ value = 1;
+ if (gvLEDCode & 0x02)
+ gvLEDCode &= 0xFD;
+ else
+ gvLEDCode |= 0x02;
+ }
+ else if (code == 0x7E)
+ {
+ value = 1;
+ if (gvLEDCode & 0x01) gvLEDCode &= 0xFE;
+ else gvLEDCode |= 0x01;
+ }
+ else if (code == 0x58)
+ {
+ value = 1;
+ if (gvLEDCode & 0x04) gvLEDCode &= 0xFB;
+ else gvLEDCode |= 0x04;
+ }
+
+ return value;
+}
+
+int SendKBCommand(unsigned char cmd)
+{
+ unsigned volatile int mctl;
+ unsigned volatile char scancode;
+ unsigned long timeout = KBD_TIMEOUT*5;
+
+ DWORD_WRITE(KBD_CNTL_REG, (cmd | 0x100));
+ mctl=DWORD_READ(KBD_CNTL_REG) & 0x100;
+ while (mctl)
+ {
+ if (!--timeout) {
+ printk("keyboard: Timeout - AT keyboard not present?(%02x)\n", cmd);
+
+ return 0;
+ }
+
+ mctl=DWORD_READ(KBD_CNTL_REG) & 0x100;
+ }
+
+ while (1)
+ {
+ if (!--timeout) {
+ printk("keyboard: Timeout - AT keyboard not present?(%02x)\n", cmd);
+
+ return 0;
+ }
+ scancode=DWORD_READ(PS2_SCANCODE_REG)&0xFF;
+ // printk("scancode:%x\n",scancode);
+ if (scancode == KBD_REPLY_ACK)
+ break;
+ }
+
+ return 1;
+}
+
+static void kb_wait(void)
+{
+ unsigned long timeout = KBC_TIMEOUT;
+
+ do {
+ /*
+ * "handle_kbd_event()" will handle any incoming events
+ * while we wait - keypresses or mouse movement.
+ */
+ unsigned char status = handle_kbd_event();
+
+ if(CapsLock)
+ status=0x14;
+
+ if (! (status & KBD_STAT_IBF))
+ return;
+
+ mdelay(1);
+ timeout--;
+ } while (timeout);
+#ifdef KBD_REPORT_TIMEOUTS
+ printk(KERN_WARNING "Keyboard timed out[1]\n");
+#endif
+}
+
+/*
+ * Translation of escaped scancodes to keycodes.
+ * This is now user-settable.
+ *
+ */
+#define E0_KPENTER 90
+#define E0_RCTRL 20
+#define E0_KPSLASH 74
+#define E0_PRSCR 99
+#define E0_RALT 100
+#define E0_BREAK 101 /* (control-pause) */
+#define E0_HOME 72
+#define E0_UP 103
+#define E0_PGUP 104
+#define E0_LEFT 128
+#define E0_RIGHT 106
+#define E0_END 129
+#define E0_DOWN 130
+#define E0_PGDN 109
+#define E0_INS 110
+#define E0_DEL 111
+
+#define E1_PAUSE 138
+
+/*
+ * They could be thrown away (and all occurrences below replaced by 0),
+ * but that would force many users to use the `setkeycodes' utility, where
+ * they needed not before. It does not matter that there are duplicates, as
+ * long as no duplication occurs for any single keyboard.
+ */
+#define SC_LIM 89
+
+#define FOCUS_PF1 85 /* actual code! */
+#define FOCUS_PF2 89
+#define FOCUS_PF3 90
+#define FOCUS_PF4 91
+#define FOCUS_PF5 92
+#define FOCUS_PF6 93
+#define FOCUS_PF7 94
+#define FOCUS_PF8 95
+#define FOCUS_PF9 120
+#define FOCUS_PF10 121
+#define FOCUS_PF11 122
+#define FOCUS_PF12 123
+
+#define JAP_86 124
+/* mcli2@winbond.com.tw:
+ * The four keys are located over the numeric keypad, and are
+ * labelled A1-A4. It's an rc930 keyboard, from
+ * Regnecentralen/RC International, Now ICL.
+ * Scancodes: 59, 5a, 5b, 5c.
+ */
+#define RGN1 124
+#define RGN2 125
+#define RGN3 126
+#define RGN4 127
+
+static unsigned char high_keys[128 - SC_LIM] = {
+ RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x69-0x6f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */
+ 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x78-0x7f */
+ 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x80-0x87 */
+ FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x88-0x8b */
+ FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x8c-0x8f */
+};
+
+/* BTC */
+#define E0_MACRO 132
+/* LK450 */
+#define E0_F13 133
+#define E0_F14 134
+#define E0_HELP 135
+#define E0_DO 136
+#define E0_F17 137
+#define E0_KPMINPLUS 138
+
+/*
+ * My OmniKey generates e0 4c for the "OMNI" key and the
+ * right alt key does nada. [mcli2@winbond.com.tw]
+ */
+#define E0_OK 144
+/*
+ * New microsoft keyboard is rumoured to have
+ * 1F (left window button), 27 (right window button),
+ * 2F (menu button). [or: LBANNER, RBANNER, RMENU]
+ * [or: Windows_L, Windows_R, TaskMan]
+ */
+#define E0_MSLW 125
+#define E0_MSRW 126
+#define E0_MSTM 127
+
+static unsigned char e0_keys[144] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */
+ 0, 0, 0, 0, E0_RCTRL, 0, 0, 0, /* 0x10-0x17 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x18-0x1f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */
+ 0, 0, 0, 0, 0, 0, 0, E0_PRSCR, /* 0x30-0x37 */
+ E0_RALT, 0, 0, 0, 0, 0, 0, 0, /* 0x38-0x3f */
+ 0, 0, 0, 0, 0, 0, E0_BREAK, E0_MSTM, /* 0x40-0x47 */
+ E0_HOME, 0, E0_KPSLASH, 0, 0, 0, 0, 0, /* 0x48-0x4f */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50-0x57 */
+ 0, 0, E0_KPENTER, 0, E0_MSRW, 0, 0, 0, /* 0x58-0x5f */
+ 0, 0, E0_MSLW, 0, 0, 0, 0, 0, /* 0x60-0x67 */
+ 0, E0_END, 0, E0_LEFT, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */
+ E0_INS, E0_DEL, E0_DOWN, E0_OK, E0_RIGHT, 0, E0_UP, 0, /* 0x70-0x77 */
+ 0, E0_KPMINPLUS, E0_PGDN, 0, E0_PGUP, 0, 0, 0, /* 0x78-0x7f */
+ 0, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x80-0x87 */
+ E0_DO, E0_F17, 0, 0, 0, 0, 0, 0 /* 0x88-0x8F */
+};
+
+char temp=0;
+
+int pckbd_setkeycode(unsigned int scancode, unsigned int keycode)
+{
+ if (scancode < SC_LIM || scancode > 255 || keycode > 143)
+ return -EINVAL;
+ if (scancode < 144)
+ high_keys[scancode - SC_LIM] = keycode;
+ else
+ e0_keys[scancode - 144] = keycode;
+
+ return 0;
+}
+
+
+int pckbd_getkeycode(unsigned int scancode)
+{
+ return
+ (scancode < SC_LIM || scancode > 255) ? -EINVAL :
+ (scancode < 144) ? high_keys[scancode - SC_LIM] :
+ e0_keys[scancode - 144];
+}
+
+static int do_acknowledge(unsigned char scancode)
+{
+ if (reply_expected) {
+ /* Unfortunately, we must recognise these codes only if we know they
+ * are known to be valid (i.e., after sending a command), because there
+ * are some brain-damaged keyboards (yes, FOCUS 9000 again) which have
+ * keys with such codes :(
+ */
+ if (scancode == KBD_REPLY_ACK) {
+ acknowledge = 1;
+ reply_expected = 0;
+ return 0;
+ } else if (scancode == KBD_REPLY_RESEND) {
+ resend = 1;
+ reply_expected = 0;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int pckbd_translate(unsigned char scancode, unsigned char *keycode, char raw_mode)
+{
+ static int prev_scancode;
+
+ /* special prefix scancodes.. */
+ if (scancode == 0xe0 || scancode == 0xe1) {
+ prev_scancode = scancode;
+ return 0;
+ }
+
+ /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */
+ if (scancode == 0x00 || scancode == 0xff) {
+ prev_scancode = 0;
+ return 0;
+ }
+
+ scancode &= 0x7f;
+
+ if (prev_scancode) {
+ /*
+ * usually it will be 0xe0, but a Pause key generates
+ * e1 14 77 e1 14 77 when pressed, and nothing when released
+ */
+ if (prev_scancode != 0xe0) {
+ if (prev_scancode == 0xe1 && scancode == 0x14) {
+ prev_scancode = 0x100;
+ return 0;
+ } else if (prev_scancode == 0x100 && scancode == 0x77) {
+ *keycode = E1_PAUSE;
+ prev_scancode = 0;
+ } else {
+#ifdef KBD_REPORT_UNKN
+ if (!raw_mode)
+ printk(KERN_INFO "keyboard: unknown e1 escape sequence\n");
+#endif
+ prev_scancode = 0;
+ return 0;
+ }
+ } else {
+ prev_scancode = 0;
+ /*
+ * The keyboard maintains its own internal caps lock and
+ * num lock statuses. In caps lock mode E0 AA precedes make
+ * code and E0 2A follows break code. In num lock mode,
+ * E0 2A precedes make code and E0 AA follows break code.
+ * We do our own book-keeping, so we will just ignore these.
+ */
+ /*
+ * For my keyboard there is no caps lock mode, but there are
+ * both Shift-L and Shift-R modes. The former mode generates
+ * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.
+ * So, we should also ignore the latter. - aeb@cwi.nl
+ */
+ if (scancode == 0x12 || scancode == 0x59)
+ return 0;
+
+ if (e0_keys[scancode])
+ *keycode = e0_keys[scancode];
+ else {
+#ifdef KBD_REPORT_UNKN
+ if (!raw_mode)
+ printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n",
+ scancode);
+#endif
+ return 0;
+ }
+ }
+ }
+ else
+ *keycode = scancode;
+
+ return 1;
+}
+
+char pckbd_unexpected_up(unsigned char keycode)
+{
+ /* unexpected, but this can happen: maybe this was a key release for a
+ FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */
+ if (keycode >= 144 || keycode == 96)
+ return 0;
+ else
+ return 0200;
+}
+
+int pckbd_pm_resume(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+ return 0;
+}
+
+
+static inline void handle_mouse_event(unsigned char scancode)
+{
+}
+
+static unsigned char kbd_exists = 1;
+
+static inline void handle_keyboard_event(unsigned int scancode)
+{
+#ifdef CONFIG_VT
+ kbd_exists = 1;
+ if (do_acknowledge(scancode&0xFF))
+ handle_scancode(scancode&0xFF, !(scancode & 0x200));
+#endif
+ tasklet_schedule(&keyboard_tasklet);
+}
+
+/*
+ * This reads the keyboard status port, and does the
+ * appropriate action.
+ *
+ * It requires that we hold the keyboard controller
+ * spinlock.
+ */
+
+static unsigned char handle_kbd_event(void)
+{
+ unsigned int scancode;
+ unsigned char aciicode;
+ static unsigned char led_count=0;
+ unsigned int status = kbd_read_status();
+
+ IsExt=CapsLock=0;
+
+ if (status & 0x01)
+ {
+ scancode = DWORD_READ(KBD_DATA_REG);
+ aciicode = DWORD_READ(PS2_ASCII_REG) & 0xFF;
+
+ if(scancode & 0x100)
+ IsExt=1;
+
+ if(scancode==0x58)
+ CapsLock=1;
+
+// printk("scancode:%x\n",scancode);
+
+ IsLEDKeyPress(scancode);
+
+ handle_keyboard_event(scancode);
+ DWORD_WRITE(KBD_STATUS_REG, 0x01); //clear the status
+ }
+ else if (status & 0x10)
+ DWORD_WRITE(KBD_STATUS_REG, 0x10); //clear the status
+ else if (status & 0x20)
+ printk("WARNING ! TX_ERR Bit is set.\n");
+
+ return status;
+}
+
+static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+#ifdef CONFIG_VT
+ kbd_pt_regs = regs;
+#endif
+ spin_lock_irq(&kbd_controller_lock);
+ handle_kbd_event();
+ spin_unlock_irq(&kbd_controller_lock);
+}
+
+/*
+ * send_data sends a character to the keyboard and waits
+ * for an acknowledge, possibly retrying if asked to. Returns
+ * the success status.
+ *
+ * Don't use 'jiffies', so that we don't depend on interrupts
+ */
+static int send_data(unsigned char data)
+{
+ int retries = 3;
+ unsigned int scancode;
+
+ do {
+ unsigned long timeout = KBD_TIMEOUT;
+
+ acknowledge = 0; /* Set by interrupt routine on receipt of ACK. */
+ resend = 0;
+ reply_expected = 1;
+ kbd_write_output_w(data);
+
+
+ if(!(DWORD_READ(KBD_CNTL_REG) & 0x100))
+ acknowledge=1;
+
+ scancode=DWORD_READ(PS2_SCANCODE_REG)&0xFF;
+ if(scancode==KBD_REPLY_ACK)
+ return 1;
+
+ for (;;) {
+ if (acknowledge)
+ return 1;
+ if (resend)
+ break;
+ mdelay(1);
+ if (!--timeout) {
+#ifdef KBD_REPORT_TIMEOUTS
+ printk(KERN_WARNING "keyboard: Timeout - AT keyboard not present?(%02x)\n", data);
+#endif
+ return 0;
+ }
+ }
+ } while (retries-- > 0);
+#ifdef KBD_REPORT_TIMEOUTS
+ printk(KERN_WARNING "keyboard: Too many NACKs -- noisy kbd cable?\n");
+#endif
+ return 0;
+}
+
+void pckbd_leds(unsigned char leds)
+{
+#if 0
+ if (kbd_exists && (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))) {
+ send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */
+ kbd_exists = 0;
+ }
+#else
+ SendKBCommand(KBD_CMD_SET_LEDS);
+ SendKBCommand(leds);
+#endif
+}
+
+#define DEFAULT_KEYB_REP_DELAY 250
+#define DEFAULT_KEYB_REP_RATE 30 /* cps */
+
+static struct kbd_repeat kbdrate={
+ DEFAULT_KEYB_REP_DELAY,
+ DEFAULT_KEYB_REP_RATE
+};
+
+static unsigned char parse_kbd_rate(struct kbd_repeat *r)
+{
+ static struct r2v{
+ int rate;
+ unsigned char val;
+ } kbd_rates[]={ {5,0x14},
+ {7,0x10},
+ {10,0x0c},
+ {15,0x08},
+ {20,0x04},
+ {25,0x02},
+ {30,0x00}
+ };
+ static struct d2v{
+ int delay;
+ unsigned char val;
+ } kbd_delays[]={{250,0},
+ {500,1},
+ {750,2},
+ {1000,3}
+ };
+ int rate=0,delay=0;
+ if (r != NULL){
+ int i,new_rate=30,new_delay=250;
+ if (r->rate <= 0)
+ r->rate=kbdrate.rate;
+ if (r->delay <= 0)
+ r->delay=kbdrate.delay;
+ for (i=0; i < sizeof(kbd_rates)/sizeof(struct r2v); i++)
+ if (kbd_rates[i].rate == r->rate){
+ new_rate=kbd_rates[i].rate;
+ rate=kbd_rates[i].val;
+ break;
+ }
+ for (i=0; i < sizeof(kbd_delays)/sizeof(struct d2v); i++)
+ if (kbd_delays[i].delay == r->delay){
+ new_delay=kbd_delays[i].delay;
+ delay=kbd_delays[i].val;
+ break;
+ }
+ r->rate=new_rate;
+ r->delay=new_delay;
+ }
+ return (delay << 5) | rate;
+}
+
+static int write_kbd_rate(unsigned char r)
+{
+ if (!send_data(KBD_CMD_SET_RATE) || !send_data(r)){
+ send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */
+ return 0;
+ }else
+ return 1;
+}
+
+static int pckbd_rate(struct kbd_repeat *rep)
+{
+ if (rep == NULL)
+ return -EINVAL;
+ else{
+ unsigned char r=parse_kbd_rate(rep);
+ struct kbd_repeat old_rep;
+ memcpy(&old_rep,&kbdrate,sizeof(struct kbd_repeat));
+ if (write_kbd_rate(r)){
+ memcpy(&kbdrate,rep,sizeof(struct kbd_repeat));
+ memcpy(rep,&old_rep,sizeof(struct kbd_repeat));
+ return 0;
+ }
+ }
+ return -EIO;
+}
+
+#define KBD_NO_DATA (-1) /* No data */
+#define KBD_BAD_DATA (-2) /* Parity or other error */
+
+static int __init kbd_read_data(void)
+{
+ int retval = KBD_NO_DATA;
+ unsigned int status;
+
+ status = kbd_read_status();
+
+ if (status & KBD_STAT_OBF) {
+ unsigned char data = kbd_read_input();
+
+ retval = data;
+ if (status & (KBD_STAT_GTO | KBD_STAT_PERR))
+ retval = KBD_BAD_DATA;
+ }
+
+ return retval;
+}
+
+static void __init kbd_clear_input(void)
+{
+ int maxread = 100; /* Random number */
+
+ do {
+ if (kbd_read_data() == KBD_NO_DATA)
+ break;
+ } while (--maxread);
+}
+
+static int __init kbd_wait_for_input(void)
+{
+ long timeout = KBD_INIT_TIMEOUT;
+
+ do {
+ int retval = kbd_read_data();
+ if (retval >= 0)
+ {
+
+ return retval;
+ }
+ mdelay(1);
+ } while (--timeout);
+ return -1;
+}
+
+static void kbd_write_command_w(int data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&kbd_controller_lock, flags);
+ kb_wait();
+ kbd_write_command(data);
+ spin_unlock_irqrestore(&kbd_controller_lock, flags);
+}
+
+static void kbd_write_output_w(int data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&kbd_controller_lock, flags);
+ kb_wait();
+ kbd_write_output(data);
+ spin_unlock_irqrestore(&kbd_controller_lock, flags);
+}
+
+#if defined(__alpha__)
+/*
+ * Some Alphas cannot mask some/all interrupts, so we have to
+ * make sure not to allow interrupts AT ALL when polling for
+ * specific return values from the keyboard.
+ *
+ * I think this should work on any architecture, but for now, only Alpha.
+ */
+static int kbd_write_command_w_and_wait(int data)
+{
+ unsigned long flags;
+ int input;
+
+ spin_lock_irqsave(&kbd_controller_lock, flags);
+ kb_wait();
+ kbd_write_command(data);
+ input = kbd_wait_for_input();
+ spin_unlock_irqrestore(&kbd_controller_lock, flags);
+ return input;
+}
+
+static int kbd_write_output_w_and_wait(int data)
+{
+ unsigned long flags;
+ int input;
+
+ spin_lock_irqsave(&kbd_controller_lock, flags);
+ kb_wait();
+ kbd_write_output(data);
+ input = kbd_wait_for_input();
+ spin_unlock_irqrestore(&kbd_controller_lock, flags);
+ return input;
+}
+#else
+static int kbd_write_command_w_and_wait(int data)
+{
+ kbd_write_command_w(data);
+ return kbd_wait_for_input();
+}
+
+static int kbd_write_output_w_and_wait(int data)
+{
+ kbd_write_output_w(data);
+ return kbd_wait_for_input();
+}
+#endif /* __alpha__ */
+
+void __init pckbd_init_hw(void)
+{
+ /* change GPIO5 [4:5] to PS2 signals */
+ DWORD_WRITE(0xFFF83050, DWORD_READ(0xFFF83050)|0xF00);
+
+ /* Flush any pending input. */
+ kbd_clear_input();
+
+ kbd_rate = pckbd_rate;
+
+ if(request_irq(INT_PS2, keyboard_interrupt, SA_SHIRQ, "keyboard", &temp))
+ printk("keyboard request_irq error !\n");
+ else
+ printk("keyboard request_irq OK !\n");
+}
+
+static int blink_frequency = HZ/2;
+
+/* Tell the user who may be running in X and not see the console that we have
+ panic'ed. This is to distingush panics from "real" lockups.
+ Could in theory send the panic message as morse, but that is left as an
+ exercise for the reader. */
+void panic_blink(void)
+{
+ static unsigned long last_jiffie;
+ static char led;
+ /* Roughly 1/2s frequency. KDB uses about 1s. Make sure it is
+ different. */
+ if (!blink_frequency)
+ return;
+ if (jiffies - last_jiffie > blink_frequency) {
+ led ^= 0x01 | 0x04;
+ while (kbd_read_status() & KBD_STAT_IBF) mdelay(1);
+ kbd_write_output(KBD_CMD_SET_LEDS);
+ mdelay(1);
+ while (kbd_read_status() & KBD_STAT_IBF) mdelay(1);
+ mdelay(1);
+ kbd_write_output(led);
+ last_jiffie = jiffies;
+ }
+}
+
+static int __init panicblink_setup(char *str)
+{
+ int par;
+ if (get_option(&str,&par))
+ blink_frequency = par*(1000/HZ);
+ return 1;
+}
+
+/* panicblink=0 disables the blinking as it caused problems with some console
+ switches. otherwise argument is ms of a blink period. */
+__setup("panicblink=", panicblink_setup);