diff -Naur linux-2.6.25.at91/drivers/char/Kconfig linux-2.6/drivers/char/Kconfig
--- linux-2.6.25.at91/drivers/char/Kconfig	2009-02-12 12:26:57.000000000 -0600
+++ linux-2.6/drivers/char/Kconfig	2009-01-27 13:12:55.000000000 -0600
@@ -819,6 +819,22 @@
 	  This option enables support for the LCD display and buttons found
 	  on Cobalt systems through a misc device.
 
+config LCD447
+ 	tristate "Generic Character LCD driver for HD44780"
+ 	default n
+ 	help
+ 	  Driver for character LCD's using the HD44780 IC. Based on the 
+ 	  sourceforge lcd-mod project, this driver communicates directly with the LCD,
+ 	  rather than using the parallel port interface.
+
+config KEYPAD
+ 	tristate "Generic Keypad driver"
+ 	default n
+ 	help
+ 	  A generic Keypad driver, originally part of the 2.0 uClinux build, this 
+ 	  driver has been updated for the 2.6 Kernel and now supports the EMAC Inc.
+ 	  carrier board architecture.
+
 config DTLK
 	tristate "Double Talk PC internal speech card support"
 	depends on ISA
diff -Naur linux-2.6.25.at91/drivers/char/keypad.c linux-2.6/drivers/char/keypad.c
--- linux-2.6.25.at91/drivers/char/keypad.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6/drivers/char/keypad.c	2009-02-04 17:32:41.000000000 -0600
@@ -0,0 +1,458 @@
+/*****************************************************************************/
+
+/*
+ *	keypad.c -- simple keypad driver.
+ *
+ *	(C) Copyright 2001, Greg Ungerer (gerg@snapgear.com)
+ *	(C) Copyright 2001, Lineo Inc. (www.lineo.com) 
+ * 
+ * Modified 2005 NZG EMAC.Inc to load in the 2.6 kernel and support the
+ * SoM-100ES. Also modified to support loading the keypad matrix via IOCTL
+ * Low level calls are broken into inlines in keypad.h
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/cdev.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/device.h>
+
+#include <asm/uaccess.h>
+#include <asm/param.h>
+#include <asm/types.h>
+
+#include <asm/mach/irq.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/arch/gpio.h>
+
+#define PLDBASE 	0x50000000
+#ifdef CONFIG_SOM_150ES_REV1
+#define KEYPAD		8
+#else
+#define KEYPAD		7
+#endif
+
+#define PLD_READ(index) *(volatile u16 *)(virt_pld_base+index)
+#define KEYROW		0x03
+#define KEYCOLUMN	0x70
+#define KEYGATE		0x04
+#define ROWCOLMASK	(KEYROW|KEYCOLUMN)
+
+u8 * virt_pld_base;
+
+inline u8 Keypad_Read(void)
+{
+	return PLD_READ(KEYPAD);
+}
+
+int keypad_major;
+
+#define FIRSTKEYMINOR 	5
+#define KEYMINORNUM 	1
+//#define POLLING 	1
+#define SLEEP_FRACTION 	50
+#define REPEAT_DELAY	SLEEP_FRACTION //in units of HZ/SLEEP_FRACTION
+
+#if (HZ/SLEEP_FRACTION)
+#define TIMEOUT  (HZ/SLEEP_FRACTION)
+#else
+#define TIMEOUT 1
+#endif
+
+#define KEYHELD	!at91_get_gpio_value(AT91_PIN_PC15)
+
+#define MASK	0x0100
+
+inline unsigned short GETKEYPOS(void)
+{
+	return (Keypad_Read()&ROWCOLMASK);
+}
+
+inline void KEYPAD_MASK_IRQ(void)
+{
+	at91_sys_write(AT91_AIC_IDCR, 1 << AT91SAM9260_ID_IRQ1);
+}
+
+inline void KEYPAD_UNMASK_IRQ(void)
+{
+	at91_sys_write(AT91_AIC_IECR, 1 << AT91SAM9260_ID_IRQ1);
+}
+
+inline void KEYPAD_IRQ_INIT(void)
+{
+	at91_set_B_periph(AT91_PIN_PC15, 0);
+	at91_set_deglitch(AT91_PIN_PC15, 0);
+	at91_sys_write(AT91_AIC_IECR, 1 << AT91SAM9260_ID_IRQ1);
+}
+
+#define ROW(pos)		((~pos)&KEYROW)
+#define COLUMN(pos) 		(pos>>4)
+#define KEYPAD_IRQ 		AT91SAM9260_ID_IRQ1
+#define KEYPAD_ROWS		4
+#define KEYPAD_COLUMNS  	6
+#define DRIVERCODE 		'K'
+#define SETKEYARRAY		_IOR(DRIVERCODE,0,char)
+#define GETKEYARRAY 		_IOW(DRIVERCODE,1,char)
+#define KEYPAD_UNDEFINED_CHAR	'x'
+
+MODULE_DESCRIPTION( "generic keypad driver" );
+MODULE_AUTHOR( "Greg Ungerer <gerg@snapgear.com>,NZG <ngustavson@emacinc.com>" );
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define DRIVER_NAME "keypad"
+
+
+/**
+ * debug statements
+ */
+//#define KEYPAD_DEBUG 1
+
+/*
+ *	Place to store key codes at interrupt/poll time.
+ */
+#define	KEYPAD_BUFSIZE		8
+
+unsigned char	keypad_buf[KEYPAD_BUFSIZE];
+int		keypad_bufhead;
+int		keypad_bufcount;
+int		keypad_isopen;
+int		keypad_blocking;
+int 		countdown;
+int 		intimer;
+
+static struct cdev *keypad_cdev;
+DECLARE_WAIT_QUEUE_HEAD(keypad_waitq_head);
+
+static struct timer_list poll_timer;
+
+//simper and quicker to copy a single dimension from user space
+char keypad_matrix[KEYPAD_ROWS*KEYPAD_COLUMNS];
+
+/*****************************************************************************/
+
+int keypad_open(struct inode *inode, struct file *filp)
+{
+#ifdef KEYPAD_DEBUG
+	printk("keypad_open()\n");
+#endif
+
+	keypad_blocking = (filp->f_flags & O_NONBLOCK) ? 0 : 1;
+	keypad_isopen++;
+	return(0);
+}
+
+/*****************************************************************************/
+
+int keypad_release(struct inode *inode, struct file *filp)
+{
+#ifdef KEYPAD_DEBUG
+	printk("keypad_close()\n");
+#endif
+	keypad_isopen = 0;
+
+	return 0;
+}
+
+/*****************************************************************************/
+static ssize_t keypad_read (struct file *file, char __user *buf, size_t count, loff_t *loff)
+{
+#ifndef POLLING
+	unsigned long	flags;
+	int		i;
+#endif
+	if (count == 0)
+		return 0;
+
+#ifdef KEYPAD_DEBUG
+	printk("keypad_read(buf=%x,count=%d)\n", (int) buf, count);
+#endif
+
+
+#ifdef POLLING
+	del_timer(&poll_timer);
+
+	put_user(keypad_buf[keypad_bufhead], buf);
+
+	if (++keypad_bufhead >= KEYPAD_BUFSIZE)
+		keypad_bufhead = 0;
+	keypad_bufcount--;
+	if (keypad_bufcount < 0)
+		keypad_bufcount = 0;
+
+	poll_timer.expires = jiffies + TIMEOUT;
+	add_timer(&poll_timer);
+	return(1);
+#else
+
+	if(keypad_blocking)
+		wait_event_interruptible(keypad_waitq_head,(keypad_bufcount));	
+	//wait_event_interruptible_timeout(keypad_waitq_head,(keypad_bufcount),HZ);	
+
+
+	save_flags(flags); cli();	
+	if (count > keypad_bufcount)
+		count = keypad_bufcount;
+	for (i = 0; i < count; i++) {
+		put_user(keypad_buf[keypad_bufhead], buf);
+		if (++keypad_bufhead >= KEYPAD_BUFSIZE)
+			keypad_bufhead = 0;
+	}
+	keypad_bufcount -= i;
+	restore_flags(flags);
+	return(i);
+#endif
+}
+
+/*****************************************************************************/
+static unsigned int keypad_poll (struct file *file, struct poll_table_struct *wait)
+{
+	int mask=0;
+	poll_wait(file, &keypad_waitq_head, wait);					
+	if (keypad_bufcount > 0)
+		mask = POLLIN|POLLRDNORM;
+	return mask;
+}
+
+
+/*****************************************************************************
+ * inlines used in keypad_timer
+ */
+inline void restart_timer(void){
+	poll_timer.expires = jiffies + TIMEOUT;
+	add_timer(&poll_timer);
+	intimer = 1;	
+}
+
+inline void timer_exit(void){
+#ifdef POLLING
+	restart_timer();
+#else
+	KEYPAD_UNMASK_IRQ();
+#endif
+	intimer = 0;
+}
+
+inline void enqueue_key(u8 key){
+	int pos;
+	pos = (keypad_bufhead + keypad_bufcount);
+	while (pos >= KEYPAD_BUFSIZE)
+		pos -= KEYPAD_BUFSIZE;
+	keypad_buf[pos] = key;
+	keypad_bufcount++;
+	if (keypad_bufcount >= KEYPAD_BUFSIZE)
+		keypad_bufcount--;
+}
+
+
+/*****************************************************************************/
+static void keypad_timer(void) {
+
+	del_timer(&poll_timer);
+
+#ifdef KEYPAD_DEBUG
+		printk("keypad_timer(countdown = %d) %x\n", countdown, KEYHELD);
+#endif	
+
+	if(countdown){
+		if(KEYHELD){
+			countdown--;
+			restart_timer();
+		}
+		else{
+			countdown=0;
+			timer_exit();
+			return;
+		}	
+	}
+	else{//countdown is zero
+		unsigned short pos = GETKEYPOS();	
+		char key = keypad_matrix[ROW(pos)*KEYPAD_COLUMNS+COLUMN(pos)];
+
+#ifdef KEYPAD_DEBUG
+		printk("keypad_timer(row = %u  column = %u-> key = %c) KEYHELD = %x\n", ROW(pos),COLUMN(pos),key,KEYHELD);
+#endif
+
+		enqueue_key(key);
+		
+		wake_up_interruptible(&keypad_waitq_head);
+		if(KEYHELD){
+			countdown=REPEAT_DELAY;
+			restart_timer();
+		}
+		else{
+			timer_exit();
+			return;
+		}		
+	}//countdown is zero
+
+}
+
+/*****************************************************************************/
+
+int keypad_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	int	rc = 0;
+
+	switch (cmd) {
+		case SETKEYARRAY:					
+			if(access_ok(VERIFY_READ,(char *)arg,sizeof(keypad_matrix)))
+				copy_from_user(keypad_matrix,(char *)arg,sizeof(keypad_matrix));
+			else
+				return -EINVAL;
+			break;
+		case GETKEYARRAY:
+			if(access_ok(VERIFY_WRITE,(char *)arg,sizeof(keypad_matrix)))
+				copy_to_user((char *)arg,keypad_matrix,sizeof(keypad_matrix));
+			else
+				return -EINVAL;
+			break;
+			break; 
+		default:
+			rc = -EINVAL;
+			break;
+	}
+	return(rc);
+}
+
+/*****************************************************************************/
+
+irqreturn_t  keypad_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	u32 status;
+#ifdef KEYPAD_DEBUG
+	printk("keypad_isr(irq=%d)\n", irq);
+#endif
+	/**
+	 * ISR is a trigger for timer state machine
+	 */
+	status = (1 << at91_sys_read(AT91_AIC_ISR)) & ~at91_sys_read(AT91_AIC_IMR);
+	
+	if(status & (1 << AT91SAM9260_ID_IRQ1))
+	{
+
+		if(KEYHELD && !intimer)
+		{
+			KEYPAD_MASK_IRQ();
+			restart_timer();
+		}
+	}
+
+	return IRQ_RETVAL(1);
+}
+
+/* Handle read from proc file */
+int readprocfile( char *buffer, char **start, off_t offset, int size, int *eof, void *data )
+{
+	char *temp = buffer;
+	int row, column;
+
+	/* Print module configuration */
+	temp += sprintf( temp, "rows:    %d\n"
+			"columns: %d\n\n",KEYPAD_ROWS, KEYPAD_COLUMNS);
+
+	for(row=0;row<KEYPAD_ROWS;row++){
+		for(column=0;column<KEYPAD_COLUMNS;column++)
+			temp+=sprintf(temp,"%c ",keypad_matrix[row*KEYPAD_COLUMNS+column]);
+		temp+=sprintf(temp,"\n");
+	}
+	return temp - buffer;
+}
+
+
+
+/*****************************************************************************/
+
+/*
+ *	Exported file operations structure for driver...
+ */
+
+struct file_operations 	keypad_fops = {
+	.owner 		= THIS_MODULE,
+	.read 		= keypad_read,	
+	.poll		= keypad_poll,		
+	.ioctl		= keypad_ioctl,		
+	.open		= keypad_open,	
+	.release	= keypad_release,	
+};
+
+/*****************************************************************************/
+static int __init start_module(void)
+{
+	dev_t dev = MKDEV(0 ,0);
+	int result;
+
+	result = alloc_chrdev_region(&dev, KEYMINORNUM, 1, DRIVER_NAME);
+
+	if(result < 0){
+		printk(DRIVER_NAME " driver failed to register Major Number"); 
+		return result; 
+	}
+
+	keypad_major = MAJOR(dev);
+
+	keypad_cdev = cdev_alloc();
+	keypad_cdev->ops = &keypad_fops;
+	keypad_cdev->owner = THIS_MODULE;
+
+	keypad_isopen = 0;
+	intimer = 0;
+
+	init_timer(&poll_timer);
+	poll_timer.function = (void *)keypad_timer;
+
+#ifdef POLLING	
+	poll_timer.expires = jiffies + TIMEOUT;
+	add_timer(&poll_timer);
+#else
+	/* register IRQ */{
+		int rc;
+		KEYPAD_IRQ_INIT();
+		rc = request_irq(KEYPAD_IRQ, keypad_isr, IRQT_FALLING, DRIVER_NAME, NULL);
+		if (rc) {
+			printk("Keypad irq not registered.  Error: %d\n", rc);
+		}
+	}
+#endif
+
+	memset(keypad_matrix,KEYPAD_UNDEFINED_CHAR,sizeof(keypad_matrix));
+
+	if(!create_proc_read_entry( "keypad", 0, 0, readprocfile, NULL ) ){
+		printk( KERN_ERR "can't create /proc/keypad\n" );
+		return -ENOMEM;
+	}
+
+	cdev_add(keypad_cdev,dev,KEYMINORNUM);
+
+	class_device_create(class_create(THIS_MODULE,"keypad"),NULL,dev,NULL,"keypad");
+
+	virt_pld_base = ioremap_nocache(PLDBASE,0xF);
+
+	printk(DRIVER_NAME": Copyright (C) 2000-2001, Greg Ungerer "
+			"(gerg@snapgear.com)\n"
+			"2.6 Update and SoM support by EMAC.Inc->NZG 2005\n"
+	      );
+
+	return 0;
+}
+
+/*****************************************************************************/
+static void __exit exit_module(void)
+{
+	dev_t dev = MKDEV(keypad_major ,FIRSTKEYMINOR);
+	unregister_chrdev_region(dev, KEYMINORNUM);
+	cdev_del(keypad_cdev);
+}
+
+module_init(start_module);
+module_exit(exit_module);
diff -Naur linux-2.6.25.at91/drivers/char/lcd447.c linux-2.6/drivers/char/lcd447.c
--- linux-2.6.25.at91/drivers/char/lcd447.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6/drivers/char/lcd447.c	2008-12-24 11:32:45.000000000 -0600
@@ -0,0 +1,578 @@
+/****************************************************************************
+ * lcd447.c 
+ * Generic Character LCD driver for HD44780 
+ * EMAC.Inc->NZG
+ * January 8, 2007
+ * 
+ * based on the lcdmod project:
+ * Copyright (C) by Michael McLellan (mikey@mclellan.org.nz)
+ * 
+ * Released under the terms of the GNU GPL, see file COPYING for more details.
+ * 
+ * NZG modifications:
+ * 
+ * Renamed everything lcd447 so as not to conflict with the strange "Cobalt" driver
+ * that has somehow made it's way into the 2.6 kernel and named itself lcd.c
+ * 
+ * Added BSD licensing to the license (ala Rubini's drivers) 
+ * not really sure what this does for us though.
+ * 
+ * Stripped down to provide LCD support using a macro for data write.control write.
+ * bus interface code provided by in-lines in the header.
+ * 
+ * Cleaned to support only the 2.6 kernel, to eventually integrate into main tree
+ * 
+ * Currently supports only 1 controller.
+ *  This may need to be put back in, but should probably be done with in-lines
+ * as heavy ifdefs are not generally mergeable into the main tree.
+ * 
+ * Merged all .h's into a singe .h, elcd.h which stands for EMAC LCD, as it provides
+ * the bus interface functions to the LCD.
+ * 
+ * inlined write_command since it's pretty small.
+ * 
+ * fixed bug preventing first word written from being readable by proc
+ * 
+ * Added LCDDEBUG optional debug statements
+ * 
+ * Moved MODULE_PARM to MODULE_PARM_DESC to support newer kernels
+ * 
+ ****************************************************************************/
+
+#include <linux/version.h>
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <asm/io.h>
+#include <linux/cdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+#include "lcd447.h"
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define MAX_DISP_ROWS	4	// The HD44780 supports up to 4 rows
+#define MAX_DISP_COLS	40	// The HD44780 supports up to 40 columns
+
+
+/* input_state states */
+#define NORMAL		0
+#define ESC		1   	// Escape sequence start
+#define DCA_Y		2   	// Direct cursor access, the next input will be the row
+#define DCA_X		3   	// Direct cursor access, the next input will be the column
+#define CGRAM_SELECT	4	// Selecting which slot to enter new character
+#define CGRAM_ENTRY	5	// Adding a new character to the CGRAM
+#define CGRAM_GENERATE	6	// Waiting fot the 8 bytes which define a character
+#define CHAR_MAP_OLD	7	// Waiting for the original char to map to another
+#define CHAR_MAP_NEW	8	// Waiting for the new char to replace the old one with
+#define ESC_		10	// Waiting for the [ in escape sequence
+
+/**
+ * HD44780  Commands
+ * 		and bitflags within them
+ */
+#define FUNCTION_SET 	0x20
+#define DATA_8BIT  	0x10
+#define DISPLAY_2LINES 	0x08
+//#define DISPLAY_ON 	0x0C
+#define DISPLAY_ON 	0x0D
+#define DISPLAY_OFF	0x08
+#define CLEAR_DISPLAY	0x01
+#define ENTRY_MODE_SET  0x04
+#define INC_RAM		0x02
+#define RETURN_HOME 	0x02
+
+static int disp_rows =	DFLT_DISP_ROWS;
+static int disp_cols = 	DFLT_DISP_COLS;
+static unsigned char state[ MAX_DISP_ROWS ][ MAX_DISP_COLS ];	// The current state of the display
+static int disp_row = 0, disp_column = 0; 			// Current actual cursor position
+static int row = 0, column = 0; 				// Current virtual cursor position
+static int wrap = 0;						// Linewrap on/off
+static char backlight =	BL_ON;					// Backlight on-off
+
+
+static struct cdev *my_cdev; // character device
+
+#undef LCDDEBUG
+//#define LCD_DEBUG
+#ifdef LCD_DEBUG
+#define LCDDEBUG(fmt, args...) printk(fmt, ## args)
+#else 
+#define LCDDEBUG(fmt, args...) 
+#endif 
+
+//backwards compatibility 
+#if defined(_LINUX_BLKDEV_H) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+#define MODULE_PARM_DESC(var, name) MODULE_PARM( var, name )
+#endif
+
+MODULE_DESCRIPTION( "General LCD driver for HD44780 compatable controllers" );
+MODULE_AUTHOR( "Michael McLellan <mikey@mclellan.org.nz>,NZG <ngustavson@emacinc.com>" );
+//MODULE_PARM( disp_rows, "i" );
+//MODULE_PARM( disp_cols, "i" );
+MODULE_PARM_DESC( disp_rows, "i" );
+MODULE_PARM_DESC( disp_cols, "i" );
+
+MODULE_PARM_DESC( disp_rows, "LCD rows (default: DFLT_DISP_ROWS, max: MAX_DISP_ROWS)" );
+MODULE_PARM_DESC( disp_cols, "LCD columns (default: DFLT_DISP_COLS, max: MAX_DISP_COLS)" );
+MODULE_PARM_DESC( lowvoltage, "LCD lowvoltage display bootean true/false - 1/0" );
+
+/**
+ *  Send an instruction to the display, e.g. move cursor
+ */
+static void writeCommand(u8 command){	    
+	LCDDEBUG("command = %x\n",command);
+	LCD_Command(command,backlight);
+	/* Minimum time to wait after most commands is 39ns except Clear Display
+	 * and Return Home which is 1.53ms, we never use Return Home.
+	 */
+	if( command <= 0x03 )
+		mdelay( 1.6 );
+	else
+		udelay( 40 );
+}
+
+static void writeCommand4(u8 command){	    
+	LCDDEBUG("command = %x\n",command);
+	LCD_Command4(command,backlight);
+	/* Minimum time to wait after most commands is 39ns except Clear Display
+	 * and Return Home which is 1.53ms, we never use Return Home.
+	 */
+	if( command <= 0x03 )
+		mdelay( 1.6 );
+	else
+		udelay( 40 );
+}
+
+/* Send character data to the display, i.e. writing to DDRAM */
+static void writeData(u8 data )
+{
+	LCDDEBUG("row=%x, column = %x\n",row,column);
+
+	/* check and see if we really need to write anything */
+	if( state[ row ][ column ] != data )
+	{
+		state[ row ][ column ] = data;
+		/* set the cursor position if need be.
+		 * Special case for 16x1 displays, They are treated as two
+		 * 8 charcter lines side by side, and dont scroll along to
+		 * the second line automaticly.
+		 */
+		if( disp_row != row || disp_column != column ||
+				( disp_rows == 1 && disp_cols == 16 && column == 8 ) )
+		{
+			LCDDEBUG("disp!=reg , resetting cursor\n");
+			/* Some transation done here so 4 line displays work */
+			writeCommand( ( row>=2?(row-2)*0x40:row*0x40 ) |
+					( row>=2?column+disp_cols:column ) |
+					0x80);
+
+			disp_row = row;
+			disp_column = column;
+		}
+
+		LCD_Data(data,backlight);
+
+		udelay( 250 );
+		/* Time to wait after write to RAM is 43ns */
+		udelay( 43 );
+		disp_column++;
+	}
+	if ( column < disp_cols - 1 )
+		column++;
+	else if ( wrap && column == disp_cols - 1 && row < disp_rows - 1 )
+	{
+		column = 0;
+		row++;
+	}
+
+}
+
+/* Write an entire (5x8) character to the CGRAM,
+ * takes the CGRAM index, and a char[ 8 ] binary bitmap.
+ */
+static void writeCGRAM(int index, u8 pixels[] )
+{
+	int i;
+
+	/* Move address pointer to index in CGRAM */
+	writeCommand(0x40 + ( 8 * index ));
+
+	for( i = 0; i < 8; i++ )
+		LCD_Data(pixels[i],backlight);
+
+	udelay( 250 );
+	/* Time to wait after write to RAM is 43 ns */
+	udelay( 45 );
+
+	/*set disp position as being different than row, to be reset by writeData*/
+	disp_row = disp_column = -1;
+}
+
+static void initDisplay(void)
+{
+
+	/* initialize state array */
+	memset( state, ' ', sizeof( state ) );
+	/* initialize controller 1 */
+	//writeCommand(FUNCTION_SET|DATA_8BIT);
+	writeCommand4(0x02);
+	mdelay( 5 );
+	//writeCommand(FUNCTION_SET|DATA_8BIT);
+	writeCommand(FUNCTION_SET);
+	udelay( 100 );
+	//writeCommand(FUNCTION_SET|DATA_8BIT|DISPLAY_2LINES);
+	writeCommand(FUNCTION_SET|DISPLAY_2LINES);
+
+	writeCommand(DISPLAY_OFF);   
+	writeCommand(CLEAR_DISPLAY);   
+	writeCommand(ENTRY_MODE_SET|INC_RAM); 
+	writeCommand(RETURN_HOME);	
+	writeCommand(DISPLAY_ON);
+
+//	/* Set the CGRAM to default values */
+//	writeCGRAM( 0, cg0 );
+//	writeCGRAM( 1, cg1 );
+//	writeCGRAM( 2, cg2 );
+//	writeCGRAM( 3, cg3 );
+//	writeCGRAM( 4, cg4 );
+//	writeCGRAM( 5, cg5 );
+//	writeCGRAM( 6, cg6 );
+//	writeCGRAM( 7, cg7 );
+//	init_charmap();
+}
+
+static void handleInput( unsigned char input )
+{
+	static int cgram_index = 0;
+	static int cgram_row_count;
+	static unsigned char cgram_pixels[ 8 ];
+	static unsigned char char_map_old;
+	static int input_state = NORMAL; 	// the current state of the input handler
+	int i;
+	int j;
+	int temp;
+
+	LCDDEBUG("handling input 0x%x\n",input);
+
+	if ( input_state == NORMAL )
+	{
+		switch ( input )
+		{
+			case 0x08: 	// Backspace
+				if ( column > 0 )
+				{
+					column--;
+					writeData( ' ' );
+					column--;
+				}
+				break;
+			case 0x09: 	// Tabstop
+				column = ( ( ( column + 1 ) / TABSTOP ) * TABSTOP ) + TABSTOP - 1;
+				break;
+			case 0x0a: 	// Newline
+				if ( row < disp_rows - 1 )
+					row++;
+				else
+				{
+					/* scroll up */
+					LCDDEBUG("scroll\n");
+					temp = column;
+					for ( i = 0; i < disp_rows - 1; i++ )
+					{
+						row = i;
+						for( j = 0; j < disp_cols; j++ )
+						{
+							column = j;
+							writeData( state[ i + 1 ][ j ] );
+						}
+					}
+					row = disp_rows - 1;
+					column = 0;
+					for ( i = 0; i < disp_cols; i++ )
+					{
+						writeData( ' ' );
+					}
+					column = temp;
+				}
+				/* Since many have trouble grasping the \r\n concept... */
+				column = 0;
+				break;
+			case 0x0d: 	// Carrage return
+				column = 0;
+				break;
+			case 0x1b: 	// esc ie. start of escape sequence
+				input_state = ESC_;
+				break;
+			default:
+				/* The character is looked up in the */
+				writeData( charmap[ input ] );
+		}
+	}
+	else if ( input_state == ESC_ )
+	{
+		input_state = ESC;
+	}
+	else if ( input_state == ESC )
+	{
+		if( input <= '7' && input >= '0' )
+		{
+			/* Chararacter from CGRAM */
+			writeData( input - 0x30 );
+		} else {
+			switch ( input )
+			{
+				case 'A': 		// Cursor up
+					if ( row > 0 )
+						row--;
+					break;
+				case 'B': 		// Cursor down
+					if ( row < disp_rows - 1 )
+						row++;
+					break;
+				case 'C': 		// Cursor Right
+					if ( column < disp_cols - 1 )
+						column++;
+					break;
+				case 'D': 		// Cursor Left
+					if ( column > 0 )
+						column--;
+					break;
+				case 'H': 		// Cursor home
+					row = 0;
+					column = 0;
+					break;
+				case 'J': 		// Clear screen, cursor doesn't move
+					memset( state, ' ', sizeof( state ) );
+					writeCommand( 0x01);
+					break;
+				case 'K': 		// Erase to end of line, cursor doesn't move
+					temp = column;
+					for ( i = column; i < disp_cols; i++ )
+						writeData( ' ' );
+					column = temp;
+					break;
+				case 'M':		// Charater mapping
+					input_state = CHAR_MAP_OLD;
+					break;
+				case 'Y': 		// Direct cursor access
+					input_state = DCA_Y;
+					break;
+				case 'R':		// CGRAM select
+					input_state = CGRAM_SELECT;
+					break;
+				case 'V':		// Linewrap on
+					wrap = 1;
+					break;
+				case 'W':		// Linewrap off
+					wrap = 0;
+					break;
+				case 'b':       	// Toggle backlight
+					backlight = ( backlight == BL_OFF ? BL_ON : BL_OFF );
+					break;
+				default:
+					printk( "LCD: unrecognized escape sequence: %#x ('%c')\n", input, input );
+			}
+		}
+		if ( input_state != DCA_Y &&
+				input_state != CGRAM_SELECT &&
+				input_state != CHAR_MAP_OLD )
+		{
+			input_state = NORMAL;
+		}
+	}
+	else if ( input_state == DCA_Y )
+	{
+		if ( input - 0x1f < disp_rows )
+			row = input - 0x1f;
+		else
+		{
+			printk( "LCD: tried to set cursor to off screen location\n" );
+			row = disp_rows - 1;
+		}
+		input_state = DCA_X;
+	}
+	else if ( input_state == DCA_X )
+	{
+		if ( input - 0x1f < disp_cols )
+			column = input - 0x1f;
+		else
+		{
+			printk( "LCD: tried to set cursor to off screen location\n" );
+			column = disp_cols - 1;
+		}
+		input_state = NORMAL;
+	}
+	else if ( input_state == CGRAM_SELECT )
+	{
+		if( input > '7' || input < '0' )
+		{
+			printk( "LCD: Bad CGRAM index %c\n", input );
+			input_state = NORMAL;
+		} else {
+			cgram_index = input - 0x30;
+			cgram_row_count = 0;
+			input_state = CGRAM_GENERATE;
+		}
+	}
+	else if( input_state == CGRAM_GENERATE )
+	{
+		cgram_pixels[ cgram_row_count++ ] = input;
+		if( cgram_row_count == 8 )
+		{
+			writeCGRAM( cgram_index, cgram_pixels );
+			input_state = NORMAL;
+		}
+	}
+	else if( input_state == CHAR_MAP_OLD )
+	{
+		char_map_old = input;
+		input_state = CHAR_MAP_NEW;
+	}
+	else if( input_state == CHAR_MAP_NEW )
+	{
+		charmap[ char_map_old ] = input;
+		input_state = NORMAL;
+	}
+}
+
+int lcd_open( struct inode *minode, struct file *mfile ){
+	return ( 0 );
+}
+
+/* Handle device file close */
+int lcd_release( struct inode *minode, struct file *mfile ){
+	return 0;
+}
+
+/* Handle write to device file */
+ssize_t lcd_write_byte( struct file *inode, const char *gdata, size_t length, loff_t *off_what ){
+	int i;
+	for ( i = 0; i < length; i++ )
+		handleInput( gdata[ i ] );
+	return ( length );
+}
+
+/* Handle read from device file */
+ssize_t lcd_read_byte( struct file *inode, char *udata, size_t length, loff_t *loff_what ){
+	return ( EACCES );
+}
+
+/* Handle read from proc file */
+int readProcFile( char *buffer, char **start, off_t offset, int size, int *eof, void *data ){
+	char *temp = buffer;
+	int i, j;
+
+	/* Print module configuration */
+	temp += sprintf( temp, "Display rows:    %d\n"
+			"Display columns: %d\n"
+			"Linewrap:        %s\n",
+			disp_rows, disp_cols,
+			wrap?"On":"Off");
+
+	/* Print display state */
+	temp += sprintf( temp, "+" );
+	for( i = 0; i < disp_cols; i++ )
+		temp += sprintf( temp, "-" );
+	temp += sprintf( temp, "+\n" );
+
+	for( i = 0; i < disp_rows; i++ )
+	{
+		temp += sprintf( temp, "|" );
+		for( j = 0; j < disp_cols ; j++ )
+			temp += sprintf( temp, "%c", (state[ i ][ j ] < 10)?'?':state[ i ][ j ] );
+		temp += sprintf( temp, "|\n" );
+	}
+
+	temp += sprintf( temp, "+" );
+	for( i = 0; i < disp_cols; i++ )
+		temp += sprintf( temp, "-" );
+	temp += sprintf( temp, "+\n" );
+	return temp - buffer;
+}
+
+/* Module cleanup */
+static void __exit exit_module(void){
+	dev_t dev = MKDEV(lcd_major,LCD_MINOR);
+	writeCommand(0x01);
+	remove_proc_entry( "lcd", 0 );
+	printk ("LCD: deleting char device %d\n", dev);
+	unregister_chrdev_region (dev, 1);
+	cdev_del (my_cdev);
+}
+
+
+/* Module initilisation */
+static int __init start_module(void)
+{
+	//dev_t dev = MKDEV(LCD_MAJOR, LCD_MINOR);
+	dev_t dev = MKDEV(0, 0);
+	int result;
+	
+	static struct file_operations lcd_fops =
+	{ 
+		.read = lcd_read_byte, 
+		.write = lcd_write_byte,
+		.open = lcd_open,
+		.release = lcd_release
+	};
+
+	//Dynamically create device node
+        result = alloc_chrdev_region(&dev, LCD_MINOR, 1, "lcd");
+        lcd_major = MAJOR(dev);
+
+	/* Make sure user didn't pass silly numbers, MAX_DISP_???? are just
+	 * arbitary numbers and can be increased if need be.
+	 */
+	disp_rows = disp_rows<=MAX_DISP_ROWS?disp_rows:MAX_DISP_ROWS;
+	disp_cols = disp_cols<=MAX_DISP_COLS?disp_cols:MAX_DISP_COLS;
+
+	///if ( register_chrdev_region(dev, 1, "lcd") ) 
+	if( result < 0)
+	{
+		printk ("LCD: char device register failed\n"); 
+		return -1; 
+	} 
+	else 
+	{
+		printk ("LCD: char device registered\n"); 
+	}
+
+	// fill cdev structure
+	my_cdev = cdev_alloc();
+	my_cdev->owner = THIS_MODULE;
+	my_cdev->ops = &lcd_fops;
+	kobject_set_name(&my_cdev->kobj, "lcd");
+
+	// register it
+	if ( cdev_add(my_cdev, dev, 1) ) { 
+		printk ("LCD char device add failed\n");
+		return -1; 
+	} 
+	else 
+		printk ("LCD: char device added\n"); 
+
+	//creates sysfs interface and allows device node to be created dynamically
+	class_device_create(class_create(THIS_MODULE,"lcd"),NULL,dev,NULL,"lcd");
+
+	//setup the LCD bus interface 
+	LCD_BUSINIT();	
+	//initialize the display controller
+	initDisplay();
+
+	if( !create_proc_read_entry( "lcd", 0, 0, readProcFile, NULL ) ){
+		printk( KERN_ERR "LCD: Error! Can't create /proc/lcd\n" );
+		return -ENOMEM;
+	}
+
+	printk( "LCD: init OK,  rows: %d, columns: %d\n", disp_rows, disp_cols );
+
+	return 0;
+}
+
+module_init(start_module);
+module_exit(exit_module);
diff -Naur linux-2.6.25.at91/drivers/char/lcd447.h linux-2.6/drivers/char/lcd447.h
--- linux-2.6.25.at91/drivers/char/lcd447.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6/drivers/char/lcd447.h	2008-12-24 11:32:45.000000000 -0600
@@ -0,0 +1,245 @@
+#ifndef _LCD447_H_
+#define _LCD447_H_
+
+#include <asm/types.h>
+
+#define LCDMOD_VERSION "1.0.1-E"
+
+/**
+ * Setup the keymap
+ */
+#define CGRAM_DEFAULT
+//#define CGRAM_SWEDISH
+
+#define DFLT_DISP_ROWS	4		// Default number of rows the display has
+#define DFLT_DISP_COLS	20		// Default number of columns the display has
+#define TABSTOP		3		// Length of tabs
+
+/**
+ * Low level write/initialization interface
+ * this should be expanded with ifdef's for different architectures/boards.
+ ******************************************************************************/
+#define PLDBASE 	0x50000000
+#define LCDDREG		1
+#define LCDCREG		0
+#define RWDELAY		500
+//#define PLD_WRITE(index,data) *(volatile u8 *)(PLDBASE+index)=(u8)data
+//#define PLD_READ(index) ENDREV(index,*(volatile u16 *)(PLDBASE+index))
+#define PLD_WRITE(index,data) *(volatile u8 *)(virt_addr+index)=(u8)data
+#define PLD_READ(index) ENDREV(index,*(volatile u16 *)(virt_addr+index))
+// LCD Control Register bitmaps
+#define RW_READ		1
+#define RW_WRITE	0	
+#define RS_DATA		2
+#define RS_INST		0
+#define BL_ON		4
+#define BL_OFF		0
+
+// Stores the virtual address of the PLD (MMU friendly)
+u8 *virt_addr;
+
+inline void LCD_Command4(u8 command,int blight){	     
+	PLD_WRITE(LCDCREG,(RW_WRITE|RS_INST|blight));	  	  
+	udelay(RWDELAY);
+	PLD_WRITE(LCDDREG,command);
+}
+/**
+ *  Send a command to the LCD
+ */
+inline void LCD_Command(u8 command,int blight){	     
+	PLD_WRITE(LCDCREG,(RW_WRITE|RS_INST|blight));	  	  
+	udelay(RWDELAY);
+	//PLD_WRITE(LCDDREG,command);
+	PLD_WRITE(LCDDREG,(command>>4));
+	PLD_WRITE(LCDDREG,(command&0xf));
+}
+
+/**
+ *  Send data to the LCD
+ */
+inline void LCD_Data(u8 data,int blight){	     
+	PLD_WRITE(LCDCREG,RW_WRITE|RS_DATA|blight);
+	udelay(RWDELAY);
+	//PLD_WRITE(LCDDREG,data);
+	PLD_WRITE(LCDDREG,(data>>4));
+	PLD_WRITE(LCDDREG,(data&0xf));
+}
+
+/**
+ * Generic LCD bus initialization
+ * Initializes the bus interface to the LCD
+ * 
+ */
+inline void LCD_BUSINIT(void){
+	//do nothing, chipset parameters set at write time
+	virt_addr = ioremap_nocache(PLDBASE,0x2);
+}
+
+int	lcd_major;
+//#define LCD_MAJOR	60
+#define LCD_MINOR    	0
+
+ /*****************************************************************************/
+
+/*
+ * Character mapping for HD44780 devices by Mark Haemmerling <mail@markh.de>.
+ *
+ * Translates ISO 8859-1 to HD44780 charset.
+ * HD44780 charset reference: http://markh.de/hd44780-charset.png
+ *
+ * Initial table taken from lcd.o Linux kernel driver by
+ * Nils Faerber <nilsf@users.sourceforge.net>. Thanks!
+ *
+ * This file is released under the GNU General Public License. Refer to the
+ * COPYING file distributed with this package.
+ *
+ * Following translations are being performed:
+ * - maps umlaut accent characters to the corresponding umlaut characters
+ * - maps other accent characters to the characters without accents
+ * - maps beta (=ringel-S), micro and Yen
+ *
+ * Alternative mappings:
+ * - #112 ("p") -> #240 (large "p"), orig. mapped -> #112
+ * - #113 ("q") -> #241 (large "q"), orig. mapped -> #113
+ *
+ * HD44780 misses backslash
+ *
+ */
+char charmap[]={
+/* #0 */
+  0,  1,  2,  3,
+  4,  5,  6,  7,
+  8,  9, 10, 11,
+ 12, 13, 14, 15,
+ 16, 17, 18, 19,
+ 20, 21, 22, 23,
+ 24, 25, 26, 27,
+ 28, 29, 30, 31,
+/* #32 */
+ 32, 33, 34, 35,
+ 36, 37, 38, 39,
+ 40, 41, 42, 43,
+ 44, 45, 46, 47,
+ 48, 49, 50, 51,
+ 52, 53, 54, 55,
+ 56, 57, 58, 59,
+ 60, 61, 62, 63,
+/* #64 */
+ 64, 65, 66, 67,
+ 68, 69, 70, 71,
+ 72, 73, 74, 75,
+ 76, 77, 78, 79,
+ 80, 81, 82, 83,
+ 84, 85, 86, 87,
+ 88, 89, 90, 91,
+/* #92 */
+ 47, 93, 94, 95,
+ 96, 97, 98, 99,
+100,101,102,103,
+104,105,106,107,
+108,109,110,111,
+112,113,114,115,
+116,117,118,119,
+120,121,122,123,
+124,125,126,127,
+/* #128 */
+128,129,130,131,
+132,133,134,135,
+136,137,138,139,
+140,141,142,143,
+144,145,146,147,
+148,149,150,151,
+152,153,154,155,
+156,157,158,159,
+/* #160 */
+160, 33,236,237,
+164, 92,124,167,
+ 34,169,170,171,
+172,173,174,175,
+223,177,178,179,
+ 39,249,247,165,
+ 44,185,186,187,
+188,189,190, 63,
+/* #192 */
+ 65, 65, 65, 65,
+225, 65, 65, 67,
+ 69, 69, 69, 69,
+ 73, 73, 73, 73,
+ 68, 78, 79, 79,
+ 79, 79,239,120,
+ 48, 85, 85, 85,
+245, 89,240,226,
+/* #224 */
+ 97, 97, 97, 97,
+225, 97, 97, 99,
+101,101,101,101,
+105,105,105,105,
+111,110,111,111,
+111,111,239,253,
+ 48,117,117,117,
+245,121,240,255};
+
+#ifdef CGRAM_DEFAULT
+/* Default characters for lcdmod
+ * 
+ * Description: characters usefull for drawing graphs, and a funny 's'
+ *              The character aren't mapped to any others.
+ */
+
+extern char charmap[];
+
+static inline void init_charmap(void)
+{
+}
+
+unsigned char cg0[] = { 0x1f, 0x1f, 0x11, 0x0f, 0x11, 0x1e, 0x01, 0x1f };
+unsigned char cg1[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f };
+unsigned char cg2[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f };
+unsigned char cg3[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f };
+unsigned char cg4[] = { 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f };
+unsigned char cg5[] = { 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
+unsigned char cg6[] = { 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
+unsigned char cg7[] = { 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
+#endif
+
+
+#ifdef CGRAM_SWEDISH
+/* Swedish characters for lcdmod
+ *
+ * Thanks to Erik Zetterberg <mr.z@linux.se>
+ * 
+ * Description: Adds support for the last three last letters in the
+ * swedish alphabet (a/A with ring above, a/A with diaeresis and o/O
+ * with diaeresis). And maps the location of where they should be 
+ * according to the ISO-8859-1 character set to their location in CGRAM.
+ *
+ */
+
+extern char charmap[];
+
+static inline void init_charmap(void)
+{
+	charmap[ 0xe5 ] = 0;
+	charmap[ 0xe4 ] = 1;
+	charmap[ 0xf6 ] = 2;
+	charmap[ 0xc5 ] = 3;
+	charmap[ 0xc4 ] = 4;
+	charmap[ 0xd6 ] = 5;
+}
+
+unsigned char cg0[] = { 0x04, 0x00, 0x0e, 0x01, 0x0f, 0x11, 0x0f, 0x00 };
+unsigned char cg1[] = { 0x0a, 0x00, 0x0e, 0x01, 0x0f, 0x11, 0x0f, 0x00 };
+unsigned char cg2[] = { 0x0a, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x00 };
+unsigned char cg3[] = { 0x04, 0x00, 0x0e, 0x11, 0x1f, 0x11, 0x11, 0x00 };
+unsigned char cg4[] = { 0x0a, 0x00, 0x0e, 0x11, 0x1f, 0x11, 0x11, 0x00 };
+unsigned char cg5[] = { 0x0a, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x00 };
+unsigned char cg6[] = { 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
+unsigned char cg7[] = { 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f };
+#endif
+
+#endif //_LCD447_H_
+
+
+
+
+
diff -Naur linux-2.6.25.at91/drivers/char/Makefile linux-2.6/drivers/char/Makefile
--- linux-2.6.25.at91/drivers/char/Makefile	2009-02-12 12:26:57.000000000 -0600
+++ linux-2.6/drivers/char/Makefile	2009-01-27 13:12:55.000000000 -0600
@@ -87,6 +87,8 @@
 obj-$(CONFIG_DS1620)		+= ds1620.o
 obj-$(CONFIG_HW_RANDOM)		+= hw_random/
 obj-$(CONFIG_COBALT_LCD)	+= lcd.o
+obj-$(CONFIG_LCD447) 		+= lcd447.o
+obj-$(CONFIG_KEYPAD) 		+= keypad.o
 obj-$(CONFIG_PPDEV)		+= ppdev.o
 obj-$(CONFIG_NWBUTTON)		+= nwbutton.o
 obj-$(CONFIG_NWFLASH)		+= nwflash.o

