diff -urN kernel26/arch/arm/mach-pxa/h4000/h4000_buttons.c kernel26.new/arch/arm/mach-pxa/h4000/h4000_buttons.c
--- kernel26/arch/arm/mach-pxa/h4000/h4000_buttons.c	2006-02-20 20:26:06.000000000 -0700
+++ kernel26.new/arch/arm/mach-pxa/h4000/h4000_buttons.c	2006-03-11 17:29:36.000000000 -0700
@@ -5,6 +5,7 @@
  * License.  See the file COPYING in the main directory of this archive for
  * more details.
  *
+ *               2006 Shawn Anderson, h4300 keyboard
  * Copyright (C) 2005 Pawel Kolodziejski
  * Copyright (C) 2003 Joshua Wise
  *
@@ -15,6 +16,7 @@
 #include <linux/input.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <asm/irq.h>
 #include <asm/mach/arch.h>
@@ -22,7 +24,6 @@
 #include <asm/mach-types.h>
 #include <asm/hardware.h>
 #include <asm/arch/pxa-regs.h>
-#include <asm/arch/pxa_keys.h>
 #include <asm/arch/irqs.h>
 #include <asm/hardware/ipaq-asic3.h>
 #include <asm/arch/h4000-gpio.h>
@@ -31,6 +32,10 @@
 #include <linux/soc/asic3_base.h>
 #include <linux/platform_device.h>
 
+static int h4300 = 0; /* h4000_buttons.h4300=1 boot param enables h4300 keyboard*/
+module_param(h4300, bool, 0);
+MODULE_PARM_DESC(h4300, "Enables h4300 keyboard");
+
 extern struct platform_device h4000_asic3;
 #define asic3 &h4000_asic3.dev
 static struct input_dev *button_dev;
@@ -49,8 +54,8 @@
 };
 
 static struct keys pxa_button_tab[] = {
-	{ KEY_ENTER,	GPIO_NR_H4000_ACTION_BUTTON_N,	"Action button"	},
 	{ KEY_SLEEP_L,	GPIO_NR_H4000_POWER_BUTTON_N,	"Power button"	},
+	{ KEY_ENTER,	GPIO_NR_H4000_ACTION_BUTTON_N,	"Action button"	},
 	{ KEY_UP,	GPIO_NR_H4000_UP_BUTTON_N,	"Up button"	},
 	{ KEY_DOWN,	GPIO_NR_H4000_DOWN_BUTTON_N,	"Down button"	},
 	{ KEY_LEFT,	GPIO_NR_H4000_LEFT_BUTTON_N,	"Left button"	},
@@ -78,6 +83,138 @@
 	{ BIT_CALENDAR, KEY_CALENDAR_L, H4000_CALENDAR_BTN_IRQ, "Calendar button" },
 };
 
+#define KEY_FUNC 0x32
+static unsigned int h4300_keycode[0x80] = {
+/*0*/   0, 0, 0, 0, 0, 0, 0, 0,
+        0,         0,              KEY_CALENDAR_L,KEY_CONTACTS_L,
+        0,         0,              KEY_MAIL_L,    KEY_HOME_L,
+/*1*/   KEY_R,     KEY_RIGHT,      KEY_W,         KEY_E,
+        KEY_SELECT,KEY_U,          KEY_I,         KEY_P,
+        KEY_F,     KEY_Y,          KEY_Q,         KEY_D,
+        KEY_DOWN,  KEY_K,          KEY_O,         KEY_BACKSPACE,
+/*2*/   KEY_X,     KEY_H,          KEY_A,         KEY_S,
+        KEY_T,     KEY_J,          KEY_L,         KEY_ENTER,
+        KEY_C,     KEY_B,          KEY_LEFTSHIFT, KEY_Z,
+        KEY_G,     KEY_M,          KEY_1/*'!'*/, KEY_APOSTROPHE,
+/*3*/   KEY_ESC,   KEY_SPACE,      0/*KEY_FUNC*/, KEY_TAB,
+        KEY_V,     KEY_N,          KEY_COMMA,     KEY_SLASH, /*'?'*/
+        KEY_LEFT,  KEY_UP,         0, 0,
+        KEY_LEFTCTRL,  KEY_DOT,    0, 0,
+/*4*/   0, 0, 0, 0, 0, 0, 0, 0,
+        0,                  0,           KEY_PROG1,      KEY_PROG2,
+        0,                  0,           KEY_PROG3,      KEY_PROG4,
+/*5*/   KEY_5/*%*/,         KEY_RIGHT,   KEY_2 /*@*/,    KEY_4/*$*/,
+        KEY_SELECT,         KEY_1,       KEY_2,          KEY_SLASH,
+        KEY_EQUAL,          KEY_MINUS,   KEY_GRAVE/*~*/, KEY_SEMICOLON,/*:*/
+        KEY_DOWN,           KEY_5,       KEY_3,          KEY_DELETE,
+/*6*/   KEY_SEMICOLON/*;*/, KEY_KPPLUS,  KEY_9/*(*/,     KEY_0/*)*/,
+        KEY_MINUS/*_*/,     KEY_4,       KEY_6,          KEY_ENTER,
+        KEY_LEFT,           KEY_RIGHT,   KEY_CAPSLOCK,   KEY_7/*&*/,
+        KEY_UP,             KEY_8,       KEY_9,          KEY_APOSTROPHE/*"*/,
+/*7*/   KEY_ESC,            KEY_COMPOSE, 0/*KEY_FUNC*/,  KEY_TAB,
+        KEY_DOWN,           KEY_7,       KEY_0,          KEY_3/*#*/,
+        KEY_LEFT,           KEY_UP,      0, 0,
+        KEY_BACKSLASH,/*menu*/ KEY_KPASTERISK, 0, 0
+};
+
+static int asic3_spi_process_byte(unsigned char data)
+{
+#define ASIC3_SPI_CTRL    (0x400)
+#define ASIC3_SPI_DATA    (0x408)
+#define SPI_CTRL_SEL      (1 << 6)
+#define SPI_CTRL_SPIE     (1 << 5)
+#define SPI_CTRL_SPE      (1 << 4)
+	unsigned long flags;
+	unsigned int timeout;
+	unsigned int ctrl;
+	int result = 0;
+
+	local_irq_save(flags);
+
+	/* Enable interrupts */
+	ctrl = asic3_read_register(asic3, ASIC3_SPI_CTRL) | SPI_CTRL_SPIE;
+	asic3_write_register(asic3, ASIC3_SPI_CTRL, ctrl);
+
+	/* Send data */
+	asic3_write_register(asic3, ASIC3_SPI_DATA, data);
+	/* Start the transfer */
+	asic3_write_register(asic3, ASIC3_SPI_CTRL, (ctrl | SPI_CTRL_SPE));
+
+	for (timeout = 255; timeout > 0; timeout--) {
+		if (!(asic3_read_register(asic3, ASIC3_SPI_CTRL)&SPI_CTRL_SPE)){
+			udelay(20);    /* wait for a while, or we'll miss it */
+			result = asic3_read_register(asic3, ASIC3_SPI_DATA);
+			break;
+		}
+	}
+
+	/* Disable interrupts */
+	ctrl = asic3_read_register(asic3, ASIC3_SPI_CTRL) & ~SPI_CTRL_SPIE;
+	asic3_write_register(asic3, ASIC3_SPI_CTRL, ctrl);
+
+	local_irq_restore(flags);
+	return result & 0xff;
+}
+
+void kbd_task(unsigned long na)
+{
+#define FNC_PRESS   (1<<0)
+#define FNC_REL     (1<<1)
+#define KEY_REL     (1<<2)
+#define FAKE_SHIFT  (1<<3)
+	static int pressed;
+	unsigned char scancode = 0;
+	static unsigned char flags = 0;
+
+	scancode = asic3_spi_process_byte(0);
+	pressed  = (scancode & 0x80) ? 0 : 1;
+	scancode &= ~0x80;
+
+	if ((scancode != KEY_FUNC) && (flags & FNC_PRESS)) {
+		if (!pressed) {
+			if (flags & FNC_REL)
+				flags &= ~(FNC_PRESS | FNC_REL | KEY_REL);
+			else
+				flags |= KEY_REL;
+		}
+		scancode += 0x40; // the keys alternate function
+	}
+
+	switch (scancode) {
+	case KEY_FUNC:
+		if (pressed)
+			flags = FNC_PRESS;
+		else if (flags & KEY_REL)
+			flags &= ~(FNC_PRESS | FNC_REL | KEY_REL);
+		else
+			flags |= FNC_REL;
+		break;
+
+	case 0x2e:case 0x37:case 0x50:case 0x52:case 0x53:case 0x5a: case 0x5b:
+	case 0x62:case 0x63:case 0x64:case 0x6b:case 0x6f:case 0x77: case 0x7c:
+		flags |= FAKE_SHIFT;
+		input_report_key(button_dev, KEY_LEFTSHIFT, pressed);
+		break;
+
+	default:
+		if (flags & FAKE_SHIFT) {
+			input_report_key(button_dev, KEY_LEFTSHIFT, 0);
+			flags &= ~FAKE_SHIFT;
+		}
+		break;
+	}
+
+	input_report_key(button_dev, h4300_keycode[scancode], pressed);
+	input_sync(button_dev);
+}
+DECLARE_TASKLET(task, kbd_task, 0);
+
+static irqreturn_t h4300_keyboard(int irq, void *dev_id, struct pt_regs *regs)
+{
+	tasklet_schedule(&task);
+	return IRQ_HANDLED;
+}
+
 static irqreturn_t h4000_buttons_asic_handle(int irq, void *data, struct pt_regs *regs)
 {
 	int i, pressed, base_irq;
@@ -119,6 +256,8 @@
 static int __init h4000_buttons_probe(struct device *dev)
 {
 	int i, base_irq;
+	int buttons = h4300 ? 1 : ARRAY_SIZE(button_tab);
+	int pxa_buttons = h4300 ? 1 : ARRAY_SIZE(pxa_button_tab);
 
 	if (!machine_is_h4000())
 		return -ENODEV;
@@ -127,18 +266,44 @@
 	if (!button_dev)
 		return -ENOMEM;
 
-	button_dev->name = "HP iPAQ h4xxx buttons driver";
+	button_dev->name = (h4300) ? "HP iPAQ h4300 buttons driver"
+	                           : "HP iPAQ h4100 buttons driver";
 	set_bit(EV_KEY, button_dev->evbit);
 
 	base_irq = asic3_irq_base(asic3);
 
-	for (i = 0; i < ARRAY_SIZE(pxa_button_tab); i++) {
+	if (h4300) {
+		asic3_set_clock_cdex(asic3, CLOCK_CDEX_SPI, CLOCK_CDEX_SPI);
+		asic3_set_clock_cdex(asic3, CLOCK_CDEX_EX1, CLOCK_CDEX_EX1);
+		ipaq_asic3_set_gpio_dir_b(asic3, GPIOB_KEYBOARD_IRQ, 0);
+		ipaq_asic3_set_gpio_out_b(asic3,
+				(GPIOB_MICRO_3V3_EN | GPIOB_KEYBOARD_WAKE_UP),
+				(GPIOB_MICRO_3V3_EN | GPIOB_KEYBOARD_WAKE_UP) );
+		ipaq_asic3_set_gpio_dir_c(asic3, GPIOC_KEY_RXD, 0);
+		ipaq_asic3_set_gpio_alt_fn_c(asic3,
+				GPIOC_KEY_RXD | GPIOC_KEY_TXD | GPIOC_KEY_CLK,
+				GPIOC_KEY_RXD | GPIOC_KEY_TXD | GPIOC_KEY_CLK);
+		asic3_write_register(asic3, 0x400, 0xa104);
+		button_dev->evbit[0]    = BIT(EV_KEY) | BIT(EV_REP);
+		button_dev->keycode     = h4300_keycode;
+		button_dev->keycodesize = sizeof(unsigned char);
+		button_dev->keycodemax  = ARRAY_SIZE(h4300_keycode);
+
+		for (i = 0; i < button_dev->keycodemax; i++)
+			if (h4300_keycode[i])
+				set_bit(h4300_keycode[i], button_dev->keybit);
+
+		request_irq(base_irq + H4000_KEYBOARD_IRQ, &h4300_keyboard,
+				SA_INTERRUPT, "keyboard", NULL);
+	}
+
+	for (i = 0; i < pxa_buttons; i++) {
 		set_bit(pxa_button_tab[i].keyno, button_dev->keybit);
 		request_irq(IRQ_GPIO(pxa_button_tab[i].gpio), h4000_buttons_handle, SA_SAMPLE_RANDOM, pxa_button_tab[i].desc, NULL);
 		set_irq_type(IRQ_GPIO(pxa_button_tab[i].gpio), IRQT_BOTHEDGE);
 	}
 
-	for (i = 0; i < ARRAY_SIZE(button_tab); i++) {
+	for (i = 0; i < buttons; i++) {
 		set_bit(button_tab[i].keyno, button_dev->keybit);
 		request_irq(base_irq + button_tab[i].irq, h4000_buttons_asic_handle, SA_SAMPLE_RANDOM, button_tab[i].desc, NULL);
 		set_irq_type(base_irq + button_tab[i].irq, IRQT_FALLING);
@@ -170,18 +335,23 @@
 static void __exit h4000_buttons_exit(void)
 {
 	int i, base_irq;
+	int buttons = h4300 ? 1 : ARRAY_SIZE(button_tab);
+	int pxa_buttons = h4300 ? 1 : ARRAY_SIZE(pxa_button_tab);
 
 	input_unregister_device(button_dev);
 
-	for (i = 0; i < ARRAY_SIZE(pxa_button_tab); i++) {
+	for (i = 0; i < pxa_buttons; i++) {
 		free_irq(IRQ_GPIO(pxa_button_tab[i].gpio), NULL);
 	}
 
 	base_irq = asic3_irq_base(asic3);
-	for (i = 0; i < ARRAY_SIZE(button_tab); i++) {
+	for (i = 0; i < buttons; i++) {
 		free_irq(base_irq + button_tab[i].irq, NULL);
 	}
 
+	if (h4300)
+		free_irq(base_irq + H4000_KEYBOARD_IRQ, NULL);
+
 	driver_unregister(&h4000_buttons_driver);
 }
 

