diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 13
-EXTRAVERSION =
+EXTRAVERSION = -h1940
 NAME=Affluent Albatross
 
 # *DOCUMENTATION*
@@ -190,8 +190,8 @@ SUBARCH := $(shell uname -m | sed -e s/i
 # Default value for CROSS_COMPILE is not to prefix executables
 # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
 
-ARCH		?= $(SUBARCH)
-CROSS_COMPILE	?=
+ARCH		?= arm
+CROSS_COMPILE	?= arm-linux-
 
 # Architecture as present in compile.h
 UTS_MACHINE := $(ARCH)
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -236,7 +236,7 @@ __syscall_start:
 		.long	sys_mincore
 /* 220 */	.long	sys_madvise
 		.long	sys_fcntl64
-		.long	sys_ni_syscall /* TUX */
+		.long	sys_mhelper
 		.long	sys_ni_syscall
 		.long	sys_gettid
 /* 225 */	.long	sys_readahead
diff --git a/arch/arm/mach-s3c2410/devs.c b/arch/arm/mach-s3c2410/devs.c
--- a/arch/arm/mach-s3c2410/devs.c
+++ b/arch/arm/mach-s3c2410/devs.c
@@ -29,12 +29,15 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 #include <asm/mach/irq.h>
-
+#include <asm/arch/fb.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 
 #include <asm/arch/regs-serial.h>
+#include <asm/arch/udc.h>
+#include <asm/arch/ts.h>
+#include <asm/arch/lcd.h>
 
 #include "devs.h"
 
@@ -103,6 +106,31 @@ struct platform_device s3c_device_lcd = 
 
 EXPORT_SYMBOL(s3c_device_lcd);
 
+static struct s3c2410fb_mach_info s3c2410fb_info;
+
+void __init set_s3c2410fb_info(struct s3c2410fb_mach_info *hard_s3c2410fb_info)
+{
+	memcpy(&s3c2410fb_info,hard_s3c2410fb_info,sizeof(struct s3c2410fb_mach_info));
+	s3c_device_lcd.dev.platform_data = &s3c2410fb_info;
+}
+EXPORT_SYMBOL(set_s3c2410fb_info);
+
+struct platform_device s3c_device_bl = {
+	.name		  = "s3c2410-bl",
+	.id		  = -1,
+};
+
+EXPORT_SYMBOL(s3c_device_bl);
+
+static struct s3c2410_bl_mach_info s3c2410bl_info;
+
+void __init set_s3c2410bl_info(struct s3c2410_bl_mach_info *hard_s3c2410bl_info)
+{
+	memcpy(&s3c2410bl_info,hard_s3c2410bl_info,sizeof(struct s3c2410_bl_mach_info));
+	s3c_device_bl.dev.platform_data = &s3c2410bl_info;
+}
+EXPORT_SYMBOL(set_s3c2410bl_info);
+
 /* NAND Controller */
 
 static struct resource s3c_nand_resource[] = {
@@ -122,6 +150,31 @@ struct platform_device s3c_device_nand =
 
 EXPORT_SYMBOL(s3c_device_nand);
 
+/* Touchscreen */
+struct platform_device s3c_device_ts = {
+	.name		  = "s3c2410-ts",
+	.id		  = -1,
+};
+
+EXPORT_SYMBOL(s3c_device_ts);
+
+static struct s3c2410_ts_mach_info s3c2410ts_info;
+
+void __init set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info)
+{
+	memcpy(&s3c2410ts_info,hard_s3c2410ts_info,sizeof(struct s3c2410_ts_mach_info));
+	s3c_device_ts.dev.platform_data = &s3c2410ts_info;
+}
+EXPORT_SYMBOL(set_s3c2410ts_info);
+
+/* 'Keyboard' */
+struct platform_device s3c_device_buttons = {
+	.name             = "s3c2410-buttons",
+	.id               = -1,
+};
+
+EXPORT_SYMBOL(s3c_device_buttons);
+
 /* USB Device (Gadget)*/
 
 static struct resource s3c_usbgadget_resource[] = {
@@ -147,6 +200,16 @@ struct platform_device s3c_device_usbgad
 
 EXPORT_SYMBOL(s3c_device_usbgadget);
 
+static struct s3c2410_udc_mach_info s3c2410udc_info;
+
+void __init set_s3c2410udc_info(struct s3c2410_udc_mach_info *hard_s3c2410udc_info)
+{
+	memcpy(&s3c2410udc_info,hard_s3c2410udc_info,sizeof(struct s3c2410_udc_mach_info));
+	s3c_device_usbgadget.dev.platform_data = &s3c2410udc_info;
+}
+EXPORT_SYMBOL(set_s3c2410udc_info);
+
+
 /* Watchdog */
 
 static struct resource s3c_wdt_resource[] = {
diff --git a/arch/arm/mach-s3c2410/devs.h b/arch/arm/mach-s3c2410/devs.h
--- a/arch/arm/mach-s3c2410/devs.h
+++ b/arch/arm/mach-s3c2410/devs.h
@@ -20,6 +20,7 @@ extern struct platform_device *s3c24xx_u
 
 extern struct platform_device s3c_device_usb;
 extern struct platform_device s3c_device_lcd;
+extern struct platform_device s3c_device_bl;
 extern struct platform_device s3c_device_wdt;
 extern struct platform_device s3c_device_i2c;
 extern struct platform_device s3c_device_iis;
@@ -38,6 +39,8 @@ extern struct platform_device s3c_device
 extern struct platform_device s3c_device_timer3;
 
 extern struct platform_device s3c_device_usbgadget;
+extern struct platform_device s3c_device_ts;
+extern struct platform_device s3c_device_buttons;
 
 /* s3c2440 specific devices */
 
diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c
--- a/arch/arm/mach-s3c2410/mach-h1940.c
+++ b/arch/arm/mach-s3c2410/mach-h1940.c
@@ -32,6 +32,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
+#include <linux/device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -45,6 +46,14 @@
 
 //#include <asm/debug-ll.h>
 #include <asm/arch/regs-serial.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-lcd.h>
+#include <asm/arch/regs-timer.h>
+
+#include <asm/arch/fb.h>
+#include <asm/arch/udc.h>
+#include <asm/arch/ts.h>
+#include <asm/arch/lcd.h>
 
 #include <linux/serial_core.h>
 
@@ -87,14 +96,150 @@ static struct s3c2410_uartcfg h1940_uart
 };
 
 
+static void pullup(unsigned char cmd)
+{
+	printk(KERN_DEBUG "udc: pullup(%d)\n",cmd);
+	switch (cmd)
+	{
+		case S3C2410_UDC_P_ENABLE :
+			break;
+		case S3C2410_UDC_P_DISABLE :
+			break;
+		case S3C2410_UDC_P_RESET :
+			break;
+		default: break;
+	}
+}
+
+static struct s3c2410_udc_mach_info h1940_udc_cfg __initdata = {
+		.udc_command = pullup,
+};
+
+
+static struct s3c2410_ts_mach_info h1940_ts_cfg __initdata = {
+		.delay = 10000,
+		.presc = 49,
+		.oversampling_shift = 2,
+};
+
+/**
+ * Set lcd on or off
+ **/
+
+static void h1940_backlight_power(int on)
+{
+	s3c2410_gpio_setpin(S3C2410_GPB0, 0);
+	s3c2410_gpio_pullup(S3C2410_GPB0, 0);
+
+	s3c2410_gpio_cfgpin(S3C2410_GPB0,
+			    (on) ? S3C2410_GPB0_TOUT0 : S3C2410_GPB0_OUTP);
+}
+
+static void h1940_lcd_power(int on)
+{
+	s3c2410_gpio_setpin(S3C2410_GPC0, on);
+}
+
+static struct s3c2410fb_mach_info h1940_lcdcfg __initdata = {
+	.fixed_syncs=		1,
+	.regs={ 
+		.lcdcon1=	S3C2410_LCDCON1_TFT16BPP | \
+				S3C2410_LCDCON1_TFT | \
+				S3C2410_LCDCON1_CLKVAL(0x0C),
+
+		.lcdcon2=	S3C2410_LCDCON2_VBPD(7) | \
+				S3C2410_LCDCON2_LINEVAL(319) | \
+				S3C2410_LCDCON2_VFPD(6) | \
+				S3C2410_LCDCON2_VSPW(0),
+
+		.lcdcon3=	S3C2410_LCDCON3_HBPD(19) | \
+				S3C2410_LCDCON3_HOZVAL(239) | \
+				S3C2410_LCDCON3_HFPD(7),
+
+		.lcdcon4=	S3C2410_LCDCON4_MVAL(0) | \
+				S3C2410_LCDCON4_HSPW(3),
+
+		.lcdcon5=	S3C2410_LCDCON5_FRM565 | \
+				S3C2410_LCDCON5_INVVLINE | \
+				S3C2410_LCDCON5_HWSWP,
+	},
+	.lpcsel=	0x02,
+	.gpccon=	0xaa940659,
+	.gpccon_mask=	0xffffffff,
+	.gpcup=		0x0000ffff,
+	.gpcup_mask=	0xffffffff,
+	.gpdcon=	0xaa84aaa0,
+	.gpdcon_mask=	0xffffffff,
+	.gpdup=		0x0000faff,
+	.gpdup_mask=	0xffffffff,
+
+	.width=		240,
+	.height=	320,
+	.xres=		{240,240,240},
+	.yres=		{320,320,320},
+	.bpp=		{16,16,16},
+};
+
+static void h1940_set_brightness(int tcmpb0)
+{
+	unsigned long tcfg0;
+	unsigned long tcfg1;
+	unsigned long tcon;
+
+	/* configure power on/off */
+	h1940_backlight_power(tcmpb0 ? 1 : 0);
+
+
+	tcfg0=readl(S3C2410_TCFG0);
+	tcfg1=readl(S3C2410_TCFG1);
+
+	tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
+	tcfg0 |= 0x18;
+
+	tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;
+	tcfg1 |= S3C2410_TCFG1_MUX0_DIV2;
+
+	writel(tcfg0, S3C2410_TCFG0);
+	writel(tcfg1, S3C2410_TCFG1);
+	writel(0x31, S3C2410_TCNTB(0));
 
+	tcon = readl(S3C2410_TCON);
+	tcon &= ~0x0F;
+	tcon |= S3C2410_TCON_T0RELOAD;
+	tcon |= S3C2410_TCON_T0MANUALUPD;
+
+	writel(tcon, S3C2410_TCON);
+	writel(0x31, S3C2410_TCNTB(0));
+	writel(tcmpb0, S3C2410_TCMPB(0));
+
+	/* start the timer running */
+	tcon |= S3C2410_TCON_T0START;
+	tcon &= ~S3C2410_TCON_T0MANUALUPD;
+	writel(tcon, S3C2410_TCON);
+}
+
+static struct s3c2410_bl_mach_info h1940_blcfg __initdata = {
+
+	.backlight_max          = 0x2c,
+	.backlight_default      = 0x16,
+	.backlight_power	= h1940_backlight_power,
+	.set_brightness		= h1940_set_brightness,
+	.backlight_power	= h1940_backlight_power,
+	.lcd_power		= h1940_lcd_power
+};
 
 static struct platform_device *h1940_devices[] __initdata = {
 	&s3c_device_usb,
 	&s3c_device_lcd,
+	&s3c_device_bl,
 	&s3c_device_wdt,
 	&s3c_device_i2c,
 	&s3c_device_iis,
+	&s3c_device_sdi,
+	&s3c_device_usbgadget,
+	&s3c_device_ts,
+	&s3c_device_buttons,
+	&s3c_device_nand,
 };
 
 static struct s3c24xx_board h1940_board __initdata = {
@@ -116,6 +261,16 @@ void __init h1940_init_irq(void)
 
 }
 
+void __init h1940_init(void)
+{
+	set_s3c2410ts_info(&h1940_ts_cfg);
+ 	set_s3c2410udc_info(&h1940_udc_cfg);
+ 	/* Set pad to usb device and usbsuspend to 'normal' */
+ 	__raw_writel(__raw_readl(S3C2410_MISCCR)&~0x3008,S3C2410_MISCCR);
+	set_s3c2410fb_info(&h1940_lcdcfg);
+	set_s3c2410bl_info(&h1940_blcfg);
+}
+
 MACHINE_START(H1940, "IPAQ-H1940")
 	/* Maintainer: Ben Dooks <ben@fluff.org> */
 	.phys_ram	= S3C2410_SDRAM_PA,
@@ -124,5 +279,6 @@ MACHINE_START(H1940, "IPAQ-H1940")
 	.boot_params	= S3C2410_SDRAM_PA + 0x100,
 	.map_io		= h1940_map_io,
 	.init_irq	= h1940_init_irq,
+	.init_machine	= h1940_init,
 	.timer		= &s3c24xx_timer,
 MACHINE_END
diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c
--- a/arch/arm/mach-s3c2410/s3c2410.c
+++ b/arch/arm/mach-s3c2410/s3c2410.c
@@ -46,8 +46,9 @@
 
 /* Initial IO mappings */
 
-static struct map_desc s3c2410_iodesc[] __initdata = {
+struct map_desc s3c2410_iodesc[] __initdata = {
 	IODESC_ENT(USBHOST),
+	IODESC_ENT(USBDEV),
 	IODESC_ENT(CLKPWR),
 	IODESC_ENT(LCD),
 	IODESC_ENT(TIMER),
@@ -55,6 +56,8 @@ static struct map_desc s3c2410_iodesc[] 
 	IODESC_ENT(WATCHDOG),
 };
 
+unsigned long s3c2410_iodesc_size = ARRAY_SIZE(s3c2410_iodesc);
+
 static struct resource s3c_uart0_resource[] = {
 	[0] = {
 		.start = S3C2410_PA_UART0,
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -182,4 +182,14 @@ config KEYBOARD_HIL
 	  This driver implements support for HIL-keyboards attached
 	  to your machine, so normally you should say Y here.
 
+config S3C2410_BUTTONS
+	tristate "s3c2410 buttons"
+	depends on ARCH_S3C2410
+	default y	
+	help
+
+	  To compile this driver as a module, choose M here: the 
+	  module will be called s3c2410_buttons.ko.
+
+
 endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -16,4 +16,5 @@ obj-$(CONFIG_KEYBOARD_98KBD)		+= 98kbd.o
 obj-$(CONFIG_KEYBOARD_CORGI)		+= corgikbd.o
 obj-$(CONFIG_KEYBOARD_HIL)		+= hil_kbd.o
 obj-$(CONFIG_KEYBOARD_HIL_OLD)		+= hilkbd.o
+obj-$(CONFIG_S3C2410_BUTTONS)		+= s3c2410_buttons.o
 
diff --git a/drivers/input/keyboard/s3c2410_buttons.c b/drivers/input/keyboard/s3c2410_buttons.c
new file mode 100644
--- /dev/null
+++ b/drivers/input/keyboard/s3c2410_buttons.c
@@ -0,0 +1,223 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Samsung S3C2410 keyboard support
+ *
+ * Based on various pxa ipaq drivers.
+ * 
+ * ChangeLog
+ *
+ * 2005-07-24: Arnaud Patard <arnaud.patard@rtp-net.org>
+ * 	- Added key repeat
+ *
+ * 2005-07-23: Arnaud Patard <arnaud.patard@rtp-net.org>
+ * 	- Renamed all kbd occurences to something like buttons
+ * 	- Added debounce (specially usefull for iPAQ power button)
+ *
+ * 2005-06-21: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *      - Initial version
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/arch/regs-gpio.h>
+
+/* For id.version */
+#define S3C2410BUTVERSION	0x0001
+#define DRV_NAME		"s3c2410-buttons"
+
+#define REPEAT_DELAY		HZ/10
+
+#ifdef DEBUG
+#define dprintk(msg...) printk(KERN_DEBUG "s3c2410_buttons: " msg);
+#else
+#define dprintk(msg...)
+#endif
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_DESCRIPTION("s3c2410 buttons driver");
+MODULE_LICENSE("GPL");
+
+struct s3c2410_button {
+	int irq;
+	int pin;
+	int pin_setting;
+	int keycode;
+	char *name;
+	int last_state;
+	struct timer_list timer;
+};
+
+/* To be moved later to a better place */
+static struct s3c2410_button s3c2410_buttons[] = {
+	{  IRQ_EINT0,  S3C2410_GPF0,   S3C2410_GPF0_EINT0,    KEY_POWER,	      "Power", 0 },
+	{  IRQ_EINT6,  S3C2410_GPF6,   S3C2410_GPF6_EINT6,    KEY_ENTER, 	     "Select", 0 },
+	{  IRQ_EINT7,  S3C2410_GPF7,   S3C2410_GPF7_EINT7,   KEY_RECORD,	     "Record", 0 },
+	{  IRQ_EINT8,  S3C2410_GPG0,   S3C2410_GPG0_EINT8, KEY_CALENDAR,	   "Calendar", 0 },
+	{ IRQ_EINT10,  S3C2410_GPG2,  S3C2410_GPG2_EINT10,   KEY_COFFEE,	   "Contacts", 0 }, /* TODO: find a better key :P */
+	{ IRQ_EINT11,  S3C2410_GPG3,  S3C2410_GPG3_EINT11,     KEY_MAIL,	       "Mail", 0 },
+	{ IRQ_EINT14,  S3C2410_GPG6,  S3C2410_GPG6_EINT14,     KEY_LEFT,	 "Left_arrow", 0 },
+	{ IRQ_EINT15,  S3C2410_GPG7,  S3C2410_GPG7_EINT15, KEY_HOMEPAGE,	       "Home", 0 },
+	{ IRQ_EINT16,  S3C2410_GPG8,  S3C2410_GPG8_EINT16,    KEY_RIGHT,	"Right_arrow", 0 },
+	{ IRQ_EINT17,  S3C2410_GPG9,  S3C2410_GPG9_EINT17,       KEY_UP,	   "Up_arrow", 0 },
+	{ IRQ_EINT18, S3C2410_GPG10, S3C2410_GPG10_EINT18,     KEY_DOWN,	 "Down_arrow", 0 },
+};
+
+struct s3c2410_buttons_private {
+	struct input_dev	dev;
+	spinlock_t		lock;
+	int 			count;
+	int			shift;	
+	char			phys[32];
+};
+
+static struct s3c2410_buttons_private priv;
+
+static irqreturn_t s3c2410but_keyevent(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct s3c2410_button *button = (struct s3c2410_button *)dev_id;
+	int down;
+
+	if (!button)
+		return IRQ_HANDLED;
+
+	down = !(s3c2410_gpio_getpin(button->pin));
+
+	/* the power button of the ipaq are tricky. They send 'released' events even
+	 * when the button are already released. The work-around is to proceed only 
+	 * if the state changed.
+	 **/
+	if (button->last_state == down)
+		return IRQ_HANDLED;
+
+	button->last_state = down;
+	
+	dprintk("%s button %s\n",button->name, down ? "pressed" : "released");
+
+	input_report_key(&priv.dev, button->keycode, down);
+	input_sync(&priv.dev);
+
+	if (down)
+		mod_timer(&button->timer, jiffies + REPEAT_DELAY);
+
+	return IRQ_HANDLED;
+}
+
+static void s3c2410but_timer_callback(unsigned long data)
+{
+		struct s3c2410_button *button = (struct s3c2410_button *) data;
+		int down;
+
+		down = !(s3c2410_gpio_getpin(button->pin));
+		
+		if (down) {
+			dprintk("Timer: %s button %s\n",button->name, down ? "pressed" : "released");
+			input_report_key(&priv.dev, button->keycode, down);
+			input_sync(&priv.dev);
+			mod_timer(&button->timer, jiffies + REPEAT_DELAY);
+		}
+}
+
+static int __init s3c2410but_probe(struct device *dev)
+{
+	int i;
+
+	/* Initialise input stuff */
+	memset(&priv, 0, sizeof(struct s3c2410_buttons_private));
+	init_input_dev(&priv.dev);
+	priv.dev.evbit[0] = BIT(EV_KEY);
+	sprintf(priv.phys, "input/s3c2410_buttons0");
+
+	priv.dev.private = &priv;
+	priv.dev.name = DRV_NAME;
+	priv.dev.phys = priv.phys;
+	priv.dev.id.bustype = BUS_HOST;
+	priv.dev.id.vendor = 0xDEAD;
+	priv.dev.id.product = 0xBEEF;
+	priv.dev.id.version = S3C2410BUTVERSION;
+
+
+	for (i = 0; i < ARRAY_SIZE (s3c2410_buttons); i++) {
+		set_bit(s3c2410_buttons[i].keycode, priv.dev.keybit);
+		s3c2410_gpio_cfgpin(s3c2410_buttons[i].pin,s3c2410_buttons[i].pin_setting);
+		request_irq (s3c2410_buttons[i].irq, s3c2410but_keyevent,\
+				SA_SAMPLE_RANDOM, s3c2410_buttons[i].name, &s3c2410_buttons[i]);
+		set_irq_type(s3c2410_buttons[i].irq, IRQT_BOTHEDGE);
+		
+		init_timer(&s3c2410_buttons[i].timer);
+		s3c2410_buttons[i].timer.function = s3c2410but_timer_callback;
+		s3c2410_buttons[i].timer.data     = (unsigned long)&s3c2410_buttons[i];
+	}
+	
+	printk(KERN_INFO "%s successfully loaded\n", DRV_NAME);
+
+	/* All went ok, so register to the input system */
+	input_register_device(&priv.dev);
+
+	return 0;
+}
+
+static int s3c2410but_remove(struct device *dev)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE (s3c2410_buttons); i++) {
+		disable_irq(s3c2410_buttons[i].irq);
+		free_irq(s3c2410_buttons[i].irq,&priv.dev);
+	}
+
+	input_unregister_device(&priv.dev);
+
+	return 0;
+}
+
+
+static struct device_driver s3c2410but_driver = {
+       .name           = DRV_NAME,
+       .bus            = &platform_bus_type,
+       .probe          = s3c2410but_probe,
+       .remove         = s3c2410but_remove,
+};
+
+
+int __init s3c2410but_init(void)
+{
+	return driver_register(&s3c2410but_driver);
+}
+
+void __exit s3c2410but_exit(void)
+{
+	driver_unregister(&s3c2410but_driver);
+}
+
+module_init(s3c2410but_init);
+module_exit(s3c2410but_exit);
+
+
+
+
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -36,6 +36,24 @@ config TOUCHSCREEN_CORGI
 	  To compile this driver as a module, choose M here: the
 	  module will be called ads7846_ts.
 
+config TOUCHSCREEN_S3C2410
+	tristate "Samsung S3C2410 touchscreen input driver"
+	depends on ARCH_S3C2410 && INPUT && INPUT_TOUCHSCREEN
+	select SERIO
+	help
+	  Say Y here if you have the s3c2410 touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called s3c2410_ts.
+
+config TOUCHSCREEN_S3C2410_DEBUG
+	boolean "Samsung S3C2410 touchscreen debug messages"
+	depends on TOUCHSCREEN_S3C2410
+	help
+	  Select this if you want debug messages
+
 config TOUCHSCREEN_GUNZE
 	tristate "Gunze AHL-51S touchscreen"
 	select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -11,3 +11,5 @@ obj-$(CONFIG_TOUCHSCREEN_ELO)	+= elo.o
 obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
 obj-$(CONFIG_TOUCHSCREEN_MK712)	+= mk712.o
 obj-$(CONFIG_TOUCHSCREEN_HP600)	+= hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
+
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
new file mode 100644
--- /dev/null
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -0,0 +1,346 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (c) 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * iPAQ H1940 touchscreen support
+ *
+ * ChangeLog
+ *
+ * 2004-09-05: Herbert PÃ¶tzl <herbert@13thfloor.at>
+ *	- added clock (de-)allocation code
+ *
+ * 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *      - h1940_ -> s3c2410 (this driver is now also used on the n30
+ *        machines :P)
+ *      - Debug messages are now enabled with the config option
+ *        TOUCHSCREEN_S3C2410_DEBUG
+ *      - Changed the way the value are read
+ *      - Input subsystem should now work
+ *      - Use ioremap and readl/writel
+ *
+ * 2005-03-23: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *      - Make use of some undocumented features of the touchscreen
+ *        controller
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/arch/regs-adc.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/ts.h>
+#include <asm/hardware/clock.h>
+
+/* For ts.dev.id.version */
+#define S3C2410TSVERSION	0x0101
+
+#define WAIT4INT(x)  (((x)<<8) | \
+		     S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
+		     S3C2410_ADCTSC_XY_PST(3))
+
+#define AUTOPST	     (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
+		     S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
+
+#define DEBUG_LVL    KERN_DEBUG
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_DESCRIPTION("s3c2410 touchscreen driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+
+static char *s3c2410ts_name = "s3c2410 TouchScreen";
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct s3c2410ts {
+	struct input_dev dev;
+	long xp;
+	long yp;
+	int count;
+	int shift;
+	char phys[32];
+};
+
+static struct s3c2410ts ts;
+static void __iomem *base_addr;
+
+static inline void s3c2410_ts_connect(void)
+{
+	s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
+	s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
+	s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
+	s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
+}
+
+static void touch_timer_fire(unsigned long data)
+{
+  	unsigned long data0;
+  	unsigned long data1;
+	int updown;
+
+  	data0 = readl(base_addr+S3C2410_ADCDAT0);
+  	data1 = readl(base_addr+S3C2410_ADCDAT1);
+
+ 	updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
+
+ 	if (updown) {
+ 		if (ts.count != 0) {
+ 			ts.xp >>= ts.shift;
+ 			ts.yp >>= ts.shift;
+
+#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
+ 			{
+ 				struct timeval tv;
+ 				do_gettimeofday(&tv);
+ 				printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp);
+ 			}
+#endif
+
+ 			input_report_abs(&ts.dev, ABS_X, ts.xp);
+ 			input_report_abs(&ts.dev, ABS_Y, ts.yp);
+
+ 			input_report_key(&ts.dev, BTN_TOUCH, 1);
+ 			input_report_abs(&ts.dev, ABS_PRESSURE, 1);
+ 			input_sync(&ts.dev);
+ 		}
+
+ 		ts.xp = 0;
+ 		ts.yp = 0;
+ 		ts.count = 0;
+
+ 		writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
+ 		writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
+ 	} else {
+ 		ts.count = 0;
+
+ 		input_report_key(&ts.dev, BTN_TOUCH, 0);
+ 		input_report_abs(&ts.dev, ABS_PRESSURE, 0);
+ 		input_sync(&ts.dev);
+
+ 		writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
+ 	}
+}
+
+static struct timer_list touch_timer =
+		TIMER_INITIALIZER(touch_timer_fire, 0, 0);
+
+static irqreturn_t stylus_updown(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long data0;
+	unsigned long data1;
+	int updown;
+
+	data0 = readl(base_addr+S3C2410_ADCDAT0);
+	data1 = readl(base_addr+S3C2410_ADCDAT1);
+
+	updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
+
+	/* TODO we should never get an interrupt with updown set while
+	 * the timer is running, but maybe we ought to verify that the
+	 * timer isn't running anyways. */
+
+	if (updown)
+		touch_timer_fire(0);
+
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t stylus_action(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long data0;
+	unsigned long data1;
+
+	data0 = readl(base_addr+S3C2410_ADCDAT0);
+	data1 = readl(base_addr+S3C2410_ADCDAT1);
+
+	ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
+	ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
+	ts.count++;
+
+        if (ts.count < (1<<ts.shift)) {
+		writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
+		writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
+	} else {
+		mod_timer(&touch_timer, jiffies+1);
+		writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct clk	*adc_clock;
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init s3c2410ts_probe(struct device *dev)
+{
+	struct s3c2410_ts_mach_info *info;
+
+	info = ( struct s3c2410_ts_mach_info *)dev->platform_data;
+
+	if (!info)
+	{
+		printk(KERN_ERR "Hm... too bad : no platform data for ts\n");
+		return -EINVAL;
+	}
+
+#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
+	printk(DEBUG_LVL "Entering s3c2410ts_init\n");
+#endif
+
+	adc_clock = clk_get(NULL, "adc");
+	if (!adc_clock) {
+		printk(KERN_ERR "failed to get adc clock source\n");
+		return -ENOENT;
+	}
+	clk_use(adc_clock);
+	clk_enable(adc_clock);
+
+#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
+	printk(DEBUG_LVL "got and enabled clock\n");
+#endif
+
+	base_addr=ioremap(S3C2410_PA_ADC,0x20);
+	if (base_addr == NULL) {
+		printk(KERN_ERR "Failed to remap register block\n");
+		return -ENOMEM;
+	}
+
+
+	/* Configure GPIOs */
+	s3c2410_ts_connect();
+
+	if ((info->presc&0xff) > 0)
+		writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
+			     base_addr+S3C2410_ADCCON);
+	else
+		writel(0,base_addr+S3C2410_ADCCON);
+
+
+	/* Initialise registers */
+	if ((info->delay&0xffff) > 0)
+		writel(info->delay & 0xffff,  base_addr+S3C2410_ADCDLY);
+
+	writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
+
+	/* Initialise input stuff */
+	memset(&ts, 0, sizeof(struct s3c2410ts));
+	init_input_dev(&ts.dev);
+	ts.dev.evbit[0] = ts.dev.evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
+	ts.dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+	input_set_abs_params(&ts.dev, ABS_X, 0, 0x3FF, 0, 0);
+	input_set_abs_params(&ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
+	input_set_abs_params(&ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
+
+	sprintf(ts.phys, "ts0");
+
+	ts.dev.private = &ts;
+	ts.dev.name = s3c2410ts_name;
+	ts.dev.phys = ts.phys;
+	ts.dev.id.bustype = BUS_RS232;
+	ts.dev.id.vendor = 0xDEAD;
+	ts.dev.id.product = 0xBEEF;
+	ts.dev.id.version = S3C2410TSVERSION;
+
+	ts.shift = info->oversampling_shift;
+
+	/* Get irqs */
+	if (request_irq(IRQ_ADC, stylus_action, SA_SAMPLE_RANDOM,
+		"s3c2410_action", &ts.dev)) {
+		printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");
+		iounmap(base_addr);
+		return -EIO;
+	}
+	if (request_irq(IRQ_TC, stylus_updown, SA_SAMPLE_RANDOM,
+			"s3c2410_action", &ts.dev)) {
+		printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");
+		iounmap(base_addr);
+		return -EIO;
+	}
+
+	printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);
+
+	/* All went ok, so register to the input system */
+	input_register_device(&ts.dev);
+
+	return 0;
+}
+
+static int s3c2410ts_remove(struct device *dev)
+{
+	disable_irq(IRQ_ADC);
+	disable_irq(IRQ_TC);
+	free_irq(IRQ_TC,&ts.dev);
+	free_irq(IRQ_ADC,&ts.dev);
+
+	if (adc_clock) {
+		clk_disable(adc_clock);
+		clk_unuse(adc_clock);
+		clk_put(adc_clock);
+		adc_clock = NULL;
+	}
+
+	input_unregister_device(&ts.dev);
+	iounmap(base_addr);
+
+	return 0;
+}
+
+static struct device_driver s3c2410ts_driver = {
+       .name           = "s3c2410-ts",
+       .bus            = &platform_bus_type,
+       .probe          = s3c2410ts_probe,
+       .remove         = s3c2410ts_remove,
+};
+
+
+int __init s3c2410ts_init(void)
+{
+	return driver_register(&s3c2410ts_driver);
+}
+
+void __exit s3c2410ts_exit(void)
+{
+	driver_unregister(&s3c2410ts_driver);
+}
+
+module_init(s3c2410ts_init);
+module_exit(s3c2410ts_exit);
+
+/*
+    Local variables:
+        compile-command: "make ARCH=arm CROSS_COMPILE=/usr/local/arm/3.3.2/bin/arm-linux- -k -C ../../.."
+        c-basic-offset: 8
+    End:
+*/
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -60,4 +60,13 @@ config MMC_WBSD
 
 	  If unsure, say N.
 
+config MMC_S3C2410
+	tristate "Samsung S3C2410 Multimedia Card Interface support"
+	depends on ARCH_S3C2410 && MMC
+	help
+	  This selects the Samsung S3C2410 Multimedia Card Interface 
+	  support. 
+
+	  If unsure, say N.
+
 endmenu
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -18,5 +18,6 @@ obj-$(CONFIG_MMC_BLOCK)		+= mmc_block.o
 obj-$(CONFIG_MMC_ARMMMCI)	+= mmci.o
 obj-$(CONFIG_MMC_PXA)		+= pxamci.o
 obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
+obj-$(CONFIG_MMC_S3C2410)	+= s3c2410mci.o
 
 mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -3,6 +3,9 @@
  *
  *  Copyright (C) 2003-2004 Russell King, All Rights Reserved.
  *
+ *  SD-Card support:
+ *    Copyright (C) 2004 Thomas Kleffel, All Rights Reserved
+ *
  * 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.
@@ -172,6 +175,34 @@ int mmc_wait_for_cmd(struct mmc_host *ho
 
 EXPORT_SYMBOL(mmc_wait_for_cmd);
 
+static int mmc_send_app_cmd(struct mmc_host *host,  u16 rca)
+{
+	struct mmc_command cmd;
+
+	cmd.opcode = MMC_APP_CMD;
+	cmd.arg =((u32)rca)<<16;
+	cmd.flags = MMC_RSP_R1;
+
+	return mmc_wait_for_cmd(host, &cmd, 0);
+}
+
+int mmc_wait_for_acmd(struct mmc_host *host, u16 rca, struct mmc_command *cmd, int retries)
+{
+	int err;
+
+	do {
+		err = mmc_send_app_cmd(host, rca);
+		if(err != MMC_ERR_NONE) continue;
+
+		err = mmc_wait_for_cmd(host, cmd, 0);
+		if(err == MMC_ERR_NONE) break;
+
+	} while (retries--);
+
+	return err;
+}
+
+
 
 
 /**
@@ -274,6 +305,7 @@ static inline void mmc_delay(unsigned in
 	}
 }
 
+#if 0 /* UNUSED */
 /*
  * Mask off any voltages we don't support and select
  * the lowest voltage
@@ -298,6 +330,7 @@ static u32 mmc_select_voltage(struct mmc
 
 	return ocr;
 }
+#endif
 
 #define UNSTUFF_BITS(resp,start,size)					\
 	({								\
@@ -326,7 +359,28 @@ static void mmc_decode_cid(struct mmc_ca
 	 * The selection of the format here is guesswork based upon
 	 * information people have sent to date.
 	 */
-	switch (card->csd.mmca_vsn) {
+	if(card->sd) {
+		if(card->csd.csd_vers != SD_CSDV_1) {
+			DBG("%s: card has unknown SD-MMCA version %d\n", mmc_hostname(card->host), card->csd.spec_vers);
+			mmc_card_set_bad(card);
+		} else {
+			card->cid.manfid	= UNSTUFF_BITS(resp, 120,  8);
+			card->cid.oemid		= UNSTUFF_BITS(resp, 104, 16);
+			card->cid.prod_name[0]	= UNSTUFF_BITS(resp,  96,  8);
+			card->cid.prod_name[1]	= UNSTUFF_BITS(resp,  88,  8);
+			card->cid.prod_name[2]	= UNSTUFF_BITS(resp,  80,  8);
+			card->cid.prod_name[3]	= UNSTUFF_BITS(resp,  72,  8);
+			card->cid.prod_name[4]	= UNSTUFF_BITS(resp,  64,  8);
+			card->cid.prod_name[5]  = 0;
+			card->cid.prod_name[6]  = 0;
+			card->cid.hwrev		= UNSTUFF_BITS(resp,  60,  4);
+			card->cid.fwrev		= UNSTUFF_BITS(resp,  56,  4);
+			card->cid.serial	= UNSTUFF_BITS(resp,  24, 32);
+			card->cid.month		= UNSTUFF_BITS(resp,  12,  4);
+			card->cid.year		= UNSTUFF_BITS(resp,   8,  4) + 2000;
+		}
+	} else { /* if(card->sd) */
+	switch (card->csd.csd_vers) {
 	case 0: /* MMC v1.? */
 	case 1: /* MMC v1.4 */
 		card->cid.manfid	= UNSTUFF_BITS(resp, 104, 24);
@@ -361,10 +415,11 @@ static void mmc_decode_cid(struct mmc_ca
 
 	default:
 		printk("%s: card has unknown MMCA version %d\n",
-			mmc_hostname(card->host), card->csd.mmca_vsn);
+			mmc_hostname(card->host), card->csd.csd_vers);
 		mmc_card_set_bad(card);
 		break;
 	}
+	} /* if(card->sd) */
 }
 
 /*
@@ -376,11 +431,69 @@ static void mmc_decode_csd(struct mmc_ca
 	unsigned int e, m, csd_struct;
 	u32 *resp = card->raw_csd;
 
+	if(card->sd) {
+		/*
+		* We only understand SD-CSD structure v1.0.
+		*/
+		csd->sd=1;
+		csd->csd_vers = UNSTUFF_BITS(resp, 126, 2);
+		if(csd->csd_vers != SD_CSDV_1) {
+			printk("%s: unrecognised SD-CSD structure version %d\n",
+				mmc_hostname(card->host), csd->csd_vers);
+			mmc_card_set_bad(card);
+			return;
+		}
+
+		csd->spec_vers 		= 0;
+		csd->taac		= UNSTUFF_BITS(resp, 112, 8);
+		csd->nsac		= UNSTUFF_BITS(resp, 104, 8);
+		csd->tran_speed		= UNSTUFF_BITS(resp,  96, 8);
+		csd->ccc		= UNSTUFF_BITS(resp,  84,12);
+		csd->read_bl_len	= UNSTUFF_BITS(resp,  80, 4);
+		csd->read_bl_partial	= UNSTUFF_BITS(resp,  79, 1);
+		csd->write_blk_misalign	= UNSTUFF_BITS(resp,  78, 1);
+		csd->read_blk_misalign	= UNSTUFF_BITS(resp,  77, 1);
+		csd->dsr_imp		= UNSTUFF_BITS(resp,  76, 1);
+		csd->c_size		= UNSTUFF_BITS(resp,  62,12);
+		csd->vdd_r_curr_min	= UNSTUFF_BITS(resp,  69, 3);
+		csd->vdd_r_curr_max	= UNSTUFF_BITS(resp,  56, 3);
+		csd->vdd_w_curr_min	= UNSTUFF_BITS(resp,  53, 3);
+		csd->vdd_w_curr_max	= UNSTUFF_BITS(resp,  50, 3);
+		csd->c_size_mult	= UNSTUFF_BITS(resp,  47, 3);
+		csd->erase.sd10.erase_blk_en	= UNSTUFF_BITS(resp,  46, 1);
+		csd->erase.sd10.erase_blk_size= UNSTUFF_BITS(resp,  39, 7);
+		csd->wp_grp_size	= UNSTUFF_BITS(resp,  32, 7);
+		csd->wp_grp_enable	= UNSTUFF_BITS(resp,  31, 1);
+		csd->r2w_factor		= UNSTUFF_BITS(resp,  26, 3);
+		csd->write_bl_len	= UNSTUFF_BITS(resp,  22, 4);
+		csd->write_bl_partial	= UNSTUFF_BITS(resp,  21, 1);
+		csd->file_format_grp	= UNSTUFF_BITS(resp,  15, 1);
+		csd->copy		= UNSTUFF_BITS(resp,  14, 1);
+		csd->perm_write_protect	= UNSTUFF_BITS(resp,  13, 1);
+		csd->tmp_write_protect	= UNSTUFF_BITS(resp,  12, 1);
+		csd->file_format	= UNSTUFF_BITS(resp,  10, 1);
+		csd->ecc		= UNSTUFF_BITS(resp,   1, 7);
+
+		m = UNSTUFF_BITS(resp, 115, 4);
+		e = UNSTUFF_BITS(resp, 112, 3);
+		csd->tacc_ns	 = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
+		csd->tacc_clks	 = UNSTUFF_BITS(resp, 104, 8) * 100;
+
+		m = UNSTUFF_BITS(resp, 99, 4);
+		e = UNSTUFF_BITS(resp, 96, 3);
+		csd->max_dtr	  = tran_exp[e] * tran_mant[m];
+
+		e = UNSTUFF_BITS(resp, 47, 3);
+		m = UNSTUFF_BITS(resp, 62, 12);
+		csd->capacity	  = (1 + m) << (e + 2);
+	} else { /* if(card->sd) { */
 	/*
 	 * We only understand CSD structure v1.1 and v2.
 	 * v2 has extra information in bits 15, 11 and 10.
 	 */
+	csd->sd=0;
 	csd_struct = UNSTUFF_BITS(resp, 126, 2);
+	csd->csd_vers = csd_struct;
 	if (csd_struct != 1 && csd_struct != 2) {
 		printk("%s: unrecognised CSD structure version %d\n",
 			mmc_hostname(card->host), csd_struct);
@@ -388,7 +501,7 @@ static void mmc_decode_csd(struct mmc_ca
 		return;
 	}
 
-	csd->mmca_vsn	 = UNSTUFF_BITS(resp, 122, 4);
+	csd->spec_vers	 = UNSTUFF_BITS(resp, 122, 4);
 	m = UNSTUFF_BITS(resp, 115, 4);
 	e = UNSTUFF_BITS(resp, 112, 3);
 	csd->tacc_ns	 = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
@@ -397,13 +510,14 @@ static void mmc_decode_csd(struct mmc_ca
 	m = UNSTUFF_BITS(resp, 99, 4);
 	e = UNSTUFF_BITS(resp, 96, 3);
 	csd->max_dtr	  = tran_exp[e] * tran_mant[m];
-	csd->cmdclass	  = UNSTUFF_BITS(resp, 84, 12);
+	csd->ccc	  = UNSTUFF_BITS(resp, 84, 12);
 
 	e = UNSTUFF_BITS(resp, 47, 3);
 	m = UNSTUFF_BITS(resp, 62, 12);
 	csd->capacity	  = (1 + m) << (e + 2);
 
-	csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
+	csd->read_bl_len = UNSTUFF_BITS(resp, 80, 4);
+	} /* if(card->sd) { */
 }
 
 /*
@@ -424,7 +538,7 @@ static struct mmc_card *mmc_find_card(st
  * Allocate a new MMC card, and assign a unique RCA.
  */
 static struct mmc_card *
-mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca)
+mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, u8 sd, unsigned int *frca)
 {
 	struct mmc_card *card, *c;
 	unsigned int rca = *frca;
@@ -435,6 +549,7 @@ mmc_alloc_card(struct mmc_host *host, u3
 
 	mmc_init_card(card, host);
 	memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid));
+	card->sd = sd;
 
  again:
 	list_for_each_entry(c, &host->cards, node)
@@ -456,6 +571,7 @@ mmc_alloc_card(struct mmc_host *host, u3
 static void mmc_idle_cards(struct mmc_host *host)
 {
 	struct mmc_command cmd;
+	struct mmc_card *card;
 
 	cmd.opcode = MMC_GO_IDLE_STATE;
 	cmd.arg = 0;
@@ -463,9 +579,83 @@ static void mmc_idle_cards(struct mmc_ho
 
 	mmc_wait_for_cmd(host, &cmd, 0);
 
-	mmc_delay(1);
+	//MMC_GO_IDLE_STATE resets all cards to bus width 1
+	list_for_each_entry(card, &host->cards, node) {
+		card->bus_width=1;
+	}
+
+
+	mmc_delay(100);
+}
+
+int sd_set_bus_width(struct mmc_host *host, u16 rca, u32 bus_width)
+{
+	struct mmc_command cmd;
+	int err;
+
+	cmd.opcode = MMC_ACMD_SD_SET_BUS_WIDTH;
+	cmd.flags  = MMC_RSP_R1;
+
+	switch(bus_width) {
+		case 1:	 cmd.arg = 00; break;
+		case 4:	 cmd.arg = 10; break;
+		default: return -EINVAL;
+	}
+
+	err = mmc_wait_for_acmd(host, rca, &cmd, 3);
+
+	if(err == MMC_ERR_TIMEOUT) {
+		printk(KERN_ERR "MMC: sd_set_bus_width timed out.\n");
+	} else if(err == MMC_ERR_BADCRC) {
+		printk(KERN_ERR "MMC: sd_set_bus_width yielded crc error.\n");
+	} else {
+		DBG("MMC: sd_app_op_cond done.\n");
+	}
+
+	return err;
 }
 
+EXPORT_SYMBOL(sd_set_bus_width);
+
+static int sd_app_op_cond(struct mmc_host *host, u16 rca, u32 parameter, u32 *response)
+{
+	struct mmc_command cmd;
+	int err;
+	int retries = 10;
+
+	cmd.opcode = MMC_ACMD_SD_APP_OP_COND;
+	cmd.arg = parameter;
+	cmd.flags = MMC_RSP_SHORT;
+
+	DBG("MMC: sd_app_op_cond to %08x\n",parameter);
+
+	while(retries--) {
+		err = mmc_wait_for_acmd(host, 0, &cmd, 0);
+		if(0 == (cmd.resp[0] & MMC_CARD_BUSY)) {
+			err=MMC_ERR_BUSY;
+			printk(KERN_ERR "MMC: sd_app_op_cond: at least one card is busy - trying again.\n");
+			mmc_delay(10);
+			continue;
+		}
+
+		if(err == MMC_ERR_NONE) {
+			if(response)*response = cmd.resp[0];
+			break;
+		}
+	}
+
+	if(err == MMC_ERR_TIMEOUT) {
+		printk(KERN_WARNING "MMC: sd_app_op_cond timed out. Probably no SD-Card here.\n");
+	} else if(err == MMC_ERR_BUSY) {
+		printk(KERN_ERR "MMC: sd_app_op_cond locked busy. Probably have broken SD-Card.\n");
+	} else {
+		DBG("MMC: sd_app_op_cond done. Results are: 0x%08x.\n",cmd.resp[0]);
+	}
+
+	return err;
+}
+
+
 /*
  * Apply power to the MMC stack.
  */
@@ -532,7 +722,7 @@ static int mmc_send_op_cond(struct mmc_h
  * Create a mmc_card entry for each discovered card, assigning
  * it an RCA, and save the raw CID for decoding later.
  */
-static void mmc_discover_cards(struct mmc_host *host)
+static void mmc_discover_cards(struct mmc_host *host, u8 sd)
 {
 	struct mmc_card *card;
 	unsigned int first_rca = 1, err;
@@ -542,7 +732,7 @@ static void mmc_discover_cards(struct mm
 
 		cmd.opcode = MMC_ALL_SEND_CID;
 		cmd.arg = 0;
-		cmd.flags = MMC_RSP_R2;
+		cmd.flags = MMC_RSP_LONG; //HACK! CRC currently not implemented
 
 		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
 		if (err == MMC_ERR_TIMEOUT) {
@@ -557,7 +747,7 @@ static void mmc_discover_cards(struct mm
 
 		card = mmc_find_card(host, cmd.resp);
 		if (!card) {
-			card = mmc_alloc_card(host, cmd.resp, &first_rca);
+			card = mmc_alloc_card(host, cmd.resp, sd, &first_rca);
 			if (IS_ERR(card)) {
 				err = PTR_ERR(card);
 				break;
@@ -574,6 +764,9 @@ static void mmc_discover_cards(struct mm
 		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
 		if (err != MMC_ERR_NONE)
 			mmc_card_set_dead(card);
+
+		//SD-Cards choose their adresses themselfes (yuck!)
+		if(card->sd) card->rca = (cmd.resp[0] >> 16);
 	}
 }
 
@@ -590,7 +783,7 @@ static void mmc_read_csds(struct mmc_hos
 
 		cmd.opcode = MMC_SEND_CSD;
 		cmd.arg = card->rca << 16;
-		cmd.flags = MMC_RSP_R2;
+		cmd.flags = MMC_RSP_LONG; 	//HACK: No CRC check as s3c-Core is broken
 
 		err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
 		if (err != MMC_ERR_NONE) {
@@ -651,63 +844,22 @@ static void mmc_check_cards(struct mmc_h
 	}
 }
 
+
 static void mmc_setup(struct mmc_host *host)
 {
-	if (host->ios.power_mode != MMC_POWER_ON) {
-		int err;
-		u32 ocr;
-
-		mmc_power_up(host);
-		mmc_idle_cards(host);
-
-		err = mmc_send_op_cond(host, 0, &ocr);
-		if (err != MMC_ERR_NONE)
-			return;
-
-		host->ocr = mmc_select_voltage(host, ocr);
-
-		/*
-		 * Since we're changing the OCR value, we seem to
-		 * need to tell some cards to go back to the idle
-		 * state.  We wait 1ms to give cards time to
-		 * respond.
-		 */
-		if (host->ocr)
-			mmc_idle_cards(host);
-	} else {
-		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
-		host->ios.clock = host->f_min;
-		host->ops->set_ios(host, &host->ios);
-
-		/*
-		 * We should remember the OCR mask from the existing
-		 * cards, and detect the new cards OCR mask, combine
-		 * the two and re-select the VDD.  However, if we do
-		 * change VDD, we should do an idle, and then do a
-		 * full re-initialisation.  We would need to notify
-		 * drivers so that they can re-setup the cards as
-		 * well, while keeping their queues at bay.
-		 *
-		 * For the moment, we take the easy way out - if the
-		 * new cards don't like our currently selected VDD,
-		 * they drop off the bus.
-		 */
-	}
-
-	if (host->ocr == 0)
-		return;
-
-	/*
-	 * Send the selected OCR multiple times... until the cards
-	 * all get the idea that they should be ready for CMD2.
-	 * (My SanDisk card seems to need this.)
-	 */
-	mmc_send_op_cond(host, host->ocr, NULL);
+	mmc_power_up(host);
+	mmc_idle_cards(host);
 
-	mmc_discover_cards(host);
+	// Wake and discover SD-Cards
+	sd_app_op_cond(host, 0, host->ocr_avail, NULL);
+	mmc_discover_cards(host, 1);
+
+	// Wake and discover MMC-Cards
+	mmc_send_op_cond(host, host->ocr_avail, NULL);
+	mmc_discover_cards(host, 0);
 
 	/*
-	 * Ok, now switch to push-pull mode.
+	 * switch to push-pull mode.
 	 */
 	host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
 	host->ops->set_ios(host, &host->ios);
diff --git a/drivers/mmc/mmc.h b/drivers/mmc/mmc.h
--- a/drivers/mmc/mmc.h
+++ b/drivers/mmc/mmc.h
@@ -13,6 +13,7 @@
 void mmc_init_card(struct mmc_card *card, struct mmc_host *host);
 int mmc_register_card(struct mmc_card *card);
 void mmc_remove_card(struct mmc_card *card);
+int sd_set_bus_width(struct mmc_host *host, u16 rca, u32 bus_width);
 
 struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev);
 int mmc_add_host_sysfs(struct mmc_host *host);
diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c
--- a/drivers/mmc/mmc_block.c
+++ b/drivers/mmc/mmc_block.c
@@ -3,6 +3,9 @@
  *
  * Copyright 2002 Hewlett-Packard Company
  *
+ *  SD-Card support:
+ *    Copyright (C) 2004 Thomas Kleffel, All Rights Reserved
+ *
  * Use consistent with the GNU GPL is permitted,
  * provided that this copyright notice is
  * preserved in its entirety in all copies and derived works.
@@ -30,11 +33,13 @@
 #include <linux/devfs_fs_kernel.h>
 
 #include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
 #include <linux/mmc/protocol.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
+#include "mmc.h"
 #include "mmc_queue.h"
 
 /*
@@ -165,11 +170,17 @@ static int mmc_blk_issue_rq(struct mmc_q
 {
 	struct mmc_blk_data *md = mq->data;
 	struct mmc_card *card = md->queue.card;
+	struct mmc_host *host = md->queue.card->host;
 	int ret;
 
 	if (mmc_card_claim_host(card))
 		goto cmd_err;
 
+	if((card->sd) && (host->flags & MMC_HOST_WIDEMODE) && (card->bus_width != 4)) {
+		if(sd_set_bus_width(host, card->rca, 4)) goto cmd_err;
+		card->bus_width=4;
+	}
+
 	do {
 		struct mmc_blk_request brq;
 		struct mmc_command cmd;
@@ -180,6 +191,7 @@ static int mmc_blk_issue_rq(struct mmc_q
 
 		brq.cmd.arg = req->sector << 9;
 		brq.cmd.flags = MMC_RSP_R1;
+		brq.data.req = req;
 		brq.data.timeout_ns = card->csd.tacc_ns * 10;
 		brq.data.timeout_clks = card->csd.tacc_clks * 10;
 		brq.data.blksz_bits = md->block_bits;
@@ -188,6 +200,9 @@ static int mmc_blk_issue_rq(struct mmc_q
 		brq.stop.arg = 0;
 		brq.stop.flags = MMC_RSP_R1B;
 
+		if(card->bus_width==4)
+			brq.data.flags |= MMC_DATA_WIDE;
+
 		if (rq_data_dir(req) == READ) {
 			brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
 			brq.data.flags |= MMC_DATA_READ;
@@ -252,7 +267,7 @@ static int mmc_blk_issue_rq(struct mmc_q
 			/*
 			 * The whole request completed successfully.
 			 */
-			add_disk_randomness(req->rq_disk);
+			//add_disk_randomness(req->rq_disk);
 			blkdev_dequeue_request(req);
 			end_that_request_last(req);
 		}
@@ -347,7 +362,7 @@ static struct mmc_blk_data *mmc_blk_allo
 		sprintf(md->disk->disk_name, "mmcblk%d", devidx);
 		sprintf(md->disk->devfs_name, "mmc/blk%d", devidx);
 
-		md->block_bits = card->csd.read_blkbits;
+		md->block_bits = card->csd.read_bl_len;
 
 		blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits);
 		set_capacity(md->disk, card->csd.capacity);
@@ -364,7 +379,7 @@ mmc_blk_set_blksize(struct mmc_blk_data 
 
 	mmc_card_claim_host(card);
 	cmd.opcode = MMC_SET_BLOCKLEN;
-	cmd.arg = 1 << card->csd.read_blkbits;
+	cmd.arg = 1 << card->csd.read_bl_len;
 	cmd.flags = MMC_RSP_R1;
 	err = mmc_wait_for_cmd(card->host, &cmd, 5);
 	mmc_card_release_host(card);
@@ -386,12 +401,12 @@ static int mmc_blk_probe(struct mmc_card
 	/*
 	 * Check that the card supports the command class(es) we need.
 	 */
-	if (!(card->csd.cmdclass & CCC_BLOCK_READ))
+	if (!(card->csd.ccc & CCC_BLOCK_READ))
 		return -ENODEV;
 
-	if (card->csd.read_blkbits < 9) {
+	if (card->csd.read_bl_len < 9) {
 		printk(KERN_WARNING "%s: read blocksize too small (%u)\n",
-			mmc_card_id(card), 1 << card->csd.read_blkbits);
+			mmc_card_id(card), 1 << card->csd.read_bl_len);
 		return -ENODEV;
 	}
 
@@ -405,7 +420,7 @@ static int mmc_blk_probe(struct mmc_card
 
 	printk(KERN_INFO "%s: %s %s %dKiB\n",
 		md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
-		(card->csd.capacity << card->csd.read_blkbits) / 1024);
+		(card->csd.capacity << card->csd.read_bl_len) / 1024);
 
 	mmc_set_drvdata(card, md);
 	add_disk(md->disk);
diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c
--- a/drivers/mmc/mmc_sysfs.c
+++ b/drivers/mmc/mmc_sysfs.c
@@ -3,6 +3,9 @@
  *
  *  Copyright (C) 2003 Russell King, All Rights Reserved.
  *
+ *  SD-Card support:
+ *    Copyright (C) 2004 Thomas Kleffel, All Rights Reserved
+ *
  * 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.
@@ -41,10 +44,13 @@ MMC_ATTR(manfid, "0x%06x\n", card->cid.m
 MMC_ATTR(name, "%s\n", card->cid.prod_name);
 MMC_ATTR(oemid, "0x%04x\n", card->cid.oemid);
 MMC_ATTR(serial, "0x%08x\n", card->cid.serial);
+MMC_ATTR(bus_width, "%u\n", card->bus_width);
+MMC_ATTR(type, "%s\n", (card->sd?"SD":"MMC"));
 
 #define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
 
 static struct device_attribute mmc_dev_attrs[] = {
+	MMC_ATTR_RO(bus_width),
 	MMC_ATTR_RO(cid),
 	MMC_ATTR_RO(csd),
 	MMC_ATTR_RO(date),
@@ -54,6 +60,7 @@ static struct device_attribute mmc_dev_a
 	MMC_ATTR_RO(name),
 	MMC_ATTR_RO(oemid),
 	MMC_ATTR_RO(serial),
+	MMC_ATTR_RO(type),
 	__ATTR_NULL
 };
 
@@ -99,7 +106,7 @@ mmc_bus_hotplug(struct device *dev, char
 	})
 
 	for (i = 0; i < 12; i++)
-		ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0';
+		ccc[i] = card->csd.ccc & (1 << i) ? '1' : '0';
 	ccc[12] = '\0';
 
 	i = 0;
diff --git a/drivers/mmc/s3c2410mci.c b/drivers/mmc/s3c2410mci.c
new file mode 100644
--- /dev/null
+++ b/drivers/mmc/s3c2410mci.c
@@ -0,0 +1,760 @@
+/*
+ *  linux/drivers/mmc/s3c2410mci.h - Samsung S3C2410 SDI Interface driver
+ *
+ *  Copyright (C) 2004 Thomas Kleffel, All Rights Reserved.
+ *
+ * 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.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+
+#include <asm/dma.h>
+#include <asm/dma-mapping.h>
+#include <asm/arch/dma.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware/amba.h>
+#include <asm/hardware/clock.h>
+#include <asm/mach/mmc.h>
+
+#include <asm/arch/regs-sdi.h>
+#include <asm/arch/regs-gpio.h>
+
+//#define S3C2410SDI_DMA_BACKBUF
+
+#ifdef CONFIG_MMC_DEBUG
+#define DBG(x...)       printk(KERN_DEBUG x)
+#else
+#define DBG(x...)       do { } while (0)
+#endif
+
+#include "s3c2410mci.h"
+
+#define DRIVER_NAME "mmci-s3c2410"
+#define PFX DRIVER_NAME ": "
+
+#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
+
+// #define KERN_DEBUG KERN_INFO
+
+typedef enum {
+	DMAP_READ,
+	DMAP_WRITE,
+} eDMAPurpose_t;
+
+static struct s3c2410_dma_client s3c2410sdi_dma_client = {
+	.name		= "s3c2410-sdi",
+};
+
+
+
+/*
+ * ISR for SDI Interface IRQ
+ * Communication between driver and ISR works as follows:
+ *   host->mrq 			points to current request
+ *   host->complete_what	tells the ISR when the request is considered done
+ *     COMPLETION_CMDSENT	  when the command was sent
+ *     COMPLETION_RSPFIN          when a response was received
+ *     COMPLETION_XFERFINISH	  when the data transfer is finished
+ *     COMPLETION_XFERFINISH_RSPFIN both of the above.
+ *   host->complete_request	is the completion-object the driver waits for
+ *
+ * 1) Driver sets up host->mrq and host->complete_what
+ * 2) Driver prepares the transfer
+ * 3) Driver enables interrupts
+ * 4) Driver starts transfer
+ * 5) Driver waits for host->complete_rquest
+ * 6) ISR checks for request status (errors and success)
+ * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
+ * 7) ISR completes host->complete_request
+ * 8) ISR disables interrupts
+ * 9) Driver wakes up and takes care of the request
+*/
+
+static irqreturn_t s3c2410sdi_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct s3c2410sdi_host *host;
+	u32 sdi_csta, sdi_dsta, sdi_dcnt;
+	u32 sdi_cclear, sdi_dclear;
+	unsigned long iflags;
+
+	host = (struct s3c2410sdi_host *)dev_id;
+
+	//Check for things not supposed to happen
+	if(!host) return IRQ_HANDLED;
+	
+	sdi_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
+	sdi_dsta 	= readl(host->base + S3C2410_SDIDSTA);
+	sdi_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
+	
+	DBG(PFX "IRQ csta=0x%08x dsta=0x%08x dcnt:0x%08x\n", sdi_csta, sdi_dsta, sdi_dcnt);
+		
+	spin_lock_irqsave( &host->complete_lock, iflags);
+	
+	if( host->complete_what==COMPLETION_NONE ) {
+		goto clear_imask;
+	}
+	
+	if(!host->mrq) { 
+		goto clear_imask;
+	}
+
+	
+	sdi_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
+	sdi_dsta 	= readl(host->base + S3C2410_SDIDSTA);
+	sdi_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
+	sdi_cclear	= 0;
+	sdi_dclear	= 0;
+	
+	
+	if(sdi_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
+		host->mrq->cmd->error = MMC_ERR_TIMEOUT;
+		goto transfer_closed;
+	}
+
+	if(sdi_csta & S3C2410_SDICMDSTAT_CMDSENT) {
+		if(host->complete_what == COMPLETION_CMDSENT) {
+			host->mrq->cmd->error = MMC_ERR_NONE;
+			goto transfer_closed;
+		}
+
+		sdi_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
+	}
+
+	if(sdi_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
+		if(host->mrq->cmd->flags & MMC_RSP_CRC) {
+			host->mrq->cmd->error = MMC_ERR_BADCRC;
+			goto transfer_closed;
+		}
+
+		sdi_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
+	}
+
+	if(sdi_csta & S3C2410_SDICMDSTAT_RSPFIN) {
+		if(host->complete_what == COMPLETION_RSPFIN) {
+			host->mrq->cmd->error = MMC_ERR_NONE;
+			goto transfer_closed;
+		}
+
+		if(host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
+			host->mrq->cmd->error = MMC_ERR_NONE;
+			host->complete_what = COMPLETION_XFERFINISH;
+		}
+
+		sdi_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
+	}
+
+	if(sdi_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
+		host->mrq->cmd->error = MMC_ERR_NONE;
+		host->mrq->data->error = MMC_ERR_FIFO;
+		goto transfer_closed;
+	}
+
+	if(sdi_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
+		host->mrq->cmd->error = MMC_ERR_NONE;
+		host->mrq->data->error = MMC_ERR_BADCRC;
+		goto transfer_closed;
+	}
+
+	if(sdi_dsta & S3C2410_SDIDSTA_CRCFAIL) {
+		host->mrq->cmd->error = MMC_ERR_NONE;
+		host->mrq->data->error = MMC_ERR_BADCRC;
+		goto transfer_closed;
+	}
+
+	if(sdi_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
+		host->mrq->cmd->error = MMC_ERR_NONE;
+		host->mrq->data->error = MMC_ERR_TIMEOUT;
+		goto transfer_closed;
+	}
+
+	if(sdi_dsta & S3C2410_SDIDSTA_XFERFINISH) {
+		if(host->complete_what == COMPLETION_XFERFINISH) {
+			host->mrq->cmd->error = MMC_ERR_NONE;
+			host->mrq->data->error = MMC_ERR_NONE;
+			goto transfer_closed;
+		}
+
+		if(host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
+			host->mrq->data->error = MMC_ERR_NONE;
+			host->complete_what = COMPLETION_RSPFIN;
+		}
+
+		sdi_dclear |= S3C2410_SDIDSTA_XFERFINISH;
+	}
+
+	writel(sdi_cclear, host->base + S3C2410_SDICMDSTAT);
+	writel(sdi_dclear, host->base + S3C2410_SDIDSTA);
+
+	spin_unlock_irqrestore( &host->complete_lock, iflags);
+	DBG(PFX "IRQ still waiting.\n");
+	return IRQ_HANDLED;
+
+
+transfer_closed:
+	host->complete_what = COMPLETION_NONE;
+	complete(&host->complete_request);
+	writel(0, host->base + S3C2410_SDIIMSK);
+	spin_unlock_irqrestore( &host->complete_lock, iflags);
+	DBG(PFX "IRQ transfer closed.\n");
+	return IRQ_HANDLED;
+	
+clear_imask:
+	writel(0, host->base + S3C2410_SDIIMSK);
+	spin_unlock_irqrestore( &host->complete_lock, iflags);
+	DBG(PFX "IRQ clear imask.\n");
+	return IRQ_HANDLED;
+
+}
+
+
+/*
+ * ISR for the CardDetect Pin
+*/
+
+static irqreturn_t s3c2410sdi_irq_cd(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct s3c2410sdi_host *host = (struct s3c2410sdi_host *)dev_id;
+	mmc_detect_change(host->mmc);
+
+	return IRQ_HANDLED;
+}
+
+
+
+void s3c2410sdi_dma_done_callback(s3c2410_dma_chan_t *dma_ch, void *buf_id,
+	int size, s3c2410_dma_buffresult_t result)
+{	unsigned long iflags;
+	u32 sdi_csta, sdi_dsta,sdi_dcnt;
+	struct s3c2410sdi_host *host = (struct s3c2410sdi_host *)buf_id;
+	
+	sdi_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
+	sdi_dsta 	= readl(host->base + S3C2410_SDIDSTA);
+	sdi_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
+	
+	DBG(PFX "DMAD csta=0x%08x dsta=0x%08x dcnt:0x%08x result:0x%08x\n", sdi_csta, sdi_dsta, sdi_dcnt, result);
+	
+	spin_lock_irqsave( &host->complete_lock, iflags);
+	
+	if(!host->mrq) goto out;
+	if(!host->mrq->data) goto out;
+	
+	
+	sdi_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
+	sdi_dsta 	= readl(host->base + S3C2410_SDIDSTA);
+	sdi_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
+		
+	if( result!=S3C2410_RES_OK ) {
+		goto fail_request;
+	}
+	
+	
+	if(host->mrq->data->flags & MMC_DATA_READ) {
+		if( sdi_dcnt>0 ) {
+			goto fail_request;
+		}
+	}
+	
+out:	
+	complete(&host->complete_dma);
+	spin_unlock_irqrestore( &host->complete_lock, iflags);
+	return;
+
+
+fail_request:
+	host->mrq->data->error = MMC_ERR_DMA;
+	host->complete_what = COMPLETION_NONE;
+	complete(&host->complete_dma);
+	complete(&host->complete_request);
+	writel(0, host->base + S3C2410_SDIIMSK);
+	goto out;
+
+}
+
+
+void s3c2410sdi_dma_setup(struct s3c2410sdi_host *host, eDMAPurpose_t purpose) {
+	s3c2410_dmasrc_t source;
+
+	switch(purpose) {
+		default:
+		case DMAP_READ:
+			source  = S3C2410_DMASRC_HW;
+			break;
+
+		case DMAP_WRITE:
+			source  = S3C2410_DMASRC_MEM;
+			break;
+	}
+
+	s3c2410_dma_devconfig(host->dma, source, 3, host->mem->start + S3C2410_SDIDATA);
+	s3c2410_dma_config(host->dma, 4, (1<<23) | (2<<24));
+	s3c2410_dma_set_buffdone_fn(host->dma, s3c2410sdi_dma_done_callback);
+	s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
+}
+
+static void s3c2410sdi_request(struct mmc_host *mmc, struct mmc_request *mrq) {
+ 	struct s3c2410sdi_host *host = mmc_priv(mmc);
+	u32 sdi_carg, sdi_ccon, sdi_timer;
+	u32 sdi_bsize, sdi_dcon, sdi_imsk;
+
+	DBG(KERN_DEBUG PFX "request: [CMD] opcode:0x%02x arg:0x%08x flags:%x retries:%u\n",
+		mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags, mrq->cmd->retries);
+
+
+	sdi_ccon = mrq->cmd->opcode & S3C2410_SDICMDCON_INDEX;
+	sdi_ccon|= S3C2410_SDICMDCON_SENDERHOST;
+	sdi_ccon|= S3C2410_SDICMDCON_CMDSTART;
+
+	sdi_carg = mrq->cmd->arg;
+
+	//FIXME: Timer value ?!
+	sdi_timer= 0xF000;
+
+	sdi_bsize= 0;
+	sdi_dcon = 0;
+	sdi_imsk = 0;
+
+	//enable interrupts for transmission errors
+	sdi_imsk |= S3C2410_SDIIMSK_RESPONSEND;
+	sdi_imsk |= S3C2410_SDIIMSK_CRCSTATUS;
+
+
+	host->complete_what = COMPLETION_CMDSENT;
+
+	if (mrq->cmd->flags & MMC_RSP_MASK) {
+		host->complete_what = COMPLETION_RSPFIN;
+
+		sdi_ccon |= S3C2410_SDICMDCON_WAITRSP;
+		sdi_imsk |= S3C2410_SDIIMSK_CMDTIMEOUT;
+
+	} else {
+		//We need the CMDSENT-Interrupt only if we want are not waiting
+		//for a response
+		sdi_imsk |= S3C2410_SDIIMSK_CMDSENT;
+	}
+
+	if(mrq->cmd->flags & MMC_RSP_LONG) {
+		sdi_ccon|= S3C2410_SDICMDCON_LONGRSP;
+	}
+
+	if(mrq->cmd->flags & MMC_RSP_CRC) {
+		sdi_imsk |= S3C2410_SDIIMSK_RESPONSECRC;
+	}
+
+
+	if (mrq->data) {
+		host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
+
+
+
+		sdi_bsize = (1 << mrq->data->blksz_bits);
+
+		sdi_dcon  = (mrq->data->blocks & S3C2410_SDIDCON_BLKNUM_MASK);
+		sdi_dcon |= S3C2410_SDIDCON_DMAEN;
+
+		sdi_imsk |= S3C2410_SDIIMSK_FIFOFAIL;
+		sdi_imsk |= S3C2410_SDIIMSK_DATACRC;
+		sdi_imsk |= S3C2410_SDIIMSK_DATATIMEOUT;
+		sdi_imsk |= S3C2410_SDIIMSK_DATAFINISH;
+		sdi_imsk |= 0xFFFFFFE0;
+
+		DBG(PFX "request: [DAT] bsize:%u blocks:%u bytes:%u\n",
+			sdi_bsize, mrq->data->blocks, mrq->data->blocks * sdi_bsize);
+
+		if(mrq->data->flags & MMC_DATA_WIDE) {
+			sdi_dcon |= S3C2410_SDIDCON_WIDEBUS;
+		}
+
+		if(!(mrq->data->flags & MMC_DATA_STREAM)) {
+			sdi_dcon |= S3C2410_SDIDCON_BLOCKMODE;
+		}
+
+		if(mrq->data->flags & MMC_DATA_WRITE) {
+			sdi_dcon |= S3C2410_SDIDCON_TXAFTERRESP;
+			sdi_dcon |= S3C2410_SDIDCON_XFER_TXSTART;
+			s3c2410sdi_dma_setup(host, DMAP_WRITE);
+#ifdef S3C2410SDI_DMA_BACKBUF			
+			memcpy(host->dmabuf_log, mrq->data->req->buffer, mrq->data->blocks * sdi_bsize);
+#endif
+		}
+
+		if(mrq->data->flags & MMC_DATA_READ) {
+			sdi_dcon |= S3C2410_SDIDCON_RXAFTERCMD;
+			sdi_dcon |= S3C2410_SDIDCON_XFER_RXSTART;
+			s3c2410sdi_dma_setup(host, DMAP_READ);
+		}
+		
+		
+
+		//start DMA
+#ifdef S3C2410SDI_DMA_BACKBUF
+		s3c2410_dma_enqueue(host->dma, (void *) host,
+			host->dmabuf_phys,
+			(mrq->data->blocks << mrq->data->blksz_bits) );
+#else
+		s3c2410_dma_enqueue(host->dma, (void *) host,
+			virt_to_phys(mrq->data->req->buffer),
+			(mrq->data->blocks << mrq->data->blksz_bits) );
+#endif
+	}
+
+	host->mrq = mrq;
+
+	init_completion(&host->complete_request);
+	init_completion(&host->complete_dma);
+
+	//Clear command and data status registers
+	writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
+	writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
+
+	// Setup SDI controller
+	writel(sdi_bsize,host->base + S3C2410_SDIBSIZE);
+	writel(sdi_timer,host->base + S3C2410_SDITIMER);
+	writel(sdi_imsk,host->base + S3C2410_SDIIMSK);
+
+	// Setup SDI command argument and data control
+	writel(sdi_carg, host->base + S3C2410_SDICMDARG);
+	writel(sdi_dcon, host->base + S3C2410_SDIDCON);
+
+	// This initiates transfer
+	writel(sdi_ccon, host->base + S3C2410_SDICMDCON);
+
+	// Wait for transfer to complete
+	wait_for_completion(&host->complete_request);
+	DBG("[CMD] request complete.\n");
+	if(mrq->data) {
+		wait_for_completion(&host->complete_dma);
+		DBG("[DAT] DMA complete.\n");
+	}
+	
+	// Wait for DMA to complete
+//	if(mrq->data) wait_for_completion(&host->complete_dma);
+
+	//Cleanup controller
+	writel(0, host->base + S3C2410_SDICMDARG);
+	writel(0, host->base + S3C2410_SDIDCON);
+	writel(0, host->base + S3C2410_SDICMDCON);
+	writel(0, host->base + S3C2410_SDIIMSK);
+
+	// Read response
+	mrq->cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);
+	mrq->cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);
+	mrq->cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);
+	mrq->cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);
+
+	host->mrq = NULL;
+
+	DBG(PFX "request done.\n");
+
+	// If we have no data transfer we are finished here
+	if (!mrq->data) goto request_done;
+
+	// Calulate the amout of bytes transfer, but only if there was
+	// no error
+	if(mrq->data->error == MMC_ERR_NONE) {
+		mrq->data->bytes_xfered = (mrq->data->blocks << mrq->data->blksz_bits);
+		if(mrq->data->flags & MMC_DATA_READ);
+#ifdef S3C2410SDI_DMA_BACKBUF
+		memcpy(mrq->data->req->buffer, host->dmabuf_log, mrq->data->bytes_xfered);
+#endif
+	} else {
+		mrq->data->bytes_xfered = 0;
+	}
+
+	// If we had an error while transfering data we flush the
+	// DMA channel to clear out any garbage
+	if(mrq->data->error != MMC_ERR_NONE) {
+		s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+		DBG(PFX "flushing DMA.\n");		
+	}
+	// Issue stop command
+	if(mrq->data->stop) mmc_wait_for_cmd(mmc, mrq->data->stop, 3);
+
+
+request_done:
+
+	mrq->done(mrq);
+}
+
+static void s3c2410sdi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) {
+	struct s3c2410sdi_host *host = mmc_priv(mmc);
+	u32 sdi_psc, sdi_con;
+
+	//Set power
+	sdi_con = readl(host->base + S3C2410_SDICON);
+	switch(ios->power_mode) {
+		case MMC_POWER_ON:
+		case MMC_POWER_UP:
+			s3c2410_gpio_setpin(S3C2410_GPA17, 1); // card power on
+
+			s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
+			s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
+			s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
+			s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
+			s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
+			s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
+
+			sdi_con|= S3C2410_SDICON_FIFORESET;
+			break;
+
+		case MMC_POWER_OFF:
+		default:
+			s3c2410_gpio_setpin(S3C2410_GPA17, 0); // card power off
+
+			s3c2410_gpio_setpin(S3C2410_GPE5, 0);
+			s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
+			break;
+	}
+
+	//Set clock
+	for(sdi_psc=0;sdi_psc<255;sdi_psc++) {
+		if( (clk_get_rate(host->clk) / (2*(sdi_psc+1))) <= ios->clock) break;
+	}
+
+	if(sdi_psc > 255) sdi_psc = 255;
+	writel(sdi_psc, host->base + S3C2410_SDIPRE);
+
+	//Set CLOCK_ENABLE
+	if(ios->clock) 	sdi_con |= S3C2410_SDICON_CLOCKTYPE;
+	else		sdi_con &=~S3C2410_SDICON_CLOCKTYPE;
+
+	writel(sdi_con, host->base + S3C2410_SDICON);
+}
+
+static struct mmc_host_ops s3c2410sdi_ops = {
+	.request	= s3c2410sdi_request,
+	.set_ios	= s3c2410sdi_set_ios,
+};
+
+static int s3c2410sdi_probe(struct device *dev)
+{
+	struct platform_device	*pdev = to_platform_device(dev);
+	struct mmc_host 	*mmc;
+	struct s3c2410sdi_host 	*host;
+
+	int ret;
+
+	mmc = mmc_alloc_host(sizeof(struct s3c2410sdi_host), dev);
+	if (!mmc) {
+		ret = -ENOMEM;
+		goto probe_out;
+	}
+
+	host = mmc_priv(mmc);
+
+	spin_lock_init( &host->complete_lock );
+	host->complete_what 	= COMPLETION_NONE;
+	host->mmc 		= mmc;
+	host->dma		= S3C2410SDI_DMA;
+	host->irq_cd		= IRQ_EINT2;
+
+
+	host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!host->mem) {
+		printk(KERN_INFO PFX "failed to get io memory region resouce.\n");
+		ret = -ENOENT;
+		goto probe_free_host;
+	}
+
+	host->mem = request_mem_region(host->mem->start,
+		RESSIZE(host->mem), pdev->name);
+
+	if (!host->mem) {
+		printk(KERN_INFO PFX "failed to request io memory region.\n");
+		ret = -ENOENT;
+		goto probe_free_host;
+	}
+
+	host->base = ioremap(host->mem->start, RESSIZE(host->mem));
+	if (host->base == 0) {
+		printk(KERN_INFO PFX "failed to ioremap() io memory region.\n");
+		ret = -EINVAL;
+		goto probe_free_mem_region;
+	}
+
+	host->irq = platform_get_irq(pdev, 0);
+	if (host->irq == 0) {
+		printk(KERN_INFO PFX "failed to get interrupt resouce.\n");
+		ret = -EINVAL;
+		goto probe_iounmap;
+	}
+
+	if(request_irq(host->irq, s3c2410sdi_irq, 0, DRIVER_NAME, host)) {
+		printk(KERN_INFO PFX "failed to request sdi interrupt.\n");
+		ret = -ENOENT;
+		goto probe_iounmap;
+	}
+
+	s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
+	set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
+
+	if(request_irq(host->irq_cd, s3c2410sdi_irq_cd, 0, DRIVER_NAME, host)) {
+		printk(KERN_WARNING PFX "failed to request card detect interrupt.\n" );
+		ret = -ENOENT;
+		goto probe_free_irq;
+	}
+
+	if(s3c2410_dma_request(S3C2410SDI_DMA, &s3c2410sdi_dma_client, NULL)) {
+		printk(KERN_WARNING PFX "unable to get DMA channel.\n" );
+		ret = -EBUSY;
+		goto probe_free_irq_cd;
+	}
+
+	host->clk = clk_get(dev, "sdi");
+	if (IS_ERR(host->clk)) {
+		printk(KERN_INFO PFX "failed to find clock source.\n");
+		ret = PTR_ERR(host->clk);
+		host->clk = NULL;
+		goto probe_free_host;
+	}
+
+	if((ret = clk_use(host->clk))) {
+		printk(KERN_INFO PFX "failed to use clock source.\n");
+		goto clk_free;
+	}
+
+	if((ret = clk_enable(host->clk))) {
+		printk(KERN_INFO PFX "failed to enable clock source.\n");
+		goto clk_unuse;
+	}
+
+
+	mmc->ops 	= &s3c2410sdi_ops;
+	mmc->ocr_avail	= MMC_VDD_32_33;
+	mmc->flags      = MMC_HOST_WIDEMODE;
+	mmc->f_min 	= clk_get_rate(host->clk) / 512;
+	mmc->f_max 	= clk_get_rate(host->clk) / 2;
+
+
+	//HACK: There seems to be a hardware bug in TomTom GO.
+	if(mmc->f_max>3000000) mmc->f_max=3000000;
+
+
+	/*
+	 * Since we only have a 16-bit data length register, we must
+	 * ensure that we don't exceed 2^16-1 bytes in a single request.
+	 * Choose 64 (512-byte) sectors as the limit.
+	 */
+	mmc->max_sectors = 64;
+
+	/*
+	 * Set the maximum segment size.  Since we aren't doing DMA
+	 * (yet) we are only limited by the data length register.
+	 */
+
+	mmc->max_seg_size = mmc->max_sectors << 9;
+#ifdef S3C2410SDI_DMA_BACKBUF	
+	host->dmabuf_log = dma_alloc_coherent(NULL, mmc->max_seg_size ,&host->dmabuf_phys, GFP_KERNEL|GFP_DMA);
+	
+	if(!host->dmabuf_log) {
+		printk(KERN_INFO PFX "failed to allocate DMA buffer.\n");
+		goto clk_disable;
+	}
+	
+	host->dmabuf_size = mmc->max_seg_size;
+	
+	printk(KERN_INFO PFX "probe: mapped sdi_base=%p irq=%u irq_cd=%u dma=%u dmabuf_l=%p dmabuf_p=%p.\n", 
+		host->base, host->irq, host->irq_cd, host->dma, host->dmabuf_log, host->dmabuf_phys);
+#else
+	printk(KERN_INFO PFX "probe: mapped sdi_base=%p irq=%u irq_cd=%u dma=%u.\n", 
+		host->base, host->irq, host->irq_cd, host->dma);
+#endif	
+	if((ret = mmc_add_host(mmc))) {
+		printk(KERN_INFO PFX "failed to add mmc host.\n");
+		goto free_dmabuf;
+	}
+
+	dev_set_drvdata(dev, mmc);
+
+	printk(KERN_INFO PFX "initialisation done.\n");
+	return 0;
+	
+ free_dmabuf:
+#ifdef S3C2410SDI_DMA_BACKBUF
+ 	dma_free_coherent(NULL, host->dmabuf_size, host->dmabuf_log, host->dmabuf_phys);
+#endif
+
+	clk_disable(host->clk);
+
+ clk_unuse:
+	clk_unuse(host->clk);
+
+ clk_free:
+	clk_put(host->clk);
+
+ probe_free_irq_cd:
+ 	free_irq(host->irq_cd, host);
+
+ probe_free_irq:
+ 	free_irq(host->irq, host);
+
+ probe_iounmap:
+	iounmap(host->base);
+
+ probe_free_mem_region:
+	release_mem_region(host->mem->start, RESSIZE(host->mem));
+
+ probe_free_host:
+	mmc_free_host(mmc);
+ probe_out:
+	return ret;
+}
+
+static int s3c2410sdi_remove(struct device *dev)
+{
+	struct mmc_host 	*mmc  = dev_get_drvdata(dev);
+	struct s3c2410sdi_host 	*host = mmc_priv(mmc);
+
+	mmc_remove_host(mmc);
+#ifdef S3C2410SDI_DMA_BACKBUF
+ 	dma_free_coherent(NULL, host->dmabuf_size, host->dmabuf_log, host->dmabuf_phys);
+#endif
+	clk_disable(host->clk);
+	clk_unuse(host->clk);
+	clk_put(host->clk);
+ 	free_irq(host->irq_cd, host);
+ 	free_irq(host->irq, host);
+	iounmap(host->base);
+	release_mem_region(host->mem->start, RESSIZE(host->mem));
+	mmc_free_host(mmc);
+
+	return 0;
+}
+
+static struct device_driver s3c2410sdi_driver =
+{
+        .name           = "s3c2410-sdi",
+        .bus            = &platform_bus_type,
+        .probe          = s3c2410sdi_probe,
+        .remove         = s3c2410sdi_remove,
+};
+
+static int __init s3c2410sdi_init(void)
+{
+	return driver_register(&s3c2410sdi_driver);
+}
+
+static void __exit s3c2410sdi_exit(void)
+{
+	driver_unregister(&s3c2410sdi_driver);
+}
+
+module_init(s3c2410sdi_init);
+module_exit(s3c2410sdi_exit);
+
+MODULE_DESCRIPTION("Samsung S3C2410 Multimedia Card Interface driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/s3c2410mci.h b/drivers/mmc/s3c2410mci.h
new file mode 100644
--- /dev/null
+++ b/drivers/mmc/s3c2410mci.h
@@ -0,0 +1,45 @@
+/*
+ *  linux/drivers/mmc/s3c2410mci.h - Samsung S3C2410 SDI Interface driver
+ *
+ *  Copyright (C) 2004 Thomas Kleffel, All Rights Reserved.
+ *
+ * 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.
+ */
+
+struct clk;
+
+//FIXME: DMA Resource management ?!
+#define S3C2410SDI_DMA 0
+
+enum s3c2410sdi_waitfor {
+	COMPLETION_NONE,
+	COMPLETION_CMDSENT,
+	COMPLETION_RSPFIN,
+	COMPLETION_XFERFINISH,
+	COMPLETION_XFERFINISH_RSPFIN,
+};
+
+struct s3c2410sdi_host {
+	struct mmc_host		*mmc;
+
+	struct resource		*mem;
+	struct clk		*clk;
+	void __iomem		*base;
+	int			irq;
+	int			irq_cd;
+	int			dma;
+#ifdef S3C2410SDI_DMA_BACKBUF
+	dma_addr_t		 dmabuf_phys;
+	void			*dmabuf_log;
+	unsigned int		 dmabuf_size;
+#endif
+	
+	struct mmc_request	*mrq;
+
+	spinlock_t		complete_lock;
+	struct completion	complete_request;
+	struct completion	complete_dma;
+	enum s3c2410sdi_waitfor	complete_what;
+};
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -179,16 +179,93 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
 	  load time (assuming you build diskonchip as a module) with the module
 	  parameter "inftl_bbt_write=1".
 	  
- config MTD_NAND_SHARPSL
+config MTD_NAND_SHARPSL
  	bool "Support for NAND Flash on Sharp SL Series (C7xx + others)"
  	depends on MTD_NAND	&& ARCH_PXA
  
- config MTD_NAND_NANDSIM
- 	bool "Support for NAND Flash Simulator"
- 	depends on MTD_NAND && MTD_PARTITIONS
-
+config MTD_NAND_NANDSIM
+	tristate "Support for NAND Flash Simulator"
+	depends on m && MTD_NAND && MTD_PARTITIONS
 	help
 	  The simulator may simulate verious NAND flash chips for the
 	  MTD nand layer.
- 
+
+config MTD_NAND_NANDSIM_FIRST_ID
+	hex "First ID byte"
+	range 0x00 0xff
+	default 0x98
+	depends on MTD_NAND_NANDSIM
+	help 
+     	  The fist byte returned by NAND Flash 'read ID' command (manufaturer ID).
+
+config MTD_NAND_NANDSIM_SECOND_ID
+	hex "Second ID byte"
+	range 0x00 0xff
+	default 0x39
+	depends on MTD_NAND_NANDSIM
+	help
+	  The second byte returned by NAND Flash 'read ID' command (chip ID).
+
+config MTD_NAND_NANDSIM_THIRD_ID
+	hex "Third ID byte"
+	range 0x00 0xff
+	default 0xff
+	depends on MTD_NAND_NANDSIM
+	help
+	  The third byte returned by NAND Flash 'read ID' command.
+
+config MTD_NAND_NANDSIM_FOURTH_ID
+	hex "Fourth ID byte"
+	range 0x00 0xff
+	default 0xff
+	depends on MTD_NAND_NANDSIM
+	help
+	  The fourth byte returned by NAND Flash 'read ID' command.
+
+config MTD_NAND_NANDSIM_DEBUG
+	bool "NAND Simulator debugging"
+	depends on MTD_NAND_NANDSIM
+	help
+	  Only helful for tracking problems.
+
+config MTD_NAND_NANDSIM_LOG
+        bool "NAND Simulator logging"
+	depends on MTD_NAND_NANDSIM
+	help
+	   Logs the track of NAND read/write/erase commands.
+
+config MTD_NAND_NANDSIM_DELAY
+	bool "Emulate NAND chip delays using busy-wait cycles"
+	default n
+	depends on MTD_NAND_NANDSIM
+
+config MTD_NAND_NANDSIM_ACCESS_DELAY
+	int "Access delay (microiseconds)"
+	default 25
+	depends on MTD_NAND_NANDSIM_DELAY
+
+config MTD_NAND_NANDSIM_PROGRAM_DELAY
+	int "Program delay (microseconds)"
+	default 200
+	depends on MTD_NAND_NANDSIM_DELAY
+
+config MTD_NAND_NANDSIM_ERASE_DELAY
+	int "Erase delay (milliseconds)"
+	default 2
+	depends on MTD_NAND_NANDSIM_DELAY
+
+config MTD_NAND_NANDSIM_OUTPUT_CYCLE
+	int "Word output (from flash) time (nanoseconds)"
+	default 40
+	depends on MTD_NAND_NANDSIM_DELAY
+	help
+	  For 16 bit devices word is 2 bytes, for 8 bit devices - 1 byte
+
+config MTD_NAND_NANDSIM_INPUT_CYCLE
+	int "Word input (to flash) time (nanoseconds)"
+	default 50
+	depends on MTD_NAND_NANDSIM_DELAY
+	help
+	  For 16 bit devices word is 2 bytes, for 8 bit devices - 1 byte
+	
 endmenu
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -187,6 +187,24 @@ config USB_OTG
 
 	   Select this only if your OMAP board has a Mini-AB connector.
 
+config USB_GADGET_S3C2410
+	boolean "S3C2410"
+	depends on ARCH_S3C2410
+	help
+	  Samsung's S3C2410 is an ARM-4 processor with an integrated
+	  full speed USB 1.1 device controller.
+	  It has 4 configurable endpoints, as well as endpoint
+	  zero (for control transfers).
+
+config USB_S3C2410
+	tristate
+	depends on USB_GADGET_S3C2410
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
+config USB_S3C2410_DEBUG
+	boolean "S3C2410 udc debug messages"
+	depends on USB_GADGET_S3C2410
 
 config USB_GADGET_DUMMY_HCD
 	boolean "Dummy HCD (DEVELOPMENT)"
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_USB_PXA2XX)	+= pxa2xx_udc.o
 obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
 obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
 obj-$(CONFIG_USB_LH7A40X)	+= lh7a40x_udc.o
-
+obj-$(CONFIG_USB_S3C2410)	+= s3c2410_udc.o
 #
 # USB gadget drivers
 #
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -3747,7 +3747,7 @@ static int __init check_parameters(struc
 			mod_data.release = 0x0310;
 		else if (gadget_is_pxa27x(fsg->gadget))
 			mod_data.release = 0x0311;
-		else if (gadget_is_s3c2410(gadget))
+		else if (gadget_is_s3c2410(fsg->gadget))
 			mod_data.release = 0x0312;
 		else if (gadget_is_at91(fsg->gadget))
 			mod_data.release = 0x0313;
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
new file mode 100644
--- /dev/null
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -0,0 +1,1682 @@
+/*
+ * linux/drivers/usb/gadget/s3c2410_udc.c
+ * Samsung on-chip full speed USB device controllers
+ *
+ * Copyright (C) 2004 Herbert PÃ¶tzl - Arnaud Patard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/version.h>
+
+#include <linux/usb.h>
+#include <linux/usb_gadget.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/arch/irqs.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-udc.h>
+#include <asm/arch/udc.h>
+#include <asm/hardware/clock.h>
+
+#include "s3c2410_udc.h"
+
+#define ENABLE_SYSFS
+
+#define DRIVER_DESC     "S3C2410 USB Device Controller Gadget"
+#define DRIVER_VERSION  "28 Aug 2005"
+#define DRIVER_AUTHOR	"Herbert PÃ¶tzl <herbert@13thfloor.at>, Arnaud Patard <arnaud.patard@rtp-net.org>"
+
+static const char       gadget_name [] = "s3c2410_udc";
+static const char	driver_desc [] = DRIVER_DESC;
+
+static struct s3c2410_udc	*the_controller;
+static struct clk      		*udc_clock;
+static void __iomem 		*base_addr;
+static u64			rsrc_start;
+static u64			rsrc_len;
+
+static inline u32 udc_readl(u32 reg)
+{
+	return readl(base_addr+reg);
+}
+static inline void udc_writel(u32 value, u32 reg)
+{
+	writel(value,base_addr+reg);
+}
+
+static struct s3c2410_udc_mach_info *udc_info;
+
+/*************************** DEBUG FUNCTION ***************************/
+#define DEBUG_NORMAL	1
+#define DEBUG_VERBOSE	2
+
+#ifdef CONFIG_USB_S3C2410_DEBUG
+#define USB_S3C2410_DEBUG_LEVEL 1
+uint32_t s3c2410_ticks=0;
+static int dprintk(int level, const char *fmt, ...)
+{
+	static char printk_buf[1024];
+	static long prevticks;
+	static int invocation;
+	va_list args;
+	int len;
+
+	if (level > USB_S3C2410_DEBUG_LEVEL)
+		return 0;
+
+	if (s3c2410_ticks != prevticks) {
+		prevticks = s3c2410_ticks;
+		invocation = 0;
+	}
+
+	len = scnprintf(printk_buf, \
+			sizeof(printk_buf), "%1lu.%02d USB: ", \
+			prevticks, invocation++);
+
+	va_start(args, fmt);
+	len = vscnprintf(printk_buf+len, \
+			sizeof(printk_buf)-len, fmt, args);
+	va_end(args);
+
+	return printk("%s", printk_buf);
+}
+#else
+static int dprintk(int level, const char *fmt, ...)  { return 0; }
+#endif
+#ifdef ENABLE_SYSFS
+static ssize_t s3c2410udc_regs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg;
+	u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
+	u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2;
+	u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2;
+
+	addr_reg       = udc_readl(S3C2410_UDC_FUNC_ADDR_REG);
+	pwr_reg        = udc_readl(S3C2410_UDC_PWR_REG);
+	ep_int_reg     = udc_readl(S3C2410_UDC_EP_INT_REG);
+	usb_int_reg    = udc_readl(S3C2410_UDC_USB_INT_REG);
+	ep_int_en_reg  = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
+	usb_int_en_reg = udc_readl(S3C2410_UDC_USB_INT_EN_REG);
+	udc_writel(0, S3C2410_UDC_INDEX_REG);
+	ep0_csr        = udc_readl(S3C2410_UDC_IN_CSR1_REG);
+	udc_writel(1, S3C2410_UDC_INDEX_REG);
+	ep1_i_csr1     = udc_readl(S3C2410_UDC_IN_CSR1_REG);
+	ep1_i_csr2     = udc_readl(S3C2410_UDC_IN_CSR2_REG);
+	ep1_o_csr1     = udc_readl(S3C2410_UDC_IN_CSR1_REG);
+	ep1_o_csr2     = udc_readl(S3C2410_UDC_IN_CSR2_REG);
+	udc_writel(2, S3C2410_UDC_INDEX_REG);
+	ep2_i_csr1     = udc_readl(S3C2410_UDC_IN_CSR1_REG);
+	ep2_i_csr2     = udc_readl(S3C2410_UDC_IN_CSR2_REG);
+	ep2_o_csr1     = udc_readl(S3C2410_UDC_IN_CSR1_REG);
+	ep2_o_csr2     = udc_readl(S3C2410_UDC_IN_CSR2_REG);
+
+
+	return snprintf(buf, PAGE_SIZE,        \
+		 "FUNC_ADDR_REG  : 0x%04X\n"   \
+		 "PWR_REG        : 0x%04X\n"   \
+		 "EP_INT_REG     : 0x%04X\n"   \
+		 "USB_INT_REG    : 0x%04X\n"   \
+		 "EP_INT_EN_REG  : 0x%04X\n"   \
+		 "USB_INT_EN_REG : 0x%04X\n"   \
+		 "EP0_CSR        : 0x%04X\n"   \
+		 "EP1_I_CSR1     : 0x%04X\n"   \
+		 "EP1_I_CSR2     : 0x%04X\n"   \
+		 "EP1_O_CSR1     : 0x%04X\n"   \
+		 "EP1_O_CSR2     : 0x%04X\n"   \
+		 "EP2_I_CSR1     : 0x%04X\n"   \
+		 "EP2_I_CSR2     : 0x%04X\n"   \
+		 "EP2_O_CSR1     : 0x%04X\n"   \
+		 "EP2_O_CSR2     : 0x%04X\n",  \
+		 addr_reg,pwr_reg,ep_int_reg,usb_int_reg,     \
+		 ep_int_en_reg, usb_int_en_reg, ep0_csr,      \
+		 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2, \
+		 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2  \
+		 );
+}
+
+static DEVICE_ATTR(regs, 0444,
+		   s3c2410udc_regs_show,
+		   NULL);
+#endif
+/*------------------------- I/O ----------------------------------*/
+static void nuke (struct s3c2410_udc *udc, struct s3c2410_ep *ep)
+{
+	/* Sanity check */
+	if (&ep->queue != NULL)
+		while (!list_empty (&ep->queue)) {
+			struct s3c2410_request  *req;
+			req = list_entry (ep->queue.next, struct s3c2410_request, queue);
+			list_del_init (&req->queue);
+			req->req.status = -ESHUTDOWN;
+			req->req.complete (&ep->ep, &req->req);
+		}
+}
+
+/*
+ * 	done
+ */
+static void done(struct s3c2410_ep *ep, struct s3c2410_request *req, int status)
+{
+	list_del_init(&req->queue);
+
+	if (likely (req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	req->req.complete(&ep->ep, &req->req);
+}
+
+static inline void clear_ep_state (struct s3c2410_udc *dev)
+{
+	        unsigned i;
+
+		/* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
+		 * fifos, and pending transactions mustn't be continued in any case.
+		 */
+		 for (i = 1; i < S3C2410_ENDPOINTS; i++)
+			 nuke(dev, &dev->ep[i]);
+}
+
+static inline int fifo_count_out(void)
+{
+	int tmp;
+
+	tmp = udc_readl(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8;
+	tmp |= udc_readl(S3C2410_UDC_OUT_FIFO_CNT1_REG);
+
+	return tmp & 0xffff;
+}
+
+/*
+ * 	write_packet
+ */
+static inline int
+write_packet(int fifo, struct s3c2410_request *req, unsigned max)
+{
+	unsigned	len;
+	u8		*buf;
+
+	buf = req->req.buf + req->req.actual;
+	len = min(req->req.length - req->req.actual, max);
+	dprintk(DEBUG_VERBOSE, "write_packet %d %d %d ",req->req.actual,req->req.length,len);
+	req->req.actual += len;
+	dprintk(DEBUG_VERBOSE, "%d\n",req->req.actual);
+
+	max = len;
+	while (max--)
+		 udc_writel(*buf++,fifo);
+	return len;
+}
+
+/*
+ * 	write_fifo
+ */
+// return:  0 = still running, 1 = completed, negative = errno
+static int write_fifo(struct s3c2410_ep *ep, struct s3c2410_request *req)
+{
+	u8		*buf;
+	unsigned	count;
+	int		is_last;
+	u32		idx;
+	int		fifo_reg;
+	u32		ep_csr;
+
+
+	switch(ep->bEndpointAddress&0x7F)
+	{
+		default:
+		case 0: idx = 0;
+			fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
+			break;
+		case 1:
+			idx = 1;
+			fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
+			break;
+		case 2:
+			idx = 2;
+			fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
+			break;
+
+		case 3:
+			idx = 3;
+			fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
+			break;
+
+		case 4:
+			idx = 4;
+			fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
+			break;
+	}
+
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+
+	count = ep->ep.maxpacket;
+	count = write_packet(fifo_reg, req, count);
+
+	/* last packet is often short (sometimes a zlp) */
+	if (count != ep->ep.maxpacket)
+		is_last = 1;
+	else if (req->req.length == req->req.actual
+			&& !req->req.zero)
+		is_last = 2;
+	else
+		is_last = 0;
+
+	/* Only ep0 debug messages are interesting */
+	if (!idx)
+		dprintk(DEBUG_NORMAL, "Written ep%d %d.%d of %d b [last %d,z %d]\n",idx,count,req->req.actual,req->req.length,is_last,req->req.zero);
+
+	if (is_last)
+	{
+		/* The order is important. It prevents to send 2 packet at the same time
+		 **/
+		if (!idx)
+		{
+			/* If we got a reset signal, no need to say 'data sent' */
+			if (! (udc_readl(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET))
+				set_ep0_de_in(base_addr);
+			ep->dev->ep0state=EP0_IDLE;
+		}
+		else
+		{
+			 udc_writel(idx, S3C2410_UDC_INDEX_REG);
+			 ep_csr=udc_readl(S3C2410_UDC_IN_CSR1_REG);
+			 udc_writel(idx, S3C2410_UDC_INDEX_REG);
+			 udc_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG);
+		}
+		done(ep, req, 0);
+		if (!list_empty(&ep->queue))
+		{
+			is_last=0;
+			req = container_of(ep->queue.next,
+				struct s3c2410_request, queue);
+		}
+		else
+			is_last=1;
+	}
+	else
+	{
+		if (!idx)
+		{
+			/* If we got a reset signal, no need to say 'data sent' */
+			if (! (udc_readl(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET))
+				set_ep0_ipr(base_addr);
+		}
+		else
+		{
+			udc_writel(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr=udc_readl(S3C2410_UDC_IN_CSR1_REG);
+			udc_writel(idx, S3C2410_UDC_INDEX_REG);
+			udc_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG);
+		}
+	}
+
+
+	return is_last;
+}
+
+static inline int
+read_packet(int fifo, u8 *buf, struct s3c2410_request *req, unsigned avail)
+{
+	unsigned	len;
+
+	len = min(req->req.length - req->req.actual, avail);
+	req->req.actual += len;
+	avail = len;
+
+	while (avail--)
+		*buf++ = (unsigned char)udc_readl(fifo);
+	return len;
+}
+
+// return:  0 = still running, 1 = queue empty, negative = errno
+static int read_fifo(struct s3c2410_ep *ep, struct s3c2410_request *req)
+{
+	u8		*buf;
+	u32		ep_csr;
+	unsigned	bufferspace;
+	int 		is_last=1;
+	unsigned 	avail;
+	int 		fifo_count = 0;
+	u32		idx;
+	int		fifo_reg;
+
+	
+	switch(ep->bEndpointAddress&0x7F)
+	{
+		default:
+		case 0: idx = 0;
+			fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
+			break;
+		case 1:
+			idx = 1;
+			fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
+			break;
+		case 2:
+			idx = 2;
+			fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
+			break;
+
+		case 3:
+			idx = 3;
+			fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
+			break;
+
+		case 4:
+			idx = 4;
+			fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
+			break;
+
+	}
+
+
+	buf = req->req.buf + req->req.actual;
+	bufferspace = req->req.length - req->req.actual;
+	if (!bufferspace)
+	{
+		dprintk(DEBUG_NORMAL, "read_fifo: Buffer full !!\n");
+		return -1;
+	}
+
+	udc_writel(idx, S3C2410_UDC_INDEX_REG);
+
+        fifo_count = fifo_count_out();
+	dprintk(DEBUG_VERBOSE, "fifo_read fifo count : %d\n",fifo_count);
+
+	if (fifo_count > ep->ep.maxpacket)
+		avail = ep->ep.maxpacket;
+	else
+		avail = fifo_count;
+
+	fifo_count=read_packet(fifo_reg,buf,req,avail);
+
+	if (fifo_count < ep->ep.maxpacket) {
+		is_last = 1;
+		/* overflowed this request?  flush extra data */
+		if (fifo_count != avail) {
+			req->req.status = -EOVERFLOW;
+		}
+	} else {
+		if (req->req.length == req->req.actual)
+			is_last = 1;
+		else
+			is_last = 0;
+	}
+
+	udc_writel(idx, S3C2410_UDC_INDEX_REG);
+	fifo_count = fifo_count_out();
+
+	/* Only ep0 debug messages are interesting */
+	if (!idx)
+		dprintk(DEBUG_VERBOSE, "fifo_read fifo count : %d [last %d]\n",fifo_count,is_last);
+
+
+	if (is_last) {
+		if (!idx)
+		{
+			set_ep0_de_out(base_addr);
+			ep->dev->ep0state=EP0_IDLE;
+		}
+		else
+		{
+			udc_writel(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr=udc_readl(S3C2410_UDC_OUT_CSR1_REG);
+			udc_writel(idx, S3C2410_UDC_INDEX_REG);
+			udc_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG);
+		}
+		done(ep, req, 0);
+		if (!list_empty(&ep->queue))
+		{
+			is_last=0;
+			req = container_of(ep->queue.next,
+				struct s3c2410_request, queue);
+		}
+		else
+			is_last=1;
+
+	}
+	else
+	{
+		if (!idx)
+		{
+			clear_ep0_opr(base_addr);
+		}
+		else
+		{
+			udc_writel(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr=udc_readl(S3C2410_UDC_OUT_CSR1_REG);
+			udc_writel(idx, S3C2410_UDC_INDEX_REG);
+			udc_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG);
+		}
+	}
+
+
+	return is_last;
+}
+
+
+static int
+read_fifo_crq(struct usb_ctrlrequest *crq)
+{
+	int bytes_read = 0;
+	int fifo_count = 0;
+	int i;
+
+
+	unsigned char *pOut = (unsigned char*)crq;
+
+	udc_writel(0, S3C2410_UDC_INDEX_REG);
+
+	fifo_count = fifo_count_out();
+
+	BUG_ON( fifo_count > 8 );
+
+	dprintk(DEBUG_VERBOSE, "read_fifo_crq(): fifo_count=%d\n", fifo_count );
+	while( fifo_count-- ) {
+		i = 0;
+
+		do {
+			*pOut = (unsigned char)udc_readl(S3C2410_UDC_EP0_FIFO_REG);
+			i++;
+		} while((fifo_count_out() != fifo_count) && (i < 10));
+
+		if ( i == 10 ) {
+			dprintk(DEBUG_NORMAL, "read_fifo(): read failure\n");
+		}
+
+		pOut++;
+		bytes_read++;
+	}
+
+	dprintk(DEBUG_VERBOSE, "read_fifo_crq: len=%d %02x:%02x {%x,%x,%x}\n",
+			bytes_read, crq->bRequest, crq->bRequestType,
+			crq->wValue, crq->wIndex, crq->wLength);
+
+	return bytes_read;
+}
+
+/*------------------------- usb state machine -------------------------------*/
+static void handle_ep0(struct s3c2410_udc *dev)
+{
+	u32			ep0csr;
+	struct s3c2410_ep	*ep = &dev->ep [0];
+	struct s3c2410_request	*req;
+	struct usb_ctrlrequest	crq;
+
+	if (list_empty(&ep->queue))
+    		req = 0;
+	else
+		req = list_entry(ep->queue.next, struct s3c2410_request, queue);
+
+
+	udc_writel(0, S3C2410_UDC_INDEX_REG);
+	ep0csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
+	dprintk(DEBUG_NORMAL,"ep0csr %x\n",ep0csr);
+
+	/* clear stall status */
+	if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
+		/* FIXME */
+		nuke(dev, ep);
+	    	dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
+	    	clear_ep0_sst(base_addr);
+		dev->ep0state = EP0_IDLE;
+		return;
+	}
+
+	/* clear setup end */
+	if (ep0csr & S3C2410_UDC_EP0_CSR_SE
+	    	/* && dev->ep0state != EP0_IDLE */) {
+	    	dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
+		nuke(dev, ep);
+	    	clear_ep0_se(base_addr);
+		dev->ep0state = EP0_IDLE;
+	}
+
+
+	switch (dev->ep0state) {
+	case EP0_IDLE:
+		/* start control request? */
+		if (ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) {
+			int len, ret, tmp;
+
+			nuke (dev, ep);
+
+			len = read_fifo_crq(&crq);
+			if (len != sizeof(crq)) {
+			  	dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR"
+				    	" wanted %d bytes got %d. Stalling out...\n",
+					sizeof(crq), len);
+ 				set_ep0_ss(base_addr);
+				return;
+			}
+
+			dprintk(DEBUG_NORMAL, "bRequest = %d wLength = %d\n", crq.bRequest, crq.wLength);
+
+			/* cope with automagic for some standard requests. */
+			dev->req_std = (crq.bRequestType & USB_TYPE_MASK)
+						== USB_TYPE_STANDARD;
+			dev->req_config = 0;
+			dev->req_pending = 1;
+			switch (crq.bRequest) {
+				/* hardware restricts gadget drivers here! */
+				case USB_REQ_SET_CONFIGURATION:
+				    	dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n");
+					if (crq.bRequestType == USB_RECIP_DEVICE) {
+config_change:
+						dev->req_config = 1;
+						clear_ep_state(dev);
+						set_ep0_de_out(base_addr);
+					}
+					break;
+				/* ... and here, even more ... */
+				case USB_REQ_SET_INTERFACE:
+				    	dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n");
+					if (crq.bRequestType == USB_RECIP_INTERFACE) {
+						goto config_change;
+					}
+					break;
+
+				/* hardware was supposed to hide this */
+				case USB_REQ_SET_ADDRESS:
+				    	dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n");
+					if (crq.bRequestType == USB_RECIP_DEVICE) {
+						tmp = crq.wValue & 0x7F;
+						dev->address = tmp;
+ 						udc_writel((tmp | 0x80), S3C2410_UDC_FUNC_ADDR_REG);
+						set_ep0_de_out(base_addr);
+						return;
+					}
+					break;
+				default:
+					clear_ep0_opr(base_addr);
+					break;
+			}
+
+			if (crq.bRequestType & USB_DIR_IN)
+				dev->ep0state = EP0_IN_DATA_PHASE;
+			else
+				dev->ep0state = EP0_OUT_DATA_PHASE;
+			ret = dev->driver->setup(&dev->gadget, &crq);
+			if (ret < 0) {
+				if (dev->req_config) {
+					dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n",
+						crq.bRequest, ret);
+					return;
+				}
+				if (ret == -EOPNOTSUPP)
+					dprintk(DEBUG_NORMAL, "Operation not supported\n");
+				else
+					dprintk(DEBUG_NORMAL, "dev->driver->setup failed. (%d)\n",ret);
+
+				set_ep0_ss(base_addr);
+				set_ep0_de_out(base_addr);
+				dev->ep0state = EP0_IDLE;
+			/* deferred i/o == no response yet */
+			} else if (dev->req_pending) {
+			    	dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n");
+				dev->req_pending=0;
+			}
+			dprintk(DEBUG_VERBOSE, "ep0state %s\n",ep0states[dev->ep0state]);
+		}
+		break;
+	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */
+	    	dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
+		if (!(ep0csr & 2) && req)
+		{
+			write_fifo(ep, req);
+		}
+		break;
+	case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR etc */
+	    	dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
+		if ((ep0csr & 1) && req ) {
+			read_fifo(ep,req);
+		}
+		break;
+	case EP0_END_XFER:
+	    	dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
+		dev->ep0state=EP0_IDLE;
+		break;
+	case EP0_STALL:
+	    	dev->ep0state=EP0_IDLE;
+		break;
+	}
+}
+/*
+ * 	handle_ep - Manage I/O endpoints
+ */
+static void handle_ep(struct s3c2410_ep *ep)
+{
+	struct s3c2410_request	*req;
+	int			is_in = ep->bEndpointAddress & USB_DIR_IN;
+	u32			ep_csr1;
+	u32			idx;
+
+	if (likely (!list_empty(&ep->queue)))
+		req = list_entry(ep->queue.next,
+				struct s3c2410_request, queue);
+	else
+		req = 0;
+
+	idx = (u32)(ep->bEndpointAddress&0x7F);
+
+	if (is_in) {
+		udc_writel(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
+		dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n",idx,ep_csr1,req ? 1 : 0);
+
+		if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL)
+		{
+			dprintk(DEBUG_VERBOSE, "st\n");
+			udc_writel(idx, S3C2410_UDC_INDEX_REG);
+			udc_writel(0x00,S3C2410_UDC_IN_CSR1_REG);
+			return;
+		}
+
+		if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
+		{
+			write_fifo(ep,req);
+		}
+	}
+	else {
+		udc_writel(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr1 = udc_readl(S3C2410_UDC_OUT_CSR1_REG);
+		dprintk(DEBUG_VERBOSE, "ep%01d read csr:%02x\n",idx,ep_csr1);
+
+		if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL)
+		{
+			udc_writel(idx, S3C2410_UDC_INDEX_REG);
+			udc_writel(0x00,S3C2410_UDC_OUT_CSR1_REG);
+			return;
+		}
+		if( (ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
+		{
+			read_fifo(ep,req);
+		}
+	}
+}
+
+#include <asm/arch/regs-irq.h>
+/*
+ *      s3c2410_udc_irq - interrupt handler
+ */
+static irqreturn_t
+s3c2410_udc_irq(int irq, void *_dev, struct pt_regs *r)
+{
+	struct s3c2410_udc      *dev = _dev;
+	int usb_status;
+	int usbd_status;
+	int pwr_reg;
+	int ep0csr;
+	int     i;
+	u32	idx;
+	unsigned long flags;
+
+
+	/* Driver connected ? */
+	if (!dev->driver) {
+		/* Clear interrupts */
+		udc_writel( \
+				udc_readl(S3C2410_UDC_USB_INT_REG), \
+				S3C2410_UDC_USB_INT_REG \
+			    );
+		udc_writel( \
+				udc_readl(S3C2410_UDC_EP_INT_REG), \
+				S3C2410_UDC_EP_INT_REG \
+			    );
+	}
+	spin_lock_irqsave(&dev->lock,flags);
+
+	/* Save index */
+	idx = udc_readl(S3C2410_UDC_INDEX_REG);
+
+	/* Read status registers */
+	usb_status = udc_readl(S3C2410_UDC_USB_INT_REG);
+	usbd_status = udc_readl(S3C2410_UDC_EP_INT_REG);
+	pwr_reg = udc_readl(S3C2410_UDC_PWR_REG);
+
+	S3C2410_UDC_SETIX(base_addr,EP0);
+	ep0csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
+
+	// dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", usb_status, usbd_status, pwr_reg,ep0csr);
+
+	/*
+	 * Now, handle interrupts. There's two types :
+	 * - Reset, Resume, Suspend coming -> usb_int_reg
+	 * - EP -> ep_int_reg
+	 */
+
+	/* RESET */
+	if (usb_status & S3C2410_UDC_USBINT_RESET )
+	{
+		dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",ep0csr,pwr_reg);
+		
+		udc_writel(0x00, S3C2410_UDC_INDEX_REG);
+		udc_writel((dev->ep[0].ep.maxpacket&0x7ff)>>3,S3C2410_UDC_MAXP_REG);
+
+		dev->gadget.speed = USB_SPEED_FULL;
+		dev->ep0state = EP0_IDLE;
+		nuke(dev,&dev->ep[0]);
+
+		/* clear interrupt */
+		udc_writel(S3C2410_UDC_USBINT_RESET,
+			S3C2410_UDC_USB_INT_REG);
+
+		udc_writel(idx,S3C2410_UDC_INDEX_REG);
+		spin_unlock_irqrestore(&dev->lock,flags);
+		return IRQ_HANDLED;
+	}
+
+	/* RESUME */
+	if (usb_status & S3C2410_UDC_USBINT_RESUME)
+	{
+		dprintk(DEBUG_NORMAL, "USB resume\n");
+
+		/* clear interrupt */
+		udc_writel(S3C2410_UDC_USBINT_RESUME,
+			S3C2410_UDC_USB_INT_REG);
+
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN
+			&& dev->driver
+			&& dev->driver->resume)
+			dev->driver->resume(&dev->gadget);
+	}
+
+	/* SUSPEND */
+	if (usb_status & S3C2410_UDC_USBINT_SUSPEND)
+	{
+		dprintk(DEBUG_NORMAL, "USB suspend\n");
+
+		/* clear interrupt */
+		udc_writel(S3C2410_UDC_USBINT_SUSPEND,
+			S3C2410_UDC_USB_INT_REG);
+
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN
+				&& dev->driver
+				&& dev->driver->suspend)
+			dev->driver->suspend(&dev->gadget);
+
+		dev->ep0state = EP0_IDLE;
+	}
+
+	/* EP */
+	/* control traffic */
+	/* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
+	 * generate an interrupt
+	 */
+	if (usbd_status & S3C2410_UDC_INT_EP0)
+	{
+		dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
+		/* Clear the interrupt bit by setting it to 1 */
+		udc_writel(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
+		handle_ep0(dev);
+	}
+	/* endpoint data transfers */
+	for (i = 1; i < S3C2410_ENDPOINTS; i++) {
+		u32 tmp = 1 << i;
+		if (usbd_status & tmp) {
+			dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);
+
+			/* Clear the interrupt bit by setting it to 1 */
+			udc_writel(tmp, S3C2410_UDC_EP_INT_REG);
+			handle_ep(&dev->ep[i]);
+		}
+	}
+
+
+	dprintk(DEBUG_VERBOSE,"irq: %d done.\n", irq);
+
+	/* Restore old index */
+	udc_writel(idx,S3C2410_UDC_INDEX_REG);
+
+	spin_unlock_irqrestore(&dev->lock,flags);
+
+	return IRQ_HANDLED;
+}
+/*------------------------- s3c2410_ep_ops ----------------------------------*/
+
+/*
+ * 	s3c2410_ep_enable
+ */
+static int
+s3c2410_ep_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c2410_udc	*dev;
+	struct s3c2410_ep	*ep;
+	u32			max, tmp;
+	unsigned long		flags;
+	u32			csr1,csr2;
+	u32			int_en_reg;
+
+
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	if (!_ep || !desc || ep->desc || _ep->name == ep0name
+			|| desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff;
+
+	spin_lock_irqsave (&dev->lock, flags);
+	_ep->maxpacket = max & 0x7ff;
+	ep->desc = desc;
+	ep->bEndpointAddress = desc->bEndpointAddress;
+
+	/* set max packet */
+	udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
+	udc_writel(max>>3,S3C2410_UDC_MAXP_REG);
+
+
+	/* set type, direction, address; reset fifo counters */
+	if (desc->bEndpointAddress & USB_DIR_IN)
+	{
+		csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
+		csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;
+
+		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
+		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_writel(csr2,S3C2410_UDC_IN_CSR2_REG);
+	}
+	else
+	{
+		/* don't flush he in fifo or there will be an interrupt for that
+		 * endpoint */
+		csr1 = S3C2410_UDC_ICSR1_CLRDT;
+		csr2 = S3C2410_UDC_ICSR2_DMAIEN;
+
+		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
+		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_writel(csr2,S3C2410_UDC_IN_CSR2_REG);
+
+		csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
+		csr2 = S3C2410_UDC_OCSR2_DMAIEN;
+
+		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_writel(csr1,S3C2410_UDC_OUT_CSR1_REG);
+		udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_writel(csr2,S3C2410_UDC_OUT_CSR2_REG);
+	}
+
+
+	/* enable irqs */
+	int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
+	udc_writel(int_en_reg | (1<<ep->num),S3C2410_UDC_EP_INT_EN_REG);
+
+
+	/* print some debug message */
+	tmp = desc->bEndpointAddress;
+	dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
+		_ep->name,ep->num, tmp, desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
+
+	spin_unlock_irqrestore (&dev->lock, flags);
+
+	return 0;
+}
+
+/*
+ * s3c2410_ep_disable
+ */
+static int s3c2410_ep_disable (struct usb_ep *_ep)
+{
+	struct s3c2410_ep	*ep = container_of(_ep, struct s3c2410_ep, ep);
+	unsigned long	flags;
+	u32			int_en_reg;
+
+
+	if (!_ep || !ep->desc) {
+		dprintk(DEBUG_NORMAL, "%s not enabled\n",
+			_ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+	ep->desc = 0;
+
+	nuke (ep->dev, ep);
+
+	/* disable irqs */
+	int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
+	udc_writel(int_en_reg & ~(1<<ep->num),S3C2410_UDC_EP_INT_EN_REG);
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);
+
+	return 0;
+}
+
+/*
+ * s3c2410_alloc_request
+ */
+static struct usb_request *
+s3c2410_alloc_request (struct usb_ep *_ep, unsigned int mem_flags)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_request	*req;
+
+    	dprintk(DEBUG_VERBOSE,"s3c2410_alloc_request(ep=%p,flags=%d)\n", _ep, mem_flags);
+
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	if (!_ep)
+		return 0;
+
+	req = kmalloc (sizeof *req, mem_flags);
+	if (!req)
+		return 0;
+	memset (req, 0, sizeof *req);
+	INIT_LIST_HEAD (&req->queue);
+	return &req->req;
+}
+
+/*
+ * s3c2410_free_request
+ */
+static void
+s3c2410_free_request (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_request	*req;
+
+    	dprintk(DEBUG_VERBOSE, "s3c2410_free_request(ep=%p,req=%p)\n", _ep, _req);
+
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
+		return;
+
+	req = container_of (_req, struct s3c2410_request, req);
+	WARN_ON (!list_empty (&req->queue));
+	kfree (req);
+}
+
+/*
+ * 	s3c2410_alloc_buffer
+ */
+static void *
+s3c2410_alloc_buffer (
+	struct usb_ep *_ep,
+	unsigned bytes,
+	dma_addr_t *dma,
+	unsigned int mem_flags)
+{
+	char *retval;
+
+    	dprintk(DEBUG_VERBOSE,"s3c2410_alloc_buffer()\n");
+
+	if (!the_controller->driver)
+		return 0;
+	retval = kmalloc (bytes, mem_flags);
+	*dma = (dma_addr_t) retval;
+	return retval;
+}
+
+/*
+ * s3c2410_free_buffer
+ */
+static void
+s3c2410_free_buffer (
+	struct usb_ep *_ep,
+	void *buf,
+	dma_addr_t dma,
+	unsigned bytes)
+{
+    	dprintk(DEBUG_VERBOSE, "s3c2410_free_buffer()\n");
+
+	if (bytes)
+		kfree (buf);
+}
+
+/*
+ * 	s3c2410_queue
+ */
+static int
+s3c2410_queue(struct usb_ep *_ep, struct usb_request *_req, unsigned int gfp_flags)
+{
+	struct s3c2410_request	*req;
+	struct s3c2410_ep	*ep;
+	struct s3c2410_udc	*dev;
+	u32			ep_csr=0;
+	int 			fifo_count=0;
+	unsigned long		flags;
+
+
+	ep = container_of(_ep, struct s3c2410_ep, ep);
+	if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+		dprintk(DEBUG_NORMAL, "s3c2410_queue: inval 2\n");
+		return -EINVAL;
+	}
+
+	dev = ep->dev;
+	if (unlikely (!dev->driver
+			|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+		return -ESHUTDOWN;
+	}
+
+	spin_lock_irqsave (&dev->lock, flags);
+	
+	req = container_of(_req, struct s3c2410_request, req);
+	if (unlikely (!_req || !_req->complete || !_req->buf
+	|| !list_empty(&req->queue))) {
+		if (!_req)
+			dprintk(DEBUG_NORMAL, "s3c2410_queue: 1 X X X\n");
+		else
+		{
+			dprintk(DEBUG_NORMAL, "s3c2410_queue: 0 %01d %01d %01d\n",!_req->complete,!_req->buf, !list_empty(&req->queue));
+		}
+		spin_unlock_irqrestore(&dev->lock,flags);
+		return -EINVAL;
+	}
+#if 0
+	/* iso is always one packet per request, that's the only way
+	 * we can report per-packet status.  that also helps with dma.
+	 */
+	if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
+			&& req->req.length > le16_to_cpu
+						(ep->desc->wMaxPacketSize)))
+		return -EMSGSIZE;
+#endif
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	if (ep->bEndpointAddress)
+	{
+		udc_writel(ep->bEndpointAddress&0x7F,S3C2410_UDC_INDEX_REG);
+		ep_csr = udc_readl(ep->bEndpointAddress&USB_DIR_IN ? S3C2410_UDC_IN_CSR1_REG : S3C2410_UDC_OUT_CSR1_REG);
+		fifo_count=fifo_count_out();
+	}
+	/* kickstart this i/o queue? */
+	if (list_empty(&ep->queue)) {
+		if (ep->bEndpointAddress == 0 /* ep0 */) {
+
+			switch (dev->ep0state) {
+			case EP0_IN_DATA_PHASE:
+				if (write_fifo(ep, req))
+				{
+					req = 0;
+				}
+				break;
+
+			case EP0_OUT_DATA_PHASE:
+				/* nothing to do here */
+				dev->ep0state = EP0_IDLE;
+				break;
+
+			default:
+				spin_unlock_irqrestore(&dev->lock,flags);
+				return -EL2HLT;
+			}
+		}
+		else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
+				&& (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) && write_fifo(ep, req)) {
+			req = 0;
+		} else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) && fifo_count && read_fifo(ep, req)) {
+			req = 0;
+		}
+
+	}
+
+	/* pio or dma irq handler advances the queue. */
+	if (likely (req != 0))
+		list_add_tail(&req->queue, &ep->queue);
+
+	spin_unlock_irqrestore(&dev->lock,flags);
+
+	dprintk(DEBUG_VERBOSE, "s3c2410_queue normal end\n");
+	return 0;
+}
+
+/*
+ * 	s3c2410_dequeue
+ */
+static int s3c2410_dequeue (struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_udc	*udc;
+	int			retval = -EINVAL;
+	unsigned long		flags;
+	struct s3c2410_request	*req = 0;
+
+    	dprintk(DEBUG_VERBOSE,"s3c2410_dequeue(ep=%p,req=%p)\n", _ep, _req);
+
+	if (!the_controller->driver)
+		return -ESHUTDOWN;
+
+	if (!_ep || !_req)
+		return retval;
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	udc = container_of (ep->gadget, struct s3c2410_udc, gadget);
+
+	spin_lock_irqsave (&udc->lock, flags);
+	list_for_each_entry (req, &ep->queue, queue) {
+		if (&req->req == _req) {
+			list_del_init (&req->queue);
+			_req->status = -ECONNRESET;
+			retval = 0;
+			break;
+		}
+	}
+	spin_unlock_irqrestore (&udc->lock, flags);
+
+	if (retval == 0) {
+		dprintk(DEBUG_VERBOSE, "dequeued req %p from %s, len %d buf %p\n",
+				req, _ep->name, _req->length, _req->buf);
+
+		_req->complete (_ep, _req);
+		done(ep, req, -ECONNRESET);
+	}
+	return retval;
+
+	return 0;
+}
+
+
+/*
+ * s3c2410_set_halt
+ */
+static int
+s3c2410_set_halt (struct usb_ep *_ep, int value)
+{
+	return 0;
+}
+
+
+static const struct usb_ep_ops s3c2410_ep_ops = {
+	.enable         = s3c2410_ep_enable,
+	.disable        = s3c2410_ep_disable,
+
+	.alloc_request  = s3c2410_alloc_request,
+	.free_request   = s3c2410_free_request,
+
+	.alloc_buffer   = s3c2410_alloc_buffer,
+	.free_buffer    = s3c2410_free_buffer,
+
+	.queue          = s3c2410_queue,
+	.dequeue        = s3c2410_dequeue,
+
+	.set_halt       = s3c2410_set_halt,
+};
+
+/*------------------------- usb_gadget_ops ----------------------------------*/
+
+/*
+ * 	s3c2410_g_get_frame
+ */
+static int s3c2410_g_get_frame (struct usb_gadget *_gadget)
+{
+	int tmp;
+
+	dprintk(DEBUG_VERBOSE,"s3c2410_g_get_frame()\n");
+
+	tmp = udc_readl(S3C2410_UDC_FRAME_NUM2_REG) << 8;
+	tmp |= udc_readl(S3C2410_UDC_FRAME_NUM1_REG);
+
+	return tmp & 0xffff;
+}
+
+/*
+ * 	s3c2410_wakeup
+ */
+static int s3c2410_wakeup (struct usb_gadget *_gadget)
+{
+
+	dprintk(DEBUG_NORMAL,"s3c2410_wakeup()\n");
+
+	return 0;
+}
+
+/*
+ * 	s3c2410_set_selfpowered
+ */
+static int s3c2410_set_selfpowered (struct usb_gadget *_gadget, int value)
+{
+	struct s3c2410_udc  *udc;
+
+	dprintk(DEBUG_NORMAL, "s3c2410_set_selfpowered()\n");
+
+	udc = container_of (_gadget, struct s3c2410_udc, gadget);
+
+	if (value)
+		udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
+	else
+		udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+
+	return 0;
+}
+
+
+
+static const struct usb_gadget_ops s3c2410_ops = {
+	.get_frame          = s3c2410_g_get_frame,
+	.wakeup             = s3c2410_wakeup,
+	.set_selfpowered    = s3c2410_set_selfpowered,
+};
+
+
+/*------------------------- gadget driver handling---------------------------*/
+/*
+ * udc_disable
+ */
+static void udc_disable(struct s3c2410_udc *dev)
+{
+	dprintk(DEBUG_NORMAL, "udc_disable called\n");
+
+	/* Disable all interrupts */
+	udc_writel(0x00, S3C2410_UDC_USB_INT_EN_REG);
+	udc_writel(0x00, S3C2410_UDC_EP_INT_EN_REG);
+
+	/* Clear the interrupt registers */
+	udc_writel(   S3C2410_UDC_USBINT_RESET  | \
+			S3C2410_UDC_USBINT_RESUME | \
+			S3C2410_UDC_USBINT_SUSPEND, \
+			S3C2410_UDC_USB_INT_REG);
+	udc_writel(   0x1F, S3C2410_UDC_EP_INT_REG);
+	
+
+	/* Good bye, cruel world */
+	if (udc_info && udc_info->udc_command)
+		udc_info->udc_command(S3C2410_UDC_P_DISABLE);
+
+	/* Set address to 0 */
+	/*udc_writel( 0x80, S3C2410_UDC_FUNC_ADDR_REG);*/
+
+	/* Set speed to unknown */
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+}
+/*
+ * udc_reinit
+ */
+static void udc_reinit(struct s3c2410_udc *dev)
+{
+	u32     i;
+
+	/* device/ep0 records init */
+	INIT_LIST_HEAD (&dev->gadget.ep_list);
+	INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+	dev->ep0state = EP0_IDLE;
+
+
+	for (i = 0; i < S3C2410_ENDPOINTS; i++) {
+		struct s3c2410_ep *ep = &dev->ep[i];
+
+		if (i != 0)
+			list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
+
+		ep->dev = dev;
+		ep->desc = 0;
+		INIT_LIST_HEAD (&ep->queue);
+	}
+}
+
+/*
+ * udc_enable
+ */
+static void udc_enable(struct s3c2410_udc *dev)
+{
+	int i;
+
+	dprintk(DEBUG_NORMAL, "udc_enable called\n");
+
+	/* dev->gadget.speed = USB_SPEED_UNKNOWN; */
+	dev->gadget.speed = USB_SPEED_FULL;
+
+	/* Set MAXP for all endpoints */
+	for (i = 0; i < S3C2410_ENDPOINTS; i++) {
+
+		udc_writel(i, S3C2410_UDC_INDEX_REG);
+		udc_writel((dev->ep[i].ep.maxpacket&0x7ff)>>3,S3C2410_UDC_MAXP_REG);
+	}
+
+	/* Set default power state */
+	udc_writel(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG);
+
+	/* Enable reset and suspend interrupt interrupts */
+	udc_writel(1<<2 | 1<<0 ,S3C2410_UDC_USB_INT_EN_REG);
+
+	/* Enable ep0 interrupt */
+	udc_writel(0x01,S3C2410_UDC_EP_INT_EN_REG);
+
+	/* time to say "hello, world" */
+	if (udc_info && udc_info->udc_command)
+		udc_info->udc_command(S3C2410_UDC_P_ENABLE);
+}
+
+
+/*
+ * 	nop_release
+ */
+static void nop_release (struct device *dev)
+{
+	        dprintk(DEBUG_NORMAL, "%s %s\n", __FUNCTION__, dev->bus_id);
+}
+/*
+ *	usb_gadget_register_driver
+ */
+int
+usb_gadget_register_driver (struct usb_gadget_driver *driver)
+{
+	struct s3c2410_udc *udc = the_controller;
+	int		retval;
+
+    	dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n",
+		driver->driver.name);
+
+	/* Sanity checks */
+	if (!udc)
+		return -ENODEV;
+	if (udc->driver)
+		return -EBUSY;
+	if (!driver->bind || !driver->unbind || !driver->setup
+			|| driver->speed == USB_SPEED_UNKNOWN)
+		return -EINVAL;
+
+	/* Hook the driver */
+	udc->driver = driver;
+	udc->gadget.dev.driver = &driver->driver;
+
+	/*Bind the driver */
+	device_add(&udc->gadget.dev);
+	dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", driver->driver.name);
+	if ((retval = driver->bind (&udc->gadget)) != 0) {
+		device_del(&udc->gadget.dev);
+		udc->driver = 0;
+		udc->gadget.dev.driver = 0;
+		return retval;
+	}
+
+        /* driver->driver.bus = 0; */
+
+	/* Enable udc */
+	udc_enable(udc);
+
+	return 0;
+}
+
+
+/*
+ * 	usb_gadget_unregister_driver
+ */
+int
+usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+{
+	struct s3c2410_udc *udc = the_controller;
+
+	if (!udc)
+		return -ENODEV;
+	if (!driver || driver != udc->driver)
+		return -EINVAL;
+
+    	dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n",
+		driver->driver.name);
+
+	driver->unbind (&udc->gadget);
+	device_del(&udc->gadget.dev);
+	udc->driver = 0;
+
+	device_release_driver (&udc->gadget.dev);
+	driver_unregister (&driver->driver);
+
+	/* Disable udc */
+	udc_disable(udc);
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+static struct s3c2410_udc memory = {
+	.gadget = {
+		.ops		= &s3c2410_ops,
+		.ep0		= &memory.ep[0].ep,
+		.name		= gadget_name,
+		.dev = {
+			.bus_id		= "gadget",
+			.release	= nop_release,
+		},
+	},
+
+	/* control endpoint */
+	.ep[0] = {
+		.num		= 0,
+		.ep = {
+			.name		= ep0name,
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP0_FIFO_SIZE,
+		},
+		.dev		= &memory,
+	},
+
+	/* first group of endpoints */
+	.ep[1] = {
+		.num		= 1,
+		.ep = {
+			.name		= "ep1-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 1,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	},
+	.ep[2] = {
+		.num		= 2,
+		.ep = {
+			.name		= "ep2-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 2,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	},
+	.ep[3] = {
+		.num		= 3,
+		.ep = {
+			.name		= "ep3-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 3,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	},
+	.ep[4] = {
+		.num		= 4,
+		.ep = {
+			.name		= "ep4-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 4,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	}
+
+};
+
+/*
+ *	probe - binds to the platform device
+ */
+static int s3c2410_udc_probe(struct device *_dev)
+{
+	struct s3c2410_udc *udc = &memory;
+	int retval;
+
+
+	dprintk(DEBUG_NORMAL,"s3c2410_udc_probe\n");
+
+	spin_lock_init (&udc->lock);
+	udc_info = _dev->platform_data;
+
+	rsrc_start = S3C2410_PA_USBDEV;
+	rsrc_len   = S3C24XX_SZ_USBDEV;
+
+	if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))
+		return -EBUSY;
+
+	base_addr = ioremap(rsrc_start, rsrc_len);
+	if (!base_addr) {
+		retval = -ENOMEM;
+		goto err_mem;
+	}
+		
+	
+	device_initialize(&udc->gadget.dev);
+	udc->gadget.dev.parent = _dev;
+	udc->gadget.dev.dma_mask = _dev->dma_mask;
+
+	the_controller = udc;
+	dev_set_drvdata(_dev, udc);
+
+	udc_disable(udc);
+	udc_reinit(udc);
+
+	/* irq setup after old hardware state is cleaned up */
+	retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
+		SA_INTERRUPT, gadget_name, udc);
+	if (retval != 0) {
+		printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+			gadget_name, IRQ_USBD, retval);
+		retval = -EBUSY;
+		goto err_map;
+	}
+	dprintk(DEBUG_VERBOSE, "%s: got irq %i\n", gadget_name, IRQ_USBD);
+
+#ifdef ENABLE_SYSFS
+	/* create device files */
+	device_create_file(_dev, &dev_attr_regs);
+#endif
+	return 0;
+
+err_map:
+	iounmap(base_addr);
+err_mem:
+	release_mem_region(rsrc_start, rsrc_len);
+
+	return retval;
+}
+
+/*
+ * 	s3c2410_udc_remove
+ */
+static int s3c2410_udc_remove(struct device *_dev)
+{
+	struct s3c2410_udc *udc = _dev->driver_data;
+
+	dprintk(DEBUG_NORMAL, "s3c2410_udc_remove\n");
+	usb_gadget_unregister_driver(udc->driver);
+
+	if (udc->got_irq) {
+		free_irq(IRQ_USBD, udc);
+		udc->got_irq = 0;
+	}
+
+	iounmap(base_addr);
+	release_mem_region(rsrc_start, rsrc_len);
+	
+	dev_set_drvdata(_dev, 0);
+	kfree(udc);
+
+	return 0;
+}
+
+static struct device_driver udc_driver = {
+	.name		= "s3c2410-usbgadget",
+	.bus            = &platform_bus_type,
+	.probe          = s3c2410_udc_probe,
+	.remove         = s3c2410_udc_remove,
+};
+
+static int __init udc_init(void)
+{
+	u32 tmp;
+	dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION);
+
+
+	udc_clock = clk_get(NULL, "usb-device");
+	if (!udc_clock) {
+		printk(KERN_INFO "failed to get udc clock source\n");
+		return -ENOENT;
+	}
+	clk_use(udc_clock);
+
+	clk_disable(udc_clock);
+
+	tmp = (
+		 0x78 << S3C2410_PLLCON_MDIVSHIFT)
+	      | (0x02 << S3C2410_PLLCON_PDIVSHIFT)
+	      | (0x03 << S3C2410_PLLCON_SDIVSHIFT);
+	writel(tmp, S3C2410_UPLLCON);
+
+
+	clk_enable(udc_clock);
+
+	mdelay(10);
+
+	dprintk(DEBUG_VERBOSE, "got and enabled clock\n");
+
+	return driver_register(&udc_driver);
+}
+
+static void __exit udc_exit(void)
+{
+	if (udc_clock) {
+		clk_disable(udc_clock);
+		clk_unuse(udc_clock);
+		clk_put(udc_clock);
+		udc_clock = NULL;
+	}
+
+	driver_unregister(&udc_driver);
+}
+
+
+EXPORT_SYMBOL (usb_gadget_unregister_driver);
+EXPORT_SYMBOL (usb_gadget_register_driver);
+
+module_init(udc_init);
+module_exit(udc_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h
new file mode 100644
--- /dev/null
+++ b/drivers/usb/gadget/s3c2410_udc.h
@@ -0,0 +1,177 @@
+#ifndef _S3C2410_UDC_H
+#define _S3C2410_UDC_H
+
+struct s3c2410_ep {
+	struct list_head		queue;
+	unsigned long			last_io;	/* jiffies timestamp */
+	struct usb_gadget		*gadget;
+	struct s3c2410_udc		*dev;
+	const struct usb_endpoint_descriptor *desc;
+	struct usb_ep			ep;
+	u8				num;
+
+	unsigned short			fifo_size;
+	u8				bEndpointAddress;
+	u8				bmAttributes;
+
+	unsigned			halted : 1;
+	unsigned			already_seen : 1;
+	unsigned			setup_stage : 1;
+};
+
+
+/* Warning : ep0 has a fifo of 16 bytes */
+/* Don't try to set 32 or 64            */
+#define EP0_FIFO_SIZE		16
+#define EP_FIFO_SIZE		64
+#define DEFAULT_POWER_STATE	0x00
+
+static const char ep0name [] = "ep0";
+
+static const char *const ep_name[] = {
+	ep0name,                                /* everyone has ep0 */
+	/* s3c2410 four bidirectional bulk endpoints */
+	"ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk",
+};
+
+#define S3C2410_ENDPOINTS       ARRAY_SIZE(ep_name)
+
+struct s3c2410_request {
+	struct list_head		queue;		/* ep's requests */
+	struct usb_request		req;
+};
+
+enum ep0_state {
+        EP0_IDLE,
+        EP0_IN_DATA_PHASE,
+        EP0_OUT_DATA_PHASE,
+        EP0_END_XFER,
+        EP0_STALL,
+};
+
+static const char *ep0states[]= {
+        "EP0_IDLE",
+        "EP0_IN_DATA_PHASE",
+        "EP0_OUT_DATA_PHASE",
+        "EP0_END_XFER",
+        "EP0_STALL",
+};
+
+struct s3c2410_udc {
+	spinlock_t			lock;
+
+	struct s3c2410_ep		ep[S3C2410_ENDPOINTS];
+	int				address;
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	struct s3c2410_request		fifo_req;
+	u8				fifo_buf[EP_FIFO_SIZE];
+	u16				devstatus;
+
+	u32				port_status;
+    	int 	    	    	    	ep0state;
+
+	unsigned			got_irq : 1;
+
+	unsigned			req_std : 1;
+	unsigned			req_config : 1;
+	unsigned			req_pending : 1;
+};
+
+/****************** MACROS ******************/
+/* #define BIT_MASK	BIT_MASK*/
+#define BIT_MASK	0xFF
+
+#define maskb(base,v,m,a)      \
+	        writeb((readb(base+a) & ~(m))|((v)&(m)), (base+a))
+
+#define maskw(base,v,m,a)      \
+	        writew((readw(base+a) & ~(m))|((v)&(m)), (base+a))
+
+#define maskl(base,v,m,a)      \
+	        writel((readl(base+a) & ~(m))|((v)&(m)), (base+a))
+
+#define clear_ep0_sst(base) do {			\
+    	S3C2410_UDC_SETIX(base,EP0); 			\
+	writel(0x00, base+S3C2410_UDC_EP0_CSR_REG); 	\
+} while(0)
+
+#define clear_ep0_se(base) do {				\
+    	S3C2410_UDC_SETIX(base,EP0); 			\
+	maskl(base,S3C2410_UDC_EP0_CSR_SSE,		\
+	    	BIT_MASK, S3C2410_UDC_EP0_CSR_REG); 	\
+} while(0)
+
+#define clear_ep0_opr(base) do {			\
+   	S3C2410_UDC_SETIX(base,EP0);			\
+	maskl(base,S3C2410_UDC_EP0_CSR_SOPKTRDY,	\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG); 	\
+} while(0)
+
+#define set_ep0_ipr(base) do {				\
+   	S3C2410_UDC_SETIX(base,EP0);			\
+	maskl(base,S3C2410_UDC_EP0_CSR_IPKRDY,		\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG); 	\
+} while(0)
+
+#define set_ep0_de(base) do {				\
+   	S3C2410_UDC_SETIX(base,EP0);			\
+	maskl(base,S3C2410_UDC_EP0_CSR_DE,		\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);	\
+} while(0)
+
+#define set_ep0_ss(base) do {				\
+   	S3C2410_UDC_SETIX(base,EP0);				\
+	maskl(base,S3C2410_UDC_EP0_CSR_SENDSTL|S3C2410_UDC_EP0_CSR_SOPKTRDY,	\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);	\
+} while(0)
+
+#define set_ep0_de_out(base) do {			\
+   	S3C2410_UDC_SETIX(base,EP0);			\
+	maskl(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY 	\
+		| S3C2410_UDC_EP0_CSR_DE),		\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);	\
+} while(0)
+
+#define set_ep0_sse_out(base) do {			\
+   	S3C2410_UDC_SETIX(base,EP0);			\
+	maskl(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY 	\
+		| S3C2410_UDC_EP0_CSR_SSE),		\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);	\
+} while(0)
+
+#define set_ep0_de_in(base) do {			\
+   	S3C2410_UDC_SETIX(base,EP0);			\
+	maskl(base,(S3C2410_UDC_EP0_CSR_IPKRDY		\
+		| S3C2410_UDC_EP0_CSR_DE),		\
+		BIT_MASK, S3C2410_UDC_EP0_CSR_REG);		\
+} while(0)
+
+
+
+#define clear_stall_ep1_out(base) do {			\
+   	S3C2410_UDC_SETIX(base,EP1);			\
+	orl(0,base+S3C2410_UDC_OUT_CSR1_REG);		\
+} while(0)
+
+
+#define clear_stall_ep2_out(base) do {			\
+   	S3C2410_UDC_SETIX(base,EP2);			\
+	orl(0, base+S3C2410_UDC_OUT_CSR1_REG);		\
+} while(0)
+
+
+#define clear_stall_ep3_out(base) do {			\
+   	S3C2410_UDC_SETIX(base,EP3);			\
+	orl(0,base+S3C2410_UDC_OUT_CSR1_REG);		\
+} while(0)
+
+
+#define clear_stall_ep4_out(base) do {			\
+   	S3C2410_UDC_SETIX(base,EP4);			\
+	orl(0, base+S3C2410_UDC_OUT_CSR1_REG);		\
+} while(0)
+
+#endif
+
+
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1484,6 +1484,30 @@ config FB_S1D13XXX
 	  working with S1D13806). Product specs at
 	  <http://www.erd.epson.com/vdc/html/legacy_13xxx.htm>
 
+config FB_S3C2410
+	tristate "S3C2410 LCD framebuffer support"
+	depends on FB && ARCH_S3C2410
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	select FB_SOFT_CURSOR
+	---help---
+	  Frame buffer driver for the built-in LCD controller in the Samsung
+	  S3C2410 processor.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted and removed from the running kernel whenever you want). The
+	  module will be called s3c2410fb. If you want to compile it as a module,
+	  say M here and read <file:Documentation/modules.txt>.
+
+	  If unsure, say N.
+config FB_S3C2410_DEBUG
+	bool "S3C2410 lcd debug messages"
+	depends on FB_S3C2410
+	help
+	  Turn on debugging messages. Note that you can set/unset at run time
+	  through sysfs
+
 config FB_VIRTUAL
 	tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
 	depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -92,6 +92,7 @@ obj-$(CONFIG_FB_MAXINE)		  += maxinefb.o
 obj-$(CONFIG_FB_TX3912)		  += tx3912fb.o
 obj-$(CONFIG_FB_S1D13XXX)	  += s1d13xxxfb.o
 obj-$(CONFIG_FB_IMX)              += imxfb.o
+obj-$(CONFIG_FB_S3C2410)	  += s3c2410fb.o
 
 # Platform or fallback drivers go here
 obj-$(CONFIG_FB_VESA)             += vesafb.o
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -50,3 +50,11 @@ config BACKLIGHT_CORGI
 	  If you have a Sharp Zaurus SL-C7xx, say y to enable the
 	  backlight driver.
 
+config BACKLIGHT_S3C2410
+	tristate "Samsung S3C2410 Backlight Driver"
+	depends on (BACKLIGHT_DEVICE || LCD_CLASS_DEVICE) && ARCH_S3C2410
+	default y
+	help
+	  If you have a backlight controler connected on a Samsung S3C2410,
+	  say y here to enable the driver.
+
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -3,3 +3,4 @@
 obj-$(CONFIG_LCD_CLASS_DEVICE)     += lcd.o
 obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
 obj-$(CONFIG_BACKLIGHT_CORGI)	+= corgi_bl.o
+obj-$(CONFIG_BACKLIGHT_S3C2410)	+= s3c2410_lcd.o
diff --git a/drivers/video/backlight/s3c2410_lcd.c b/drivers/video/backlight/s3c2410_lcd.c
new file mode 100644
--- /dev/null
+++ b/drivers/video/backlight/s3c2410_lcd.c
@@ -0,0 +1,276 @@
+/*
+ * linux/drivers/video/backlight/s3c2410_lcd.c
+ * Copyright (c) Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ *	    S3C2410 LCD Controller Backlight Driver
+ *
+ * ChangeLog
+ *
+ * 2005-04-07: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *   - Renamed to s3c2410_lcd.c as it include also lcd power controls
+ *
+ * 2005-03-17: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *   - First version
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fb.h>
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+#include <linux/backlight.h>
+#endif
+#ifdef CONFIG_LCD_CLASS_DEVICE
+#include <linux/lcd.h>
+#endif
+#include <asm/arch/lcd.h>
+#include <asm/arch/regs-gpio.h>
+
+struct s3c2410bl_devs {
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+	struct backlight_device *bl;
+#endif
+#ifdef CONFIG_LCD_CLASS_DEVICE
+	struct lcd_device	*lcd;
+#endif
+};
+
+#ifdef CONFIG_LCD_CLASS_DEVICE
+static int s3c2410bl_get_lcd_power(struct lcd_device *lcd)
+{
+	struct s3c2410_bl_mach_info *info;
+
+	info = (struct s3c2410_bl_mach_info *)class_get_devdata(&lcd->class_dev);
+
+	if (info)
+		return info->lcd_power_value;
+
+	return 0;
+}
+
+static int s3c2410bl_set_lcd_power(struct lcd_device *lcd, int power)
+{
+	struct s3c2410_bl_mach_info *info;
+
+	info = (struct s3c2410_bl_mach_info *)class_get_devdata(&lcd->class_dev);
+
+	if (info && info->lcd_power) {
+		info->lcd_power_value=power;
+		switch(power) {
+			case FB_BLANK_NORMAL:
+			case FB_BLANK_POWERDOWN:
+				info->lcd_power(0);
+				break;
+			default:
+			case FB_BLANK_VSYNC_SUSPEND:
+			case FB_BLANK_HSYNC_SUSPEND:
+			case FB_BLANK_UNBLANK:
+				info->lcd_power(1);
+				break;
+		}
+	}
+
+	return 0;
+}
+#endif
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+static int s3c2410bl_get_bl_power(struct backlight_device *bl)
+{
+	struct s3c2410_bl_mach_info *info;
+
+	info = (struct s3c2410_bl_mach_info *)class_get_devdata(&bl->class_dev);
+
+	if (info)
+		return info->backlight_power_value;
+
+	return 0;
+}
+
+static int s3c2410bl_set_bl_power(struct backlight_device *bl, int power)
+{
+	struct s3c2410_bl_mach_info *info;
+
+	info = (struct s3c2410_bl_mach_info *)class_get_devdata(&bl->class_dev);
+
+	power = !!power;
+
+	if (info && info->backlight_power) {
+		info->backlight_power_value=power;
+		switch(power) {
+			case FB_BLANK_NORMAL:
+			case FB_BLANK_VSYNC_SUSPEND:
+			case FB_BLANK_HSYNC_SUSPEND:
+			case FB_BLANK_POWERDOWN:
+				info->backlight_power(0);
+				break;
+			default:
+			case FB_BLANK_UNBLANK:
+				info->backlight_power(1);
+				break;
+		}
+	}
+
+	return 0;
+}
+
+static int s3c2410bl_get_bl_brightness(struct backlight_device *bl)
+{
+	struct s3c2410_bl_mach_info *info;
+
+	info = (struct s3c2410_bl_mach_info *)class_get_devdata(&bl->class_dev);
+
+	if(info)
+		return info->brightness_value;
+
+	return 0;
+}
+
+static int s3c2410bl_set_bl_brightness(struct backlight_device *bl, int brightness)
+{
+	struct s3c2410_bl_mach_info *info;
+
+	info = (struct s3c2410_bl_mach_info *)class_get_devdata(&bl->class_dev);
+
+	if (info && info->set_brightness) {
+		if ( (brightness <= info->backlight_max) && brightness )
+		{
+			info->brightness_value=brightness;
+			info->set_brightness(brightness);
+		}
+		else
+			return -1;
+	}
+
+	return 0;
+}
+#endif
+
+static int is_s3c2410fb(struct fb_info *info)
+{
+	return (!strcmp(info->fix.id,"s3c2410fb"));
+}
+
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+static struct backlight_properties s3c2410bl_props = {
+	.owner    	= THIS_MODULE,
+	.get_power	= s3c2410bl_get_bl_power,
+	.set_power	= s3c2410bl_set_bl_power,
+	.get_brightness = s3c2410bl_get_bl_brightness,
+	.set_brightness	= s3c2410bl_set_bl_brightness,
+	.check_fb 	= is_s3c2410fb
+};
+#endif
+#ifdef CONFIG_LCD_CLASS_DEVICE
+static struct lcd_properties s3c2410lcd_props = {
+	.owner		= THIS_MODULE,
+	.get_power	= s3c2410bl_get_lcd_power,
+	.set_power	= s3c2410bl_set_lcd_power,
+	.check_fb	= is_s3c2410fb
+};
+#endif
+
+static int __init s3c2410bl_probe(struct device *dev)
+{
+	struct s3c2410bl_devs *devs;
+	struct s3c2410_bl_mach_info *info;
+
+	info = ( struct s3c2410_bl_mach_info *)dev->platform_data;
+
+	/* Register the backlight device */
+	if (!info) {
+		printk(KERN_ERR "Hm... too bad : no platform data for bl\n");
+	}
+	else {
+		s3c2410bl_props.max_brightness = info->backlight_max;
+	}
+
+	devs = (struct s3c2410bl_devs *)kmalloc(sizeof(*devs),GFP_KERNEL);
+	if (!devs) {
+		return -ENOMEM;
+	}
+
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+	devs->bl = backlight_device_register ("s3c2410-bl",info, &s3c2410bl_props);
+
+	if (IS_ERR (devs->bl)) {
+		kfree(devs);
+		return PTR_ERR (devs->bl);
+	}
+#endif
+#ifdef CONFIG_LCD_CLASS_DEVICE
+	devs->lcd = lcd_device_register("s3c2410-lcd",info,&s3c2410lcd_props);
+
+	if (IS_ERR (devs->bl)) {
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+		backlight_device_unregister(devs->bl);
+#endif
+		kfree(devs);
+		return PTR_ERR (devs->lcd);
+	}
+#endif
+	dev_set_drvdata(dev, devs);
+
+	/* Set power */
+	s3c2410bl_set_bl_power(devs->bl,1);
+
+	/* Set default brightness */
+	s3c2410bl_set_bl_brightness(devs->bl,info->backlight_default);
+
+	printk(KERN_ERR "s3c2410 Backlight Driver Initialized.\n");
+	return 0;
+}
+
+static int s3c2410bl_remove(struct device *dev)
+{
+	struct s3c2410bl_devs *devs = dev->driver_data;
+
+	if (devs) {
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+		if (devs->bl) {
+			backlight_device_unregister(devs->bl);
+			s3c2410bl_set_bl_brightness(devs->bl,0);
+		}
+#endif
+#ifdef CONFIG_LCD_CLASS_DEVICE
+		if (devs->lcd) {
+			lcd_device_unregister(devs->lcd);
+		}
+#endif
+		kfree(devs);
+	}
+	
+	printk("s3c2410 Backlight Driver Unloaded\n");
+
+	return 0;
+
+}
+
+static struct device_driver s3c2410bl_driver = {
+	.name		= "s3c2410-bl",
+	.bus		= &platform_bus_type,
+	.probe		= s3c2410bl_probe,
+	.remove		= s3c2410bl_remove,
+};
+
+
+
+static int __init s3c2410bl_init(void)
+{
+	return driver_register(&s3c2410bl_driver);
+}
+
+static void __exit s3c2410fb_cleanup(void)
+{
+	driver_unregister(&s3c2410bl_driver);
+}
+
+module_init(s3c2410bl_init);
+module_exit(s3c2410fb_cleanup);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_DESCRIPTION("s3c2410 Backlight Driver");
+MODULE_LICENSE("GPLv2");
+
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -180,6 +180,20 @@ config FONT_ACORN_8x8
 config FONT_MINI_4x6
 	bool "Mini 4x6 font"
 	depends on !SPARC32 && !SPARC64 && FONTS
+	help
+          Mini console font for tiny displays
+
+config FONT_CLEAN_4x6
+	bool "Clean 4x6 font"
+	depends on !SPARC32 && !SPARC64 && FONTS
+	help
+          Mini console font for tiny displays
+
+config FONT_CLEAN_5x8
+	bool "Clean 5x8 font"
+	depends on !SPARC32 && !SPARC64 && FONTS
+	help
+          Small console font for small displays
 
 config FONT_SUN8x16
 	bool "Sparc console 8x16 font"
diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
--- a/drivers/video/console/Makefile
+++ b/drivers/video/console/Makefile
@@ -15,6 +15,8 @@ font-objs-$(CONFIG_FONT_10x18)     += fo
 font-objs-$(CONFIG_FONT_PEARL_8x8) += font_pearl_8x8.o
 font-objs-$(CONFIG_FONT_ACORN_8x8) += font_acorn_8x8.o
 font-objs-$(CONFIG_FONT_MINI_4x6)  += font_mini_4x6.o
+font-objs-$(CONFIG_FONT_CLEAN_4x6) += font_clean_4x6.o
+font-objs-$(CONFIG_FONT_CLEAN_5x8) += font_clean_5x8.o
 
 font-objs += $(font-objs-y)
 
diff --git a/drivers/video/console/font_clean_4x6.c b/drivers/video/console/font_clean_4x6.c
new file mode 100644
--- /dev/null
+++ b/drivers/video/console/font_clean_4x6.c
@@ -0,0 +1,1059 @@
+/*       Font file generated by Jay Carlson from clR4x6.bdf */
+
+/*
+COMMENT  Copyright 1989 Dale Schumacher, dal@syntel.mn.org
+COMMENT                 399 Beacon Ave.
+COMMENT                 St. Paul, MN  55104-3527
+COMMENT
+COMMENT  Permission to use, copy, modify, and distribute this software and
+COMMENT  its documentation for any purpose and without fee is hereby
+COMMENT  granted, provided that the above copyright notice appear in all
+COMMENT  copies and that both that copyright notice and this permission
+COMMENT  notice appear in supporting documentation, and that the name of
+COMMENT  Dale Schumacher not be used in advertising or publicity pertaining to
+COMMENT  distribution of the software without specific, written prior
+COMMENT  permission.  Dale Schumacher makes no representations about the
+COMMENT  suitability of this software for any purpose.  It is provided "as
+COMMENT  is" without express or implied warranty.
+*/
+
+#include <linux/font.h>
+
+#define FONTDATAMAX (6 * 256)
+
+static unsigned char fontdata_clean_4x6[FONTDATAMAX] = {
+
+	 /* 0 0x00 C000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0xf0, /* 11110000 */
+
+	 /* 1 0x01 C001 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 2 0x02 C002 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 3 0x03 C003 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 4 0x04 C004 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 5 0x05 C005 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 6 0x06 C006 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 7 0x07 C007 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 8 0x08 C010 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 9 0x09 C011 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 10 0x0a C012 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 11 0x0b C013 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 12 0x0c C014 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 13 0x0d C015 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 14 0x0e C016 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 15 0x0f C017 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 16 0x10 C020 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 17 0x11 C021 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 18 0x12 C022 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 19 0x13 C023 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 20 0x14 C024 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 21 0x15 C025 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 22 0x16 C026 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 23 0x17 C027 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 24 0x18 C030 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 25 0x19 C031 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 26 0x1a C032 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 27 0x1b C033 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 28 0x1c C034 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 29 0x1d C035 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 30 0x1e C036 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 31 0x1f C037 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 32 0x20 C040 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 33 0x21 ! */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 34 0x22 " */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 35 0x23 # */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 36 0x24 $ */
+	0xe0, /* 11100000 */
+	0xc0, /* 11000000 */
+	0xe0, /* 11100000 */
+	0x60, /* 01100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 37 0x25 % */
+	0xa0, /* 10100000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x80, /* 10000000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 38 0x26 & */
+	0xe0, /* 11100000 */
+	0x40, /* 01000000 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 39 0x27 ' */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 40 0x28 ( */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 41 0x29 ) */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 42 0x2a * */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0xe0, /* 11100000 */
+	0x40, /* 01000000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 43 0x2b + */
+	0x00, /* 00000000 */
+	0x40, /* 01000000 */
+	0xe0, /* 11100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 44 0x2c , */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 45 0x2d - */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 46 0x2e . */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 47 0x2f / */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+	0x00, /* 00000000 */
+
+	 /* 48 0x30 0 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 49 0x31 1 */
+	0x40, /* 01000000 */
+	0xc0, /* 11000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 50 0x32 2 */
+	0xe0, /* 11100000 */
+	0x20, /* 00100000 */
+	0xe0, /* 11100000 */
+	0x80, /* 10000000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 51 0x33 3 */
+	0xe0, /* 11100000 */
+	0x20, /* 00100000 */
+	0xe0, /* 11100000 */
+	0x20, /* 00100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 52 0x34 4 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 53 0x35 5 */
+	0xe0, /* 11100000 */
+	0x80, /* 10000000 */
+	0xe0, /* 11100000 */
+	0x20, /* 00100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 54 0x36 6 */
+	0xe0, /* 11100000 */
+	0x80, /* 10000000 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 55 0x37 7 */
+	0xe0, /* 11100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 56 0x38 8 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 57 0x39 9 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0x20, /* 00100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 58 0x3a : */
+	0x00, /* 00000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 59 0x3b ; */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 60 0x3c < */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x80, /* 10000000 */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 61 0x3d = */
+	0x00, /* 00000000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 62 0x3e > */
+	0x80, /* 10000000 */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x80, /* 10000000 */
+	0x00, /* 00000000 */
+
+	 /* 63 0x3f ? */
+	0xc0, /* 11000000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 64 0x40 @ */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0x80, /* 10000000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 65 0x41 A */
+	0x40, /* 01000000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 66 0x42 B */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+
+	 /* 67 0x43 C */
+	0x60, /* 01100000 */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+
+	 /* 68 0x44 D */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+
+	 /* 69 0x45 E */
+	0xe0, /* 11100000 */
+	0x80, /* 10000000 */
+	0xc0, /* 11000000 */
+	0x80, /* 10000000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 70 0x46 F */
+	0xe0, /* 11100000 */
+	0x80, /* 10000000 */
+	0xc0, /* 11000000 */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+	0x00, /* 00000000 */
+
+	 /* 71 0x47 G */
+	0x60, /* 01100000 */
+	0x80, /* 10000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+
+	 /* 72 0x48 H */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 73 0x49 I */
+	0xe0, /* 11100000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 74 0x4a J */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0xa0, /* 10100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 75 0x4b K */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 76 0x4c L */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 77 0x4d M */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 78 0x4e N */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0xe0, /* 11100000 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 79 0x4f O */
+	0x40, /* 01000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 80 0x50 P */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0xc0, /* 11000000 */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+	0x00, /* 00000000 */
+
+	 /* 81 0x51 Q */
+	0x40, /* 01000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xc0, /* 11000000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+
+	 /* 82 0x52 R */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 83 0x53 S */
+	0x60, /* 01100000 */
+	0x80, /* 10000000 */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+
+	 /* 84 0x54 T */
+	0xe0, /* 11100000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 85 0x55 U */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 86 0x56 V */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 87 0x57 W */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 88 0x58 X */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x40, /* 01000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 89 0x59 Y */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 90 0x5a Z */
+	0xe0, /* 11100000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x80, /* 10000000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 91 0x5b [ */
+	0x60, /* 01100000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+
+	 /* 92 0x5c \ */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 93 0x5d ] */
+	0x60, /* 01100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+
+	 /* 94 0x5e ^ */
+	0x40, /* 01000000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 95 0x5f _ */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 96 0x60 ` */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 97 0x61 a */
+	0x00, /* 00000000 */
+	0x40, /* 01000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+
+	 /* 98 0x62 b */
+	0x80, /* 10000000 */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+
+	 /* 99 0x63 c */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+
+	 /* 100 0x64 d */
+	0x20, /* 00100000 */
+	0x60, /* 01100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+
+	 /* 101 0x65 e */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0xe0, /* 11100000 */
+	0x80, /* 10000000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+
+	 /* 102 0x66 f */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0xe0, /* 11100000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 103 0x67 g */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0xa0, /* 10100000 */
+	0x60, /* 01100000 */
+	0x20, /* 00100000 */
+	0xc0, /* 11000000 */
+
+	 /* 104 0x68 h */
+	0x80, /* 10000000 */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 105 0x69 i */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 106 0x6a j */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0xa0, /* 10100000 */
+	0x40, /* 01000000 */
+
+	 /* 107 0x6b k */
+	0x80, /* 10000000 */
+	0xa0, /* 10100000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 108 0x6c l */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 109 0x6d m */
+	0x00, /* 00000000 */
+	0xe0, /* 11100000 */
+	0xe0, /* 11100000 */
+	0xe0, /* 11100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 110 0x6e n */
+	0x00, /* 00000000 */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 111 0x6f o */
+	0x00, /* 00000000 */
+	0x40, /* 01000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 112 0x70 p */
+	0x00, /* 00000000 */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0xc0, /* 11000000 */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+
+	 /* 113 0x71 q */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0xa0, /* 10100000 */
+	0x60, /* 01100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+
+	 /* 114 0x72 r */
+	0x00, /* 00000000 */
+	0xc0, /* 11000000 */
+	0xa0, /* 10100000 */
+	0x80, /* 10000000 */
+	0x80, /* 10000000 */
+	0x00, /* 00000000 */
+
+	 /* 115 0x73 s */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+
+	 /* 116 0x74 t */
+	0x40, /* 01000000 */
+	0xe0, /* 11100000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 117 0x75 u */
+	0x00, /* 00000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 118 0x76 v */
+	0x00, /* 00000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 119 0x77 w */
+	0x00, /* 00000000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0xe0, /* 11100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 120 0x78 x */
+	0x00, /* 00000000 */
+	0xa0, /* 10100000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0xa0, /* 10100000 */
+	0x00, /* 00000000 */
+
+	 /* 121 0x79 y */
+	0x00, /* 00000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0x60, /* 01100000 */
+	0x20, /* 00100000 */
+	0xc0, /* 11000000 */
+
+	 /* 122 0x7a z */
+	0x00, /* 00000000 */
+	0xe0, /* 11100000 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+	 /* 123 0x7b { */
+	0x60, /* 01100000 */
+	0x40, /* 01000000 */
+	0xc0, /* 11000000 */
+	0x40, /* 01000000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+
+	 /* 124 0x7c | */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 125 0x7d } */
+	0xc0, /* 11000000 */
+	0x40, /* 01000000 */
+	0x60, /* 01100000 */
+	0x40, /* 01000000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+
+	 /* 126 0x7e ~ */
+	0x20, /* 00100000 */
+	0xe0, /* 11100000 */
+	0x80, /* 10000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 127 0x7f C177 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0xa0, /* 10100000 */
+	0xa0, /* 10100000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+
+};
+
+struct font_desc font_clean_4x6 = {
+	CLEAN4x6_IDX,
+	"Clean4x6",
+	4,
+	6,
+	fontdata_clean_4x6,
+	3
+};
diff --git a/drivers/video/console/font_clean_5x8.c b/drivers/video/console/font_clean_5x8.c
new file mode 100644
--- /dev/null
+++ b/drivers/video/console/font_clean_5x8.c
@@ -0,0 +1,1314 @@
+/*       Font file generated by Jay Carlson from clR5x8.bdf */
+
+/*
+COMMENT  Copyright 1989 Dale Schumacher, dal@syntel.mn.org
+COMMENT                 399 Beacon Ave.
+COMMENT                 St. Paul, MN  55104-3527
+COMMENT
+COMMENT  Permission to use, copy, modify, and distribute this software and
+COMMENT  its documentation for any purpose and without fee is hereby
+COMMENT  granted, provided that the above copyright notice appear in all
+COMMENT  copies and that both that copyright notice and this permission
+COMMENT  notice appear in supporting documentation, and that the name of
+COMMENT  Dale Schumacher not be used in advertising or publicity pertaining to
+COMMENT  distribution of the software without specific, written prior
+COMMENT  permission.  Dale Schumacher makes no representations about the
+COMMENT  suitability of this software for any purpose.  It is provided "as
+COMMENT  is" without express or implied warranty.
+*/
+
+#include <linux/font.h>
+
+#define FONTDATAMAX (8 * 256)
+
+static unsigned char fontdata_clean_5x8[FONTDATAMAX] = {
+
+	 /* 0 0x00 C000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 1 0x01 C001 */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x78, /* 01111000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 2 0x02 C002 */
+	0x00, /* 00000000 */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 3 0x03 C003 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 4 0x04 C004 */
+	0x00, /* 00000000 */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 5 0x05 C005 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x40, /* 01000000 */
+	0x70, /* 01110000 */
+	0x40, /* 01000000 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 6 0x06 C006 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x40, /* 01000000 */
+	0x70, /* 01110000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 7 0x07 C007 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x40, /* 01000000 */
+	0x58, /* 01011000 */
+	0x48, /* 01001000 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 8 0x08 C010 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x78, /* 01111000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 9 0x09 C011 */
+	0x00, /* 00000000 */
+	0x70, /* 01110000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 10 0x0a C012 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x08, /* 00001000 */
+	0x08, /* 00001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 11 0x0b C013 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x50, /* 01010000 */
+	0x60, /* 01100000 */
+	0x50, /* 01010000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 12 0x0c C014 */
+	0x00, /* 00000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 13 0x0d C015 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x78, /* 01111000 */
+	0x78, /* 01111000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 14 0x0e C016 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x68, /* 01101000 */
+	0x58, /* 01011000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 15 0x0f C017 */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 16 0x10 C020 */
+	0x00, /* 00000000 */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 17 0x11 C021 */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	 /* 18 0x12 C022 */
+	0x00, /* 00000000 */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x50, /* 01010000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 19 0x13 C023 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x40, /* 01000000 */
+	0x30, /* 00110000 */
+	0x08, /* 00001000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 20 0x14 C024 */
+	0x00, /* 00000000 */
+	0xf8, /* 11111000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 21 0x15 C025 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 22 0x16 C026 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 23 0x17 C027 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x78, /* 01111000 */
+	0x78, /* 01111000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 24 0x18 C030 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 25 0x19 C031 */
+	0x00, /* 00000000 */
+	0x88, /* 10001000 */
+	0x50, /* 01010000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 26 0x1a C032 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 27 0x1b C033 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 28 0x1c C034 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 29 0x1d C035 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 30 0x1e C036 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 31 0x1f C037 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 32 0x20 C040 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 33 0x21 ! */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 34 0x22 " */
+	0x28, /* 00101000 */
+	0x28, /* 00101000 */
+	0x28, /* 00101000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 35 0x23 # */
+	0x50, /* 01010000 */
+	0x50, /* 01010000 */
+	0xf8, /* 11111000 */
+	0x50, /* 01010000 */
+	0xf8, /* 11111000 */
+	0x50, /* 01010000 */
+	0x50, /* 01010000 */
+	0x00, /* 00000000 */
+
+	 /* 36 0x24 $ */
+	0x20, /* 00100000 */
+	0x78, /* 01111000 */
+	0xa0, /* 10100000 */
+	0x70, /* 01110000 */
+	0x28, /* 00101000 */
+	0xf0, /* 11110000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 37 0x25 % */
+	0x60, /* 01100000 */
+	0x68, /* 01101000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x58, /* 01011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 38 0x26 & */
+	0x30, /* 00110000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x28, /* 00101000 */
+	0x50, /* 01010000 */
+	0x50, /* 01010000 */
+	0x28, /* 00101000 */
+	0x00, /* 00000000 */
+
+	 /* 39 0x27 ' */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 40 0x28 ( */
+	0x08, /* 00001000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x10, /* 00010000 */
+	0x08, /* 00001000 */
+	0x00, /* 00000000 */
+
+	 /* 41 0x29 ) */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 42 0x2a * */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x78, /* 01111000 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 43 0x2b + */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0xf8, /* 11111000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 44 0x2c , */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+
+	 /* 45 0x2d - */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xf8, /* 11111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 46 0x2e . */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 47 0x2f / */
+	0x08, /* 00001000 */
+	0x08, /* 00001000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+
+	 /* 48 0x30 0 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x58, /* 01011000 */
+	0x68, /* 01101000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 49 0x31 1 */
+	0x20, /* 00100000 */
+	0x60, /* 01100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 50 0x32 2 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x08, /* 00001000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+
+	 /* 51 0x33 3 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x08, /* 00001000 */
+	0x30, /* 00110000 */
+	0x08, /* 00001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 52 0x34 4 */
+	0x08, /* 00001000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x28, /* 00101000 */
+	0x28, /* 00101000 */
+	0x78, /* 01111000 */
+	0x08, /* 00001000 */
+	0x00, /* 00000000 */
+
+	 /* 53 0x35 5 */
+	0x78, /* 01111000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x70, /* 01110000 */
+	0x08, /* 00001000 */
+	0x08, /* 00001000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	 /* 54 0x36 6 */
+	0x30, /* 00110000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 55 0x37 7 */
+	0x78, /* 01111000 */
+	0x08, /* 00001000 */
+	0x08, /* 00001000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 56 0x38 8 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 57 0x39 9 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x38, /* 00111000 */
+	0x08, /* 00001000 */
+	0x08, /* 00001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 58 0x3a : */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 59 0x3b ; */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+
+	 /* 60 0x3c < */
+	0x08, /* 00001000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0x10, /* 00010000 */
+	0x08, /* 00001000 */
+	0x00, /* 00000000 */
+
+	 /* 61 0x3d = */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xf8, /* 11111000 */
+	0x00, /* 00000000 */
+	0xf8, /* 11111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 62 0x3e > */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0x10, /* 00010000 */
+	0x08, /* 00001000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 63 0x3f ? */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x08, /* 00001000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 64 0x40 @ */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x58, /* 01011000 */
+	0x58, /* 01011000 */
+	0x40, /* 01000000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 65 0x41 A */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x78, /* 01111000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 66 0x42 B */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	 /* 67 0x43 C */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 68 0x44 D */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	 /* 69 0x45 E */
+	0x78, /* 01111000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x70, /* 01110000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+
+	 /* 70 0x46 F */
+	0x78, /* 01111000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x70, /* 01110000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 71 0x47 G */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x40, /* 01000000 */
+	0x58, /* 01011000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+
+	 /* 72 0x48 H */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x78, /* 01111000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 73 0x49 I */
+	0x70, /* 01110000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	 /* 74 0x4a J */
+	0x18, /* 00011000 */
+	0x08, /* 00001000 */
+	0x08, /* 00001000 */
+	0x08, /* 00001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 75 0x4b K */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x50, /* 01010000 */
+	0x60, /* 01100000 */
+	0x50, /* 01010000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 76 0x4c L */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+
+	 /* 77 0x4d M */
+	0x48, /* 01001000 */
+	0x78, /* 01111000 */
+	0x78, /* 01111000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 78 0x4e N */
+	0x48, /* 01001000 */
+	0x68, /* 01101000 */
+	0x68, /* 01101000 */
+	0x58, /* 01011000 */
+	0x58, /* 01011000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 79 0x4f O */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 80 0x50 P */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 81 0x51 Q */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+
+	 /* 82 0x52 R */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x50, /* 01010000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 83 0x53 S */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x40, /* 01000000 */
+	0x30, /* 00110000 */
+	0x08, /* 00001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 84 0x54 T */
+	0xf8, /* 11111000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 85 0x55 U */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 86 0x56 V */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 87 0x57 W */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x78, /* 01111000 */
+	0x78, /* 01111000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 88 0x58 X */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 89 0x59 Y */
+	0x88, /* 10001000 */
+	0x88, /* 10001000 */
+	0x50, /* 01010000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 90 0x5a Z */
+	0x78, /* 01111000 */
+	0x08, /* 00001000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+
+	 /* 91 0x5b [ */
+	0x38, /* 00111000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+
+	 /* 92 0x5c \ */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x08, /* 00001000 */
+	0x08, /* 00001000 */
+
+	 /* 93 0x5d ] */
+	0x70, /* 01110000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	 /* 94 0x5e ^ */
+	0x20, /* 00100000 */
+	0x50, /* 01010000 */
+	0x88, /* 10001000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 95 0x5f _ */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xf8, /* 11111000 */
+
+	 /* 96 0x60 ` */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x10, /* 00010000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 97 0x61 a */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x58, /* 01011000 */
+	0x28, /* 00101000 */
+	0x00, /* 00000000 */
+
+	 /* 98 0x62 b */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	 /* 99 0x63 c */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+
+	 /* 100 0x64 d */
+	0x08, /* 00001000 */
+	0x08, /* 00001000 */
+	0x38, /* 00111000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+
+	 /* 101 0x65 e */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x78, /* 01111000 */
+	0x40, /* 01000000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 102 0x66 f */
+	0x18, /* 00011000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x70, /* 01110000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 103 0x67 g */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x38, /* 00111000 */
+	0x08, /* 00001000 */
+	0x30, /* 00110000 */
+
+	 /* 104 0x68 h */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 105 0x69 i */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	 /* 106 0x6a j */
+	0x10, /* 00010000 */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x60, /* 01100000 */
+
+	 /* 107 0x6b k */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x48, /* 01001000 */
+	0x50, /* 01010000 */
+	0x60, /* 01100000 */
+	0x50, /* 01010000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 108 0x6c l */
+	0x60, /* 01100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	 /* 109 0x6d m */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xd0, /* 11010000 */
+	0xa8, /* 10101000 */
+	0xa8, /* 10101000 */
+	0xa8, /* 10101000 */
+	0x88, /* 10001000 */
+	0x00, /* 00000000 */
+
+	 /* 110 0x6e n */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 111 0x6f o */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 112 0x70 p */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x70, /* 01110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x70, /* 01110000 */
+	0x40, /* 01000000 */
+
+	 /* 113 0x71 q */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x38, /* 00111000 */
+	0x08, /* 00001000 */
+
+	 /* 114 0x72 r */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x58, /* 01011000 */
+	0x60, /* 01100000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 115 0x73 s */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x40, /* 01000000 */
+	0x30, /* 00110000 */
+	0x08, /* 00001000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+
+	 /* 116 0x74 t */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x78, /* 01111000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+
+	 /* 117 0x75 u */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+
+	 /* 118 0x76 v */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+
+	 /* 119 0x77 w */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x88, /* 10001000 */
+	0xa8, /* 10101000 */
+	0xa8, /* 10101000 */
+	0xa8, /* 10101000 */
+	0x50, /* 01010000 */
+	0x00, /* 00000000 */
+
+	 /* 120 0x78 x */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x30, /* 00110000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x00, /* 00000000 */
+
+	 /* 121 0x79 y */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x48, /* 01001000 */
+	0x38, /* 00111000 */
+	0x08, /* 00001000 */
+	0x30, /* 00110000 */
+
+	 /* 122 0x7a z */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+
+	 /* 123 0x7b { */
+	0x08, /* 00001000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x10, /* 00010000 */
+	0x10, /* 00010000 */
+	0x08, /* 00001000 */
+	0x00, /* 00000000 */
+
+	 /* 124 0x7c | */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x00, /* 00000000 */
+
+	 /* 125 0x7d } */
+	0x40, /* 01000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x10, /* 00010000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x40, /* 01000000 */
+	0x00, /* 00000000 */
+
+	 /* 126 0x7e ~ */
+	0x28, /* 00101000 */
+	0x50, /* 01010000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	 /* 127 0x7f C177 */
+	0x00, /* 00000000 */
+	0x20, /* 00100000 */
+	0x20, /* 00100000 */
+	0x50, /* 01010000 */
+	0x50, /* 01010000 */
+	0x88, /* 10001000 */
+	0xf8, /* 11111000 */
+	0x00, /* 00000000 */
+};
+
+struct font_desc font_clean_5x8 = {
+	CLEAN5x8_IDX,
+	"Clean5x8",
+	5,
+	8,
+	fontdata_clean_5x8,
+	3
+};
diff --git a/drivers/video/console/fonts.c b/drivers/video/console/fonts.c
--- a/drivers/video/console/fonts.c
+++ b/drivers/video/console/fonts.c
@@ -64,6 +64,14 @@ static struct font_desc *fonts[] = {
 #undef NO_FONTS
     &font_mini_4x6,
 #endif
+#ifdef CONFIG_FONT_CLEAN_4x6
+#undef NO_FONTS
+    &font_clean_4x6,
+#endif
+#ifdef CONFIG_FONT_CLEAN_5x8
+#undef NO_FONTS
+    &font_clean_5x8,
+#endif
 };
 
 #define num_fonts (sizeof(fonts)/sizeof(*fonts))
diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c
new file mode 100644
--- /dev/null
+++ b/drivers/video/s3c2410fb.c
@@ -0,0 +1,902 @@
+/*
+ * linux/drivers/video/s3c2410fb.c
+ *	Copyright (c) Arnaud Patard, Ben Dooks
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ *	    S3C2410 LCD Controller Frame Buffer Driver
+ *	    based on skeletonfb.c, sa1100fb.c and others
+ *
+ * ChangeLog
+ * 2005-04-07: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *      - u32 state -> pm_message_t state
+ *      - S3C2410_{VA,SZ}_LCD -> S3C24XX
+ *
+ * 2005-03-15: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *      - Removed the ioctl
+ *      - use readl/writel instead of __raw_writel/__raw_readl
+ *
+ * 2004-12-04: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *      - Added the possibility to set on or off the
+ *      debugging mesaages
+ *      - Replaced 0 and 1 by on or off when reading the
+ *      /sys files
+ *
+ * 2005-03-23: Ben Dooks <ben-linux@fluff.org>
+ *	- added non 16bpp modes
+ *	- updated platform information for range of x/y/bpp
+ *	- add code to ensure palette is written correctly
+ *	- add pixel clock divisor control
+ *
+ * 2004-11-11: Arnaud Patard <arnaud.patard@rtp-net.org>
+ * 	- Removed the use of currcon as it no more exist
+ * 	- Added LCD power sysfs interface
+ *
+ * 2004-11-03: Ben Dooks <ben-linux@fluff.org>
+ *	- minor cleanups
+ *	- add suspend/resume support
+ *	- s3c2410fb_setcolreg() not valid in >8bpp modes
+ *	- removed last CONFIG_FB_S3C2410_FIXED
+ *	- ensure lcd controller stopped before cleanup
+ *	- added sysfs interface for backlight power
+ *	- added mask for gpio configuration
+ *	- ensured IRQs disabled during GPIO configuration
+ *	- disable TPAL before enabling video
+ *
+ * 2004-09-20: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *      - Suppress command line options
+ *
+ * 2004-09-15: Arnaud Patard <arnaud.patard@rtp-net.org>
+ * 	- code cleanup
+ *
+ * 2004-09-07: Arnaud Patard <arnaud.patard@rtp-net.org>
+ * 	- Renamed from h1940fb.c to s3c2410fb.c
+ * 	- Add support for different devices
+ * 	- Backlight support
+ *
+ * 2004-09-05: Herbert Pötzl <herbert@13thfloor.at>
+ *	- added clock (de-)allocation code
+ *	- added fixem fbmem option
+ *
+ * 2004-07-27: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *	- code cleanup
+ *	- added a forgotten return in h1940fb_init
+ *
+ * 2004-07-19: Herbert Pötzl <herbert@13thfloor.at>
+ *	- code cleanup and extended debugging
+ *
+ * 2004-07-15: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *	- First version
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/div64.h>
+
+#include <asm/mach/map.h>
+#include <asm/arch/regs-lcd.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/fb.h>
+#include <asm/hardware/clock.h>
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+#include "s3c2410fb.h"
+
+
+static struct s3c2410fb_info	   info;
+static struct s3c2410fb_mach_info *mach_info;
+
+/* Debugging stuff */
+#ifdef CONFIG_FB_S3C2410_DEBUG
+static int debug	   = 1;
+#else
+static int debug	   = 0;
+#endif
+
+#define dprintk(msg...)	if (debug) { printk(KERN_DEBUG "s3c2410fb: " msg); }
+
+/* useful functions */
+
+static inline struct s3c2410fb_info *fb_to_s3cfb(struct fb_info *info)
+{
+	return container_of(info, struct s3c2410fb_info, fb);
+}
+
+/* s3c2410fb_set_lcdaddr
+ *
+ * initialise lcd controller address pointers
+*/
+
+static void s3c2410fb_set_lcdaddr(struct s3c2410fb_info *fbi)
+{
+	struct fb_var_screeninfo *var = &fbi->fb.var;
+	unsigned long saddr1, saddr2, saddr3;
+
+	saddr1  = fbi->fb.fix.smem_start >> 1;
+	saddr2  = fbi->fb.fix.smem_start;
+	saddr2 += (var->xres * var->yres * var->bits_per_pixel)/8;
+	saddr2>>= 1;
+
+	saddr3 =  S3C2410_OFFSIZE(0) | S3C2410_PAGEWIDTH(var->xres);
+
+	dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
+	dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
+	dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
+
+	writel(saddr1, S3C2410_LCDSADDR1);
+	writel(saddr2, S3C2410_LCDSADDR2);      
+	writel(saddr3, S3C2410_LCDSADDR3);
+}
+
+/* s3c2410fb_calc_pixclk()
+ *
+ * calculate divisor for clk->pixclk
+*/
+
+static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
+					  unsigned long pixclk)
+{
+	unsigned long clk = clk_get_rate(fbi->clk);
+	unsigned long long div;
+
+	/* pixclk is in picoseoncds, our clock is in Hz 
+	 *
+	 * Hz -> picoseconds is / 10^-12
+	 */
+
+	div = (unsigned long long)clk * pixclk;
+	do_div(div,1000000UL);
+	do_div(div,1000000UL);
+
+	dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
+	return div;
+}
+
+/*
+ *	s3c2410fb_check_var():
+ *	Get the video params out of 'var'. If a value doesn't fit, round it up,
+ *	if it's too big, return -EINVAL.
+ *
+ */
+static int s3c2410fb_check_var(struct fb_var_screeninfo *var,
+			       struct fb_info *info)
+{
+	struct s3c2410fb_info *fbi = fb_to_s3cfb(info);
+
+	dprintk("check_var(var=%p, info=%p)\n", var, info);
+
+	/* validate x/y resolution */
+
+	if (var->yres > fbi->mach_info->yres.max)
+		var->yres = fbi->mach_info->yres.max;
+	else if (var->yres < fbi->mach_info->yres.min)
+		var->yres = fbi->mach_info->yres.min;
+
+	if (var->xres > fbi->mach_info->xres.max)
+		var->yres = fbi->mach_info->xres.max;
+	else if (var->xres < fbi->mach_info->xres.min)
+		var->xres = fbi->mach_info->xres.min;
+
+	/* validate bpp */
+
+	if (var->bits_per_pixel > fbi->mach_info->bpp.max)
+		var->bits_per_pixel = fbi->mach_info->bpp.max;
+	else if (var->bits_per_pixel < fbi->mach_info->bpp.min)
+		var->bits_per_pixel = fbi->mach_info->bpp.min;
+
+	/* set r/g/b positions */
+
+	if (var->bits_per_pixel == 16) {
+		var->red.offset		= 11;
+		var->green.offset	= 5;
+		var->blue.offset	= 0;
+		var->red.length		= 5;
+		var->green.length	= 6;
+		var->blue.length	= 5;
+		var->transp.length	= 0;
+	} else {
+		var->red.length		= 8;
+		var->red.offset		= 0;
+		var->green.length	= 0;
+		var->green.offset	= 8;
+		var->blue.length	= 8;
+		var->blue.offset	= 0;
+		var->transp.length	= 0;
+	}
+
+	return 0;
+}
+
+/* s3c2410fb_activate_var
+ *
+ * activate (set) the controller from the given framebuffer
+ * information
+*/
+
+static int s3c2410fb_activate_var(struct s3c2410fb_info *fbi,
+				  struct fb_var_screeninfo *var)
+{
+	fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_MODEMASK;
+
+	dprintk("%s: var->xres  = %d\n", __FUNCTION__, var->xres);
+	dprintk("%s: var->yres  = %d\n", __FUNCTION__, var->yres);
+	dprintk("%s: var->bpp   = %d\n", __FUNCTION__, var->bits_per_pixel);
+
+	switch (var->bits_per_pixel) {
+	case 1:
+		fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;
+		break;
+	case 2:
+		fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
+		break;
+	case 4:
+		fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
+		break;
+	case 8:
+		fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
+		break;
+	case 16:
+		fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
+		break;
+
+	default:
+		/* invalid pixel depth */
+		dev_err(fbi->dev, "invalid bpp %d\n", var->bits_per_pixel);
+	}
+
+	/* check to see if we need to update sync/borders */
+
+	if (!fbi->mach_info->fixed_syncs) {
+		dprintk("setting vert: up=%d, low=%d, sync=%d\n",
+			var->upper_margin, var->lower_margin,
+			var->vsync_len);
+
+		dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
+			var->left_margin, var->right_margin,
+			var->hsync_len);
+
+		fbi->regs.lcdcon2 = 
+			S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
+			S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
+			S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
+
+		fbi->regs.lcdcon3 = 
+			S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
+			S3C2410_LCDCON3_HFPD(var->left_margin - 1);
+
+		fbi->regs.lcdcon4 &= ~S3C2410_LCDCON4_HSPW(0xff);
+		fbi->regs.lcdcon4 |=  S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
+	}
+
+	/* update X/Y info */
+
+	fbi->regs.lcdcon2 &= ~S3C2410_LCDCON2_LINEVAL(0x3ff);
+	fbi->regs.lcdcon2 |=  S3C2410_LCDCON2_LINEVAL(var->yres - 1);
+
+	fbi->regs.lcdcon3 &= ~S3C2410_LCDCON3_HOZVAL(0x7ff);
+	fbi->regs.lcdcon3 |=  S3C2410_LCDCON3_HOZVAL(var->xres - 1);
+
+	if (var->pixclock > 0) {
+		int clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock);
+
+		clkdiv = (clkdiv / 2) -1;
+		if (clkdiv < 0)
+			clkdiv = 0;
+
+		fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_CLKVAL(0x3ff);
+		fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);
+	}
+
+	/* write new registers */
+
+	dprintk("new register set:\n");
+	dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
+	dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
+	dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
+	dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
+	dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
+
+	writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
+	writel(fbi->regs.lcdcon2, S3C2410_LCDCON2);
+	writel(fbi->regs.lcdcon3, S3C2410_LCDCON3);
+	writel(fbi->regs.lcdcon4, S3C2410_LCDCON4);
+	writel(fbi->regs.lcdcon5, S3C2410_LCDCON5);
+
+	/* set lcd address pointers */
+	s3c2410fb_set_lcdaddr(fbi);
+
+	writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);
+}
+
+
+/*
+ *      s3c2410fb_set_par - Optional function. Alters the hardware state.
+ *      @info: frame buffer structure that represents a single frame buffer
+ *
+ */
+static int s3c2410fb_set_par(struct fb_info *info)
+{
+	struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
+	struct fb_var_screeninfo *var = &info->var;
+
+	if (var->bits_per_pixel == 16)
+		fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+	else
+		fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
+
+	fbi->fb.fix.line_length     = (var->width*var->bits_per_pixel)/8;
+
+	/* activate this new configuration */
+
+	s3c2410fb_activate_var(fbi, var);
+	return 0;
+}
+
+static void schedule_palette_update(struct s3c2410fb_info *fbi,
+				    unsigned int regno, unsigned int val)
+{
+	unsigned long flags;
+	unsigned long irqen;
+
+	local_irq_save(flags);
+
+	fbi->palette_buffer[regno] = val;
+
+	if (!fbi->palette_ready) {
+		fbi->palette_ready = 1;
+
+		/* enable IRQ */
+		irqen = readl(S3C2410_LCDINTMSK);
+		irqen &= ~S3C2410_LCDINT_FRSYNC;
+		writel(irqen, S3C2410_LCDINTMSK);
+	}
+
+	local_irq_restore(flags);
+}
+
+/* from pxafb.c */
+static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+static int s3c2410fb_setcolreg(unsigned regno,
+			       unsigned red, unsigned green, unsigned blue,
+			       unsigned transp, struct fb_info *info)
+{
+	struct s3c2410fb_info *fbi = (struct s3c2410fb_info *)info;
+	unsigned int val;
+
+	/* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n", regno, red, green, blue); */
+
+	switch (fbi->fb.fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+		/* true-colour, use pseuo-palette */
+
+		if (regno < 16) {
+			u32 *pal = fbi->fb.pseudo_palette;
+
+			val  = chan_to_field(red,   &fbi->fb.var.red);
+			val |= chan_to_field(green, &fbi->fb.var.green);
+			val |= chan_to_field(blue,  &fbi->fb.var.blue);
+
+			pal[regno] = val;
+		}
+
+	case FB_VISUAL_STATIC_PSEUDOCOLOR:
+	case FB_VISUAL_PSEUDOCOLOR:
+		if (regno < 256) {
+			/* currently assume RGB 5-6-5 mode */
+
+			val  = ((red   >>  0) & 0xf800);
+			val |= ((green >>  5) & 0x07e0);
+			val |= ((blue  >> 11) & 0x001f);
+			
+			writel(val, S3C2410_TFTPAL(regno));
+			schedule_palette_update(fbi, regno, val);
+		}
+
+		break;
+
+	default:
+		return 1;   /* unknown type */
+	}
+
+	return 0;
+}
+
+
+/**
+ *	s3c2410fb_pan_display
+ *	@var: frame buffer variable screen structure
+ *	@info: frame buffer structure that represents a single frame buffer
+ *
+ *	Pan (or wrap, depending on the `vmode' field) the display using the
+ *	`xoffset' and `yoffset' fields of the `var' structure.
+ *	If the values don't fit, return -EINVAL.
+ *
+ *	Returns negative errno on error, or zero on success.
+ */
+static int s3c2410fb_pan_display(struct fb_var_screeninfo *var,
+			     struct fb_info *info)
+{
+	dprintk("pan_display(var=%p, info=%p)\n", var, info);
+
+	dprintk("pan_display: xoffset=%d\n", var->xoffset);
+	dprintk("pan_display: yoffset=%d\n", var->yoffset);
+
+	return 0;
+}
+
+/**
+ *      s3c2410fb_blank
+ *	@blank_mode: the blank mode we want.
+ *	@info: frame buffer structure that represents a single frame buffer
+ *
+ *	Blank the screen if blank_mode != 0, else unblank. Return 0 if
+ *	blanking succeeded, != 0 if un-/blanking failed due to e.g. a
+ *	video mode which doesn't support it. Implements VESA suspend
+ *	and powerdown modes on hardware that supports disabling hsync/vsync:
+ *	blank_mode == 2: suspend vsync
+ *	blank_mode == 3: suspend hsync
+ *	blank_mode == 4: powerdown
+ *
+ *	Returns negative errno on error, or zero on success.
+ *
+ */
+static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
+{
+	dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
+
+	if (mach_info == NULL)
+		return -EINVAL;
+
+	if (blank_mode == FB_BLANK_UNBLANK)
+		writel(0x0, S3C2410_TPAL);
+	else {
+		dprintk("setting TPAL to output 0x000000\n");
+		writel(S3C2410_TPAL_EN, S3C2410_TPAL);
+	}
+
+	return 0;
+}
+
+static int s3c2410fb_debug_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", debug ? "on" : "off");
+}
+static int s3c2410fb_debug_store(struct device *dev, struct device_attribute *attr,
+					   const char *buf, size_t len)
+{
+	if (mach_info == NULL)
+		return -EINVAL;
+
+	if (len < 1)
+		return -EINVAL;
+
+	if (strnicmp(buf, "on", 2) == 0 ||
+	    strnicmp(buf, "1", 1) == 0) {
+		debug = 1;
+		printk(KERN_DEBUG "s3c2410fb: Debug On");
+	} else if (strnicmp(buf, "off", 3) == 0 ||
+		   strnicmp(buf, "0", 1) == 0) {
+		debug = 0;
+		printk(KERN_DEBUG "s3c2410fb: Debug Off");
+	} else {
+		return -EINVAL;
+	}
+
+	return len;
+}
+
+
+static DEVICE_ATTR(debug, 0666,
+		   s3c2410fb_debug_show,
+		   s3c2410fb_debug_store);
+
+static struct fb_ops s3c2410fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_check_var	= s3c2410fb_check_var,
+	.fb_set_par	= s3c2410fb_set_par,
+	.fb_blank	= s3c2410fb_blank,
+	.fb_pan_display	= s3c2410fb_pan_display,
+	.fb_setcolreg	= s3c2410fb_setcolreg,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+	.fb_cursor	= soft_cursor,
+};
+
+
+/* Fake monspecs to fill in fbinfo structure */
+/* Don't know if the values are important    */
+static struct fb_monspecs monspecs __initdata = {
+	.hfmin	= 30000,
+	.hfmax	= 70000,
+	.vfmin	= 50,
+	.vfmax	= 65,
+};
+
+/*
+ * s3c2410fb_map_video_memory():
+ *	Allocates the DRAM memory for the frame buffer.  This buffer is
+ *	remapped into a non-cached, non-buffered, memory region to
+ *	allow palette and pixel writes to occur without flushing the
+ *	cache.  Once this area is remapped, all virtual memory
+ *	access to the video memory should occur at the new region.
+ */
+static int __init s3c2410fb_map_video_memory(struct s3c2410fb_info *fbi)
+{
+	dprintk("map_video_memory(fbi=%p)\n", fbi);
+
+	fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE);
+	fbi->map_cpu  = dma_alloc_writecombine(fbi->dev, fbi->map_size,
+					       &fbi->map_dma, GFP_KERNEL);
+
+	fbi->map_size = fbi->fb.fix.smem_len;
+
+	if (fbi->map_cpu) {
+		/* prevent initial garbage on screen */
+		dprintk("map_video_memory: clear %p:%08x\n",
+			fbi->map_cpu, fbi->map_size);
+		memset(fbi->map_cpu, 0xf0, fbi->map_size);
+
+		fbi->screen_dma		= fbi->map_dma;
+		fbi->fb.screen_base	= fbi->map_cpu;
+		fbi->fb.fix.smem_start  = fbi->screen_dma;
+
+		dprintk("map_video_memory: dma=%08x cpu=%p size=%08x\n",
+			fbi->map_dma, fbi->map_cpu, fbi->fb.fix.smem_len);
+	}
+
+	return fbi->map_cpu ? 0 : -ENOMEM;
+}
+
+static inline void modify_gpio(void __iomem *reg,
+			       unsigned long set, unsigned long mask)
+{
+	unsigned long tmp;
+
+	tmp = readl(reg) & ~mask;
+	writel(tmp | set, reg);
+}
+
+
+/*
+ * s3c2410fb_init_registers - Initialise all LCD-related registers
+ */
+
+int s3c2410fb_init_registers(struct s3c2410fb_info *fbi)
+{
+	unsigned long flags;
+
+	/* Initialise LCD with values from haret */
+
+	local_irq_save(flags);
+
+	/* modify the gpio(s) with interrupts set (bjd) */
+
+	modify_gpio(S3C2410_GPCUP,  mach_info->gpcup,  mach_info->gpcup_mask);
+	modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
+	modify_gpio(S3C2410_GPDUP,  mach_info->gpdup,  mach_info->gpdup_mask);
+	modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
+
+	local_irq_restore(flags);
+
+	writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);
+	writel(fbi->regs.lcdcon2, S3C2410_LCDCON2);
+	writel(fbi->regs.lcdcon3, S3C2410_LCDCON3);
+	writel(fbi->regs.lcdcon4, S3C2410_LCDCON4);
+	writel(fbi->regs.lcdcon5, S3C2410_LCDCON5);
+  
+ 	s3c2410fb_set_lcdaddr(fbi);
+
+	dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel);
+	writel(mach_info->lpcsel, S3C2410_LPCSEL);
+
+	dprintk("replacing TPAL %08x\n", readl(S3C2410_TPAL));
+
+	/* ensure temporary palette disabled */
+	writel(0x00, S3C2410_TPAL);
+
+	/* Enable video by setting the ENVID bit to 1 */
+	fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID;
+	writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);
+	return 0;
+}
+
+static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
+{
+	unsigned int i;
+	unsigned long ent;
+
+	fbi->palette_ready = 0;
+	
+	for (i = 0; i < 256; i++) {		
+		if ((ent = fbi->palette_buffer[i]) == PALETTE_BUFF_CLEAR)
+			continue;
+
+		writel(ent, S3C2410_TFTPAL(i));
+		
+		/* it seems the only way to know exactly
+		 * if the palette wrote ok, is to check
+		 * to see if the value verifies ok
+		 */
+		
+		if (readw(S3C2410_TFTPAL(i)) == ent)
+			fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR;
+		else
+			fbi->palette_ready = 1;   /* retry */
+	}
+}
+
+static irqreturn_t s3c2410fb_irq(int irq, void *dev_id, struct pt_regs *r)
+{
+	struct s3c2410fb_info *fbi = dev_id;
+	unsigned long lcdirq = readl(S3C2410_LCDINTPND);
+
+	if (lcdirq & S3C2410_LCDINT_FRSYNC) {
+		if (fbi->palette_ready)
+			s3c2410fb_write_palette(fbi);
+
+		writel(S3C2410_LCDINT_FRSYNC, S3C2410_LCDINTPND);
+		writel(S3C2410_LCDINT_FRSYNC, S3C2410_LCDSRCPND);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static char driver_name[]="s3c2410fb";
+ 
+int __init s3c2410fb_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct s3c2410fb_hw *mregs;
+	int ret;
+	int irq;
+	int i;
+
+	mach_info = dev->platform_data;
+	if (mach_info == NULL) {
+		dev_err(dev,"no platform data for lcd, cannot attach\n");
+		return -EINVAL;
+	}
+
+	mregs = &mach_info->regs;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "no irq for device\n");
+		return -ENOENT;
+	}
+	
+	s3c2410fb_init_registers(&info);
+	
+	dprintk("devinit\n");
+
+	strcpy(info.fb.fix.id, driver_name);
+
+	memcpy(&info.regs, &mach_info->regs, sizeof(info.regs));
+
+	info.mach_info		    = dev->platform_data;
+
+	info.fb.fix.type	    = FB_TYPE_PACKED_PIXELS;
+	info.fb.fix.type_aux	    = 0;
+	info.fb.fix.xpanstep	    = 0;
+	info.fb.fix.ypanstep	    = 0;
+	info.fb.fix.ywrapstep	    = 0;
+	info.fb.fix.accel	    = FB_ACCEL_NONE;
+
+	info.fb.var.nonstd	    = 0;
+	info.fb.var.activate	    = FB_ACTIVATE_NOW;
+	info.fb.var.height	    = mach_info->height;
+	info.fb.var.width	    = mach_info->width;
+	info.fb.var.accel_flags     = 0;
+	info.fb.var.vmode	    = FB_VMODE_NONINTERLACED;
+
+	info.fb.fbops		    = &s3c2410fb_ops;
+	info.fb.flags		    = FBINFO_FLAG_DEFAULT;
+	info.fb.monspecs	    = monspecs;
+	info.fb.pseudo_palette      = &info.pseudo_pal;
+
+	info.fb.var.xres	    = mach_info->xres.defval;
+	info.fb.var.xres_virtual    = mach_info->xres.defval;
+	info.fb.var.yres	    = mach_info->yres.defval;
+	info.fb.var.yres_virtual    = mach_info->yres.defval;
+	info.fb.var.bits_per_pixel  = mach_info->bpp.defval;
+
+	info.fb.var.upper_margin    = S3C2410_LCDCON2_GET_VBPD(mregs->lcdcon2) +1;
+	info.fb.var.lower_margin    = S3C2410_LCDCON2_GET_VFPD(mregs->lcdcon2) +1;
+	info.fb.var.vsync_len	    = S3C2410_LCDCON2_GET_VSPW(mregs->lcdcon2) + 1;
+
+	info.fb.var.left_margin	    = S3C2410_LCDCON3_GET_HFPD(mregs->lcdcon3) + 1;
+	info.fb.var.right_margin    = S3C2410_LCDCON3_GET_HBPD(mregs->lcdcon3) + 1;
+	info.fb.var.hsync_len	    = S3C2410_LCDCON4_GET_HSPW(mregs->lcdcon4) + 1;
+
+	info.fb.var.red.offset      = 11;
+	info.fb.var.green.offset    = 5;
+	info.fb.var.blue.offset     = 0;
+	info.fb.var.transp.offset   = 0;
+	info.fb.var.red.length      = 5;
+	info.fb.var.green.length    = 6;
+	info.fb.var.blue.length     = 5;
+	info.fb.var.transp.length   = 0;
+	info.fb.fix.smem_len        =	mach_info->xres.max *
+					mach_info->yres.max *
+					mach_info->bpp.max / 8;
+
+	for (i = 0; i < 256; i++)
+		info.palette_buffer[i] = PALETTE_BUFF_CLEAR;
+
+	if (!request_mem_region((unsigned long)S3C24XX_VA_LCD, SZ_1M, "s3c2410-lcd"))
+		return -EBUSY;
+
+	dprintk("got LCD region\n");
+
+	ret = request_irq(irq, s3c2410fb_irq, SA_INTERRUPT, pdev->name, &info);
+	if (ret) {
+		dev_err(dev, "cannot get irq %d - err %d\n", irq, ret);
+		return -EBUSY;
+	}
+	
+	info.clk = clk_get(NULL, "lcd");
+	if (!info.clk || IS_ERR(info.clk)) {
+		printk(KERN_ERR "failed to get lcd clock source\n");
+		return -ENOENT;
+	}
+
+	clk_use(info.clk);
+	clk_enable(info.clk);
+	dprintk("got and enabled clock\n");
+
+	msleep(1);
+
+
+	/* Initialize video memory */
+	ret = s3c2410fb_map_video_memory(&info);
+	if (ret) {
+		printk( KERN_ERR "Failed to allocate video RAM: %d\n", ret);
+		ret = -ENOMEM;
+		goto failed;
+	}
+	dprintk("got video memory\n");
+
+	ret = s3c2410fb_init_registers(&info);
+
+	ret = s3c2410fb_check_var(&info.fb.var, &info.fb);
+
+	ret = register_framebuffer(&info.fb);
+	if (ret < 0) {
+		printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret);
+		goto failed;
+	}
+
+	/* create device files */
+	device_create_file(dev, &dev_attr_debug);
+
+	printk(KERN_INFO "fb%d: %s frame buffer device\n",
+		info.fb.node, info.fb.fix.id);
+
+	return 0;
+failed:
+ 	release_mem_region((unsigned long)S3C24XX_VA_LCD, S3C24XX_SZ_LCD);
+	return ret;
+}
+
+/* s3c2410fb_stop_lcd
+ *
+ * shutdown the lcd controller
+*/
+
+static void s3c2410fb_stop_lcd(void)
+{
+	unsigned long flags;
+	unsigned long tmp;
+
+	local_irq_save(flags);
+
+	tmp = readl(S3C2410_LCDCON1);
+	writel(tmp & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
+
+	local_irq_restore(flags);
+}
+
+/*
+ *  Cleanup
+ */
+static void __exit s3c2410fb_cleanup(void)
+{
+	s3c2410fb_stop_lcd();
+	msleep(1);
+
+ 	if (info.clk) {
+ 		clk_disable(info.clk);
+ 		clk_unuse(info.clk);
+ 		clk_put(info.clk);
+ 		info.clk = NULL;
+	}
+
+	unregister_framebuffer(&info.fb);
+	release_mem_region((unsigned long)S3C24XX_VA_LCD, S3C24XX_SZ_LCD);
+}
+
+#ifdef CONFIG_PM
+
+/* suspend and resume support for the lcd controller */
+
+static int s3c2410fb_suspend(struct device *dev, pm_message_t state, u32 level)
+{
+	if (level == SUSPEND_DISABLE || level == SUSPEND_POWER_DOWN) {
+		s3c2410fb_stop_lcd();
+
+		/* sleep before disabling the clock, we need to ensure
+		 * the LCD DMA engine is not going to get back on the bus
+		 * before the clock goes off again (bjd) */
+
+		msleep(1);
+		clk_disable(info.clk);
+	}
+
+	return 0;
+}
+
+static int s3c2410fb_resume(struct device *dev, u32 level)
+{
+	if (level == RESUME_ENABLE) {
+		clk_enable(info.clk);
+		msleep(1);
+
+		s3c2410fb_init_registers(&info);
+
+	}
+
+	return 0;
+}
+
+#else
+#define s3c2410fb_suspend NULL
+#define s3c2410fb_resume  NULL
+#endif
+
+static struct device_driver s3c2410fb_driver = {
+	.name		= "s3c2410-lcd",
+	.bus		= &platform_bus_type,
+	.probe		= s3c2410fb_probe,
+	.suspend	= s3c2410fb_suspend,
+	.resume		= s3c2410fb_resume,
+};
+
+int __devinit s3c2410fb_init(void)
+{
+	return driver_register(&s3c2410fb_driver);
+}
+
+module_init(s3c2410fb_init);
+module_exit(s3c2410fb_cleanup);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, Ben Dooks <ben-linux@fluff.org>");
+MODULE_DESCRIPTION("Framebuffer driver for the s3c2410");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/s3c2410fb.h b/drivers/video/s3c2410fb.h
new file mode 100644
--- /dev/null
+++ b/drivers/video/s3c2410fb.h
@@ -0,0 +1,56 @@
+/*
+ * linux/drivers/s3c2410fb.h
+ * Copyright (c) Arnaud Patard
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ *	    S3C2410 LCD Controller Frame Buffer Driver
+ *	    based on skeletonfb.c, sa1100fb.h
+ *
+ * ChangeLog
+ *
+ * 2004-12-04: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *      - Moved dprintk to s3c2410fb.c
+ *
+ * 2004-09-07: Arnaud Patard <arnaud.patard@rtp-net.org>
+ * 	- Renamed from h1940fb.h to s3c2410fb.h
+ * 	- Chenged h1940 to s3c2410
+ *
+ * 2004-07-15: Arnaud Patard <arnaud.patard@rtp-net.org>
+ *	- First version
+ */
+
+#ifndef __S3C2410FB_H
+#define __S3C2410FB_H
+
+struct s3c2410fb_info {
+	struct fb_info		fb;
+	struct device		*dev;
+	struct clk		*clk;
+
+	struct s3c2410fb_mach_info *mach_info;
+
+	/* raw memory addresses */
+	dma_addr_t		map_dma;	/* physical */
+	u_char *		map_cpu;	/* virtual */
+	u_int			map_size;
+
+	struct s3c2410fb_hw	regs;
+
+	/* addresses of pieces placed in raw buffer */
+	u_char *		screen_cpu;	/* virtual address of buffer */
+	dma_addr_t		screen_dma;	/* physical address of buffer */
+	unsigned int		palette_ready;
+
+	/* keep these registers in case we need to re-write palette */
+	u32			palette_buffer[256];
+	u32			pseudo_pal[16];
+};
+
+#define PALETTE_BUFF_CLEAR (0x80000000)	/* entry is clear/invalid */
+
+int s3c2410fb_init(void);
+
+#endif
diff --git a/include/asm-arm/arch-s3c2410/fb.h b/include/asm-arm/arch-s3c2410/fb.h
new file mode 100644
--- /dev/null
+++ b/include/asm-arm/arch-s3c2410/fb.h
@@ -0,0 +1,69 @@
+/* linux/include/asm/arch-s3c2410/fb.h
+ *
+ * Copyright (c) 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ * Inspired by pxafb.h
+ *
+ * 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.
+ *
+ *
+ *  Changelog:
+ *	07-Sep-2004	RTP	Created file
+ *	03-Nov-2004	BJD	Updated and minor cleanups
+ *	03-Aug-2005     RTP     Renamed to fb.h
+*/
+
+#ifndef __ASM_ARM_FB_H
+#define __ASM_ARM_FB_H
+
+#include <asm/arch/regs-lcd.h>
+
+struct s3c2410fb_val {
+	unsigned int	defval;
+	unsigned int	min;
+	unsigned int	max;
+};
+
+struct s3c2410fb_hw {
+	unsigned long	lcdcon1;
+	unsigned long	lcdcon2;
+	unsigned long	lcdcon3;
+	unsigned long	lcdcon4;
+	unsigned long	lcdcon5;
+};
+
+struct s3c2410fb_mach_info {
+	unsigned char	fixed_syncs;	/* do not update sync/border */
+
+	/* Screen size */
+	int		width;
+	int		height;
+
+	/* Screen info */
+	struct s3c2410fb_val xres;
+	struct s3c2410fb_val yres;
+	struct s3c2410fb_val bpp;
+
+	/* lcd configuration registers */
+	struct s3c2410fb_hw  regs;
+
+	/* GPIOs */
+
+	unsigned long	gpcup;
+	unsigned long	gpcup_mask;
+	unsigned long	gpccon;
+	unsigned long	gpccon_mask;
+	unsigned long	gpdup;
+	unsigned long	gpdup_mask;
+	unsigned long	gpdcon;
+	unsigned long	gpdcon_mask;
+
+	/* lpc3600 control register */
+	unsigned long	lpcsel;
+};
+
+void __init set_s3c2410fb_info(struct s3c2410fb_mach_info *hard_s3c2410fb_info);
+
+#endif /* __ASM_ARM_FB_H */
diff --git a/include/asm-arm/arch-s3c2410/lcd.h b/include/asm-arm/arch-s3c2410/lcd.h
new file mode 100644
--- /dev/null
+++ b/include/asm-arm/arch-s3c2410/lcd.h
@@ -0,0 +1,33 @@
+/* linux/include/asm/arch-s3c2410/lcd.h
+ *
+ * Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ *
+ * 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.
+ *
+ *
+ *  Changelog:
+ *     14-Mar-2005     RTP     Created file
+ *     07-Apr-2005     RTP     Renamed to s3c2410_lcd.h
+ *     03-Aug-2005     RTP     Renamed to lcd.h
+ */
+
+#ifndef __ASM_ARM_LCD_H
+#define __ASM_ARM_LCD_H
+
+struct s3c2410_bl_mach_info {
+	int		lcd_power_value;
+	int		backlight_power_value;
+	int		brightness_value;
+	int             backlight_max;
+	int             backlight_default;
+	void            (*backlight_power)(int);
+	void            (*set_brightness)(int);
+	void		(*lcd_power)(int);
+};
+
+void __init set_s3c2410bl_info(struct s3c2410_bl_mach_info *hard_s3c2410bl_info);
+
+#endif /* __ASM_ARM_LCD_H */
diff --git a/include/asm-arm/arch-s3c2410/regs-adc.h b/include/asm-arm/arch-s3c2410/regs-adc.h
--- a/include/asm-arm/arch-s3c2410/regs-adc.h
+++ b/include/asm-arm/arch-s3c2410/regs-adc.h
@@ -44,7 +44,7 @@
 #define S3C2410_ADCTSC_XP_SEN		(1<<4)
 #define S3C2410_ADCTSC_PULL_UP_DISABLE	(1<<3)
 #define S3C2410_ADCTSC_AUTO_PST		(1<<2)
-#define S3C2410_ADCTSC_XY_PST		(0x3<<0)
+#define S3C2410_ADCTSC_XY_PST(x)	(((x)&0x3)<<0)
 
 /* ADCDAT0 Bits */
 #define S3C2410_ADCDAT0_UPDOWN		(1<<15)
diff --git a/include/asm-arm/arch-s3c2410/regs-lcd.h b/include/asm-arm/arch-s3c2410/regs-lcd.h
--- a/include/asm-arm/arch-s3c2410/regs-lcd.h
+++ b/include/asm-arm/arch-s3c2410/regs-lcd.h
@@ -51,21 +51,32 @@
 
 #define S3C2410_LCDCON1_ENVID	   (1)
 
+#define S3C2410_LCDCON1_MODEMASK    0x1E
+
 #define S3C2410_LCDCON2_VBPD(x)	    ((x) << 24)
 #define S3C2410_LCDCON2_LINEVAL(x)  ((x) << 14)
 #define S3C2410_LCDCON2_VFPD(x)	    ((x) << 6)
 #define S3C2410_LCDCON2_VSPW(x)	    ((x) << 0)
 
+#define S3C2410_LCDCON2_GET_VBPD(x) ( ((x) >> 24) & 0xFF)
+#define S3C2410_LCDCON2_GET_VFPD(x) ( ((x) >>  6) & 0xFF)
+#define S3C2410_LCDCON2_GET_VSPW(x) ( ((x) >>  0) & 0x3F)
+
 #define S3C2410_LCDCON3_HBPD(x)	    ((x) << 19)
 #define S3C2410_LCDCON3_WDLY(x)	    ((x) << 19)
 #define S3C2410_LCDCON3_HOZVAL(x)   ((x) << 8)
 #define S3C2410_LCDCON3_HFPD(x)	    ((x) << 0)
 #define S3C2410_LCDCON3_LINEBLANK(x)((x) << 0)
 
+#define S3C2410_LCDCON3_GET_HBPD(x) ( ((x) >> 19) & 0x7F)
+#define S3C2410_LCDCON3_GET_HFPD(x) ( ((x) >>  0) & 0xFF)
+
 #define S3C2410_LCDCON4_MVAL(x)	    ((x) << 8)
 #define S3C2410_LCDCON4_HSPW(x)	    ((x) << 0)
 #define S3C2410_LCDCON4_WLH(x)	    ((x) << 0)
 
+#define S3C2410_LCDCON4_GET_HSPW(x) ( ((x) >>  0) & 0xFF)
+
 #define S3C2410_LCDCON5_BPP24BL	    (1<<12)
 #define S3C2410_LCDCON5_FRM565	    (1<<11)
 #define S3C2410_LCDCON5_INVVCLK	    (1<<10)
@@ -100,10 +111,16 @@
 #define S3C2410_DITHMODE   S3C2410_LCDREG(0x4C)
 #define S3C2410_TPAL	   S3C2410_LCDREG(0x50)
 
+#define S3C2410_TPAL_EN		(1<<24)
+
 /* interrupt info */
 #define S3C2410_LCDINTPND  S3C2410_LCDREG(0x54)
 #define S3C2410_LCDSRCPND  S3C2410_LCDREG(0x58)
 #define S3C2410_LCDINTMSK  S3C2410_LCDREG(0x5C)
+#define S3C2410_LCDINT_FIWSEL	(1<<2)
+#define	S3C2410_LCDINT_FRSYNC	(1<<1)
+#define S3C2410_LCDINT_FICNT	(1<<0)
+
 #define S3C2410_LPCSEL	   S3C2410_LCDREG(0x60)
 
 #define S3C2410_TFTPAL(x)  S3C2410_LCDREG((0x400 + (x)*4))
diff --git a/include/asm-arm/arch-s3c2410/regs-sdi.h b/include/asm-arm/arch-s3c2410/regs-sdi.h
--- a/include/asm-arm/arch-s3c2410/regs-sdi.h
+++ b/include/asm-arm/arch-s3c2410/regs-sdi.h
@@ -47,7 +47,8 @@
 #define S3C2410_SDICMDCON_LONGRSP     (1<<10)
 #define S3C2410_SDICMDCON_WAITRSP     (1<<9)
 #define S3C2410_SDICMDCON_CMDSTART    (1<<8)
-#define S3C2410_SDICMDCON_INDEX       (0xff)
+#define S3C2410_SDICMDCON_SENDERHOST  (1<<6)
+#define S3C2410_SDICMDCON_INDEX       (0x3f)
 
 #define S3C2410_SDICMDSTAT_CRCFAIL    (1<<12)
 #define S3C2410_SDICMDSTAT_CMDSENT    (1<<11)
@@ -73,6 +74,7 @@
 #define S3C2410_SDIDCON_XFER_RXSTART  (2<<12)
 #define S3C2410_SDIDCON_XFER_TXSTART  (3<<12)
 
+#define S3C2410_SDIDCON_BLKNUM_MASK   (0xFFF)
 #define S3C2410_SDIDCNT_BLKNUM_SHIFT  (12)
 
 #define S3C2410_SDIDSTA_RDYWAITREQ    (1<<10)
@@ -115,4 +117,14 @@
 #define S3C2410_SDIIMSK_RXFIFOFULL     (1<<1)
 #define S3C2410_SDIIMSK_RXFIFOHALF     (1<<0)
 
+#define S3C2410_SDICMDCON_ABORT       (1<<12)
+#define S3C2410_SDICMDCON_WITHDATA    (1<<11)
+#define S3C2410_SDICMDCON_LONGRSP     (1<<10)
+#define S3C2410_SDICMDCON_WAITRSP     (1<<9)
+#define S3C2410_SDICMDCON_CMDSTART    (1<<8)
+#define S3C2410_SDICMDCON_SENDERHOST  (1<<6)
+
+#define S3C2410_SDIDCON_BLKNUM_MASK   (0xFFF)
+#define S3C2410_SDIDCNT_BLKNUM_SHIFT  (12)
+
 #endif /* __ASM_ARM_REGS_SDI */
diff --git a/include/asm-arm/arch-s3c2410/regs-udc.h b/include/asm-arm/arch-s3c2410/regs-udc.h
--- a/include/asm-arm/arch-s3c2410/regs-udc.h
+++ b/include/asm-arm/arch-s3c2410/regs-udc.h
@@ -17,8 +17,7 @@
 #ifndef __ASM_ARCH_REGS_UDC_H
 #define __ASM_ARCH_REGS_UDC_H
 
-
-#define S3C2410_USBDREG(x) ((x) + S3C24XX_VA_USBDEV)
+#define S3C2410_USBDREG(x) (x)
 
 #define S3C2410_UDC_FUNC_ADDR_REG	S3C2410_USBDREG(0x0140)
 #define S3C2410_UDC_PWR_REG		S3C2410_USBDREG(0x0144)
@@ -142,8 +141,8 @@
 #define S3C2410_UDC_OCSR2_ISO		(1<<6) // R/W
 #define S3C2410_UDC_OCSR2_DMAIEN	(1<<5) // R/W
 
-#define S3C2410_UDC_SETIX(x)	    \
-	__raw_writel(S3C2410_UDC_INDEX_ ## x, S3C2410_UDC_INDEX_REG);
+#define S3C2410_UDC_SETIX(base,x)	    \
+	writel(S3C2410_UDC_INDEX_ ## x, base+S3C2410_UDC_INDEX_REG);
 
 
 #define S3C2410_UDC_EP0_CSR_OPKRDY	(1<<0)
diff --git a/include/asm-arm/arch-s3c2410/ts.h b/include/asm-arm/arch-s3c2410/ts.h
new file mode 100644
--- /dev/null
+++ b/include/asm-arm/arch-s3c2410/ts.h
@@ -0,0 +1,28 @@
+/* linux/include/asm/arch-s3c2410/ts.h
+ *
+ * Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ *
+ * 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.
+ *
+ *
+ *  Changelog:
+ *     24-Mar-2005     RTP     Created file
+ *     03-Aug-2005     RTP     Renamed to ts.h
+ */
+
+#ifndef __ASM_ARM_TS_H
+#define __ASM_ARM_TS_H
+
+struct s3c2410_ts_mach_info {
+       int             delay;
+       int             presc;
+       int             oversampling_shift;
+};
+
+void __init set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info);
+
+#endif /* __ASM_ARM_TS_H */
+
diff --git a/include/asm-arm/arch-s3c2410/udc.h b/include/asm-arm/arch-s3c2410/udc.h
new file mode 100644
--- /dev/null
+++ b/include/asm-arm/arch-s3c2410/udc.h
@@ -0,0 +1,32 @@
+/* linux/include/asm/arch-s3c2410/udc.h
+ *
+ * Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ *
+ * 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.
+ *
+ *
+ *  Changelog:
+ *     14-Mar-2005     RTP     Created file
+ *     02-Aug-2005     RTP     File rename
+ */
+
+#ifndef __ASM_ARM_UDC_H
+#define __ASM_ARM_UDC_H
+
+#include <asm/arch/regs-udc.h>
+
+#define S3C2410_UDC_P_ENABLE	1	/* Pull-up enable        */
+#define S3C2410_UDC_P_DISABLE	2	/* Pull-up disable       */
+#define S3C2410_UDC_P_RESET	3	/* UDC reset, in case of */
+
+struct s3c2410_udc_mach_info {
+	void            (*udc_command)(unsigned char);
+
+};
+
+void __init set_s3c2410udc_info(struct s3c2410_udc_mach_info *hard_s3c2410udc_info);
+
+#endif /* __ASM_ARM_UDC_H */
diff --git a/include/linux/font.h b/include/linux/font.h
--- a/include/linux/font.h
+++ b/include/linux/font.h
@@ -31,6 +31,8 @@ struct font_desc {
 #define SUN12x22_IDX	7
 #define ACORN8x8_IDX	8
 #define	MINI4x6_IDX	9
+#define	CLEAN4x6_IDX   10
+#define	CLEAN5x8_IDX   11
 
 extern struct font_desc	font_vga_8x8,
 			font_vga_8x16,
@@ -41,7 +43,9 @@ extern struct font_desc	font_vga_8x8,
 			font_sun_8x16,
 			font_sun_12x22,
 			font_acorn_8x8,
-			font_mini_4x6;
+			font_mini_4x6,
+			font_clean_4x6,
+			font_clean_5x8;
 
 /* Find a font with a specific name */
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -24,12 +24,55 @@ struct mmc_cid {
 };
 
 struct mmc_csd {
-	unsigned char		mmca_vsn;
-	unsigned short		cmdclass;
+	u8  sd;
+	u8  csd_vers;
+	u8  spec_vers;
+	u8  taac;
+	u8  nsac;
+	u16 ccc;
+	u8  tran_speed;
+	u8  read_bl_len;
+	u8  read_bl_partial;
+	u8  write_blk_misalign;
+	u8  read_blk_misalign;
+	u8  dsr_imp;
+	u16 c_size;
+	u8  vdd_r_curr_min;
+	u8  vdd_r_curr_max;
+	u8  vdd_w_curr_min;
+	u8  vdd_w_curr_max;
+	u8  c_size_mult;
+	union {
+		struct { /* MMC system specification version 3.1 */
+			u8  erase_grp_size;
+			u8  erase_grp_mult;
+		} mmc31;
+		struct { /* MMC system specification version 2.2 */
+			u8  sector_size;
+			u8  erase_grp_size;
+		} mmc22;
+		struct { /* SD Version 1.0 */
+			u8  erase_blk_en;
+			u8  erase_blk_size;
+		} sd10;
+	} erase;
+
+	u8  wp_grp_size;
+	u8  wp_grp_enable;
+	u8  default_ecc;
+	u8  r2w_factor;
+	u8  write_bl_len;
+	u8  write_bl_partial;
+	u8  file_format_grp;
+	u8  copy;
+	u8  perm_write_protect;
+	u8  tmp_write_protect;
+	u8  file_format;
+	u8  ecc;
+
 	unsigned short		tacc_clks;
 	unsigned int		tacc_ns;
 	unsigned int		max_dtr;
-	unsigned int		read_blkbits;
 	unsigned int		capacity;
 };
 
@@ -42,8 +85,10 @@ struct mmc_card {
 	struct list_head	node;		/* node in hosts devices list */
 	struct mmc_host		*host;		/* the host this device belongs to */
 	struct device		dev;		/* the device */
+	unsigned char		sd;
 	unsigned int		rca;		/* relative card address of device */
 	unsigned int		state;		/* (our) card state */
+	u8			bus_width;	/* bus_width the card is set to */
 #define MMC_STATE_PRESENT	(1<<0)		/* present in sysfs */
 #define MMC_STATE_DEAD		(1<<1)		/* device no longer in stack */
 #define MMC_STATE_BAD		(1<<2)		/* unrecognised device */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -61,6 +61,8 @@ struct mmc_host_ops {
 struct mmc_card;
 struct device;
 
+#define MMC_HOST_WIDEMODE	1
+
 struct mmc_host {
 	struct device		*dev;
 	struct class_device	class_dev;
@@ -69,6 +71,7 @@ struct mmc_host {
 	unsigned int		f_min;
 	unsigned int		f_max;
 	u32			ocr_avail;
+	u32			flags;
 
 	/* host specific block data */
 	unsigned int		max_seg_size;	/* see blk_queue_max_segment_size */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -48,6 +48,8 @@ struct mmc_command {
 #define MMC_ERR_FIFO	3
 #define MMC_ERR_FAILED	4
 #define MMC_ERR_INVALID	5
+#define MMC_ERR_BUSY	6
+#define MMC_ERR_DMA	7
 
 	struct mmc_data		*data;		/* data segment associated with cmd */
 	struct mmc_request	*mrq;		/* assoicated request */
@@ -64,6 +66,7 @@ struct mmc_data {
 #define MMC_DATA_WRITE	(1 << 8)
 #define MMC_DATA_READ	(1 << 9)
 #define MMC_DATA_STREAM	(1 << 10)
+#define MMC_DATA_WIDE	(1 << 11)
 
 	unsigned int		bytes_xfered;
 
@@ -72,6 +75,8 @@ struct mmc_data {
 
 	unsigned int		sg_len;		/* size of scatter list */
 	struct scatterlist	*sg;		/* I/O scatter list */
+
+	struct request		*req;
 };
 
 struct mmc_request {
diff --git a/include/linux/mmc/protocol.h b/include/linux/mmc/protocol.h
--- a/include/linux/mmc/protocol.h
+++ b/include/linux/mmc/protocol.h
@@ -86,6 +86,9 @@
 #define SD_APP_OP_COND           41   /* bcr  [31:0] OCR         R3  */
 #define SD_APP_SEND_SCR          51   /* adtc                    R1  */
 
+#define MMC_ACMD_SD_APP_OP_COND  41   /* ????  op conds		 ??? */
+#define MMC_ACMD_SD_SET_BUS_WIDTH 6   /* ????  00=1 10=4	 ??? */
+
 /*
   MMC status in R1
   Type
@@ -128,7 +131,8 @@
 /* These are unpacked versions of the actual responses */
 
 struct _mmc_csd {
-	u8  csd_structure;
+	u8  sd;
+	u8  csd_vers;
 	u8  spec_vers;
 	u8  taac;
 	u8  nsac;
@@ -149,11 +153,15 @@ struct _mmc_csd {
 		struct { /* MMC system specification version 3.1 */
 			u8  erase_grp_size;
 			u8  erase_grp_mult;
-		} v31;
+		} mmc31;
 		struct { /* MMC system specification version 2.2 */
 			u8  sector_size;
 			u8  erase_grp_size;
-		} v22;
+		} mmc22;
+		struct { /* SD Version 1.0 */
+			u8  erase_blk_en;
+			u8  erase_blk_size;
+		} sd10;
 	} erase;
 	u8  wp_grp_size;
 	u8  wp_grp_enable;
@@ -169,6 +177,13 @@ struct _mmc_csd {
 	u8  ecc;
 };
 
+#define SD_CSDV_1	0
+
+#define MMC_CSDV_1	0
+#define MMC_CSDV_14	1
+#define MMC_CSDV_2	2
+#define MMC_CSDV_3	3
+
 #define MMC_VDD_145_150	0x00000001	/* VDD voltage 1.45 - 1.50 */
 #define MMC_VDD_150_155	0x00000002	/* VDD voltage 1.50 - 1.55 */
 #define MMC_VDD_155_160	0x00000004	/* VDD voltage 1.55 - 1.60 */
diff --git a/kernel/Makefile b/kernel/Makefile
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -7,7 +7,7 @@ obj-y     = sched.o fork.o exec_domain.o
 	    sysctl.o capability.o ptrace.o timer.o user.o \
 	    signal.o sys.o kmod.o workqueue.o pid.o \
 	    rcupdate.o intermodule.o extable.o params.o posix-timers.o \
-	    kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o
+	    kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mhelper.o
 
 obj-$(CONFIG_FUTEX) += futex.o
 obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
diff --git a/kernel/mhelper.c b/kernel/mhelper.c
new file mode 100644
--- /dev/null
+++ b/kernel/mhelper.c
@@ -0,0 +1,114 @@
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/errno.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <asm/mach/map.h>
+
+#include "mhelper.h"
+
+
+
+#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, S3C2410_PA_##x, S3C24XX_SZ_##x, MT_DEVICE }
+
+struct map_desc mhelper_iodesc[] = {
+        IODESC_ENT(IRQ),
+        IODESC_ENT(MEMCTRL),
+        IODESC_ENT(USBHOST),
+        IODESC_ENT(DMA),
+        IODESC_ENT(CLKPWR),
+        IODESC_ENT(LCD),
+        IODESC_ENT(NAND),
+        IODESC_ENT(UART),
+        IODESC_ENT(TIMER),
+        IODESC_ENT(USBDEV),
+        IODESC_ENT(WATCHDOG),
+        IODESC_ENT(IIC),
+        IODESC_ENT(IIS),
+        IODESC_ENT(GPIO),
+        IODESC_ENT(RTC),
+        IODESC_ENT(ADC),
+        IODESC_ENT(SPI),
+        IODESC_ENT(SDI),
+};
+
+#define       IODESC_SIZE     (sizeof(mhelper_iodesc)/sizeof(struct map_desc))
+	
+static inline unsigned long
+map_io_to_va(unsigned long addr)
+{
+	int index;
+
+      for (index=0; index<IODESC_SIZE; index++) {
+               unsigned long phys = mhelper_iodesc[index].physical;
+               unsigned long len = mhelper_iodesc[index].length;
+
+		if ((addr >= phys) & (addr < phys+len))
+			break;
+	}
+      if (index < IODESC_SIZE) {
+               unsigned long phys = mhelper_iodesc[index].physical;
+               unsigned long virt = mhelper_iodesc[index].virtual;
+
+		return (addr - phys + virt);
+	}
+	return 0;
+}
+
+
+extern asmlinkage long
+sys_mhelper(uint32_t cmd, uint32_t addr, uint32_t value)
+{
+	unsigned long vaddr = 0;
+	int verb;
+
+	if (!(vaddr = map_io_to_va(addr)))
+		return -ENXIO;
+
+
+	verb = (cmd & OPT_VERB);	
+	cmd &= ~OPT_VERB;
+
+        switch (cmd) {
+
+	case CMD_READ_B:
+		value = __raw_readb(vaddr);
+		if (verb)
+			printk("0x%08x: 0x%02x (%d)\n", 
+				addr, value, value);
+		break;
+	case CMD_READ_W:
+		value = __raw_readw(vaddr);
+		if (verb)
+			printk("0x%08x: 0x%04x (%d)\n", 
+				addr, value, value);
+		break;
+	case CMD_READ_L:
+		value = __raw_readl(vaddr);
+		if (verb)
+			printk("0x%08x: 0x%08x (%d)\n", 
+				addr, value, value);
+		break;
+
+	case CMD_WRITE_B:
+		__raw_writeb(value, vaddr);
+		break;
+	case CMD_WRITE_W:
+		__raw_writew(value, vaddr);
+		break;
+	case CMD_WRITE_L:
+		__raw_writel(value, vaddr);
+		break;
+
+	default:
+		return -EINVAL;
+		break;
+	}
+
+	return verb?0:value;
+}
+
diff --git a/kernel/mhelper.h b/kernel/mhelper.h
new file mode 100644
--- /dev/null
+++ b/kernel/mhelper.h
@@ -0,0 +1,24 @@
+
+#define CAT_READ	0x10
+#define CAT_WRITE	0x20
+
+#define CAT_BYTE	0x01
+#define CAT_WORD	0x02
+#define CAT_LONG	0x03
+
+#define	OPT_VERB	0x80
+
+
+enum {
+	CMD_VERSION = 0,
+
+	CMD_READ_B  = 0x11,
+	CMD_READ_W,
+	CMD_READ_L,
+
+	CMD_WRITE_B = 0x21,
+	CMD_WRITE_W,
+	CMD_WRITE_L,
+
+};
+
