diff -urpN -X ../dontdiff linux-2.6.17-rc1/drivers/input/keyboard/Kconfig linux-2.6.17-rc1-spi4/drivers/input/keyboard/Kconfig
--- linux-2.6.17-rc1/drivers/input/keyboard/Kconfig	2006-04-03 13:03:33.000000000 +0100
+++ linux-2.6.17-rc1-spi4/drivers/input/keyboard/Kconfig	2006-04-15 16:04:51.000000000 +0100
@@ -183,4 +183,11 @@ config KEYBOARD_HIL
 	  This driver implements support for HIL-keyboards attached
 	  to your machine, so normally you should say Y here.
 
+config KEYBOARD_UR5HCSPI
+	tristate "Semtech UR5HCSPI SPI Keyboard controller"
+	depends on SPI
+	help
+	  A driver for the Semtech UR5HCSPI keyboard interface
+	  chips, found on boards like the Samsung SMDK2410	
+
 endif
diff -urpN -X ../dontdiff linux-2.6.17-rc1/drivers/input/keyboard/Makefile linux-2.6.17-rc1-spi4/drivers/input/keyboard/Makefile
--- linux-2.6.17-rc1/drivers/input/keyboard/Makefile	2006-03-20 05:53:29.000000000 +0000
+++ linux-2.6.17-rc1-spi4/drivers/input/keyboard/Makefile	2006-04-15 16:03:34.000000000 +0100
@@ -15,4 +15,4 @@ obj-$(CONFIG_KEYBOARD_CORGI)		+= corgikb
 obj-$(CONFIG_KEYBOARD_SPITZ)		+= spitzkbd.o
 obj-$(CONFIG_KEYBOARD_HIL)		+= hil_kbd.o
 obj-$(CONFIG_KEYBOARD_HIL_OLD)		+= hilkbd.o
-
+obj-$(CONFIG_KEYBOARD_UR5HCSPI)		+= ur5hcspi_kbd.o
diff -urpN -X ../dontdiff linux-2.6.17-rc1/drivers/input/keyboard/ur5hcspi_kbd.c linux-2.6.17-rc1-spi4/drivers/input/keyboard/ur5hcspi_kbd.c
--- linux-2.6.17-rc1/drivers/input/keyboard/ur5hcspi_kbd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17-rc1-spi4/drivers/input/keyboard/ur5hcspi_kbd.c	2006-05-02 00:57:05.000000000 +0100
@@ -0,0 +1,373 @@
+/* drivers/input/keyboard/ur5hcspi_kbd.c
+ *
+ * Copyright (c) 2006 Ben Dooks
+ * Copyright (c) 2006 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* datasheet found at:
+ *	http://www.ortodoxism.ro/datasheets/SemtechCorp/mXuqvvt.pdf
+*/
+
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#define MAX_ROWS	(7)
+#define MAX_COLUMNS	(14)
+
+struct ur5hci_context {
+	struct spi_device	*spi;
+	struct input_dev	*input;
+	struct workqueue_struct	*workqueue;
+	struct work_struct	 work;
+
+	unsigned short		*keymap;
+};
+
+/* default keymap (fujitsu keyboard) */
+
+static unsigned short def_keymap[MAX_ROWS][MAX_COLUMNS] = {
+	[0] = {
+		[0]	= KEY_LEFTALT,
+		[1]	= KEY_GRAVE,
+		[3]	= KEY_LEFTCTRL,
+		[4]	= KEY_FN,
+		[5]	= KEY_ESC,
+		[6]	= KEY_1,
+		[7]	= KEY_2,
+		[8]	= KEY_9,
+		[10]	= KEY_MINUS,
+		[11]	= KEY_EQUAL,
+		[13]	= KEY_BACKSPACE,		
+	},
+	[1] = {
+		[1]	= KEY_BACKSLASH,
+		[2]	= KEY_LEFTSHIFT,
+		[5]	= KEY_DELETE,
+		[7]	= KEY_T,
+		[8]	= KEY_Y,
+		[9]	= KEY_U,
+		[10]	= KEY_I,
+		[11]	= KEY_ENTER,
+		[12]	= KEY_RIGHTSHIFT,
+		[13]	= KEY_DOWN,
+	},
+	[2] = {
+		[1]	= KEY_TAB,
+		[5]	= KEY_Q,
+		[6]	= KEY_W,
+		[7]	= KEY_E,
+		[8]	= KEY_R,
+		[9]	= KEY_O,
+		[10]	= KEY_P,
+		[11]	= KEY_LEFTBRACE,
+		[13]	= KEY_RIGHTBRACE,
+	},
+	[3] = {
+		[1]	= KEY_Z,
+		[5]	= KEY_CAPSLOCK,
+		[8]	= KEY_K,
+		[9]	= KEY_L,
+		[10]	= KEY_SEMICOLON,
+		[11]	= KEY_APOSTROPHE,
+		[13]	= KEY_UP,
+	},
+	[4] = {
+		[1]	= KEY_A,
+		[5]	= KEY_S,
+		[6]	= KEY_D,
+		[7]	= KEY_F,
+		[8]	= KEY_G,
+		[9]	= KEY_H,
+		[10]	= KEY_J,
+		[11]	= KEY_SLASH,
+		[13]	= KEY_LEFT,
+	},
+	[5] = {
+		[1] 	= KEY_X,
+		[5]	= KEY_C,
+		[6]	= KEY_V,
+		[7]	= KEY_B,
+		[8]	= KEY_N,
+		[9]	= KEY_M,
+		[10]	= KEY_COMMA,
+		[11]	= KEY_DOT,
+		[13]	= KEY_SPACE,
+	},
+	[6] = {
+		[5]	= KEY_3,
+		[6]	= KEY_4,
+		[7]	= KEY_5,
+		[6]	= KEY_6,
+		[7]	= KEY_7,
+		[8]	= KEY_8,
+		[13]	= KEY_RIGHT,
+	},	
+};
+
+#define CMD_INIT	(0xA0)
+#define CMD_INIT_DONE	(0xA1)
+#define CMD_HEARTBEAT	(0xA2)
+#define CMD_IDREQ	(0xF2)
+#define CMD_RESEND	(0xA5)
+#define CMD_OP_IO	(0xA8)
+#define CMD_SETWAKE	(0xA9)
+
+
+static inline unsigned char calc_lrc(unsigned char *b, int l)
+{
+	unsigned int tmp = 0;
+	int ptr;
+
+	for (ptr = 0; ptr < l; ptr++)
+		tmp ^= b[ptr];
+
+	if (tmp & 0x80)
+		tmp ^= 0xC0;
+
+	return tmp;
+}
+
+static int our_spi_write(struct spi_device *spi, unsigned char *data, int len)
+{
+	struct spi_message msg;
+	struct spi_transfer tr[len];
+	int m;
+
+	spi_message_init(&msg);
+
+	for (m = 0; m < len; m++) {
+		struct spi_transfer *t = &tr[m];
+
+		memset(t, 0, sizeof(struct spi_transfer));
+
+		t->tx_buf      = data + m;
+		t->len         = 1;
+		t->delay_usecs = 250;
+		spi_message_add_tail(t, &msg);
+	}
+
+	return spi_sync(spi, &msg);
+}
+
+static irqreturn_t ur5hcspi_irq(int irq, void *param, struct pt_regs *regs)
+{
+	struct ur5hci_context *ur = param;
+
+	queue_work(ur->workqueue, &ur->work);
+	return IRQ_HANDLED;
+}
+
+static void ur5hcspi_work(void *work)
+{
+	struct ur5hci_context *ur = work;
+	unsigned char rx[1];
+	unsigned int scancode;
+	int up;
+
+	spi_read(ur->spi, rx, 1);
+	
+	up = rx[0] & 0x80;
+	scancode = rx[0] & 0x7f;
+	scancode = ur->keymap[scancode];
+
+	dev_dbg(&ur->spi->dev, "read %02x, up %d, scan %d\n",
+		rx[0], up, scancode);
+
+	input_report_key(ur->input, scancode, up);
+	input_sync(ur->input);
+}
+
+static int ur5hci_send_cmd(struct ur5hci_context *ur, unsigned int cmd)
+{
+	unsigned char tx[3];
+
+	tx[0] = 0x1b;
+	tx[1] = cmd;
+	tx[2] = calc_lrc(tx, 2);
+
+	dev_info(&ur->spi->dev, "send %02x,%02x,%02x\n", tx[0], tx[1], tx[2]);
+
+	return our_spi_write(ur->spi, tx, 3);
+}
+
+static int ur5hci_send_cmd2(struct ur5hci_context *ur, unsigned int cmd,
+			    const char *txdata, unsigned int txlen)
+{
+	unsigned char tx[4 + txlen];
+
+	tx[0] = 0x1b;
+	tx[1] = cmd;
+	memcpy(tx+2, txdata, txlen);
+	tx[2+txlen] = calc_lrc(tx, txlen+2);
+
+	return our_spi_write(ur->spi, tx, txlen+3);
+}
+
+static int __devinit ur5hcspi_probe(struct spi_device *spi)
+{
+	struct ur5hci_context *ur;
+	unsigned char tx[3];
+	int ret;
+	int i;
+
+	dev_info(&spi->dev, "%s: keyboard driver\n", __FUNCTION__);
+
+	ur = kzalloc(sizeof(struct ur5hci_context), GFP_KERNEL);
+	if (ur == NULL) {
+		dev_err(&spi->dev, "no memory for state\n");
+		ret = -ENOMEM;
+		goto err_1;
+	}		     
+
+	ur->spi = spi;
+	ur->keymap = def_keymap;
+
+	/* -SA is mode 0 */
+	spi->mode = SPI_MODE_0;
+	spi->bits_per_word = 8;
+
+	spi_setup(spi);
+	
+	ur->input = input_allocate_device();
+	if (ur->input == NULL) {
+		dev_err(&spi->dev, "cannot allocate input device\n");
+		ret = -ENOMEM;
+		goto err_free;
+	}
+
+	ur->input->name = "ur5hcspi";
+	ur->input->cdev.dev = &spi->dev;
+	ur->input->id.bustype = BUS_RS232 /* BUS_SPI */;
+	ur->input->id.vendor = 0x0001;
+	ur->input->id.product = 0x0001;
+	ur->input->id.version = 0x0100;
+	ur->input->private = ur;
+	ur->input->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+	ur->input->keycode = ur->keymap;
+	ur->input->keycodesize = sizeof(unsigned short);
+	ur->input->keycodemax = MAX_ROWS * MAX_COLUMNS;
+
+	for (i = 0; i < ur->input->keycodemax; i++) {
+		if (ur->keymap[i])
+			set_bit(ur->keymap[i], ur->input->keybit);
+	}
+
+	INIT_WORK(&ur->work, ur5hcspi_work, ur);
+	PREPARE_WORK(&ur->work, ur5hcspi_work, ur);
+
+	ur->workqueue = create_singlethread_workqueue("ur5hcspi");
+	if (ur->workqueue == NULL) {
+		dev_err(&spi->dev, "cannot create workqueue\n");
+		goto err_no_wq;
+	}
+
+	ret = request_irq(spi->irq, ur5hcspi_irq,
+			  SA_INTERRUPT | SA_TRIGGER_FALLING, 
+			   "ur5hcspi", ur);
+
+	if (ret) {
+		dev_err(&spi->dev, "cannot allocate irq\n");
+		goto err_free_wq;
+	}
+
+	dev_info(&spi->dev, "sending init command\n");
+
+	/* we seem to have little success actually getting the device
+	 * to respond to commands, which is also noted by the BSD driver.
+	 * 
+	 * This could be down to the pin configuration on the SMDK2440, or
+	 * some other problem we have yet to diagnose. Send the commands
+	 * anyway
+	 */
+
+	ret = ur5hci_send_cmd(ur, CMD_INIT);
+	if (ret) {
+		dev_err(&spi->dev, "%s: spi_write %d\n", __FUNCTION__, ret);
+		goto err_free_irq;
+	}
+
+	msleep(1);
+
+	ret = ur5hci_send_cmd(ur, CMD_HEARTBEAT);
+	if (ret) {
+		dev_err(&spi->dev, "%s: spi_write %d\n", __FUNCTION__, ret);
+		goto err_free_irq;
+	}
+
+	/* register ourselves with device and input layers */
+
+	dev_set_drvdata(&spi->dev, ur);
+	input_register_device(ur->input);
+
+	return 0;
+
+ err_free_irq:
+	free_irq(spi->irq, ur);
+ err_free_wq:
+	destroy_workqueue(ur->workqueue);
+ err_no_wq:
+	input_free_device(ur->input);
+ err_free:
+	kfree(ur);
+ err_1:
+	return ret;
+}
+
+static int ur5hcspi_remove(struct spi_device *spi)
+{
+	struct ur5hci_context *ur = dev_get_drvdata(&spi->dev);
+
+	free_irq(spi->irq, ur);
+
+	destroy_workqueue(ur->workqueue);
+
+	input_unregister_device(ur->input);
+	input_free_device(ur->input);
+
+	kfree(ur);
+	return 0;
+}
+
+static struct spi_driver ur5hc_driver = {
+	.driver		= {
+		.name	= "ur5hcspi_kbd",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ur5hcspi_probe,
+	.remove		= ur5hcspi_remove,
+};
+
+
+static int __devinit ur5hc_init(void)
+{
+	return spi_register_driver(&ur5hc_driver);
+}
+
+static void __exit ur5hc_exit(void)
+{
+	spi_unregister_driver(&ur5hc_driver);
+}
+
+module_init(ur5hc_init);
+module_exit(ur5hc_exit);
+
+MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>");
+MODULE_DESCRIPTION("Semtech UR5HCSPI Keyboard driver");
+MODULE_LICENSE("GPLv2");

