diff --git a/Makefile b/Makefile
index d970cb1..1ea6235 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 21
-EXTRAVERSION =
+EXTRAVERSION = -h1940
 NAME = Nocturnal Monster Puppy
 
 # *DOCUMENTATION*
@@ -182,8 +182,8 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
 # 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
index ae89cdd..0ff174e 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -231,7 +231,7 @@
 		CALL(sys_mincore)
 /* 220 */	CALL(sys_madvise)
 		CALL(ABI(sys_fcntl64, sys_oabi_fcntl64))
-		CALL(sys_ni_syscall) /* TUX */
+		CALL(sys_mhelper) /* TUX */
 		CALL(sys_ni_syscall)
 		CALL(sys_gettid)
 /* 225 */	CALL(ABI(sys_readahead, sys_oabi_readahead))
diff --git a/arch/arm/mach-s3c2410/mach-bast.c b/arch/arm/mach-s3c2410/mach-bast.c
index 7b81296..67f5e48 100644
--- a/arch/arm/mach-s3c2410/mach-bast.c
+++ b/arch/arm/mach-s3c2410/mach-bast.c
@@ -411,6 +411,7 @@ static struct s3c2410_platform_i2c bast_i2c_info = {
 
 
 static struct s3c2410fb_mach_info __initdata bast_lcd_info = {
+	.type		= S3C2410_LCDCON1_TFT,
 	.width		= 640,
 	.height		= 480,
 
diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c
index d052ab2..11332da 100644
--- a/arch/arm/mach-s3c2410/mach-h1940.c
+++ b/arch/arm/mach-s3c2410/mach-h1940.c
@@ -32,12 +32,18 @@
 #include <asm/arch/regs-serial.h>
 #include <asm/arch/regs-lcd.h>
 #include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-timer.h>
 #include <asm/arch/regs-clock.h>
+#include <asm/arch/irqs.h>
 
 #include <asm/arch/h1940.h>
 #include <asm/arch/h1940-latch.h>
 #include <asm/arch/fb.h>
 #include <asm/arch/udc.h>
+#include <asm/arch/ts.h>
+#include <asm/arch/lcd.h>
+#include <asm/arch/mmc.h>
+#include <asm/arch/buttons.h>
 
 #include <asm/plat-s3c24xx/clock.h>
 #include <asm/plat-s3c24xx/devs.h>
@@ -129,12 +135,54 @@ static struct s3c2410_udc_mach_info h1940_udc_cfg __initdata = {
 };
 
 
+static struct s3c2410_ts_mach_info h1940_ts_cfg __initdata = {
+		.delay = 10000,
+		.presc = 49,
+		.oversampling_shift = 2,
+};
+
+static struct s3c24xx_mmc_platdata h1940_mmc_cfg = {
+	.gpio_detect    = S3C2410_GPF5,
+};
 
 /**
  * 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)
+{
+	int value;
+
+	if (!on) {
+		/* set to 3ec */
+		s3c2410_gpio_setpin(S3C2410_GPC0, 0);
+		/* wait for 3ac */
+		do {
+			value = s3c2410_gpio_getpin(S3C2410_GPC6);
+		} while (value);
+		/* set to 38c */
+		s3c2410_gpio_setpin(S3C2410_GPC5, 0);
+	}
+	else {
+		/* Set to 3ac */
+		s3c2410_gpio_setpin(S3C2410_GPC5, 1);
+		/* Set to 3ad */
+		s3c2410_gpio_setpin(S3C2410_GPC0, 1);
+	}
+}
+
 static struct s3c2410fb_mach_info h1940_lcdcfg __initdata = {
 	.fixed_syncs=		1,
+	.type=			S3C2410_LCDCON1_TFT,
 	.regs={
 		.lcdcon1=	S3C2410_LCDCON1_TFT16BPP | \
 				S3C2410_LCDCON1_TFT | \
@@ -173,6 +221,72 @@ static struct s3c2410fb_mach_info h1940_lcdcfg __initdata = {
 	.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,
+	.lcd_power		= h1940_lcd_power
+};
+
+static struct s3c_button h1940_butts[] = {
+	DECLARE_BUTTON(  S3C2410_GPF0,    KEY_POWER,	      "Power", 1 ),
+	DECLARE_BUTTON(  S3C2410_GPF6,    KEY_ENTER, 	     "Select", 1 ),
+	DECLARE_BUTTON(  S3C2410_GPF7,       KEY_RO,	     "Record", 0 ),
+	DECLARE_BUTTON(  S3C2410_GPG0,       KEY_F9,	   "Calendar", 0 ),
+	DECLARE_BUTTON(  S3C2410_GPG2,      KEY_F10,	   "Contacts", 0 ),
+	DECLARE_BUTTON(  S3C2410_GPG3,      KEY_F11,	       "Mail", 0 ),
+	DECLARE_BUTTON(  S3C2410_GPG6,     KEY_LEFT,	 "Left_arrow", 0 ),
+	DECLARE_BUTTON(  S3C2410_GPG7,      KEY_F12,	       "Home", 0 ),
+	DECLARE_BUTTON(  S3C2410_GPG8,    KEY_RIGHT,	"Right_arrow", 0 ),
+	DECLARE_BUTTON(  S3C2410_GPG9,       KEY_UP,	   "Up_arrow", 0 ),
+	DECLARE_BUTTON( S3C2410_GPG10,     KEY_DOWN,	 "Down_arrow", 0 ),
+};
+
+static struct s3c_butt_mach_info h1940_butt_cfg __initdata = {
+	.buttons  = h1940_butts,
+	.size = ARRAY_SIZE(h1940_butts),
+};
+
 static struct platform_device s3c_device_leds = {
 	.name             = "h1940-leds",
 	.id               = -1,
@@ -181,11 +295,16 @@ static struct platform_device s3c_device_leds = {
 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_leds,
+	&s3c_device_ts,
+	&s3c_device_buttons,
+	&s3c_device_nand,
 };
 
 static struct s3c24xx_board h1940_board __initdata = {
@@ -218,7 +337,12 @@ static void __init h1940_init(void)
 	u32 tmp;
 
 	s3c24xx_fb_set_platdata(&h1940_lcdcfg);
+	set_s3c2410ts_info(&h1940_ts_cfg);
+	set_s3c2410bl_info(&h1940_blcfg);
  	s3c24xx_udc_set_platdata(&h1940_udc_cfg);
+	s3c24xx_butt_set_platdata(&h1940_butt_cfg);
+
+	s3c24xx_sdi_set_platdata(&h1940_mmc_cfg);
 
 	/* Turn off suspend on both USB ports, and switch the
 	 * selectable USB port to USB device mode. */
diff --git a/arch/arm/mach-s3c2440/mach-rx3715.c b/arch/arm/mach-s3c2440/mach-rx3715.c
index ae1d0a8..98dd078 100644
--- a/arch/arm/mach-s3c2440/mach-rx3715.c
+++ b/arch/arm/mach-s3c2440/mach-rx3715.c
@@ -110,6 +110,7 @@ static struct s3c2410_uartcfg rx3715_uartcfgs[] = {
 /* framebuffer lcd controller information */
 
 static struct s3c2410fb_mach_info rx3715_lcdcfg __initdata = {
+	.type	=		S3C2410_LCDCON1_TFT,
 	.regs	= {
 		.lcdcon1 =	S3C2410_LCDCON1_TFT16BPP | \
 				S3C2410_LCDCON1_TFT | \
diff --git a/arch/arm/mach-s3c2440/mach-smdk2440.c b/arch/arm/mach-s3c2440/mach-smdk2440.c
index c17eb5b..c66a094 100644
--- a/arch/arm/mach-s3c2440/mach-smdk2440.c
+++ b/arch/arm/mach-s3c2440/mach-smdk2440.c
@@ -104,7 +104,8 @@ static struct s3c2410_uartcfg smdk2440_uartcfgs[] __initdata = {
 /* LCD driver info */
 
 static struct s3c2410fb_mach_info smdk2440_lcd_cfg __initdata = {
-	.regs	= {
+	.type	=		  S3C2410_LCDCON1_TFT,
+  	.regs	= {
 
 		.lcdcon1	= S3C2410_LCDCON1_TFT16BPP |
 				  S3C2410_LCDCON1_TFT |
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
index 0fe53b3..f9106f1 100644
--- a/arch/arm/plat-s3c24xx/devs.c
+++ b/arch/arm/plat-s3c24xx/devs.c
@@ -30,6 +30,10 @@
 
 #include <asm/arch/regs-serial.h>
 #include <asm/arch/udc.h>
+#include <asm/arch/ts.h>
+#include <asm/arch/lcd.h>
+#include <asm/arch/mmc.h>
+#include <asm/arch/buttons.h>
 
 #include <asm/plat-s3c24xx/devs.h>
 #include <asm/plat-s3c24xx/cpu.h>
@@ -187,6 +191,22 @@ void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
 	}
 }
 
+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[] = {
@@ -206,6 +226,44 @@ 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             = "s3c-buttons",
+	.id               = -1,
+};
+
+EXPORT_SYMBOL(s3c_device_buttons);
+
+void __init s3c24xx_butt_set_platdata(struct s3c_butt_mach_info *pd)
+{
+	struct s3c_butt_mach_info *npd;
+
+	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
+	if (npd) {
+		memcpy(npd, pd, sizeof(*npd));
+		s3c_device_buttons.dev.platform_data = npd;
+	} else {
+		printk(KERN_ERR "no memory for buttons platform data\n");
+	}
+}
+
 /* USB Device (Gadget)*/
 
 static struct resource s3c_usbgadget_resource[] = {
@@ -244,7 +302,6 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
 	}
 }
 
-
 /* Watchdog */
 
 static struct resource s3c_wdt_resource[] = {
@@ -402,6 +459,20 @@ struct platform_device s3c_device_sdi = {
 
 EXPORT_SYMBOL(s3c_device_sdi);
 
+void __init s3c24xx_sdi_set_platdata(struct s3c24xx_mmc_platdata *pd)
+{
+	struct s3c24xx_mmc_platdata *npd;
+
+	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
+	if (npd) {
+		memcpy(npd, pd, sizeof(*npd));
+		s3c_device_sdi.dev.platform_data = npd;
+	} else {
+		printk(KERN_ERR "no memory for sdi platform data\n");
+	}
+}
+
+
 /* SPI (0) */
 
 static struct resource s3c_spi0_resource[] = {
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index f17e9c7..8bb06f2 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -214,6 +214,15 @@ config KEYBOARD_AAED2000
 	  To compile this driver as a module, choose M here: the
 	  module will be called aaed2000_kbd.
 
+config S3C_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.
+
 config KEYBOARD_GPIO
 	tristate "GPIO Buttons"
 	depends on GENERIC_GPIO
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 586a0fe..de2af23 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -18,5 +18,6 @@ obj-$(CONFIG_KEYBOARD_HIL)		+= hil_kbd.o
 obj-$(CONFIG_KEYBOARD_HIL_OLD)		+= hilkbd.o
 obj-$(CONFIG_KEYBOARD_OMAP)		+= omap-keypad.o
 obj-$(CONFIG_KEYBOARD_AAED2000)		+= aaed2000_kbd.o
+obj-$(CONFIG_S3C_BUTTONS)		+= s3c_buttons.o
 obj-$(CONFIG_KEYBOARD_GPIO)		+= gpio_keys.o
 
diff --git a/drivers/input/keyboard/s3c_buttons.c b/drivers/input/keyboard/s3c_buttons.c
new file mode 100644
index 0000000..85a361f
--- /dev/null
+++ b/drivers/input/keyboard/s3c_buttons.c
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ * 
+ */
+
+#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/platform_device.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/buttons.h>
+
+/* For id.version */
+#define DRV_VERSION	0x0001
+#define DRV_NAME	"s3c-buttons"
+
+#define REPEAT_DELAY	HZ/10
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_DESCRIPTION("s3c24xx buttons driver");
+MODULE_LICENSE("GPL");
+
+struct s3c_buttons_private {
+	struct device		*parent;
+	struct input_dev	*dev;
+	spinlock_t		lock;
+	int 			count;
+	int			shift;	
+};
+
+static struct s3c_buttons_private priv;
+
+static irqreturn_t s3c_but_keyevent(int irq, void *dev)
+{
+	struct s3c_button *button = (struct s3c_button *)dev;
+	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;
+	
+	dev_dbg(priv.parent, "%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 s3c_but_timer_callback(unsigned long data)
+{
+		struct s3c_button *button = (struct s3c_button *) data;
+		int down;
+
+		down = !(s3c2410_gpio_getpin(button->pin));
+		
+		if (down) {
+			dev_dbg(priv.parent, "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 s3c_but_probe(struct platform_device *pdev)
+{
+	struct input_dev *input_dev;
+	struct s3c_button *s3c_buttons;
+	struct s3c_button *bp;
+	struct s3c_butt_mach_info *butt_info;
+	int i, err;
+
+	butt_info = pdev->dev.platform_data;
+	s3c_buttons = butt_info->buttons;
+
+	/* Initialise input stuff */
+	memset(&priv, 0, sizeof(struct s3c_buttons_private));
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		printk(KERN_ERR "Unable to allocate the input device !!\n");
+		return -ENOMEM;
+	}
+
+	priv.parent	     = &pdev->dev;
+	priv.dev	     = input_dev;
+	priv.dev->evbit[0]   = BIT(EV_KEY);
+
+	priv.dev->private    = &priv;
+	priv.dev->cdev.dev   = &pdev->dev;
+	priv.dev->name       = DRV_NAME;
+	priv.dev->id.bustype = BUS_HOST;
+	priv.dev->id.vendor  = 0xDEAD;
+	priv.dev->id.product = 0xBEEF;
+	priv.dev->id.version = DRV_VERSION;
+
+	for (i = 0, bp = s3c_buttons; i < butt_info->size; i++, bp++) {
+		set_bit(bp->keycode, priv.dev->keybit);
+		bp->irq = s3c2410_gpio_getirq(bp->pin);
+		s3c2410_gpio_cfgpin(bp->pin, S3C2410_GPIO_SFN2);
+		err = request_irq (bp->irq, s3c_but_keyevent,
+			     IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+			     bp->name, bp);
+		if (err) {
+			dev_err(&pdev->dev, "error requesting irq %d\n", bp->irq);
+			goto err_irq;
+		}
+
+		if (bp->wake)
+			enable_irq_wake(bp->irq);
+
+		init_timer(&bp->timer);
+		bp->timer.function = s3c_but_timer_callback;
+		bp->timer.data     = (unsigned long)bp;
+	}
+	
+	dev_info(&pdev->dev, "successfully loaded\n");
+
+	/* All went ok, so register to the input system */
+	input_register_device(priv.dev);
+
+	return 0;
+
+err_irq:
+	for (--i,--bp ; i >= 0; i--, bp--) {
+		if (bp->wake)
+			disable_irq_wake(bp->irq);
+		disable_irq(bp->irq);
+		free_irq(bp->irq, bp);
+	}
+	input_free_device(input_dev);	
+	return err;
+}
+
+static int s3c_but_remove(struct platform_device *pdev)
+{
+	int i;
+	struct s3c_button *s3c_buttons;
+	struct s3c_button *bp;
+	struct s3c_butt_mach_info *butt_info;
+
+	butt_info = pdev->dev.platform_data;
+	s3c_buttons = butt_info->buttons;
+
+	for (i = 0, bp = s3c_buttons; i < butt_info->size; i++, bp++) {
+		if (bp->wake)
+			disable_irq_wake(bp->irq);
+		disable_irq(bp->irq);
+		free_irq(bp->irq, bp);
+	}
+
+	input_unregister_device(priv.dev);
+
+	return 0;
+}
+
+
+static struct platform_driver s3c_but_driver = {
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= s3c_but_probe,
+	.remove		= s3c_but_remove,
+};
+
+
+static int __init s3c_but_init(void)
+{
+	return platform_driver_register(&s3c_but_driver);
+}
+
+static void __exit s3c_but_exit(void)
+{
+	platform_driver_unregister(&s3c_but_driver);
+}
+
+module_init(s3c_but_init);
+module_exit(s3c_but_exit);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 9716180..6cee240 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -54,6 +54,24 @@ config TOUCHSCREEN_CORGI
 	  To compile this driver as a module, choose M here: the
 	  module will be called corgi_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
index 30e6e22..8cbaac5 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)	+= touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)	+= touchwin.o
 obj-$(CONFIG_TOUCHSCREEN_UCB1400)	+= ucb1400_ts.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
index 0000000..0dea2ef
--- /dev/null
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -0,0 +1,401 @@
+/*
+ * 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/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 <linux/platform_device.h>
+#include <linux/clk.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>
+
+/* For ts.dev.id.version */
+#define S3C2410TSVERSION	0x0101
+
+#define TSC_SLEEP  (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
+
+#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;
+};
+
+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)
+{
+	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)
+{
+	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 platform_device *pdev)
+{
+	struct s3c2410_ts_mach_info *info;
+	struct input_dev *input_dev;
+
+	info = ( struct s3c2410_ts_mach_info *)pdev->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_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));
+	input_dev = input_allocate_device();
+
+	if (!input_dev) {
+		printk(KERN_ERR "Unable to allocate the input device !!\n");
+		return -ENOMEM;
+	}
+
+	ts.dev = input_dev;
+	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);
+
+	ts.dev->private = &ts;
+	ts.dev->name = s3c2410ts_name;
+	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, IRQF_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, IRQF_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 platform_device *pdev)
+{
+	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_put(adc_clock);
+		adc_clock = NULL;
+	}
+
+	input_unregister_device(ts.dev);
+	iounmap(base_addr);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c2410ts_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	writel(TSC_SLEEP, base_addr+S3C2410_ADCTSC);
+	writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_STDBM,
+	       base_addr+S3C2410_ADCCON);
+
+	disable_irq(IRQ_ADC);
+	disable_irq(IRQ_TC);
+
+	clk_disable(adc_clock);
+
+	return 0;
+}
+
+static int s3c2410ts_resume(struct platform_device *pdev)
+{
+	struct s3c2410_ts_mach_info *info =
+		( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
+
+	clk_enable(adc_clock);
+	msleep(1);
+
+	enable_irq(IRQ_ADC);
+	enable_irq(IRQ_TC);
+
+	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);
+
+	return 0;
+}
+
+#else
+#define s3c2410ts_suspend NULL
+#define s3c2410ts_resume  NULL
+#endif
+
+static struct platform_driver s3c2410ts_driver = {
+       .driver         = {
+	       .name   = "s3c2410-ts",
+	       .owner  = THIS_MODULE,
+       },
+       .probe          = s3c2410ts_probe,
+       .remove         = s3c2410ts_remove,
+       .suspend        = s3c2410ts_suspend,
+       .resume         = s3c2410ts_resume,
+
+};
+
+
+static int __init s3c2410ts_init(void)
+{
+	return platform_driver_register(&s3c2410ts_driver);
+}
+
+static void __exit s3c2410ts_exit(void)
+{
+	platform_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
index 12af9c7..d15f30f 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -125,4 +125,16 @@ config MMC_TIFM_SD
           To compile this driver as a module, choose M here: the
 	  module will be called tifm_sd.
 
+config MMC_S3C
+	tristate "Samsung S3C SD/MMC Card Interface support"
+	depends on ARCH_S3C2410 && MMC
+	help
+	  This selects a driver for the MCI interface found in
+          Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
+	  If you have a board based on one of those and a MMC/SD
+	  slot, say Y or M here.
+
+	  If unsure, say N.
+
+
 endmenu
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 83ffb93..7c2eb45 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -24,10 +24,12 @@ obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
 obj-$(CONFIG_MMC_AT91)		+= at91_mci.o
 obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
+obj-$(CONFIG_MMC_S3C)		+= s3cmci.o
 
 mmc_core-y := mmc.o mmc_sysfs.o
 mmc_core-$(CONFIG_BLOCK) += mmc_queue.o
 
 ifeq ($(CONFIG_MMC_DEBUG),y)
+obj-$(CONFIG_MMC)		+= mmc_debug.o
 EXTRA_CFLAGS += -DDEBUG
 endif
diff --git a/drivers/mmc/mmc_debug.c b/drivers/mmc/mmc_debug.c
new file mode 100644
index 0000000..f13e223
--- /dev/null
+++ b/drivers/mmc/mmc_debug.c
@@ -0,0 +1,59 @@
+/*
+ *  linux/drivers/mmc/mmc_debug.c
+ *
+ *  Copyright (C) 2003 maintech GmbH, Thomas Kleffel <tk@maintech.de>
+ *
+ * This file contains debug helper functions for the MMC/SD stack
+ *
+ * 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/mmc/mmc.h>
+#include "mmc_debug.h"
+
+char *mmc_cmd2str(int cmd)
+{
+	switch(cmd) {
+		case  0: return "GO_IDLE_STATE";
+		case  1: return "ALL_SEND_OCR";
+		case  2: return "ALL_SEND_CID";
+		case  3: return "ALL_SEND_RELATIVE_ADD";
+		case  6: return "ACMD: SD_SET_BUSWIDTH";
+		case  7: return "SEL_DESEL_CARD";
+		case  9: return "SEND_CSD";
+		case 10: return "SEND_CID";
+		case 11: return "READ_UNTIL_STOP";
+		case 12: return "STOP_TRANSMISSION";
+		case 13: return "SEND_STATUS";
+		case 15: return "GO_INACTIVE_STATE";
+		case 16: return "SET_BLOCKLEN";
+		case 17: return "READ_SINGLE_BLOCK";
+		case 18: return "READ_MULTIPLE_BLOCK";
+		case 24: return "WRITE_SINGLE_BLOCK";
+		case 25: return "WRITE_MULTIPLE_BLOCK";
+		case 41: return "ACMD: SD_APP_OP_COND";
+		case 55: return "APP_CMD";
+		default: return "UNKNOWN";
+	}
+}
+EXPORT_SYMBOL(mmc_cmd2str);
+
+char *mmc_err2str(int err)
+{
+	switch(err) {
+		case MMC_ERR_NONE:	return "OK";
+		case MMC_ERR_TIMEOUT:	return "TIMEOUT";
+		case MMC_ERR_BADCRC: 	return "BADCRC";
+		case MMC_ERR_FIFO: 	return "FIFO";
+		case MMC_ERR_FAILED: 	return "FAILED";
+		case MMC_ERR_INVALID: 	return "INVALID";
+		case MMC_ERR_BUSY:	return "BUSY";
+		case MMC_ERR_DMA:	return "DMA";
+		case MMC_ERR_CANCELED:	return "CANCELED";
+		default: return "UNKNOWN";
+	}
+}
+EXPORT_SYMBOL(mmc_err2str);
diff --git a/drivers/mmc/mmc_debug.h b/drivers/mmc/mmc_debug.h
new file mode 100644
index 0000000..5a84852
--- /dev/null
+++ b/drivers/mmc/mmc_debug.h
@@ -0,0 +1,7 @@
+#ifndef MMC_DEBUG_H
+#define MMC_DEBUG_H
+
+char *mmc_cmd2str(int err);
+char *mmc_err2str(int err);
+
+#endif /* MMC_DEBUG_H */
diff --git a/drivers/mmc/s3cmci.c b/drivers/mmc/s3cmci.c
new file mode 100644
index 0000000..3762628
--- /dev/null
+++ b/drivers/mmc/s3cmci.c
@@ -0,0 +1,1355 @@
+/*
+ *  linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
+ *
+ *  Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
+ *
+ * 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/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+#include <linux/platform_device.h>
+
+#include <asm/dma.h>
+#include <asm/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/arch/regs-sdi.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/mmc.h>
+
+#include "mmc_debug.h"
+#include "s3cmci.h"
+
+#define DRIVER_NAME "s3c-mci"
+
+enum dbg_channels {
+	dbg_err   = (1 << 0),
+	dbg_debug = (1 << 1),
+	dbg_info  = (1 << 2),
+	dbg_irq   = (1 << 3),
+	dbg_sg    = (1 << 4),
+	dbg_dma   = (1 << 5),
+	dbg_pio   = (1 << 6),
+	dbg_fail  = (1 << 7),
+	dbg_conf  = (1 << 8),
+};
+
+static const int dbgmap_err   = dbg_err | dbg_fail;
+static const int dbgmap_info  = dbg_info | dbg_conf;
+static const int dbgmap_debug = dbg_debug;
+
+#define dbg(host, channels, args...)		 \
+	if (dbgmap_err & channels) 		 \
+		dev_err(&host->pdev->dev, args); \
+	else if (dbgmap_info & channels)	 \
+		dev_info(&host->pdev->dev, args);\
+	else if (dbgmap_debug & channels)	 \
+		dev_dbg(&host->pdev->dev, args);
+
+#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
+
+static struct s3c2410_dma_client s3cmci_dma_client = {
+	.name		= "s3c-mci",
+};
+
+static void finalize_request(struct s3cmci_host *host);
+static void s3cmci_send_request(struct mmc_host *mmc);
+static void s3cmci_reset(struct s3cmci_host *host);
+
+#ifdef CONFIG_MMC_DEBUG
+
+static inline void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
+{
+	u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize;
+	u32 datcon, datcnt, datsta, fsta, imask;
+
+	con 	= readl(host->base + S3C2410_SDICON);
+	pre 	= readl(host->base + S3C2410_SDIPRE);
+	cmdarg 	= readl(host->base + S3C2410_SDICMDARG);
+	cmdcon 	= readl(host->base + S3C2410_SDICMDCON);
+	cmdsta 	= readl(host->base + S3C2410_SDICMDSTAT);
+	r0 	= readl(host->base + S3C2410_SDIRSP0);
+	r1 	= readl(host->base + S3C2410_SDIRSP1);
+	r2 	= readl(host->base + S3C2410_SDIRSP2);
+	r3 	= readl(host->base + S3C2410_SDIRSP3);
+	timer 	= readl(host->base + S3C2410_SDITIMER);
+	bsize 	= readl(host->base + S3C2410_SDIBSIZE);
+	datcon 	= readl(host->base + S3C2410_SDIDCON);
+	datcnt 	= readl(host->base + S3C2410_SDIDCNT);
+	datsta 	= readl(host->base + S3C2410_SDIDSTA);
+	fsta 	= readl(host->base + S3C2410_SDIFSTA);
+	imask   = readl(host->base + host->sdiimsk);
+
+	dbg(host, dbg_debug, "%s  CON:[%08x]  PRE:[%08x]  TMR:[%08x]\n",
+				prefix, con, pre, timer);
+
+	dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n",
+				prefix, cmdcon, cmdarg, cmdsta);
+
+	dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]"
+			       " DSTA:[%08x] DCNT:[%08x]\n",
+				prefix, datcon, fsta, datsta, datcnt);
+
+	dbg(host, dbg_debug, "%s   R0:[%08x]   R1:[%08x]"
+			       "   R2:[%08x]   R3:[%08x]\n",
+				prefix, r0, r1, r2, r3);
+}
+
+static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd,
+								int stop)
+{
+ 	snprintf(host->dbgmsg_cmd, 300,
+		"#%u%s op:%s(%i) arg:0x%08x flags:0x08%x retries:%u",
+		host->ccnt, (stop?" (STOP)":""), mmc_cmd2str(cmd->opcode),
+		cmd->opcode, cmd->arg, cmd->flags, cmd->retries);
+
+	if (cmd->data) {
+		snprintf(host->dbgmsg_dat, 300,
+			"#%u bsize:%u blocks:%u bytes:%u",
+			host->dcnt, cmd->data->blksz,
+			cmd->data->blocks,
+			cmd->data->blocks * cmd->data->blksz);
+	} else {
+		host->dbgmsg_dat[0] = '\0';
+	}
+}
+
+static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd,
+								int fail)
+{
+	unsigned int dbglvl = fail?dbg_fail:dbg_debug;
+
+	if (!cmd)
+		return;
+
+	if (cmd->error == MMC_ERR_NONE) {
+
+		dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",
+			host->dbgmsg_cmd, cmd->resp[0]);
+	} else {
+		dbg(host, dbglvl, "CMD[%s] %s Status:%s\n",
+			mmc_err2str(cmd->error), host->dbgmsg_cmd,
+			host->status);
+	}
+
+	if (!cmd->data)
+		return;
+
+	if (cmd->data->error == MMC_ERR_NONE) {
+		dbg(host, dbglvl, "DAT[%s] %s\n",
+			mmc_err2str(cmd->data->error), host->dbgmsg_dat);
+	} else {
+		dbg(host, dbglvl, "DAT[%s] %s DCNT:0x%08x\n",
+			mmc_err2str(cmd->data->error), host->dbgmsg_dat,
+			readl(host->base + S3C2410_SDIDCNT));
+	}
+}
+#endif
+
+static inline u32 enable_imask(struct s3cmci_host *host, u32 imask)
+{
+	u32 newmask;
+
+	newmask = readl(host->base + host->sdiimsk);
+	newmask|= imask;
+
+	writel(newmask, host->base + host->sdiimsk);
+
+	return newmask;
+}
+
+static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
+{
+	u32 newmask;
+
+	newmask = readl(host->base + host->sdiimsk);
+	newmask&= ~imask;
+
+	writel(newmask, host->base + host->sdiimsk);
+
+	return newmask;
+}
+
+static inline void clear_imask(struct s3cmci_host *host)
+{
+	writel(0, host->base + host->sdiimsk);
+}
+
+static inline int get_data_buffer(struct s3cmci_host *host,
+			volatile u32 *words, volatile u32 **pointer)
+{
+	struct scatterlist *sg;
+
+	if (host->pio_active == XFER_NONE)
+		return -EINVAL;
+
+	if ((!host->mrq) || (!host->mrq->data))
+		return -EINVAL;
+
+	if (host->pio_sgptr >= host->mrq->data->sg_len) {
+		dbg(host, dbg_debug, "no more buffers (%i/%i)\n",
+		      host->pio_sgptr, host->mrq->data->sg_len);
+		return -EBUSY;
+	}
+	sg = &host->mrq->data->sg[host->pio_sgptr];
+
+	*words	= sg->length >> 2;
+	*pointer= page_address(sg->page) + sg->offset;
+
+	host->pio_sgptr++;
+
+	dbg(host, dbg_sg, "new buffer (%i/%i)\n",
+	      host->pio_sgptr, host->mrq->data->sg_len);
+
+	return 0;
+}
+
+#define FIFO_FILL(host) ((readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK) >> 2)
+#define FIFO_FREE(host) ((63 - (readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK)) >> 2)
+
+static inline void do_pio_read(struct s3cmci_host *host)
+{
+	int res;
+	u32 fifo;
+	void __iomem *from_ptr;
+
+	//write real prescaler to host, it might be set slow to fix
+	writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+	from_ptr = host->base + host->sdidata;
+
+	while ((fifo = FIFO_FILL(host))) {
+		if (!host->pio_words) {
+			res = get_data_buffer(host, &host->pio_words,
+							&host->pio_ptr);
+			if (res) {
+				host->pio_active = XFER_NONE;
+				host->complete_what = COMPLETION_FINALIZE;
+
+				dbg(host, dbg_pio, "pio_read(): "
+					"complete (no more data).\n");
+				return;
+			}
+
+			dbg(host, dbg_pio, "pio_read(): new target: [%i]@[%p]\n",
+			       host->pio_words, host->pio_ptr);
+		}
+
+		dbg(host, dbg_pio, "pio_read(): fifo:[%02i] "
+				   "buffer:[%03i] dcnt:[%08X]\n",
+				   fifo, host->pio_words,
+				   readl(host->base + S3C2410_SDIDCNT));
+
+		if (fifo > host->pio_words)
+			fifo = host->pio_words;
+
+		host->pio_words-= fifo;
+		host->pio_count+= fifo;
+
+		while(fifo--) {
+			*(host->pio_ptr++) = readl(from_ptr);
+		}
+	}
+
+	if (!host->pio_words) {
+		res = get_data_buffer(host, &host->pio_words, &host->pio_ptr);
+		if (res) {
+			dbg(host, dbg_pio, "pio_read(): "
+				"complete (no more buffers).\n");
+			host->pio_active = XFER_NONE;
+			host->complete_what = COMPLETION_FINALIZE;
+
+			return;
+		}
+	}
+
+	enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
+}
+
+static inline void do_pio_write(struct s3cmci_host *host)
+{
+	int res;
+	u32 fifo;
+
+	void __iomem *to_ptr;
+
+	to_ptr = host->base + host->sdidata;
+
+	while ((fifo = FIFO_FREE(host))) {
+		if (!host->pio_words) {
+			res = get_data_buffer(host, &host->pio_words,
+							&host->pio_ptr);
+			if (res) {
+				dbg(host, dbg_pio, "pio_write(): "
+					"complete (no more data).\n");
+				host->pio_active = XFER_NONE;
+
+				return;
+			}
+
+			dbg(host, dbg_pio, "pio_write(): "
+				"new source: [%i]@[%p]\n",
+				host->pio_words, host->pio_ptr);
+
+		}
+
+		if (fifo > host->pio_words)
+			fifo = host->pio_words;
+
+		host->pio_words-= fifo;
+		host->pio_count+= fifo;
+
+		while(fifo--) {
+			writel(*(host->pio_ptr++), to_ptr);
+		}
+	}
+
+	enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
+}
+
+static void pio_tasklet(unsigned long data)
+{
+	struct s3cmci_host *host = (struct s3cmci_host *) data;
+
+
+	if (host->pio_active == XFER_WRITE)
+		do_pio_write(host);
+
+	if (host->pio_active == XFER_READ)
+		do_pio_read(host);
+
+	if (host->complete_what == COMPLETION_FINALIZE) {
+		clear_imask(host);
+		if (host->pio_active != XFER_NONE) {
+			dbg(host, dbg_err, "unfinished %s "
+				"- pio_count:[%u] pio_words:[%u]\n",
+				(host->pio_active == XFER_READ)?"read":"write",
+				host->pio_count, host->pio_words);
+
+			host->mrq->data->error = MMC_ERR_DMA;
+		}
+
+		disable_irq(host->irq);
+		finalize_request(host);
+	}
+}
+
+/*
+ * 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
+ *
+ * Note: "->error"-fields are expected to be set to 0 before the request
+ *       was issued by mmc.c - therefore they are only set, when an error
+ *       contition comes up
+ */
+
+static irqreturn_t s3cmci_irq(int irq, void *dev_id)
+{
+	struct s3cmci_host *host;
+	struct mmc_command *cmd;
+	u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
+	u32 mci_cclear, mci_dclear;
+	unsigned long iflags;
+
+	host = (struct s3cmci_host *)dev_id;
+
+	spin_lock_irqsave(&host->complete_lock, iflags);
+
+	mci_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
+	mci_dsta 	= readl(host->base + S3C2410_SDIDSTA);
+	mci_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
+	mci_fsta 	= readl(host->base + S3C2410_SDIFSTA);
+	mci_imsk	= readl(host->base + host->sdiimsk);
+	mci_cclear	= 0;
+	mci_dclear	= 0;
+
+	if ((host->complete_what == COMPLETION_NONE) ||
+			(host->complete_what == COMPLETION_FINALIZE)) {
+		host->status = "nothing to complete";
+		clear_imask(host);
+		goto irq_out;
+	}
+
+	if (!host->mrq) {
+		host->status = "no active mrq";
+		clear_imask(host);
+		goto irq_out;
+	}
+
+	cmd = host->cmd_is_stop?host->mrq->stop:host->mrq->cmd;
+
+	if (!cmd) {
+		host->status = "no active cmd";
+		clear_imask(host);
+		goto irq_out;
+	}
+
+	if (!host->dodma) {
+		if ((host->pio_active == XFER_WRITE) &&
+				(mci_fsta & S3C2410_SDIFSTA_TFDET)) {
+
+			disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
+			tasklet_schedule(&host->pio_tasklet);
+			host->status = "pio tx";
+		}
+
+		if ((host->pio_active == XFER_READ) &&
+				(mci_fsta & S3C2410_SDIFSTA_RFDET)) {
+
+			disable_imask(host,
+				S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
+
+			tasklet_schedule(&host->pio_tasklet);
+			host->status = "pio rx";
+		}
+	}
+
+	if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
+		cmd->error = MMC_ERR_TIMEOUT;
+		host->status = "error: command timeout";
+		goto fail_transfer;
+	}
+
+	if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) {
+		if (host->complete_what == COMPLETION_CMDSENT) {
+			host->status = "ok: command sent";
+			goto close_transfer;
+		}
+
+		mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
+	}
+
+	if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
+		if (cmd->flags & MMC_RSP_CRC) {
+			if (host->is2440 || !(cmd->flags & MMC_RSP_136)) {
+				cmd->error = MMC_ERR_BADCRC;
+				host->status = "error: bad command crc";
+				goto fail_transfer;
+			}
+		}
+		mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
+	}
+
+	if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) {
+		if (host->complete_what == COMPLETION_RSPFIN) {
+			host->status = "ok: command response received";
+			goto close_transfer;
+		}
+
+		if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
+			host->complete_what = COMPLETION_XFERFINISH;
+
+		mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
+	}
+
+	/* errors handled after this point are only relevant
+	   when a data transfer is in progress */
+
+	if (!cmd->data)
+		goto clear_status_bits;
+
+	/* Check for FIFO failure */
+	if (host->is2440) {
+		if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {
+			host->mrq->data->error = MMC_ERR_FIFO;
+			host->status = "error: 2440 fifo failure";
+			goto fail_transfer;
+		}
+	} else {
+		if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
+			cmd->data->error = MMC_ERR_FIFO;
+			host->status = "error:  fifo failure";
+			goto fail_transfer;
+		}
+	}
+
+	if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
+		cmd->data->error = MMC_ERR_BADCRC;
+		host->status = "error: bad data crc (outgoing)";
+		goto fail_transfer;
+	}
+
+	if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {
+		cmd->data->error = MMC_ERR_BADCRC;
+		host->status = "error: bad data crc (incoming)";
+		goto fail_transfer;
+	}
+
+	if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
+		cmd->data->error = MMC_ERR_TIMEOUT;
+		host->status = "error: data timeout";
+		goto fail_transfer;
+	}
+
+	if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) {
+		if (host->complete_what == COMPLETION_XFERFINISH) {
+			host->status = "ok: data transfer completed";
+			goto close_transfer;
+		}
+
+		if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
+			host->complete_what = COMPLETION_RSPFIN;
+		}
+
+		mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;
+	}
+
+clear_status_bits:
+	writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);
+	writel(mci_dclear, host->base + S3C2410_SDIDSTA);
+
+	goto irq_out;
+
+fail_transfer:
+	host->pio_active = XFER_NONE;
+
+close_transfer:
+	host->complete_what = COMPLETION_FINALIZE;
+
+	clear_imask(host);
+	tasklet_schedule(&host->pio_tasklet);
+
+	goto irq_out;
+
+irq_out:
+	dbg(host, dbg_irq, "csta:0x%08x dsta:0x%08x "
+			   "fsta:0x%08x dcnt:0x%08x status:%s.\n",
+				mci_csta, mci_dsta, mci_fsta,
+				mci_dcnt, host->status);
+
+	spin_unlock_irqrestore(&host->complete_lock, iflags);
+	return IRQ_HANDLED;
+
+}
+
+/*
+ * ISR for the CardDetect Pin
+*/
+
+static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
+{
+	struct s3cmci_host *host = (struct s3cmci_host *)dev_id;
+
+	dbg(host, dbg_irq, "card detect\n");
+
+	mmc_detect_change(host->mmc, 500);
+
+	return IRQ_HANDLED;
+}
+
+void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id,
+	int size, enum s3c2410_dma_buffresult result)
+{
+	unsigned long iflags;
+	u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt;
+	struct s3cmci_host *host = (struct s3cmci_host *)buf_id;
+
+	mci_csta 	= readl(host->base + S3C2410_SDICMDSTAT);
+	mci_dsta 	= readl(host->base + S3C2410_SDIDSTA);
+	mci_fsta 	= readl(host->base + S3C2410_SDIFSTA);
+	mci_dcnt 	= readl(host->base + S3C2410_SDIDCNT);
+
+	if ((!host->mrq) || (!host->mrq) || (!host->mrq->data))
+		return;
+
+	if (!host->dmatogo)
+		return;
+
+	spin_lock_irqsave(&host->complete_lock, iflags);
+
+	if (result != S3C2410_RES_OK) {
+		dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
+			"fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
+			mci_csta, mci_dsta, mci_fsta,
+			mci_dcnt, result, host->dmatogo);
+
+		goto fail_request;
+	}
+
+	host->dmatogo--;
+	if (host->dmatogo) {
+		dbg(host, dbg_dma, "DMA DONE  Size:%i DSTA:[%08x] "
+			"DCNT:[%08x] toGo:%u\n",
+			size, mci_dsta, mci_dcnt, host->dmatogo);
+
+		goto out;
+	}
+
+	dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
+		size, mci_dsta, mci_dcnt);
+
+	host->complete_what = COMPLETION_FINALIZE;
+
+out:
+	tasklet_schedule(&host->pio_tasklet);
+	spin_unlock_irqrestore(&host->complete_lock, iflags);
+	return;
+
+
+fail_request:
+	host->mrq->data->error = MMC_ERR_DMA;
+	host->complete_what = COMPLETION_FINALIZE;
+	writel(0, host->base + host->sdiimsk);
+	goto out;
+
+}
+
+static void finalize_request(struct s3cmci_host *host)
+{
+	struct mmc_request *mrq = host->mrq;
+	struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd;
+	int debug_as_failure = 0;
+
+	if (host->complete_what != COMPLETION_FINALIZE)
+		return;
+
+	if (!mrq)
+		return;
+
+	if (cmd->data && (cmd->error == MMC_ERR_NONE) &&
+		  (cmd->data->error == MMC_ERR_NONE)) {
+
+		if (host->dodma && (!host->dma_complete)) {
+			dbg(host, dbg_dma, "DMA Missing!\n");
+			return;
+		}
+	}
+
+	// Read response
+	cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);
+	cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);
+	cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);
+	cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);
+
+	// reset clock speed, as it could still be set low for
+	writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+	if (cmd->error)
+		debug_as_failure = 1;
+
+	if (cmd->data && cmd->data->error)
+		debug_as_failure = 1;
+
+	//if(cmd->flags & MMC_RSP_MAYFAIL) debug_as_failure = 0;
+
+#ifdef CONFIG_MMC_DEBUG
+	dbg_dumpcmd(host, cmd, debug_as_failure);
+#endif
+	//Cleanup controller
+	writel(0, host->base + S3C2410_SDICMDARG);
+	writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
+	writel(0, host->base + S3C2410_SDICMDCON);
+	writel(0, host->base + host->sdiimsk);
+
+	if (cmd->data && cmd->error)
+		cmd->data->error = cmd->error;
+
+	if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) {
+		host->cmd_is_stop = 1;
+		s3cmci_send_request(host->mmc);
+		return;
+	}
+
+	// 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);
+	} else {
+		mrq->data->bytes_xfered = 0;
+	}
+
+	// If we had an error while transfering data we flush the
+	// DMA channel and the fifo to clear out any garbage
+	if (mrq->data->error != MMC_ERR_NONE) {
+		if (host->dodma)
+			s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+
+		if (host->is2440) {
+			//Clear failure register and reset fifo
+			writel(S3C2440_SDIFSTA_FIFORESET |
+			       S3C2440_SDIFSTA_FIFOFAIL,
+			       host->base + S3C2410_SDIFSTA);
+		} else {
+			u32 mci_con;
+
+			//reset fifo
+			mci_con = readl(host->base + S3C2410_SDICON);
+			mci_con|= S3C2410_SDICON_FIFORESET;
+
+			writel(mci_con, host->base + S3C2410_SDICON);
+		}
+	}
+
+request_done:
+	host->complete_what = COMPLETION_NONE;
+	host->mrq = NULL;
+	mmc_request_done(host->mmc, mrq);
+}
+
+
+void s3cmci_dma_setup(struct s3cmci_host *host, enum s3c2410_dmasrc source)
+{
+	static int setup_ok = 0;
+	static enum s3c2410_dmasrc last_source = -1;
+
+	if (last_source == source)
+		return;
+
+	last_source = source;
+
+	s3c2410_dma_devconfig(host->dma, source, 3,
+		host->mem->start + host->sdidata);
+
+	if (!setup_ok) {
+		s3c2410_dma_config(host->dma, 4,
+			(S3C2410_DCON_HWTRIG | S3C2410_DCON_CH0_SDI));
+		s3c2410_dma_set_buffdone_fn(host->dma, s3cmci_dma_done_callback);
+		s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
+		setup_ok = 1;
+	}
+}
+
+static void s3cmci_send_command(struct s3cmci_host *host,
+					struct mmc_command *cmd)
+{
+	u32 ccon, imsk;
+
+	imsk  = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
+		S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
+		S3C2410_SDIIMSK_RESPONSECRC;
+
+	enable_imask(host, imsk);
+
+	if (cmd->data) {
+		host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
+	} else if (cmd->flags & MMC_RSP_PRESENT) {
+		host->complete_what = COMPLETION_RSPFIN;
+	} else {
+		host->complete_what = COMPLETION_CMDSENT;
+	}
+
+	writel(cmd->arg, host->base + S3C2410_SDICMDARG);
+
+	ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;
+	ccon|= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;
+
+	if (cmd->flags & MMC_RSP_PRESENT)
+		ccon |= S3C2410_SDICMDCON_WAITRSP;
+
+	if (cmd->flags & MMC_RSP_136)
+		ccon|= S3C2410_SDICMDCON_LONGRSP;
+
+	writel(ccon, host->base + S3C2410_SDICMDCON);
+}
+
+static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
+{
+	u32 dcon, imsk, stoptries=3;
+
+	/* write DCON register */
+
+	if (!data) {
+		writel(0, host->base + S3C2410_SDIDCON);
+		return 0;
+	}
+
+	while(readl(host->base + S3C2410_SDIDSTA) &
+		(S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
+
+		dbg(host, dbg_err,
+			"mci_setup_data() transfer stillin progress.\n");
+
+		writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
+		s3cmci_reset(host);
+
+		if (0 == (stoptries--)) {
+#ifdef CONFIG_MMC_DEBUG
+			dbg_dumpregs(host, "DRF");
+#endif
+
+			return -EINVAL;
+		}
+	}
+
+	dcon  = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
+
+	if (host->dodma) {
+		dcon |= S3C2410_SDIDCON_DMAEN;
+	}
+
+	if (host->bus_width == MMC_BUS_WIDTH_4) {
+		dcon |= S3C2410_SDIDCON_WIDEBUS;
+	}
+
+	if (!(data->flags & MMC_DATA_STREAM)) {
+		dcon |= S3C2410_SDIDCON_BLOCKMODE;
+	}
+
+	if (data->flags & MMC_DATA_WRITE) {
+		dcon |= S3C2410_SDIDCON_TXAFTERRESP;
+		dcon |= S3C2410_SDIDCON_XFER_TXSTART;
+	}
+
+	if (data->flags & MMC_DATA_READ) {
+		dcon |= S3C2410_SDIDCON_RXAFTERCMD;
+		dcon |= S3C2410_SDIDCON_XFER_RXSTART;
+	}
+
+	if (host->is2440) {
+		dcon |= S3C2440_SDIDCON_DS_WORD;
+		dcon |= S3C2440_SDIDCON_DATSTART;
+	}
+
+	writel(dcon, host->base + S3C2410_SDIDCON);
+
+	/* write BSIZE register */
+
+	writel(data->blksz, host->base + S3C2410_SDIBSIZE);
+
+	/* add to IMASK register */
+	imsk =	S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
+		S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
+
+	enable_imask(host, imsk);
+
+	/* write TIMER register */
+
+	if (host->is2440) {
+		writel(0x007FFFFF, host->base + S3C2410_SDITIMER);
+	} else {
+		writel(0x0000FFFF, host->base + S3C2410_SDITIMER);
+
+		//FIX: set slow clock to prevent timeouts on read
+		if (data->flags & MMC_DATA_READ) {
+			writel(0xFF, host->base + S3C2410_SDIPRE);
+		}
+	}
+
+	//debug_dump_registers(host, "Data setup:");
+
+	return 0;
+}
+
+static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
+{
+	int rw = (data->flags & MMC_DATA_WRITE)?1:0;
+
+	if (rw != ((data->flags & MMC_DATA_READ)?0:1))
+		return -EINVAL;
+
+	host->pio_sgptr = 0;
+	host->pio_words = 0;
+	host->pio_count = 0;
+	host->pio_active = rw?XFER_WRITE:XFER_READ;
+
+	if (rw) {
+		do_pio_write(host);
+		enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
+	} else {
+		enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF
+			| S3C2410_SDIIMSK_RXFIFOLAST);
+	}
+
+	return 0;
+}
+
+static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
+{
+	int dma_len, i;
+
+	int rw = (data->flags & MMC_DATA_WRITE)?1:0;
+
+	if (rw != ((data->flags & MMC_DATA_READ)?0:1))
+		return -EINVAL;
+
+	s3cmci_dma_setup(host, rw?S3C2410_DMASRC_MEM:S3C2410_DMASRC_HW);
+	s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+
+	dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+				(rw)?DMA_TO_DEVICE:DMA_FROM_DEVICE);
+
+
+	if (dma_len == 0)
+		return -ENOMEM;
+
+	host->dma_complete = 0;
+	host->dmatogo = dma_len;
+
+	for (i = 0; i < dma_len; i++) {
+		int res;
+
+		dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,
+			sg_dma_address(&data->sg[i]),
+			sg_dma_len(&data->sg[i]));
+
+		res = s3c2410_dma_enqueue(host->dma, (void *) host,
+				sg_dma_address(&data->sg[i]),
+				sg_dma_len(&data->sg[i]));
+
+		if (res) {
+			s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+			return -EBUSY;
+		}
+ 	}
+
+	s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);
+
+	return 0;
+}
+
+static void s3cmci_send_request(struct mmc_host *mmc)
+{
+	struct s3cmci_host *host = mmc_priv(mmc);
+	struct mmc_request *mrq = host->mrq;
+	struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd;
+
+	host->ccnt++;
+#ifdef CONFIG_MMC_DEBUG
+	prepare_dbgmsg(host, cmd, host->cmd_is_stop);
+#endif
+	//Clear command, data and fifo status registers
+	//Fifo clear only necessary on 2440, but doesn't hurt on 2410
+	writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
+	writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
+	writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);
+
+	if (cmd->data) {
+		int res;
+		res = s3cmci_setup_data(host, cmd->data);
+
+		host->dcnt++;
+
+		if (res) {
+			cmd->error = MMC_ERR_DMA;
+			cmd->data->error = MMC_ERR_DMA;
+
+			mmc_request_done(mmc, mrq);
+			return;
+		}
+
+
+		if (host->dodma) {
+			res = s3cmci_prepare_dma(host, cmd->data);
+		} else {
+			res = s3cmci_prepare_pio(host, cmd->data);
+		}
+
+		if (res) {
+			cmd->error = MMC_ERR_DMA;
+			cmd->data->error = MMC_ERR_DMA;
+
+			mmc_request_done(mmc, mrq);
+			return;
+		}
+
+	}
+
+	// Send command
+	s3cmci_send_command(host, cmd);
+
+	// Enable Interrupt
+	enable_irq(host->irq);
+}
+
+static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ 	struct s3cmci_host *host = mmc_priv(mmc);
+
+	host->cmd_is_stop = 0;
+	host->mrq = mrq;
+
+	s3cmci_send_request(mmc);
+}
+
+static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct s3cmci_host *host = mmc_priv(mmc);
+	u32 mci_psc, mci_con;
+
+	//Set power
+	mci_con = readl(host->base + S3C2410_SDICON);
+	switch(ios->power_mode) {
+		case MMC_POWER_ON:
+		case MMC_POWER_UP:
+			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);
+
+			if (!host->is2440)
+				mci_con|=S3C2410_SDICON_FIFORESET;
+
+			break;
+
+		case MMC_POWER_OFF:
+		default:
+			s3c2410_gpio_setpin(S3C2410_GPE5, 0);
+			s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
+
+			if (host->is2440)
+				mci_con|=S3C2440_SDICON_SDRESET;
+
+			break;
+	}
+
+	//Set clock
+	for (mci_psc=0; mci_psc<255; mci_psc++) {
+		host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
+
+		if (host->real_rate <= ios->clock)
+			break;
+	}
+
+	if(mci_psc > 255) mci_psc = 255;
+	host->prescaler = mci_psc;
+
+	writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+	//If requested clock is 0, real_rate will be 0, too
+	if (ios->clock == 0)
+		host->real_rate = 0;
+
+	//Set CLOCK_ENABLE
+	if (ios->clock)
+		mci_con |= S3C2410_SDICON_CLOCKTYPE;
+	else
+		mci_con &=~S3C2410_SDICON_CLOCKTYPE;
+
+	writel(mci_con, host->base + S3C2410_SDICON);
+
+	if ((ios->power_mode==MMC_POWER_ON)
+		|| (ios->power_mode==MMC_POWER_UP)) {
+
+		dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",
+			host->real_rate/1000, ios->clock/1000);
+	} else {
+		dbg(host, dbg_conf, "powered down.\n");
+	}
+
+	host->bus_width = ios->bus_width;
+
+}
+
+static void s3cmci_reset(struct s3cmci_host *host)
+{
+	u32 con = readl(host->base + S3C2410_SDICON);
+
+	con |= S3C2440_SDICON_SDRESET;
+
+	writel(con, host->base + S3C2410_SDICON);
+}
+
+static struct mmc_host_ops s3cmci_ops = {
+	.request	= s3cmci_request,
+	.set_ios	= s3cmci_set_ios,
+};
+
+static struct s3c24xx_mmc_platdata s3c2410_mmc_defplat = {
+	.gpio_detect	= S3C2410_GPF2,
+};
+
+static int s3cmci_probe(struct platform_device *pdev, int is2440)
+{
+	struct mmc_host 	*mmc;
+	struct s3cmci_host 	*host;
+	s3c24xx_mmc_pdata_t	*pdata;
+
+	int ret;
+
+	mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
+	if (!mmc) {
+		ret = -ENOMEM;
+		goto probe_out;
+	}
+
+	host = mmc_priv(mmc);
+	host->mmc 	= mmc;
+	host->pdev	= pdev;
+
+	spin_lock_init(&host->complete_lock);
+	tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
+	if (is2440) {
+		host->is2440	= 1;
+		host->sdiimsk	= S3C2440_SDIIMSK;
+		host->sdidata	= S3C2440_SDIDATA;
+		host->clk_div	= 1;
+	} else {
+		host->is2440	= 0;
+		host->sdiimsk	= S3C2410_SDIIMSK;
+		host->sdidata	= S3C2410_SDIDATA;
+		host->clk_div	= 2;
+	}
+	host->dodma		= 0;
+	host->complete_what 	= COMPLETION_NONE;
+	host->pio_active 	= XFER_NONE;
+
+	host->dma		= S3CMCI_DMA;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		pdev->dev.platform_data = &s3c2410_mmc_defplat;
+		pdata = &s3c2410_mmc_defplat;
+	}
+
+	host->pdata = pdata;
+	host->irq_cd = s3c2410_gpio_getirq(pdata->gpio_detect);
+
+	host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!host->mem) {
+		dev_err(&pdev->dev,
+			"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) {
+		dev_err(&pdev->dev, "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) {
+		dev_err(&pdev->dev, "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) {
+		dev_err(&pdev->dev, "failed to get interrupt resouce.\n");
+		ret = -EINVAL;
+		goto probe_iounmap;
+	}
+
+	if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
+		dev_err(&pdev->dev, "failed to request mci interrupt.\n");
+		ret = -ENOENT;
+		goto probe_iounmap;
+	}
+
+	disable_irq(host->irq);
+
+	s3c2410_gpio_cfgpin(pdata->gpio_detect, S3C2410_GPIO_IRQ);
+
+	if (request_irq(host->irq_cd, s3cmci_irq_cd, \
+		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DRIVER_NAME, host)) {
+		dev_err(&pdev->dev,
+			"failed to request card detect interrupt.\n");
+
+		ret = -ENOENT;
+		goto probe_free_irq;
+	}
+
+	if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
+		dev_err(&pdev->dev, "unable to get DMA channel.\n");
+		ret = -EBUSY;
+		goto probe_free_irq_cd;
+	}
+
+	host->clk = clk_get(&pdev->dev, "sdi");
+	if (IS_ERR(host->clk)) {
+		dev_err(&pdev->dev, "failed to find clock source.\n");
+		ret = PTR_ERR(host->clk);
+		host->clk = NULL;
+		goto probe_free_irq_cd;
+	}
+
+	if ((ret = clk_enable(host->clk))) {
+		dev_err(&pdev->dev, "failed to enable clock source.\n");
+		goto clk_free;
+	}
+
+	host->clk_rate = clk_get_rate(host->clk);
+
+	mmc->ops 	= &s3cmci_ops;
+	mmc->ocr_avail	= MMC_VDD_32_33;
+	mmc->caps	= MMC_CAP_4_BIT_DATA;
+	mmc->f_min 	= host->clk_rate / (host->clk_div * 256);
+	mmc->f_max 	= host->clk_rate / host->clk_div;
+
+	mmc->max_blk_size	= 4096;
+	mmc->max_blk_count	= 4096;
+	mmc->max_req_size	= mmc->max_blk_size * 512;
+	mmc->max_seg_size	= mmc->max_req_size;
+
+	mmc->max_phys_segs	= 128;
+	mmc->max_hw_segs	= 128;
+
+	dbg(host, dbg_debug, "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
+		(host->is2440?"2440":""),
+		host->base, host->irq, host->irq_cd, host->dma);
+
+	if ((ret = mmc_add_host(mmc))) {
+		dev_err(&pdev->dev, "failed to add mmc host.\n");
+		goto free_dmabuf;
+	}
+
+	platform_set_drvdata(pdev, mmc);
+
+	dev_info(&pdev->dev,"initialisation done.\n");
+	return 0;
+
+ free_dmabuf:
+	clk_disable(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 s3cmci_remove(struct platform_device *pdev)
+{
+	struct mmc_host 	*mmc  = platform_get_drvdata(pdev);
+	struct s3cmci_host 	*host = mmc_priv(mmc);
+
+	mmc_remove_host(mmc);
+	clk_disable(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 int s3cmci_probe_2410(struct platform_device *dev)
+{
+	return s3cmci_probe(dev, 0);
+}
+
+static int s3cmci_probe_2412(struct platform_device *dev)
+{
+	return s3cmci_probe(dev, 1);
+}
+
+static int s3cmci_probe_2440(struct platform_device *dev)
+{
+	return s3cmci_probe(dev, 1);
+}
+
+#ifdef CONFIG_PM
+
+static int s3cmci_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+
+	return  mmc_suspend_host(mmc, state);
+}
+
+static int s3cmci_resume(struct platform_device *dev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+
+	return mmc_resume_host(mmc);
+}
+
+#else /* CONFIG_PM */
+#define s3cmci_suspend NULL
+#define s3cmci_resume NULL
+#endif /* CONFIG_PM */
+
+
+static struct platform_driver s3cmci_driver_2410 =
+{
+	.driver.name	= "s3c2410-sdi",
+	.probe		= s3cmci_probe_2410,
+	.remove		= s3cmci_remove,
+	.suspend	= s3cmci_suspend,
+	.resume		= s3cmci_resume,
+};
+
+static struct platform_driver s3cmci_driver_2412 =
+{
+	.driver.name	= "s3c2412-sdi",
+	.probe		= s3cmci_probe_2412,
+	.remove		= s3cmci_remove,
+	.suspend	= s3cmci_suspend,
+	.resume		= s3cmci_resume,
+};
+
+static struct platform_driver s3cmci_driver_2440 =
+{
+	.driver.name	= "s3c2440-sdi",
+	.probe		= s3cmci_probe_2440,
+	.remove		= s3cmci_remove,
+	.suspend	= s3cmci_suspend,
+	.resume		= s3cmci_resume,
+};
+
+
+static int __init s3cmci_init(void)
+{
+	platform_driver_register(&s3cmci_driver_2410);
+	platform_driver_register(&s3cmci_driver_2412);
+	platform_driver_register(&s3cmci_driver_2440);
+	return 0;
+}
+
+static void __exit s3cmci_exit(void)
+{
+	platform_driver_unregister(&s3cmci_driver_2410);
+	platform_driver_unregister(&s3cmci_driver_2412);
+	platform_driver_unregister(&s3cmci_driver_2440);
+}
+
+module_init(s3cmci_init);
+module_exit(s3cmci_exit);
+
+MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>");
+
diff --git a/drivers/mmc/s3cmci.h b/drivers/mmc/s3cmci.h
new file mode 100644
index 0000000..ec63f78
--- /dev/null
+++ b/drivers/mmc/s3cmci.h
@@ -0,0 +1,74 @@
+/*
+ *  linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
+ *
+ *  Copyright (C) 2004-2006 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.
+ */
+
+//FIXME: DMA Resource management ?!
+#define S3CMCI_DMA 0
+
+enum s3cmci_waitfor {
+	COMPLETION_NONE,
+	COMPLETION_FINALIZE,
+	COMPLETION_CMDSENT,
+	COMPLETION_RSPFIN,
+	COMPLETION_XFERFINISH,
+	COMPLETION_XFERFINISH_RSPFIN,
+};
+
+typedef struct s3c24xx_mmc_platdata s3c24xx_mmc_pdata_t;
+
+struct s3cmci_host {
+	struct platform_device	*pdev;
+	s3c24xx_mmc_pdata_t	*pdata;
+	struct mmc_host		*mmc;
+	struct resource		*mem;
+	struct clk		*clk;
+	void __iomem		*base;
+	int			irq;
+	int			irq_cd;
+	int			dma;
+
+	unsigned long		clk_rate;
+	unsigned long		clk_div;
+	unsigned long		real_rate;
+	u8			prescaler;
+
+	int			is2440;
+	unsigned		sdiimsk;
+	unsigned		sdidata;
+	int			dodma;
+
+	volatile int		dmatogo;
+
+	struct mmc_request	*mrq;
+	int			cmd_is_stop;
+
+	spinlock_t		complete_lock;
+	volatile enum s3cmci_waitfor
+				complete_what;
+
+	volatile int		dma_complete;
+
+	volatile u32		pio_sgptr;
+	volatile u32		pio_words;
+	volatile u32		pio_count;
+	volatile u32		*pio_ptr;
+#define XFER_NONE 0
+#define XFER_READ 1
+#define XFER_WRITE 2
+	volatile u32		pio_active;
+
+	int			bus_width;
+
+	char 			dbgmsg_cmd[301];
+	char 			dbgmsg_dat[301];
+	volatile char		*status;
+
+	unsigned int		ccnt, dcnt;
+	struct tasklet_struct	pio_tasklet;
+};
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 8065f2b..790c2d8 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -208,6 +208,25 @@ 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_AT91
 	boolean "AT91 USB Device Port"
 	depends on ARCH_AT91
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5db1939..8fed08c 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -7,6 +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
 obj-$(CONFIG_USB_AT91)		+= at91_udc.o
 obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o
 
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
new file mode 100644
index 0000000..317c6ec
--- /dev/null
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -0,0 +1,2061 @@
+/*
+ * linux/drivers/usb/gadget/s3c2410_udc.c
+ * Samsung on-chip full speed USB device controllers
+ *
+ * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard
+ *	Additional cleanups by Ben Dooks <ben-linux@fluff.org>
+ *
+ * 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/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/platform_device.h>
+#include <linux/version.h>
+#include <linux/clk.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.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/mach-types.h>
+
+#include "s3c2410_udc.h"
+
+#define DRIVER_DESC     "S3C2410 USB Device Controller Gadget"
+#define DRIVER_VERSION  "29 Apr 2007"
+#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 struct clk      		*usb_bus_clock;
+static void __iomem 		*base_addr;
+static u64			rsrc_start;
+static u64			rsrc_len;
+static struct dentry		*s3c2410_udc_debugfs_root;
+
+static inline u32 udc_read(u32 reg)
+{
+	return readb(base_addr + reg);
+}
+
+static inline void udc_write(u32 value, u32 reg)
+{
+	writeb(value, base_addr + reg);
+}
+
+static inline void udc_writeb(void __iomem *base, u32 value, u32 reg)
+{
+	writeb(value, base + 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 0
+
+static 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(KERN_DEBUG "%s", printk_buf);
+}
+#else
+static int dprintk(int level, const char *fmt, ...)  { return 0; }
+#endif
+static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
+{
+	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_read(S3C2410_UDC_FUNC_ADDR_REG);
+	pwr_reg        = udc_read(S3C2410_UDC_PWR_REG);
+	ep_int_reg     = udc_read(S3C2410_UDC_EP_INT_REG);
+	usb_int_reg    = udc_read(S3C2410_UDC_USB_INT_REG);
+	ep_int_en_reg  = udc_read(S3C2410_UDC_EP_INT_EN_REG);
+	usb_int_en_reg = udc_read(S3C2410_UDC_USB_INT_EN_REG);
+	udc_write(0, S3C2410_UDC_INDEX_REG);
+	ep0_csr        = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	udc_write(1, S3C2410_UDC_INDEX_REG);
+	ep1_i_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep1_i_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+	ep1_o_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep1_o_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+	udc_write(2, S3C2410_UDC_INDEX_REG);
+	ep2_i_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep2_i_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+	ep2_o_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep2_o_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+
+	seq_printf(m, "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
+		);
+
+	return 0;
+}
+
+static int s3c2410_udc_debugfs_fops_open(struct inode *inode,
+					 struct file *file)
+{
+        return single_open(file, s3c2410_udc_debugfs_seq_show, NULL);
+}
+
+static const struct file_operations s3c2410_udc_debugfs_fops = {
+	.open           = s3c2410_udc_debugfs_fops_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+	.owner          = THIS_MODULE,
+};
+
+/* io macros */
+
+static inline void s3c2410_udc_clear_ep0_opr(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_SOPKTRDY,
+		   S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_clear_ep0_sst(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	writeb(0x00, base + S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_clear_ep0_se(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_SSE, S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_ipr(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_IPKRDY, S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_de(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_DE, S3C2410_UDC_EP0_CSR_REG);
+}
+
+inline void s3c2410_udc_set_ep0_ss(void __iomem *b)
+{
+	udc_writeb(b, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(b, S3C2410_UDC_EP0_CSR_SENDSTL, S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+
+	udc_writeb(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY
+			 | S3C2410_UDC_EP0_CSR_DE),
+		   S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_sse_out(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY
+			  | S3C2410_UDC_EP0_CSR_SSE),
+		   S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, (S3C2410_UDC_EP0_CSR_IPKRDY
+			  | S3C2410_UDC_EP0_CSR_DE),
+		   S3C2410_UDC_EP0_CSR_REG);
+}
+
+/*------------------------- I/O ----------------------------------*/
+
+/*
+ * 	s3c2410_udc_done
+ */
+static void s3c2410_udc_done(struct s3c2410_ep *ep,
+			     struct s3c2410_request *req, int status)
+{
+	unsigned halted = ep->halted;
+
+	list_del_init(&req->queue);
+
+	if (likely (req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	ep->halted = 1;
+	req->req.complete(&ep->ep, &req->req);
+	ep->halted = halted;
+}
+
+static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
+			     struct s3c2410_ep *ep, int status)
+{
+	/* Sanity check */
+	if (&ep->queue == NULL)
+		return;
+
+	while (!list_empty (&ep->queue)) {
+		struct s3c2410_request  *req;
+		req = list_entry (ep->queue.next, struct s3c2410_request, queue);
+		s3c2410_udc_done(ep, req, status);
+	}
+}
+
+static inline void s3c2410_udc_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++)
+		s3c2410_udc_nuke(dev, &dev->ep[i], -ECONNABORTED);
+}
+
+static inline int s3c2410_udc_fifo_count_out(void)
+{
+	int tmp;
+
+	tmp = udc_read(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8;
+	tmp |= udc_read(S3C2410_UDC_OUT_FIFO_CNT1_REG);
+	return tmp;
+}
+
+/*
+ * 	s3c2410_udc_write_packet
+ */
+static inline int s3c2410_udc_write_packet(int fifo,
+					   struct s3c2410_request *req,
+					   unsigned max)
+{
+	unsigned	len;
+	u8		*buf;
+
+	buf = req->req.buf + req->req.actual;
+	prefetch(buf);
+
+	len = min(req->req.length - req->req.actual, max);
+	dprintk(DEBUG_VERBOSE, "s3c2410_udc_write_packet %d %d %d ",req->req.actual,req->req.length,len);
+	req->req.actual += len;
+	dprintk(DEBUG_VERBOSE, "%d\n",req->req.actual);
+
+	udelay(5);
+	writesb(base_addr+fifo, buf, len);
+	return len;
+}
+
+/*
+ * 	s3c2410_udc_write_fifo
+ *
+ * return:  0 = still running, 1 = completed, negative = errno
+ */
+static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep,
+				  struct s3c2410_request *req)
+{
+	unsigned	count;
+	int		is_last;
+	u32		idx;
+	int		fifo_reg;
+	u32		ep_csr;
+
+	idx = ep->bEndpointAddress & 0x7F;
+	switch (idx) {
+	default:
+		idx = 0;
+	case 0:
+		fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
+		break;
+	case 1:
+		fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
+		break;
+	case 2:
+		fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
+		break;
+	case 3:
+		fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
+		break;
+	case 4:
+		fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
+		break;
+	}
+
+	count = s3c2410_udc_write_packet(fifo_reg, req, ep->ep.maxpacket);
+
+	/* 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 = 0;
+	else
+		is_last = 2;
+
+	/* 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 sending 2 packets
+		   at the same time */
+
+		if (!idx) {
+			/* Reset signal => no need to say 'data sent' */
+			if (! (udc_read(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET))
+				s3c2410_udc_set_ep0_de_in(base_addr);
+			ep->dev->ep0state=EP0_IDLE;
+		} else {
+			 udc_write(idx, S3C2410_UDC_INDEX_REG);
+			 ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+			 udc_write(idx, S3C2410_UDC_INDEX_REG);
+			 udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
+				   S3C2410_UDC_IN_CSR1_REG);
+		}
+		s3c2410_udc_done(ep, req, 0);
+		is_last=1;
+	} else {
+		if (!idx) {
+			/* Reset signal => no need to say 'data sent' */
+			if (! (udc_read(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET))
+				s3c2410_udc_set_ep0_ipr(base_addr);
+		} else {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr=udc_read(S3C2410_UDC_IN_CSR1_REG);
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
+				  S3C2410_UDC_IN_CSR1_REG);
+		}
+	}
+
+	return is_last;
+}
+
+static inline int s3c2410_udc_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;
+
+	readsb(fifo + base_addr, buf, len);
+	return len;
+}
+
+/*
+ * return:  0 = still running, 1 = queue empty, negative = errno
+ */
+static int s3c2410_udc_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;
+
+	idx = ep->bEndpointAddress & 0x7F;
+
+	switch (idx) {
+	default:
+		idx = 0;
+	case 0:
+		fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
+		break;
+	case 1:
+		fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
+		break;
+	case 2:
+		fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
+		break;
+	case 3:
+		fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
+		break;
+	case 4:
+		fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
+		break;
+	}
+
+	if (!req->req.length)
+		return 1;
+
+	buf = req->req.buf + req->req.actual;
+	bufferspace = req->req.length - req->req.actual;
+	if (!bufferspace) {
+		dprintk(DEBUG_NORMAL, "%s: buffer full !!\n", __func__);
+		return -1;
+	}
+
+	udc_write(idx, S3C2410_UDC_INDEX_REG);
+
+        fifo_count = s3c2410_udc_fifo_count_out();
+	dprintk(DEBUG_NORMAL, "%s fifo count : %d\n", __func__, fifo_count);
+
+	if (fifo_count > ep->ep.maxpacket)
+		avail = ep->ep.maxpacket;
+	else
+		avail = fifo_count;
+
+	fifo_count = s3c2410_udc_read_packet(fifo_reg, buf, req, avail);
+
+	/* checking this with ep0 is not accurate as we already
+	 * read a control request
+	 **/
+	if (idx && 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_write(idx, S3C2410_UDC_INDEX_REG);
+	fifo_count = s3c2410_udc_fifo_count_out();
+
+	/* Only ep0 debug messages are interesting */
+	if (!idx)
+		dprintk(DEBUG_VERBOSE, "%s fifo count : %d [last %d]\n",
+			__func__, fifo_count,is_last);
+
+	if (is_last) {
+		if (!idx) {
+			s3c2410_udc_set_ep0_de_out(base_addr);
+			ep->dev->ep0state = EP0_IDLE;
+		} else {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG);
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY,
+				   S3C2410_UDC_OUT_CSR1_REG);
+		}
+
+		s3c2410_udc_done(ep, req, 0);
+	} else {
+		if (!idx) {
+			s3c2410_udc_clear_ep0_opr(base_addr);
+		} else {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG);
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY,
+				   S3C2410_UDC_OUT_CSR1_REG);
+		}
+	}
+
+	return is_last;
+}
+
+static int s3c2410_udc_read_fifo_crq(struct usb_ctrlrequest *crq)
+{
+	unsigned char *outbuf = (unsigned char*)crq;
+	int bytes_read = 0;
+
+	udc_write(0, S3C2410_UDC_INDEX_REG);
+
+	bytes_read = s3c2410_udc_fifo_count_out();
+
+	dprintk(DEBUG_NORMAL, "%s: fifo_count=%d\n", __func__, bytes_read);
+
+	if ( bytes_read > sizeof(struct usb_ctrlrequest))
+		bytes_read = sizeof(struct usb_ctrlrequest);
+
+	readsb(S3C2410_UDC_EP0_FIFO_REG + base_addr, outbuf, bytes_read);
+
+	dprintk(DEBUG_VERBOSE, "%s: len=%d %02x:%02x {%x,%x,%x}\n", __func__,
+			bytes_read, crq->bRequest, crq->bRequestType,
+			crq->wValue, crq->wIndex, crq->wLength);
+
+	return bytes_read;
+}
+
+static int s3c2410_udc_get_status(struct s3c2410_udc *dev,
+				  struct usb_ctrlrequest *crq)
+{
+	u16 status = 0;
+	u8 ep_num = crq->wIndex & 0x7F;
+	u8 is_in = crq->wIndex & USB_DIR_IN;
+
+	switch (crq->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		break;
+
+	case USB_RECIP_DEVICE:
+		status = dev->devstatus;
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		if (ep_num > 4 || crq->wLength > 2)
+			return 1;
+
+		if (ep_num == 0) {
+			udc_write(0, S3C2410_UDC_INDEX_REG);
+			status = udc_read(S3C2410_UDC_IN_CSR1_REG);
+			status = status & S3C2410_UDC_EP0_CSR_SENDSTL;
+		} else {
+			udc_write(ep_num, S3C2410_UDC_INDEX_REG);
+			if (is_in) {
+				status = udc_read(S3C2410_UDC_IN_CSR1_REG);
+				status = status & S3C2410_UDC_ICSR1_SENDSTL;
+			} else {
+				status = udc_read(S3C2410_UDC_OUT_CSR1_REG);
+				status = status & S3C2410_UDC_OCSR1_SENDSTL;
+			}
+		}
+
+		status = status ? 1 : 0;
+		break;
+
+	default:
+		return 1;
+	}
+
+	/* Seems to be needed to get it working. ouch :( */
+	udelay(5);
+	udc_write(status & 0xFF, S3C2410_UDC_EP0_FIFO_REG);
+	udc_write(status >> 8, S3C2410_UDC_EP0_FIFO_REG);
+	s3c2410_udc_set_ep0_de_in(base_addr);
+
+	return 0;
+}
+/*------------------------- usb state machine -------------------------------*/
+static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value);
+static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev,
+					struct s3c2410_ep *ep,
+					struct usb_ctrlrequest *crq,
+					u32 ep0csr)
+{
+	int len, ret, tmp;
+
+	/* start control request? */
+	if (!(ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY))
+		return;
+
+	s3c2410_udc_nuke (dev, ep, -EPROTO);
+
+	len = s3c2410_udc_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);
+		s3c2410_udc_set_ep0_ss(base_addr);
+		return;
+	}
+
+	dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n",
+		crq->bRequest, crq->bRequestType, 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) {
+	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;
+			s3c2410_udc_set_ep0_de_out(base_addr);
+		}
+		break;
+
+	case USB_REQ_SET_INTERFACE:
+		dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n");
+
+		if (crq->bRequestType == USB_RECIP_INTERFACE)
+			goto config_change;
+		break;
+
+	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_write((tmp | S3C2410_UDC_FUNCADDR_UPDATE),
+				   S3C2410_UDC_FUNC_ADDR_REG);
+			s3c2410_udc_set_ep0_de_out(base_addr);
+			return;
+		}
+		break;
+
+	case USB_REQ_GET_STATUS:
+		dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n");
+		s3c2410_udc_clear_ep0_opr(base_addr);
+
+		if (dev->req_std) {
+			if (!s3c2410_udc_get_status(dev, crq)) {
+				return;
+			}
+		}
+		break;
+
+	case USB_REQ_CLEAR_FEATURE:
+		s3c2410_udc_clear_ep0_opr(base_addr);
+		if (crq->bRequestType != USB_RECIP_ENDPOINT)
+			break;
+		if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)
+			break;
+		s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 0);
+		s3c2410_udc_set_ep0_de_out(base_addr);
+		return;
+		break;
+
+	case USB_REQ_SET_FEATURE:
+		s3c2410_udc_clear_ep0_opr(base_addr);
+		if (crq->bRequestType != USB_RECIP_ENDPOINT)
+			break;
+		if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)
+			break;
+		s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 1);
+		s3c2410_udc_set_ep0_de_out(base_addr);
+		return;
+		break;
+	
+	default:
+		s3c2410_udc_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);
+
+		udelay(5);
+		s3c2410_udc_set_ep0_ss(base_addr);
+		s3c2410_udc_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]);
+}
+
+static void s3c2410_udc_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 = NULL;
+	else
+		req = list_entry(ep->queue.next, struct s3c2410_request, queue);
+
+	/* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to
+	   S3C2410_UDC_EP0_CSR_REG when index is zero */
+
+	udc_write(0, S3C2410_UDC_INDEX_REG);
+	ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+
+	dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n",
+		ep0csr, ep0states[dev->ep0state]);
+
+	/* clear stall status */
+	if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
+		s3c2410_udc_nuke(dev, ep, -EPIPE);
+	    	dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
+	    	s3c2410_udc_clear_ep0_sst(base_addr);
+		dev->ep0state = EP0_IDLE;
+		return;
+	}
+
+	/* clear setup end */
+	if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {
+	    	dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
+		s3c2410_udc_nuke(dev, ep, 0);
+	    	s3c2410_udc_clear_ep0_se(base_addr);
+		dev->ep0state = EP0_IDLE;
+	}
+
+	switch (dev->ep0state) {
+	case EP0_IDLE:
+		s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr);
+		break;
+
+	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */
+	    	dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
+		if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) {
+			s3c2410_udc_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 & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) {
+			s3c2410_udc_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:
+	    	dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
+	    	dev->ep0state = EP0_IDLE;
+		break;
+	}
+}
+
+/*
+ * 	handle_ep - Manage I/O endpoints
+ */
+
+static void  s3c2410_udc_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 = NULL;
+
+	idx = ep->bEndpointAddress & 0x7F;
+
+	if (is_in) {
+		udc_write(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr1 = udc_read(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_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL, S3C2410_UDC_IN_CSR1_REG);
+			return;
+		}
+
+		if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) {
+			s3c2410_udc_write_fifo(ep,req);
+		}
+	} else {
+		udc_write(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr1 = udc_read(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_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL, S3C2410_UDC_OUT_CSR1_REG);
+			return;
+		}
+
+		if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) {
+			s3c2410_udc_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 s3c2410_udc      *dev = _dev;
+	int usb_status;
+	int usbd_status;
+	int pwr_reg;
+	int ep0csr;
+	int i;
+	u32 idx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock,flags);
+
+	/* Driver connected ? */
+	if (!dev->driver) {
+		/* Clear interrupts */
+		udc_write(udc_read(S3C2410_UDC_USB_INT_REG),
+			   S3C2410_UDC_USB_INT_REG);
+		udc_write(udc_read(S3C2410_UDC_EP_INT_REG),
+			    S3C2410_UDC_EP_INT_REG);
+	}
+
+	/* Save index */
+	idx = udc_read(S3C2410_UDC_INDEX_REG);
+
+	/* Read status registers */
+	usb_status = udc_read(S3C2410_UDC_USB_INT_REG);
+	usbd_status = udc_read(S3C2410_UDC_EP_INT_REG);
+	pwr_reg = udc_read(S3C2410_UDC_PWR_REG);
+
+	udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	ep0csr = udc_read(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) {
+		/* two kind of reset :
+		 * - reset start -> pwr reg = 8
+		 * - reset end   -> pwr reg = 0
+		 **/
+		dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",ep0csr,pwr_reg);
+		dev->gadget.speed = USB_SPEED_UNKNOWN;
+		udc_write(0x00, S3C2410_UDC_INDEX_REG);
+		udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3,
+			   S3C2410_UDC_MAXP_REG);
+		dev->address = 0;
+
+		dev->ep0state = EP0_IDLE;
+		dev->gadget.speed = USB_SPEED_FULL;
+
+		/* clear interrupt */
+		udc_write(S3C2410_UDC_USBINT_RESET,
+			S3C2410_UDC_USB_INT_REG);
+
+		udc_write(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_write(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_write(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_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
+		s3c2410_udc_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_write(tmp, S3C2410_UDC_EP_INT_REG);
+			s3c2410_udc_handle_ep(&dev->ep[i]);
+		}
+	}
+
+	dprintk(DEBUG_VERBOSE,"irq: %d s3c2410_udc_done.\n", irq);
+
+	/* Restore old index */
+	udc_write(idx,S3C2410_UDC_INDEX_REG);
+
+	spin_unlock_irqrestore(&dev->lock,flags);
+
+	return IRQ_HANDLED;
+}
+/*------------------------- s3c2410_ep_ops ----------------------------------*/
+
+/*
+ * 	s3c2410_udc_ep_enable
+ */
+static int s3c2410_udc_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;
+
+	local_irq_save (flags);
+	_ep->maxpacket = max & 0x7ff;
+	ep->desc = desc;
+	ep->halted = 0;
+	ep->bEndpointAddress = desc->bEndpointAddress;
+
+	/* set max packet */
+	udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+	udc_write(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_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(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_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
+
+		csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
+		csr2 = S3C2410_UDC_OCSR2_DMAIEN;
+
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr1, S3C2410_UDC_OUT_CSR1_REG);
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr2, S3C2410_UDC_OUT_CSR2_REG);
+	}
+
+	/* enable irqs */
+	int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
+	udc_write(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);
+
+	local_irq_restore (flags);
+	s3c2410_udc_set_halt(_ep, 0);
+
+	return 0;
+}
+
+/*
+ * s3c2410_udc_ep_disable
+ */
+static int s3c2410_udc_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;
+	}
+
+	local_irq_save(flags);
+
+	dprintk(DEBUG_NORMAL, "ep_disable: %s\n",_ep->name);
+
+	ep->desc = NULL;
+	ep->halted = 1;
+
+	s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN);
+
+	/* disable irqs */
+	int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
+	udc_write(int_en_reg & ~(1<<ep->num), S3C2410_UDC_EP_INT_EN_REG);
+
+	local_irq_restore(flags);
+
+	dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);
+
+	return 0;
+}
+
+/*
+ * s3c2410_udc_alloc_request
+ */
+static struct usb_request * s3c2410_udc_alloc_request(struct usb_ep *_ep,
+						      gfp_t mem_flags)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_request	*req;
+
+    	dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags);
+
+	ep = container_of (_ep, struct s3c2410_ep, ep);
+	if (!_ep)
+		return NULL;
+
+	req = kzalloc (sizeof *req, mem_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD (&req->queue);
+	return &req->req;
+}
+
+/*
+ * s3c2410_udc_free_request
+ */
+static void s3c2410_udc_free_request(struct usb_ep *_ep,
+				     struct usb_request *_req)
+{
+	struct s3c2410_ep	*ep;
+	struct s3c2410_request	*req;
+
+    	dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _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_udc_alloc_buffer
+ */
+static void * s3c2410_udc_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
+				       dma_addr_t *dma, gfp_t mem_flags)
+{
+	char *retval;
+
+    	dprintk(DEBUG_VERBOSE, "%s()\n", __func__);
+
+	if (!the_controller->driver)
+		return NULL;
+
+	retval = kmalloc (bytes, mem_flags);
+	*dma = (dma_addr_t) retval;
+	return retval;
+}
+
+/*
+ * s3c2410_udc_free_buffer
+ */
+static void s3c2410_udc_free_buffer (struct usb_ep *_ep, void *buf,
+				     dma_addr_t dma, unsigned bytes)
+{
+    	dprintk(DEBUG_VERBOSE, "%s()\n", __func__);
+
+	if (bytes)
+		kfree (buf);
+}
+
+/*
+ * 	s3c2410_udc_queue
+ */
+static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
+			     gfp_t 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_udc_queue: inval 2\n");
+		return -EINVAL;
+	}
+
+	dev = ep->dev;
+	if (unlikely (!dev->driver ||
+		      dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+		return -ESHUTDOWN;
+	}
+
+	local_irq_save (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_udc_queue: 1 X X X\n");
+		else {
+			dprintk(DEBUG_NORMAL, "s3c2410_udc_queue: 0 %01d %01d %01d\n",!_req->complete,!_req->buf, !list_empty(&req->queue));
+		}
+
+		local_irq_restore(flags);
+		return -EINVAL;
+	}
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	dprintk(DEBUG_VERBOSE, "s3c2410_udc_queue: ep%x len %d\n",
+		ep->bEndpointAddress, _req->length);
+
+	if (ep->bEndpointAddress) {
+		udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);
+
+		ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN) ?
+				   S3C2410_UDC_IN_CSR1_REG :
+				   S3C2410_UDC_OUT_CSR1_REG);
+		fifo_count = s3c2410_udc_fifo_count_out();
+	} else {
+		udc_write(0, S3C2410_UDC_INDEX_REG);
+		ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+		fifo_count = s3c2410_udc_fifo_count_out();
+	}
+
+	/* kickstart this i/o queue? */
+	if (list_empty(&ep->queue) && !ep->halted) {
+		if (ep->bEndpointAddress == 0 /* ep0 */) {
+			switch (dev->ep0state) {
+			case EP0_IN_DATA_PHASE:
+				if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY) &&
+				     s3c2410_udc_write_fifo(ep, req)) {
+					dev->ep0state = EP0_IDLE;
+					req = NULL;
+				}
+				break;
+
+			case EP0_OUT_DATA_PHASE:
+				if ((!_req->length) ||
+				    ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) &&
+				     s3c2410_udc_read_fifo(ep,req))) {
+					dev->ep0state = EP0_IDLE;
+					req = NULL;
+				}
+				break;
+
+			default:
+				local_irq_restore(flags);
+				return -EL2HLT;
+			}
+		}
+		else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 &&
+			 (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) &&
+			 s3c2410_udc_write_fifo(ep, req)) {
+			req = NULL;
+		} else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) &&
+			   fifo_count && s3c2410_udc_read_fifo(ep, req)) {
+			req = NULL;
+		}
+	}
+
+	/* pio or dma irq handler advances the queue. */
+	if (likely (req != 0))
+		list_add_tail(&req->queue, &ep->queue);
+
+	local_irq_restore(flags);
+
+	dprintk(DEBUG_VERBOSE, "s3c2410_udc_queue normal end\n");
+	return 0;
+}
+
+/*
+ * 	s3c2410_udc_dequeue
+ */
+static int s3c2410_udc_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 = NULL;
+
+    	dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _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);
+
+	local_irq_save (flags);
+
+	list_for_each_entry (req, &ep->queue, queue) {
+		if (&req->req == _req) {
+			list_del_init (&req->queue);
+			_req->status = -ECONNRESET;
+			retval = 0;
+			break;
+		}
+	}
+
+	if (retval == 0) {
+		dprintk(DEBUG_VERBOSE, "dequeued req %p from %s, len %d buf %p\n",
+				req, _ep->name, _req->length, _req->buf);
+
+		s3c2410_udc_done(ep, req, -ECONNRESET);
+	}
+	local_irq_restore (flags);
+
+	return retval;
+}
+
+/*
+ * s3c2410_udc_set_halt
+ */
+static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value)
+{
+	struct s3c2410_ep	*ep;
+	u32			ep_csr=0;
+	unsigned long		flags;
+	u32			idx;
+
+	ep = container_of(_ep, struct s3c2410_ep, ep);
+	if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+		dprintk(DEBUG_NORMAL, "s3c2410_udc_set_halt: inval 2\n");
+		return -EINVAL;
+	}
+
+	local_irq_save (flags);
+
+	idx = ep->bEndpointAddress&0x7F;
+
+	if (!idx) {
+		s3c2410_udc_set_ep0_ss(base_addr);
+		s3c2410_udc_set_ep0_de_out(base_addr);
+	} else {
+		udc_write(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN) ?
+				   S3C2410_UDC_IN_CSR1_REG :
+				   S3C2410_UDC_OUT_CSR1_REG);
+
+		if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
+			if (value)
+				udc_write(ep_csr | S3C2410_UDC_ICSR1_SENDSTL,
+					  S3C2410_UDC_IN_CSR1_REG);
+			else {
+				ep_csr &= ~S3C2410_UDC_ICSR1_SENDSTL;
+				udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
+				ep_csr |= S3C2410_UDC_ICSR1_CLRDT;
+				udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
+			}
+		} else {
+			if (value)
+				udc_write(ep_csr | S3C2410_UDC_OCSR1_SENDSTL,
+					  S3C2410_UDC_OUT_CSR1_REG);
+			else {
+				ep_csr &= ~S3C2410_UDC_OCSR1_SENDSTL;
+				udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
+				ep_csr |= S3C2410_UDC_OCSR1_CLRDT;
+				udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
+			}
+		}
+	}
+
+	ep->halted = value ? 1 : 0;
+	local_irq_restore (flags);
+
+	return 0;
+}
+
+static const struct usb_ep_ops s3c2410_ep_ops = {
+	.enable         = s3c2410_udc_ep_enable,
+	.disable        = s3c2410_udc_ep_disable,
+
+	.alloc_request  = s3c2410_udc_alloc_request,
+	.free_request   = s3c2410_udc_free_request,
+
+	.alloc_buffer   = s3c2410_udc_alloc_buffer,
+	.free_buffer    = s3c2410_udc_free_buffer,
+
+	.queue          = s3c2410_udc_queue,
+	.dequeue        = s3c2410_udc_dequeue,
+
+	.set_halt       = s3c2410_udc_set_halt,
+};
+
+/*------------------------- usb_gadget_ops ----------------------------------*/
+
+/*
+ * 	s3c2410_udc_get_frame
+ */
+static int s3c2410_udc_get_frame(struct usb_gadget *_gadget)
+{
+	int tmp;
+
+	dprintk(DEBUG_VERBOSE, "%s()\n", __func__);
+
+	tmp = udc_read(S3C2410_UDC_FRAME_NUM2_REG) << 8;
+	tmp |= udc_read(S3C2410_UDC_FRAME_NUM1_REG);
+	return tmp;
+}
+
+/*
+ * 	s3c2410_udc_wakeup
+ */
+static int s3c2410_udc_wakeup(struct usb_gadget *_gadget)
+{
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	return 0;
+}
+
+/*
+ * 	s3c2410_udc_set_selfpowered
+ */
+static int s3c2410_udc_set_selfpowered(struct usb_gadget *_gadget, int value)
+{
+	struct s3c2410_udc  *udc;
+
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	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 void s3c2410_udc_disable(struct s3c2410_udc *dev);
+static void s3c2410_udc_enable(struct s3c2410_udc *dev);
+
+static int pull_up(struct s3c2410_udc  *udc, int is_on)
+{
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	if (udc_info && udc_info->udc_command) {
+		if (is_on)
+			s3c2410_udc_enable(udc);
+		else {
+			if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+				if (udc->driver && udc->driver->disconnect)
+					udc->driver->disconnect(&udc->gadget);
+
+			}
+			s3c2410_udc_disable(udc);
+		}
+	}
+	else
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int s3c2410_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+	struct s3c2410_udc  *udc;
+
+	dprintk(DEBUG_NORMAL, "s3c2410_udc_vbus_session()\n");
+	udc = container_of (_gadget, struct s3c2410_udc, gadget);
+
+	udc->vbus = (is_active != 0);
+	pull_up(udc, is_active);
+	return 0;
+}
+
+static int s3c2410_udc_pullup(struct usb_gadget *_gadget, int is_on)
+{
+	struct s3c2410_udc  *udc;
+
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	udc = container_of (_gadget, struct s3c2410_udc, gadget);
+	is_on = is_on ? 0 : 1;
+	pull_up(udc, is_on);
+
+	return 0;
+}
+
+static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev)
+{
+	struct s3c2410_udc      *dev = _dev;
+	unsigned int 		value;
+
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+	value = s3c2410_gpio_getpin(udc_info->vbus_pin);
+
+	if (udc_info->vbus_pin_inverted)
+		value = !value;
+
+	if (value != dev->vbus)
+		s3c2410_udc_vbus_session(&dev->gadget, value);
+
+	return IRQ_HANDLED;
+}
+
+static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
+{
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	if (udc_info && udc_info->vbus_draw) {
+		udc_info->vbus_draw(ma);
+		return 0;
+	}
+
+	return -ENOTSUPP;
+}
+
+static const struct usb_gadget_ops s3c2410_ops = {
+	.get_frame          = s3c2410_udc_get_frame,
+	.wakeup             = s3c2410_udc_wakeup,
+	.set_selfpowered    = s3c2410_udc_set_selfpowered,
+        .pullup             = s3c2410_udc_pullup,
+	.vbus_session	    = s3c2410_udc_vbus_session,
+	.vbus_draw	    = s3c2410_vbus_draw,
+};
+
+/*------------------------- gadget driver handling---------------------------*/
+/*
+ * s3c2410_udc_disable
+ */
+static void s3c2410_udc_disable(struct s3c2410_udc *dev)
+{
+	dprintk(DEBUG_NORMAL, "s3c2410_udc_disable called\n");
+
+	/* Disable all interrupts */
+	udc_write(0x00, S3C2410_UDC_USB_INT_EN_REG);
+	udc_write(0x00, S3C2410_UDC_EP_INT_EN_REG);
+
+	/* Clear the interrupt registers */
+	udc_write(S3C2410_UDC_USBINT_RESET |
+		   S3C2410_UDC_USBINT_RESUME |
+		   S3C2410_UDC_USBINT_SUSPEND,
+		   S3C2410_UDC_USB_INT_REG);
+
+	udc_write(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 speed to unknown */
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
+/*
+ * s3c2410_udc_reinit
+ */
+static void s3c2410_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 = NULL;
+		ep->halted = 0;
+		INIT_LIST_HEAD (&ep->queue);
+	}
+}
+
+/*
+ * s3c2410_udc_enable
+ */
+static void s3c2410_udc_enable(struct s3c2410_udc *dev)
+{
+	int i;
+
+	dprintk(DEBUG_NORMAL, "s3c2410_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_write(i, S3C2410_UDC_INDEX_REG);
+		udc_write((dev->ep[i].ep.maxpacket&0x7ff)>>3,
+			   S3C2410_UDC_MAXP_REG);
+	}
+
+	/* Set default power state */
+	udc_write(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG);
+
+	/* Enable reset and suspend interrupt interrupts */
+	udc_write(1<<2 | 1<<0 ,S3C2410_UDC_USB_INT_EN_REG);
+
+	/* Enable ep0 interrupt */
+	udc_write(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->setup ||
+	    driver->speed != USB_SPEED_FULL) {
+		printk(KERN_ERR "Invalid driver : bind %p setup %p speed %d\n",
+			driver->bind, driver->setup, driver->speed);
+		return -EINVAL;
+	}
+#if defined(MODULE)
+	if (!driver->unbind) {
+		printk(KERN_ERR "Invalid driver : no unbind method\n");
+		return -EINVAL;
+	}
+#endif
+
+	/* Hook the driver */
+	udc->driver = driver;
+	udc->gadget.dev.driver = &driver->driver;
+
+	/* Bind the driver */
+	if ((retval = device_add(&udc->gadget.dev)) != 0) {
+		printk(KERN_ERR "Error in device_add() : %d\n",retval);
+		goto register_error;
+	}
+
+	dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", driver->driver.name);
+	if ((retval = driver->bind (&udc->gadget)) != 0) {
+		device_del(&udc->gadget.dev);
+		goto register_error;
+	}
+
+	/* Enable udc */
+	s3c2410_udc_enable(udc);
+
+	return 0;
+
+register_error:
+	udc->driver = NULL;
+	udc->gadget.dev.driver = NULL;
+	return retval;
+}
+
+/*
+ * 	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);
+
+	if (driver->disconnect)
+		driver->disconnect(&udc->gadget);
+
+	if (driver->unbind)
+		driver->unbind (&udc->gadget);
+
+	device_del(&udc->gadget.dev);
+	udc->driver = NULL;
+
+	/* Disable udc */
+	s3c2410_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 platform_device *pdev)
+{
+	struct s3c2410_udc *udc = &memory;
+	struct device *dev = &pdev->dev;
+	int retval;
+	unsigned int irq;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	usb_bus_clock = clk_get(NULL, "usb-bus-gadget");
+	if (IS_ERR(usb_bus_clock)) {
+		dev_err(dev, "failed to get usb bus clock source\n");
+		return PTR_ERR(usb_bus_clock);
+	}
+
+	clk_enable(usb_bus_clock);
+
+	udc_clock = clk_get(NULL, "usb-device");
+	if (IS_ERR(udc_clock)) {
+		dev_err(dev, "failed to get udc clock source\n");
+		return PTR_ERR(udc_clock);
+	}
+
+	clk_enable(udc_clock);
+
+	mdelay(10);
+
+	dev_dbg(dev, "got and enabled clocks\n");
+
+	if (strncmp(pdev->name, "s3c2440", 7) == 0) {
+		dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n");
+		memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;
+		memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;
+		memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;
+		memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
+	}
+
+	spin_lock_init (&udc->lock);
+	udc_info = pdev->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 = &pdev->dev;
+	udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
+
+	the_controller = udc;
+	platform_set_drvdata(pdev, udc);
+
+	s3c2410_udc_disable(udc);
+	s3c2410_udc_reinit(udc);
+
+	/* irq setup after old hardware state is cleaned up */
+	retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
+		IRQF_DISABLED, gadget_name, udc);
+
+	if (retval != 0) {
+		dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
+		retval = -EBUSY;
+		goto err_map;
+	}
+
+	dev_dbg(dev, "got irq %i\n", IRQ_USBD);
+
+	if (udc_info && udc_info->vbus_pin > 0) {
+		irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
+		retval = request_irq(irq, s3c2410_udc_vbus_irq,
+			IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+			gadget_name, udc);
+
+		if (retval != 0) {
+			dev_err(dev, "can't get vbus irq %i, err %d\n",
+				irq, retval);
+			retval = -EBUSY;
+			goto err_int;
+		}
+
+		dev_dbg(dev, "got irq %i\n", irq);
+	} else {
+		udc->vbus = 1;
+	}
+
+	if (s3c2410_udc_debugfs_root) {
+		udc->regs_info = debugfs_create_file("registers", S_IRUGO,
+			s3c2410_udc_debugfs_root, udc, &s3c2410_udc_debugfs_fops);
+		if (IS_ERR(udc->regs_info)) {
+			dev_warn(dev, "debugfs file creation failed %ld\n",
+				PTR_ERR(udc->regs_info));
+			udc->regs_info = NULL;
+		}
+	}
+
+	dev_dbg(dev, "probe ok\n");
+
+	return 0;
+
+err_int:
+	free_irq(IRQ_USBD, udc);
+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 platform_device *pdev)
+{
+	struct s3c2410_udc *udc = platform_get_drvdata(pdev);
+	unsigned int irq;
+
+	dev_dbg(&pdev->dev, "%s()\n", __func__);
+	usb_gadget_unregister_driver(udc->driver);
+
+	if (udc->regs_info)
+		debugfs_remove(udc->regs_info);
+
+	if (udc_info && udc_info->vbus_pin > 0) {
+		irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
+		free_irq(irq, udc);
+	}
+
+	free_irq(IRQ_USBD, udc);
+
+	iounmap(base_addr);
+	release_mem_region(rsrc_start, rsrc_len);
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (!IS_ERR(udc_clock) && udc_clock != NULL) {
+		clk_disable(udc_clock);
+		clk_put(udc_clock);
+		udc_clock = NULL;
+	}
+
+	if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
+		clk_disable(usb_bus_clock);
+		clk_put(usb_bus_clock);
+		usb_bus_clock = NULL;
+	}
+
+	dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
+{
+	if (udc_info && udc_info->udc_command)
+		udc_info->udc_command(S3C2410_UDC_P_DISABLE);
+
+	return 0;
+}
+
+static int s3c2410_udc_resume(struct platform_device *pdev)
+{
+	if (udc_info && udc_info->udc_command)
+		udc_info->udc_command(S3C2410_UDC_P_ENABLE);
+
+	return 0;
+}
+#else
+#define s3c2410_udc_suspend      NULL
+#define s3c2410_udc_resume       NULL
+#endif
+
+static struct platform_driver udc_driver_2410 = {
+	.driver		= {
+		.name 	= "s3c2410-usbgadget",
+		.owner	= THIS_MODULE,
+	},
+	.probe          = s3c2410_udc_probe,
+	.remove         = s3c2410_udc_remove,
+	.suspend	= s3c2410_udc_suspend,
+	.resume		= s3c2410_udc_resume,
+};
+
+static struct platform_driver udc_driver_2440 = {
+	.driver		= {
+		.name	= "s3c2440-usbgadget",
+		.owner	= THIS_MODULE,
+	},
+	.probe          = s3c2410_udc_probe,
+	.remove         = s3c2410_udc_remove,
+	.suspend	= s3c2410_udc_suspend,
+	.resume		= s3c2410_udc_resume,
+};
+
+static int __init udc_init(void)
+{
+	int retval;
+
+	dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION);
+
+	s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);
+	if (IS_ERR(s3c2410_udc_debugfs_root)) {
+		printk(KERN_ERR "%s: debugfs dir creation failed %lx\n",
+			gadget_name, PTR_ERR(s3c2410_udc_debugfs_root));
+		s3c2410_udc_debugfs_root = NULL;
+	}
+
+	retval = platform_driver_register(&udc_driver_2410);
+	if (retval)
+		goto err;
+
+	retval = platform_driver_register(&udc_driver_2440);
+	if (retval)
+		goto err;
+
+	return 0;
+err:
+	if (s3c2410_udc_debugfs_root)
+		debugfs_remove(s3c2410_udc_debugfs_root);
+	return retval;
+}
+
+static void __exit udc_exit(void)
+{
+	platform_driver_unregister(&udc_driver_2410);
+	platform_driver_unregister(&udc_driver_2440);
+	if (s3c2410_udc_debugfs_root)
+		debugfs_remove(s3c2410_udc_debugfs_root);
+}
+
+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
index 0000000..de56646
--- /dev/null
+++ b/drivers/usb/gadget/s3c2410_udc.h
@@ -0,0 +1,112 @@
+/*
+ * linux/drivers/usb/gadget/s3c2410_udc.h
+ * Samsung on-chip full speed USB device controllers
+ *
+ * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard
+ *	Additional cleanups by Ben Dooks <ben-linux@fluff.org>
+ *
+ * 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
+ *
+ */
+
+#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            */
+/* also testusb 14 fails  wit 16 but is */
+/* fine with 8                          */
+#define EP0_FIFO_SIZE		 8
+#define EP_FIFO_SIZE		64
+#define DEFAULT_POWER_STATE	0x00
+
+#define S3C2440_EP_FIFO_SIZE	128
+
+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;
+	u8				vbus;
+	struct dentry			*regs_info;
+};
+
+#endif
+
+
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 47d15b5..e420dea 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -40,6 +40,14 @@ config BACKLIGHT_CORGI
 	  If you have a Sharp Zaurus SL-C7xx, SL-Cxx00 or SL-6000x say y to enable the
 	  backlight driver.
 
+config BACKLIGHT_S3C2410
+	tristate "Samsung S3C2410 Backlight Driver"
+	depends on BACKLIGHT_CLASS_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.
+
 config BACKLIGHT_LOCOMO
 	tristate "Sharp LOCOMO LCD/Backlight Driver"
 	depends on BACKLIGHT_CLASS_DEVICE && SHARP_LOCOMO
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index 0c3ce46..cec48c0 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -3,6 +3,7 @@
 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
 obj-$(CONFIG_BACKLIGHT_HP680)	+= hp680_bl.o
 obj-$(CONFIG_BACKLIGHT_LOCOMO)	+= locomolcd.o
 obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
diff --git a/drivers/video/backlight/s3c2410_lcd.c b/drivers/video/backlight/s3c2410_lcd.c
new file mode 100644
index 0000000..f8184cf
--- /dev/null
+++ b/drivers/video/backlight/s3c2410_lcd.c
@@ -0,0 +1,248 @@
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+#include <asm/arch/lcd.h>
+#include <asm/arch/regs-gpio.h>
+
+struct s3c2410bl_devs {
+	struct backlight_device *bl;
+	struct lcd_device	*lcd;
+};
+
+static char suspended;
+
+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;
+	int lcd_power = 1;
+
+	info = (struct s3c2410_bl_mach_info *)class_get_devdata(&lcd->class_dev);
+
+	if (info && info->lcd_power) {
+		info->lcd_power_value = power;
+		if (power != FB_BLANK_UNBLANK)
+			lcd_power = 0;
+		if (suspended)
+			lcd_power = 0;
+		info->lcd_power(lcd_power);
+	}
+
+	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)
+{
+	struct s3c2410_bl_mach_info *info;
+	int brightness = bl->props.brightness;
+	int power = 1;
+
+	info = (struct s3c2410_bl_mach_info *)class_get_devdata(&bl->class_dev);
+
+	if (bl->props.power != FB_BLANK_UNBLANK)
+		power = 0;
+	if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+		power = 0;
+	if (suspended)
+		power = 0;
+
+	if (info && info->set_brightness) {
+		if ( brightness )
+		{
+			info->brightness_value = brightness;
+			info->set_brightness(brightness);
+		}
+		else
+			power = 0;
+	}
+	if (info && info->backlight_power) {
+		info->backlight_power_value = power;
+		info->backlight_power(power);
+	}
+
+	return 0;
+}
+
+static int is_s3c2410fb(struct fb_info *info)
+{
+	return (!strcmp(info->fix.id,"s3c2410fb"));
+}
+
+static struct backlight_ops s3c2410bl_ops = {
+	.get_brightness = s3c2410bl_get_bl_brightness,
+	.update_status	= s3c2410bl_set_bl_brightness,
+	.check_fb 	= is_s3c2410fb
+};
+static struct lcd_ops s3c2410lcd_ops = {
+	.get_power	= s3c2410bl_get_lcd_power,
+	.set_power	= s3c2410bl_set_lcd_power,
+	.check_fb	= is_s3c2410fb
+};
+
+static int __init s3c2410bl_probe(struct platform_device *pdev)
+{
+	struct s3c2410bl_devs *devs;
+	struct s3c2410_bl_mach_info *info;
+
+	suspended = 0;
+
+	info = ( struct s3c2410_bl_mach_info *)pdev->dev.platform_data;
+
+	if (!info) {
+		printk(KERN_ERR "Hm... too bad : no platform data for bl\n");
+		return -EINVAL;
+	}
+
+	devs = (struct s3c2410bl_devs *)kmalloc(sizeof(*devs),GFP_KERNEL);
+	if (!devs) {
+		return -ENOMEM;
+	}
+
+	/* Register the backlight device */
+	devs->bl = backlight_device_register ("s3c2410-bl", &pdev->dev, info, \
+		&s3c2410bl_ops);
+
+	if (IS_ERR (devs->bl)) {
+		kfree(devs);
+		return PTR_ERR (devs->bl);
+	}
+
+	devs->bl->props.max_brightness = info->backlight_max;
+
+	/* Set default brightness */
+	devs->bl->props.power = FB_BLANK_UNBLANK;
+	devs->bl->props.brightness = info->backlight_default;
+	s3c2410bl_set_bl_brightness(devs->bl);
+
+	devs->lcd = lcd_device_register("s3c2410-lcd", info, &s3c2410lcd_ops);
+
+	if (IS_ERR (devs->lcd)) {
+		backlight_device_unregister(devs->bl);
+		kfree(devs);
+		return PTR_ERR (devs->lcd);
+	}
+	platform_set_drvdata(pdev, devs);
+
+	printk(KERN_ERR "s3c2410 Backlight Driver Initialized.\n");
+	return 0;
+}
+
+static int s3c2410bl_remove(struct platform_device *pdev)
+{
+	struct s3c2410bl_devs *devs = platform_get_drvdata(pdev);
+
+	if (devs) {
+		if (devs->bl) {
+			devs->bl->props.power = FB_BLANK_POWERDOWN;
+			s3c2410bl_set_bl_brightness(devs->bl);
+			backlight_device_unregister(devs->bl);
+		}
+		if (devs->lcd) {
+			lcd_device_unregister(devs->lcd);
+		}
+		kfree(devs);
+	}
+	
+	printk("s3c2410 Backlight Driver Unloaded\n");
+
+	return 0;
+
+}
+
+#ifdef CONFIG_PM
+static int s3c2410bl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct s3c2410bl_devs *devs = platform_get_drvdata(pdev);
+	struct s3c2410_bl_mach_info *info = (struct s3c2410_bl_mach_info *)pdev->dev.platform_data;
+
+	if (devs) {
+		suspended = 1;
+		s3c2410bl_set_bl_brightness(devs->bl);
+		s3c2410bl_set_lcd_power(devs->lcd, info->lcd_power_value);
+	}
+
+	return 0;
+}
+
+static int s3c2410bl_resume(struct platform_device *pdev)
+{
+	struct s3c2410bl_devs *devs = platform_get_drvdata(pdev);
+	struct s3c2410_bl_mach_info *info = (struct s3c2410_bl_mach_info *)pdev->dev.platform_data;
+
+	if (devs) {
+		suspended = 0;
+		s3c2410bl_set_lcd_power(devs->lcd, info->lcd_power_value);
+		s3c2410bl_set_bl_brightness(devs->bl);
+	}
+	return 0;
+}
+#else
+#define s3c2410bl_suspend NULL
+#define s3c2410bl_resume  NULL
+#endif
+
+static struct platform_driver s3c2410bl_driver = {
+	.driver		= {
+		.name	= "s3c2410-bl",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= s3c2410bl_probe,
+	.remove		= s3c2410bl_remove,
+	.suspend        = s3c2410bl_suspend,
+	.resume         = s3c2410bl_resume,
+};
+
+
+
+static int __init s3c2410bl_init(void)
+{
+	return platform_driver_register(&s3c2410bl_driver);
+}
+
+static void __exit s3c2410bl_cleanup(void)
+{
+	platform_driver_unregister(&s3c2410bl_driver);
+}
+
+module_init(s3c2410bl_init);
+module_exit(s3c2410bl_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
index aa3935d..18706e3 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -217,6 +217,18 @@ config FONT_MINI_4x6
 	bool "Mini 4x6 font"
 	depends on !SPARC && FONTS
 
+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"
 	depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC)
diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
index 9b26dda..457fd9c 100644
--- a/drivers/video/console/Makefile
+++ b/drivers/video/console/Makefile
@@ -15,6 +15,8 @@ font-objs-$(CONFIG_FONT_10x18)     += font_10x18.o
 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
index 0000000..e0257ab
--- /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 */
+
+};
+
+const 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
index 0000000..3266cf4
--- /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 */
+};
+
+const 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
index c960728..7bf3ab7 100644
--- a/drivers/video/console/fonts.c
+++ b/drivers/video/console/fonts.c
@@ -63,6 +63,14 @@ static const 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 ARRAY_SIZE(fonts)
diff --git a/include/asm-arm/arch-s3c2410/buttons.h b/include/asm-arm/arch-s3c2410/buttons.h
new file mode 100644
index 0000000..d47a2ec
--- /dev/null
+++ b/include/asm-arm/arch-s3c2410/buttons.h
@@ -0,0 +1,41 @@
+/* linux/include/asm/arch-s3c2410/buttons.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.
+ *
+ */
+
+#ifndef __ASM_ARM_BUTTONS_H
+#define __ASM_ARM_BUTTONS_H
+
+#include <linux/input.h>
+
+struct s3c_button {
+	int		pin;
+	int		keycode;
+	char		*name;
+	char		wake;
+	int		irq;
+	int		last_state;
+	struct timer_list timer;
+};
+
+struct s3c_butt_mach_info {
+	struct s3c_button	*buttons;
+	int			size;
+};
+
+#define DECLARE_BUTTON(p, k, n, w)	\
+	{				\
+		.pin     = p,		\
+		.keycode = k,		\
+		.name	 = n,		\
+		.wake	 = w,		\
+	}
+
+
+extern void __init s3c24xx_butt_set_platdata(struct s3c_butt_mach_info *);
+#endif
diff --git a/include/asm-arm/arch-s3c2410/lcd.h b/include/asm-arm/arch-s3c2410/lcd.h
new file mode 100644
index 0000000..0efef0b
--- /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/mmc.h b/include/asm-arm/arch-s3c2410/mmc.h
new file mode 100644
index 0000000..67efdc7
--- /dev/null
+++ b/include/asm-arm/arch-s3c2410/mmc.h
@@ -0,0 +1,24 @@
+/* linux/include/asm-arm/arch-s3c2410/mmc.h
+ *
+ * (c) 2004-2005 Simtec Electronics
+ *	http://www.simtec.co.uk/products/SWLINUX/
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX - MMC/SD platform data
+ *
+ * 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.
+ *
+*/
+
+#ifndef __ASM_ARCH_MMC_H
+#define __ASM_ARCH_MMC_H __FILE__
+
+struct s3c24xx_mmc_platdata {
+	unsigned int	gpio_detect;
+};
+
+extern void __init s3c24xx_sdi_set_platdata(struct s3c24xx_mmc_platdata *pd);
+#endif /* __ASM_ARCH_MMC_H */
+
diff --git a/include/asm-arm/arch-s3c2410/regs-sdi.h b/include/asm-arm/arch-s3c2410/regs-sdi.h
index bb9d30b..43249b3 100644
--- a/include/asm-arm/arch-s3c2410/regs-sdi.h
+++ b/include/asm-arm/arch-s3c2410/regs-sdi.h
@@ -7,7 +7,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  *
- * S3C2410 MMC/SDIO register definitions
+ * Samsung S3C MCI register definitions
 */
 
 #ifndef __ASM_ARM_REGS_SDI
@@ -28,9 +28,15 @@
 #define S3C2410_SDIDCNT               (0x30)
 #define S3C2410_SDIDSTA               (0x34)
 #define S3C2410_SDIFSTA               (0x38)
+
 #define S3C2410_SDIDATA               (0x3C)
 #define S3C2410_SDIIMSK               (0x40)
 
+#define S3C2440_SDIDATA               (0x40)
+#define S3C2440_SDIIMSK               (0x3C)
+
+#define S3C2440_SDICON_SDRESET        (1<<8)
+#define S3C2440_SDICON_MMCCLOCK       (1<<5)
 #define S3C2410_SDICON_BYTEORDER      (1<<4)
 #define S3C2410_SDICON_SDIOIRQ        (1<<3)
 #define S3C2410_SDICON_RWAITEN        (1<<2)
@@ -42,7 +48,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)
@@ -51,6 +58,9 @@
 #define S3C2410_SDICMDSTAT_XFERING    (1<<8)
 #define S3C2410_SDICMDSTAT_INDEX      (0xff)
 
+#define S3C2440_SDIDCON_DS_BYTE       (0<<22)
+#define S3C2440_SDIDCON_DS_HALFWORD   (1<<22)
+#define S3C2440_SDIDCON_DS_WORD       (2<<22)
 #define S3C2410_SDIDCON_IRQPERIOD     (1<<21)
 #define S3C2410_SDIDCON_TXAFTERRESP   (1<<20)
 #define S3C2410_SDIDCON_RXAFTERCMD    (1<<19)
@@ -59,6 +69,7 @@
 #define S3C2410_SDIDCON_WIDEBUS       (1<<16)
 #define S3C2410_SDIDCON_DMAEN         (1<<15)
 #define S3C2410_SDIDCON_STOP          (1<<14)
+#define S3C2440_SDIDCON_DATSTART      (1<<14)
 #define S3C2410_SDIDCON_DATMODE	      (3<<12)
 #define S3C2410_SDIDCON_BLKNUM        (0x7ff)
 
@@ -68,6 +79,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)
@@ -82,10 +94,12 @@
 #define S3C2410_SDIDSTA_TXDATAON      (1<<1)
 #define S3C2410_SDIDSTA_RXDATAON      (1<<0)
 
+#define S3C2440_SDIFSTA_FIFORESET      (1<<16)
+#define S3C2440_SDIFSTA_FIFOFAIL       (3<<14)  /* 3 is correct (2 bits) */
 #define S3C2410_SDIFSTA_TFDET          (1<<13)
 #define S3C2410_SDIFSTA_RFDET          (1<<12)
-#define S3C2410_SDIFSTA_TXHALF         (1<<11)
-#define S3C2410_SDIFSTA_TXEMPTY        (1<<10)
+#define S3C2410_SDIFSTA_TFHALF         (1<<11)
+#define S3C2410_SDIFSTA_TFEMPTY        (1<<10)
 #define S3C2410_SDIFSTA_RFLAST         (1<<9)
 #define S3C2410_SDIFSTA_RFFULL         (1<<8)
 #define S3C2410_SDIFSTA_RFHALF         (1<<7)
diff --git a/include/asm-arm/arch-s3c2410/regs-udc.h b/include/asm-arm/arch-s3c2410/regs-udc.h
index 3c83546..0298863 100644
--- a/include/asm-arm/arch-s3c2410/regs-udc.h
+++ b/include/asm-arm/arch-s3c2410/regs-udc.h
@@ -61,6 +61,8 @@
 
 #define S3C2410_UDC_INDEX_REG		S3C2410_USBDREG(0x0178)
 
+#define S3C2410_UDC_FUNCADDR_UPDATE	(1<<7)
+
 /* indexed registers */
 
 #define S3C2410_UDC_MAXP_REG		S3C2410_USBDREG(0x0180)
@@ -135,10 +137,6 @@
 #define S3C2410_UDC_OCSR2_ISO		(1<<6) // R/W
 #define S3C2410_UDC_OCSR2_DMAIEN	(1<<5) // R/W
 
-#define S3C2410_UDC_SETIX(base,x)	    \
-	writel(S3C2410_UDC_INDEX_ ## x, base+S3C2410_UDC_INDEX_REG);
-
-
 #define S3C2410_UDC_EP0_CSR_OPKRDY	(1<<0)
 #define S3C2410_UDC_EP0_CSR_IPKRDY	(1<<1)
 #define S3C2410_UDC_EP0_CSR_SENTSTL	(1<<2)
diff --git a/include/asm-arm/arch-s3c2410/ts.h b/include/asm-arm/arch-s3c2410/ts.h
new file mode 100644
index 0000000..8566f25
--- /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/plat-s3c24xx/devs.h b/include/asm-arm/plat-s3c24xx/devs.h
index dddf485..2241950 100644
--- a/include/asm-arm/plat-s3c24xx/devs.h
+++ b/include/asm-arm/plat-s3c24xx/devs.h
@@ -23,6 +23,7 @@ extern struct platform_device *s3c24xx_uart_src[];
 
 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;
@@ -41,6 +42,8 @@ extern struct platform_device s3c_device_timer2;
 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/include/linux/font.h b/include/linux/font.h
index 53b129f..2b252c1 100644
--- 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 const struct font_desc	font_vga_8x8,
 			font_vga_8x16,
@@ -41,7 +43,9 @@ extern const 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/mmc.h b/include/linux/mmc/mmc.h
index cdc54be..7b690cf 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -55,12 +55,15 @@ struct mmc_command {
 	unsigned int		retries;	/* max number of retries */
 	unsigned int		error;		/* command error */
 
-#define MMC_ERR_NONE	0
-#define MMC_ERR_TIMEOUT	1
-#define MMC_ERR_BADCRC	2
-#define MMC_ERR_FIFO	3
-#define MMC_ERR_FAILED	4
-#define MMC_ERR_INVALID	5
+#define MMC_ERR_NONE		0
+#define MMC_ERR_TIMEOUT		1
+#define MMC_ERR_BADCRC		2
+#define MMC_ERR_FIFO		3
+#define MMC_ERR_DMA		4
+#define MMC_ERR_BUSY		5
+#define MMC_ERR_FAILED		6
+#define MMC_ERR_INVALID		7
+#define MMC_ERR_CANCELED	8
 
 	struct mmc_data		*data;		/* data segment associated with cmd */
 	struct mmc_request	*mrq;		/* associated request */
diff --git a/kernel/Makefile b/kernel/Makefile
index ac6b27a..db6ee10 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -8,7 +8,7 @@ obj-y     = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
 	    signal.o sys.o kmod.o workqueue.o pid.o \
 	    rcupdate.o extable.o params.o posix-timers.o \
 	    kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
-	    hrtimer.o rwsem.o latency.o nsproxy.o srcu.o
+	    hrtimer.o rwsem.o latency.o nsproxy.o srcu.o mhelper.o
 
 obj-$(CONFIG_STACKTRACE) += stacktrace.o
 obj-y += time/
diff --git a/kernel/mhelper.c b/kernel/mhelper.c
new file mode 100644
index 0000000..3048ca6
--- /dev/null
+++ b/kernel/mhelper.c
@@ -0,0 +1,70 @@
+
+#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"
+
+
+
+
+extern asmlinkage long
+sys_mhelper(uint32_t cmd, uint32_t addr, uint32_t value)
+{
+	void __iomem *vaddr = 0;
+	int verb;
+
+	vaddr = ioremap(addr, 4);
+	if (!vaddr)
+		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:
+		iounmap(vaddr);
+		return -EINVAL;
+		break;
+	}
+
+	iounmap(vaddr);
+	return verb?0:value;
+}
+
diff --git a/kernel/mhelper.h b/kernel/mhelper.h
new file mode 100644
index 0000000..df73297
--- /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,
+
+};
+
