Merge OpenMoko kernel patches
git://git.openmoko.org/git/kernel.git#(no

lars@lars-laptop   Thu May 14 18:33:23 UTC 2009

---

--- /dev/null
+++ b/arch/arm/mach-s3c2410/include/mach/mci.h
@@ -0,0 +1,13 @@
+#ifndef _ARCH_MCI_H
+#define _ARCH_MCI_H
+
+struct s3c24xx_mci_pdata {
+	unsigned int	gpio_detect;
+	unsigned int	gpio_wprotect;
+	unsigned long	ocr_avail;
+	unsigned int	do_dma;
+	void		(*set_power)(unsigned char power_mode,
+				     unsigned short vdd);
+};
+
+#endif /* _ARCH_NCI_H */
--- a/arch/arm/mach-s3c2410/include/mach/regs-sdi.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-sdi.h
@@ -30,6 +30,7 @@
 #define S3C2410_SDIFSTA               (0x38)
 
 #define S3C2410_SDIDATA               (0x3C)
+#define S3C2410_SDIDATA_BYTE          (0x3C)
 #define S3C2410_SDIIMSK               (0x40)
 
 #define S3C2440_SDIDATA               (0x40)
@@ -37,6 +38,8 @@
 
 #define S3C2440_SDICON_SDRESET        (1<<8)
 #define S3C2440_SDICON_MMCCLOCK       (1<<5)
+#define S3C2440_SDIDATA_BYTE          (0x48)
+
 #define S3C2410_SDICON_BYTEORDER      (1<<4)
 #define S3C2410_SDICON_SDIOIRQ        (1<<3)
 #define S3C2410_SDICON_RWAITEN        (1<<2)
--- a/arch/arm/mach-s3c2440/s3c2440.c
+++ b/arch/arm/mach-s3c2440/s3c2440.c
@@ -46,6 +46,9 @@ int __init s3c2440_init(void)
 	s3c_device_wdt.resource[1].start = IRQ_S3C2440_WDT;
 	s3c_device_wdt.resource[1].end   = IRQ_S3C2440_WDT;
 
+	/* make sure SD/MMC driver can distinguish 2440 from 2410 */
+	s3c_device_sdi.name = "s3c2440-sdi";
+
 	/* register our system device for everything else */
 
 	return sysdev_register(&s3c2440_sysdev);
--- a/arch/arm/mach-s3c2442/s3c2442.c
+++ b/arch/arm/mach-s3c2442/s3c2442.c
@@ -21,6 +21,7 @@
 
 #include <plat/s3c2442.h>
 #include <plat/cpu.h>
+#include <plat/devs.h>
 
 static struct sys_device s3c2442_sysdev = {
 	.cls		= &s3c2442_sysclass,
@@ -30,5 +31,8 @@ int __init s3c2442_init(void)
 {
 	printk("S3C2442: Initialising architecture\n");
 
+	/* make sure SD/MMC driver can distinguish 2440 from 2410 */
+	s3c_device_sdi.name = "s3c2440-sdi";
+
 	return sysdev_register(&s3c2442_sysdev);
 }
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -55,7 +55,8 @@ ifeq ($(CONFIG_CPU_32v6),y)
 arch-$(CONFIG_CPU_32v6K)	:=-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6k,-march=armv5t -Wa$(comma)-march=armv6k)
 endif
 arch-$(CONFIG_CPU_32v5)		:=-D__LINUX_ARM_ARCH__=5 $(call cc-option,-march=armv5te,-march=armv4t)
-arch-$(CONFIG_CPU_32v4T)	:=-D__LINUX_ARM_ARCH__=4 -march=armv4t
+# We can't load armv4t modules, but still need to assemble some armv4t code to be linked in.
+arch-$(CONFIG_CPU_32v4T)	:=-D__LINUX_ARM_ARCH__=4 -march=armv4 -Wa,-march=armv4t
 arch-$(CONFIG_CPU_32v4)		:=-D__LINUX_ARM_ARCH__=4 -march=armv4
 arch-$(CONFIG_CPU_32v3)		:=-D__LINUX_ARM_ARCH__=3 -march=armv3
 
--- /dev/null
+++ b/arch/arm/plat-s3c/include/mach/cpu.h
@@ -0,0 +1,165 @@
+/*
+ * arch/arm/plat-s3c/include/mach/cpu.h
+ *
+ *  S3C cpu type detection
+ *
+ *  Copyright (C) 2008 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Derived from OMAP cpu.h
+ *
+ * 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 __ASM_ARCH_S3C_CPU_H
+#define __ASM_ARCH_S3C_CPU_H
+
+extern unsigned int system_rev;
+
+#define S3C_SYSTEM_REV_ATAG (system_rev & 0xffff)
+#define S3C_SYSTEM_REV_CPU (system_rev & 0xffff0000)
+
+/*
+ * cpu_is_s3c24xx():	True for s3c2400, s3c2410, s3c2440 and so on
+ * cpu_is_s3c241x():	True fro s3c2410, s3c2412
+ * cpu_is_s3c244x():	True fro s3c2440, s3c2442, s3c2443
+ * cpu_is_s3c64xx():	True for s3c6400, s3c6410
+ */
+#define GET_S3C_CLASS	((system_rev >> 24) & 0xff)
+
+#define IS_S3C_CLASS(class, id)						\
+static inline int is_s3c ##class (void)					\
+{									\
+	return (GET_S3C_CLASS == (id)) ? 1 : 0;				\
+}
+
+#define GET_S3C_SUBCLASS	((system_rev >> 20) & 0xfff)
+
+#define IS_S3C_SUBCLASS(subclass, id)					\
+static inline int is_s3c ##subclass (void)				\
+{									\
+	return (GET_S3C_SUBCLASS == (id)) ? 1 : 0;			\
+}
+
+IS_S3C_CLASS(24xx, 0x24)
+IS_S3C_CLASS(64xx, 0x64)
+
+IS_S3C_SUBCLASS(241x, 0x241)
+IS_S3C_SUBCLASS(244x, 0x244)
+
+#define cpu_is_s3c24xx()		0
+#define cpu_is_s3c241x()		0
+#define cpu_is_s3c244x()		0
+#define cpu_is_s3c64xx()		0
+
+#if defined(CONFIG_ARCH_S3C2410)
+# undef  cpu_is_s3c24xx
+# undef  cpu_is_s3c241x
+# undef  cpu_is_s3c244x
+# define cpu_is_s3c24xx()		is_s3c24xx()
+# define cpu_is_s3c241x()		is_s3c241x()
+# define cpu_is_s3c244x()		is_s3c244x()
+#endif
+
+#if defined(CONFIG_ARCH_S3C64XX)
+# undef  cpu_is_s3c64xx
+# define cpu_is_s3c64xx()		is_s3c64xx()
+#endif
+
+/*
+ * Macros to detect individual cpu types.
+ * cpu_is_s3c2410():	True for s3c2410
+ * cpu_is_s3c2440():	True for s3c2440
+ * cpu_is_s3c6400():	True for s3c6400
+ * cpu_is_s3c6410():	True for s3c6410
+ *
+ * Exception:
+ * Store Revision A to 1
+ * s3c2410a -> s3c2411
+ * s3c2440a -> s3c2441
+ */
+
+#define GET_S3C_TYPE	((system_rev >> 16) & 0xffff)
+
+#define IS_S3C_TYPE(type, id)						\
+static inline int is_s3c ##type (void)					\
+{									\
+	return (GET_S3C_TYPE == (id)) ? 1 : 0;				\
+}
+
+IS_S3C_TYPE(2400, 0x2400)
+IS_S3C_TYPE(2410, 0x2410)
+IS_S3C_TYPE(2410a, 0x2411)
+IS_S3C_TYPE(2412, 0x2412)
+IS_S3C_TYPE(2440, 0x2440)
+IS_S3C_TYPE(2440a, 0x2441)
+IS_S3C_TYPE(2442, 0x2442)
+IS_S3C_TYPE(2443, 0x2443)
+IS_S3C_TYPE(6400, 0x6400)
+IS_S3C_TYPE(6410, 0x6410)
+
+#define cpu_is_s3c2400()		0
+#define cpu_is_s3c2410()		0
+#define cpu_is_s3c2410a()		0
+#define cpu_is_s3c2412()		0
+#define cpu_is_s3c2440()		0
+#define cpu_is_s3c2440a()		0
+#define cpu_is_s3c2442()		0
+#define cpu_is_s3c2443()		0
+#define cpu_is_s3c6400()		0
+#define cpu_is_s3c6410()		0
+
+#if defined(CONFIG_ARCH_S3C2410)
+# undef  cpu_is_s3c2400
+# define cpu_is_s3c2400()		is_s3c2400()
+#endif
+
+#if defined(CONFIG_CPU_S3C2410)
+# undef  cpu_is_s3c2410
+# undef  cpu_is_s3c2410a
+# define cpu_is_s3c2410()		is_s3c2410()
+# define cpu_is_s3c2410a()		is_s3c2410a()
+#endif
+
+#if defined(CONFIG_CPU_S3C2412)
+# undef  cpu_is_s3c2412
+# define cpu_is_s3c2412()		is_s3c2412()
+#endif
+
+#if defined(CONFIG_CPU_S3C2440)
+# undef  cpu_is_s3c2440
+# undef  cpu_is_s3c2440a
+# define cpu_is_s3c2440()		is_s3c2440()
+# define cpu_is_s3c2440a()		is_s3c2440a()
+#endif
+
+#if defined(CONFIG_CPU_S3C2442)
+# undef  cpu_is_s3c2442
+# define cpu_is_s3c2442()		is_s3c2442()
+#endif
+
+#if defined(CONFIG_CPU_S3C2443)
+# undef  cpu_is_s3c2443
+# define cpu_is_s3c2443()		is_s3c2443()
+#endif
+
+#if defined(CONFIG_ARCH_S3C64XX)
+# undef  cpu_is_s3c6400
+# undef  cpu_is_s3c6410
+# define cpu_is_s3c6400()		is_s3c6400()
+# define cpu_is_s3c6410()		is_s3c6410()
+#endif
+
+#endif
--- a/arch/arm/plat-s3c/include/plat/devs.h
+++ b/arch/arm/plat-s3c/include/plat/devs.h
@@ -16,6 +16,10 @@ struct s3c24xx_uart_resources {
 	unsigned long		 nr_resources;
 };
 
+struct s3c_plat_otg_data {
+	int		phyclk;
+};
+
 extern struct s3c24xx_uart_resources s3c2410_uart_resources[];
 extern struct s3c24xx_uart_resources s3c64xx_uart_resources[];
 
--- a/arch/arm/plat-s3c/include/plat/gpio-core.h
+++ b/arch/arm/plat-s3c/include/plat/gpio-core.h
@@ -20,6 +20,19 @@
  * specific code.
 */
 
+struct s3c_gpio_chip;
+
+/**
+ * struct s3c_gpio_pm - power management (suspend/resume) information
+ * @save: Routine to save the state of the GPIO block
+ * @resume: Routine to resume the GPIO block.
+ */
+struct s3c_gpio_pm {
+	void (*save)(struct s3c_gpio_chip *chip);
+	void (*resume)(struct s3c_gpio_chip *chip);
+};
+
+
 struct s3c_gpio_cfg;
 
 /**
@@ -27,6 +40,7 @@ struct s3c_gpio_cfg;
  * @chip: The chip structure to be exported via gpiolib.
  * @base: The base pointer to the gpio configuration registers.
  * @config: special function and pull-resistor control information.
+ * @pm_save: Save information for suspend/resume support.
  *
  * This wrapper provides the necessary information for the Samsung
  * specific gpios being registered with gpiolib.
@@ -34,7 +48,11 @@ struct s3c_gpio_cfg;
 struct s3c_gpio_chip {
 	struct gpio_chip	chip;
 	struct s3c_gpio_cfg	*config;
+	struct s3c_gpio_pm	*pm;
 	void __iomem		*base;
+#ifdef CONFIG_PM
+	u32			pm_save[4];
+#endif
 };
 
 static inline struct s3c_gpio_chip *to_s3c_gpio(struct gpio_chip *gpc)
@@ -75,3 +93,16 @@ static inline struct s3c_gpio_chip *s3c_
 
 static inline void s3c_gpiolib_track(struct s3c_gpio_chip *chip) { }
 #endif
+
+#ifdef CONFIG_PM
+extern struct s3c_gpio_pm s3c_gpio_pm_1bit;
+extern struct s3c_gpio_pm s3c_gpio_pm_2bit;
+extern struct s3c_gpio_pm s3c_gpio_pm_4bit;
+#define __gpio_pm(x) x
+#else
+#define s3c_gpio_pm_1bit NULL
+#define s3c_gpio_pm_2bit NULL
+#define s3c_gpio_pm_4bit NULL
+#define __gpio_pm(x) NULL
+
+#endif /* CONFIG_PM */
--- a/arch/arm/plat-s3c/include/plat/map-base.h
+++ b/arch/arm/plat-s3c/include/plat/map-base.h
@@ -36,5 +36,7 @@
 #define S3C_VA_TIMER	S3C_ADDR(0x00300000)	/* timer block */
 #define S3C_VA_WATCHDOG	S3C_ADDR(0x00400000)	/* watchdog */
 #define S3C_VA_UART	S3C_ADDR(0x01000000)	/* UART */
+#define S3C_VA_OTG	S3C_ADDR(0x03900000)	/* OTG */
+#define S3C_VA_OTGSFR	S3C_ADDR(0x03a00000)	/* OTGSFR */
 
 #endif /* __ASM_PLAT_MAP_H */
--- a/arch/arm/plat-s3c/include/plat/nand.h
+++ b/arch/arm/plat-s3c/include/plat/nand.h
@@ -21,11 +21,14 @@
  * partitions	 = mtd partition list
 */
 
+#define S3C2410_NAND_BBT	0x0001
+
 struct s3c2410_nand_set {
 	unsigned int		disable_ecc : 1;
 
 	int			nr_chips;
 	int			nr_partitions;
+	unsigned int		flags;
 	char			*name;
 	int			*nr_map;
 	struct mtd_partition	*partitions;
@@ -44,6 +47,9 @@ struct s3c2410_platform_nand {
 	int			nr_sets;
 	struct s3c2410_nand_set *sets;
 
+	/* force software_ecc at runtime */
+	int	software_ecc;
+
 	void			(*select_chip)(struct s3c2410_nand_set *,
 					       int chip);
 };
--- a/arch/arm/plat-s3c/include/plat/pm.h
+++ b/arch/arm/plat-s3c/include/plat/pm.h
@@ -9,6 +9,8 @@
  * published by the Free Software Foundation.
 */
 
+#include <linux/sysdev.h>
+
 /* s3c_pm_init
  *
  * called from board at initialisation time to setup the power
@@ -44,6 +46,8 @@ extern void (*pm_cpu_sleep)(void);
 
 extern unsigned long s3c_pm_flags;
 
+extern unsigned char pm_uart_udivslot;  /* true to save UART UDIVSLOT */
+
 /* from sleep.S */
 
 extern int  s3c_cpu_save(unsigned long *saveblk);
@@ -88,6 +92,7 @@ struct pm_uart_save {
 	u32	ufcon;
 	u32	umcon;
 	u32	ubrdiv;
+	u32	udivslot;
 };
 
 /* helper functions to save/restore lists of registers. */
--- a/arch/arm/plat-s3c/include/plat/sdhci.h
+++ b/arch/arm/plat-s3c/include/plat/sdhci.h
@@ -29,6 +29,7 @@ struct mmc_ios;
  *            is necessary the controllers and/or GPIO blocks require the
  *	      changing of driver-strength and other controls dependant on
  *	      the card and speed of operation.
+ * sdhci_host: Pointer kept during init, allows presence change notification
  *
  * Initialisation data specific to either the machine or the platform
  * for the device driver to use or call-back when configuring gpio or
@@ -45,8 +46,11 @@ struct s3c_sdhci_platdata {
 			    void __iomem *regbase,
 			    struct mmc_ios *ios,
 			    struct mmc_card *card);
+	struct sdhci_host * sdhci_host;
 };
 
+extern void sdhci_s3c_force_presence_change(struct platform_device *pdev);
+
 /**
  * s3c_sdhci0_set_platdata - Set platform data for S3C SDHCI device.
  * @pd: Platform data to register to device.
--- a/arch/arm/plat-s3c/init.c
+++ b/arch/arm/plat-s3c/init.c
@@ -31,6 +31,34 @@
 
 static struct cpu_table *cpu;
 
+static void __init set_system_rev(unsigned int idcode)
+{
+	/*
+	 * system_rev encoding is as follows
+	 * system_rev & 0xff000000 -> S3C Class (24xx/64xx)
+	 * system_rev & 0xfff00000 -> S3C Sub Class (241x/244x)
+	 * system_rev & 0xffff0000 -> S3C Type (2410/2440/6400/6410)
+	 *
+	 * Remaining[15:0] are preserved from the value set by ATAG
+	 *
+	 * Exception:
+	 *  Store Revision A to 1 such as
+	 *  s3c2410A to s3c2411
+	 *  s3c2440A to s3c2441
+	 */
+
+	system_rev &= 0xffff;
+	system_rev |= (idcode & 0x0ffff000) << 4;
+
+	if (idcode == 0x32410002 || idcode == 0x32440001)
+		system_rev |= (0x1 << 16);
+	if (idcode == 0x32440aaa	/* s3c2442 */
+	    || idcode == 0x32440aab)	/* s3c2442b */
+		system_rev |= (0x2 << 16);
+	if (idcode == 0x0)		/* s3c2400 */
+		system_rev |= (0x2400 << 16);
+}
+
 static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode,
 						struct cpu_table *tab,
 						unsigned int count)
@@ -53,6 +81,8 @@ void __init s3c_init_cpu(unsigned long i
 		panic("Unknown S3C24XX CPU");
 	}
 
+	set_system_rev(idcode);
+
 	printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode);
 
 	if (cpu->map_io == NULL || cpu->init == NULL) {
--- a/arch/arm/plat-s3c/Makefile
+++ b/arch/arm/plat-s3c/Makefile
@@ -21,6 +21,7 @@ obj-y				+= gpio-config.o
 # PM support
 
 obj-$(CONFIG_PM)		+= pm.o
+obj-$(CONFIG_PM)		+= pm-gpio.o
 obj-$(CONFIG_S3C2410_PM_CHECK)	+= pm-check.o
 
 # devices
--- a/arch/arm/plat-s3c/pm.c
+++ b/arch/arm/plat-s3c/pm.c
@@ -21,11 +21,10 @@
 
 #include <asm/cacheflush.h>
 #include <mach/hardware.h>
+#include <mach/map.h>
 
 #include <plat/regs-serial.h>
 #include <mach/regs-clock.h>
-#include <mach/regs-gpio.h>
-#include <mach/regs-mem.h>
 #include <mach/regs-irq.h>
 #include <asm/irq.h>
 
@@ -70,6 +69,8 @@ static inline void s3c_pm_debug_init(voi
 
 /* Save the UART configurations if we are configured for debug. */
 
+unsigned char pm_uart_udivslot;
+
 #ifdef CONFIG_S3C2410_PM_DEBUG
 
 struct pm_uart_save uart_save[CONFIG_SERIAL_SAMSUNG_UARTS];
@@ -83,6 +84,12 @@ static void s3c_pm_save_uart(unsigned in
 	save->ufcon = __raw_readl(regs + S3C2410_UFCON);
 	save->umcon = __raw_readl(regs + S3C2410_UMCON);
 	save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV);
+
+	if (pm_uart_udivslot)
+		save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT);
+
+	S3C_PMDBG("UART[%d]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n",
+		  uart, save->ulcon, save->ucon, save->ufcon, save->ubrdiv);
 }
 
 static void s3c_pm_save_uarts(void)
@@ -98,11 +105,16 @@ static void s3c_pm_restore_uart(unsigned
 {
 	void __iomem *regs = S3C_VA_UARTx(uart);
 
+	s3c_pm_arch_update_uart(regs, save);
+
 	__raw_writel(save->ulcon, regs + S3C2410_ULCON);
 	__raw_writel(save->ucon,  regs + S3C2410_UCON);
 	__raw_writel(save->ufcon, regs + S3C2410_UFCON);
 	__raw_writel(save->umcon, regs + S3C2410_UMCON);
 	__raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV);
+
+	if (pm_uart_udivslot)
+		__raw_writel(save->udivslot, regs + S3C2443_DIVSLOT);
 }
 
 static void s3c_pm_restore_uarts(void)
@@ -289,11 +301,14 @@ static int s3c_pm_enter(suspend_state_t 
 
 	s3c_pm_arch_stop_clocks();
 
-	/* s3c_cpu_save will also act as our return point from when
-	 * we resume as it saves its own register state and restores it
-	 * during the resume.  */
-
-	s3c_cpu_save(regs_save);
+	/* s3c2410_cpu_save will also act as our return point from when
+	 * we resume as it saves its own register state, so use the return
+	 * code to differentiate return from save and return from sleep */
+
+	if (s3c_cpu_save(regs_save) == 0) {
+		flush_cache_all();
+		pm_cpu_sleep();
+	}
 
 	/* restore the cpu state using the kernel's cpu init code. */
 
--- /dev/null
+++ b/arch/arm/plat-s3c/pm-gpio.c
@@ -0,0 +1,378 @@
+/* linux/arch/arm/plat-s3c/pm-gpio.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *	http://armlinux.simtec.co.uk/
+ *
+ * S3C series GPIO PM code
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+
+#include <mach/gpio-core.h>
+#include <plat/pm.h>
+
+/* PM GPIO helpers */
+
+#define OFFS_CON	(0x00)
+#define OFFS_DAT	(0X04)
+#define OFFS_UP		(0X08)
+
+static void s3c_gpio_pm_1bit_save(struct s3c_gpio_chip *chip)
+{
+	chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON);
+	chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT);
+}
+
+static void s3c_gpio_pm_1bit_resume(struct s3c_gpio_chip *chip)
+{
+	void __iomem *base = chip->base;
+	u32 old_gpcon = __raw_readl(base + OFFS_CON);
+	u32 old_gpdat = __raw_readl(base + OFFS_DAT);
+	u32 gps_gpcon = chip->pm_save[0];
+	u32 gps_gpdat = chip->pm_save[1];
+	u32 gpcon;
+
+	/* GPACON only has one bit per control / data and no PULLUPs.
+	 * GPACON[x] = 0 => Output, 1 => SFN */
+
+	/* first set all SFN bits to SFN */
+
+	gpcon = old_gpcon | gps_gpcon;
+	__raw_writel(gpcon, base + OFFS_CON);
+
+	/* now set all the other bits */
+
+	__raw_writel(gps_gpdat, base + OFFS_DAT);
+	__raw_writel(gps_gpcon, base + OFFS_CON);
+
+	S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n",
+		  chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
+}
+
+struct s3c_gpio_pm s3c_gpio_pm_1bit = {
+	.save	= s3c_gpio_pm_1bit_save,
+	.resume = s3c_gpio_pm_1bit_resume,
+};
+
+static void s3c_gpio_pm_2bit_save(struct s3c_gpio_chip *chip)
+{
+	chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON);
+	chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT);
+	chip->pm_save[2] = __raw_readl(chip->base + OFFS_UP);
+}
+
+/* Test whether the given masked+shifted bits of an GPIO configuration
+ * are one of the SFN (special function) modes. */
+
+static inline int is_sfn(unsigned long con)
+{
+	return con >= 2;
+}
+
+/* Test if the given masked+shifted GPIO configuration is an input */
+
+static inline int is_in(unsigned long con)
+{
+	return con == 0;
+}
+
+/* Test if the given masked+shifted GPIO configuration is an output */
+
+static inline int is_out(unsigned long con)
+{
+	return con == 1;
+}
+
+/**
+ * s3c_gpio_pm_2bit_resume() - restore the given GPIO bank
+ * @chip: The chip information to resume.
+ *
+ * Restore one of the GPIO banks that was saved during suspend. This is
+ * not as simple as once thought, due to the possibility of glitches
+ * from the order that the CON and DAT registers are set in.
+ *
+ * The three states the pin can be are {IN,OUT,SFN} which gives us 9
+ * combinations of changes to check. Three of these, if the pin stays
+ * in the same configuration can be discounted. This leaves us with
+ * the following:
+ *
+ * { IN => OUT }  Change DAT first
+ * { IN => SFN }  Change CON first
+ * { OUT => SFN } Change CON first, so new data will not glitch
+ * { OUT => IN }  Change CON first, so new data will not glitch
+ * { SFN => IN }  Change CON first
+ * { SFN => OUT } Change DAT first, so new data will not glitch [1]
+ *
+ * We do not currently deal with the UP registers as these control
+ * weak resistors, so a small delay in change should not need to bring
+ * these into the calculations.
+ *
+ * [1] this assumes that writing to a pin DAT whilst in SFN will set the
+ *     state for when it is next output.
+ */
+static void s3c_gpio_pm_2bit_resume(struct s3c_gpio_chip *chip)
+{
+	void __iomem *base = chip->base;
+	u32 old_gpcon = __raw_readl(base + OFFS_CON);
+	u32 old_gpdat = __raw_readl(base + OFFS_DAT);
+	u32 gps_gpcon = chip->pm_save[0];
+	u32 gps_gpdat = chip->pm_save[1];
+	u32 gpcon, old, new, mask;
+	u32 change_mask = 0x0;
+	int nr;
+
+	/* restore GPIO pull-up settings */
+	__raw_writel(chip->pm_save[2], base + OFFS_UP);
+
+	/* Create a change_mask of all the items that need to have
+	 * their CON value changed before their DAT value, so that
+	 * we minimise the work between the two settings.
+	 */
+
+	for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) {
+		old = (old_gpcon & mask) >> nr;
+		new = (gps_gpcon & mask) >> nr;
+
+		/* If there is no change, then skip */
+
+		if (old == new)
+			continue;
+
+		/* If both are special function, then skip */
+
+		if (is_sfn(old) && is_sfn(new))
+			continue;
+
+		/* Change is IN => OUT, do not change now */
+
+		if (is_in(old) && is_out(new))
+			continue;
+
+		/* Change is SFN => OUT, do not change now */
+
+		if (is_sfn(old) && is_out(new))
+			continue;
+
+		/* We should now be at the case of IN=>SFN,
+		 * OUT=>SFN, OUT=>IN, SFN=>IN. */
+
+		change_mask |= mask;
+	}
+
+
+	/* Write the new CON settings */
+
+	gpcon = old_gpcon & ~change_mask;
+	gpcon |= gps_gpcon & change_mask;
+
+	__raw_writel(gpcon, base + OFFS_CON);
+
+	/* Now change any items that require DAT,CON */
+
+	__raw_writel(gps_gpdat, base + OFFS_DAT);
+	__raw_writel(gps_gpcon, base + OFFS_CON);
+
+	S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n",
+		  chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
+}
+
+struct s3c_gpio_pm s3c_gpio_pm_2bit = {
+	.save	= s3c_gpio_pm_2bit_save,
+	.resume = s3c_gpio_pm_2bit_resume,
+};
+
+#ifdef CONFIG_ARCH_S3C64XX
+static void s3c_gpio_pm_4bit_save(struct s3c_gpio_chip *chip)
+{
+	chip->pm_save[1] = __raw_readl(chip->base + OFFS_CON);
+	chip->pm_save[2] = __raw_readl(chip->base + OFFS_DAT);
+	chip->pm_save[3] = __raw_readl(chip->base + OFFS_UP);
+
+	if (chip->chip.ngpio > 8)
+		chip->pm_save[0] = __raw_readl(chip->base - 4);
+}
+
+static u32 s3c_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon)
+{
+	u32 old, new, mask;
+	u32 change_mask = 0x0;
+	int nr;
+
+	for (nr = 0, mask = 0x0f; nr < 16; nr += 4, mask <<= 4) {
+		old = (old_gpcon & mask) >> nr;
+		new = (gps_gpcon & mask) >> nr;
+
+		/* If there is no change, then skip */
+
+		if (old == new)
+			continue;
+
+		/* If both are special function, then skip */
+
+		if (is_sfn(old) && is_sfn(new))
+			continue;
+
+		/* Change is IN => OUT, do not change now */
+
+		if (is_in(old) && is_out(new))
+			continue;
+
+		/* Change is SFN => OUT, do not change now */
+
+		if (is_sfn(old) && is_out(new))
+			continue;
+
+		/* We should now be at the case of IN=>SFN,
+		 * OUT=>SFN, OUT=>IN, SFN=>IN. */
+
+		change_mask |= mask;
+	}
+
+	return change_mask;
+}
+
+static void s3c_gpio_pm_4bit_con(struct s3c_gpio_chip *chip, int index)
+{
+	void __iomem *con = chip->base + (index * 4);
+	u32 old_gpcon = __raw_readl(con);
+	u32 gps_gpcon = chip->pm_save[index + 1];
+	u32 gpcon, mask;
+
+	mask = s3c_gpio_pm_4bit_mask(old_gpcon, gps_gpcon);
+
+	gpcon = old_gpcon & ~mask;
+	gpcon |= gps_gpcon & mask;
+
+	__raw_writel(gpcon, con);
+}
+
+static void s3c_gpio_pm_4bit_resume(struct s3c_gpio_chip *chip)
+{
+	void __iomem *base = chip->base;
+	u32 old_gpcon[2];
+	u32 old_gpdat = __raw_readl(base + OFFS_DAT);
+	u32 gps_gpdat = chip->pm_save[2];
+
+	/* First, modify the CON settings */
+
+	old_gpcon[0] = 0;
+	old_gpcon[1] = __raw_readl(base + OFFS_CON);
+
+	s3c_gpio_pm_4bit_con(chip, 0);
+	if (chip->chip.ngpio > 8) {
+		old_gpcon[0] = __raw_readl(base - 4);
+		s3c_gpio_pm_4bit_con(chip, -1);
+	}
+
+	/* Now change the configurations that require DAT,CON */
+
+	__raw_writel(chip->pm_save[2], base + OFFS_DAT);
+	__raw_writel(chip->pm_save[1], base + OFFS_CON);
+	if (chip->chip.ngpio > 8)
+		__raw_writel(chip->pm_save[0], base - 4);
+
+	__raw_writel(chip->pm_save[2], base + OFFS_DAT);
+	__raw_writel(chip->pm_save[3], base + OFFS_UP);
+
+	if (chip->chip.ngpio > 8) {
+		S3C_PMDBG("%s: CON4 %08x,%08x => %08x,%08x, DAT %08x => %08x\n",
+			  chip->chip.label, old_gpcon[0], old_gpcon[1],
+			  __raw_readl(base - 4),
+			  __raw_readl(base + OFFS_CON),
+			  old_gpdat, gps_gpdat);
+	} else
+		S3C_PMDBG("%s: CON4 %08x => %08x, DAT %08x => %08x\n",
+			  chip->chip.label, old_gpcon[1],
+			  __raw_readl(base + OFFS_CON),
+			  old_gpdat, gps_gpdat);
+}
+
+struct s3c_gpio_pm s3c_gpio_pm_4bit = {
+	.save	= s3c_gpio_pm_4bit_save,
+	.resume = s3c_gpio_pm_4bit_resume,
+};
+#endif /* CONFIG_ARCH_S3C64XX */
+
+/**
+ * s3c_pm_save_gpio() - save gpio chip data for suspend
+ * @ourchip: The chip for suspend.
+ */
+static void s3c_pm_save_gpio(struct s3c_gpio_chip *ourchip)
+{
+	struct s3c_gpio_pm *pm = ourchip->pm;
+
+	if (pm == NULL || pm->save == NULL)
+		S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label);
+	else
+		pm->save(ourchip);
+}
+
+/**
+ * s3c_pm_save_gpios() - Save the state of the GPIO banks.
+ *
+ * For all the GPIO banks, save the state of each one ready for going
+ * into a suspend mode.
+ */
+void s3c_pm_save_gpios(void)
+{
+	struct s3c_gpio_chip *ourchip;
+	unsigned int gpio_nr;
+
+	for (gpio_nr = 0; gpio_nr < S3C_GPIO_END; gpio_nr++) {
+		ourchip = s3c_gpiolib_getchip(gpio_nr);
+		if (!ourchip)
+			continue;
+
+		s3c_pm_save_gpio(ourchip);
+
+		S3C_PMDBG("%s: save %08x,%08x,%08x,%08x\n",
+			  ourchip->chip.label,
+			  ourchip->pm_save[0],
+			  ourchip->pm_save[1],
+			  ourchip->pm_save[2],
+			  ourchip->pm_save[3]);
+
+		gpio_nr += ourchip->chip.ngpio;
+		gpio_nr += CONFIG_S3C_GPIO_SPACE;
+	}
+}
+
+/**
+ * s3c_pm_resume_gpio() - restore gpio chip data after suspend
+ * @ourchip: The suspended chip.
+ */
+static void s3c_pm_resume_gpio(struct s3c_gpio_chip *ourchip)
+{
+	struct s3c_gpio_pm *pm = ourchip->pm;
+
+	if (pm == NULL || pm->resume == NULL)
+		S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label);
+	else
+		pm->resume(ourchip);
+}
+
+void s3c_pm_restore_gpios(void)
+{
+	struct s3c_gpio_chip *ourchip;
+	unsigned int gpio_nr;
+
+	for (gpio_nr = 0; gpio_nr < S3C_GPIO_END; gpio_nr++) {
+		ourchip = s3c_gpiolib_getchip(gpio_nr);
+		if (!ourchip)
+			continue;
+
+		s3c_pm_resume_gpio(ourchip);
+
+		gpio_nr += ourchip->chip.ngpio;
+		gpio_nr += CONFIG_S3C_GPIO_SPACE;
+	}
+}
--- a/arch/arm/plat-s3c24xx/clock-dclk.c
+++ b/arch/arm/plat-s3c24xx/clock-dclk.c
@@ -18,6 +18,7 @@
 
 #include <mach/regs-clock.h>
 #include <mach/regs-gpio.h>
+#include <mach/hardware.h>
 
 #include <plat/clock.h>
 #include <plat/cpu.h>
--- a/arch/arm/plat-s3c24xx/cpu.c
+++ b/arch/arm/plat-s3c24xx/cpu.c
@@ -61,6 +61,7 @@ static const char name_s3c2410[]  = "S3C
 static const char name_s3c2412[]  = "S3C2412";
 static const char name_s3c2440[]  = "S3C2440";
 static const char name_s3c2442[]  = "S3C2442";
+static const char name_s3c2442b[]  = "S3C2442B";
 static const char name_s3c2443[]  = "S3C2443";
 static const char name_s3c2410a[] = "S3C2410A";
 static const char name_s3c2440a[] = "S3C2440A";
@@ -112,6 +113,15 @@ static struct cpu_table cpu_ids[] __init
 		.name		= name_s3c2442
 	},
 	{
+		.idcode		= 0x32440aab,
+		.idmask		= 0xffffffff,
+		.map_io		= s3c244x_map_io,
+		.init_clocks	= s3c244x_init_clocks,
+		.init_uarts	= s3c244x_init_uarts,
+		.init		= s3c2442_init,
+		.name		= name_s3c2442b
+	},
+	{
 		.idcode		= 0x32412001,
 		.idmask		= 0xffffffff,
 		.map_io		= s3c2412_map_io,
--- a/arch/arm/plat-s3c24xx/gpiolib.c
+++ b/arch/arm/plat-s3c24xx/gpiolib.c
@@ -19,9 +19,10 @@
 #include <linux/io.h>
 #include <linux/gpio.h>
 
-#include <mach/gpio-core.h>
+#include <plat/gpio-core.h>
 #include <mach/hardware.h>
 #include <asm/irq.h>
+#include <plat/pm.h>
 
 #include <mach/regs-gpio.h>
 
@@ -78,6 +79,7 @@ static int s3c24xx_gpiolib_bankg_toirq(s
 struct s3c_gpio_chip s3c24xx_gpios[] = {
 	[0] = {
 		.base	= S3C24XX_GPIO_BASE(S3C2410_GPA0),
+		.pm	= __gpio_pm(&s3c_gpio_pm_1bit),
 		.chip	= {
 			.base			= S3C2410_GPA0,
 			.owner			= THIS_MODULE,
@@ -89,6 +91,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
 	},
 	[1] = {
 		.base	= S3C24XX_GPIO_BASE(S3C2410_GPB0),
+		.pm	= __gpio_pm(&s3c_gpio_pm_2bit),
 		.chip	= {
 			.base			= S3C2410_GPB0,
 			.owner			= THIS_MODULE,
@@ -98,6 +101,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
 	},
 	[2] = {
 		.base	= S3C24XX_GPIO_BASE(S3C2410_GPC0),
+		.pm	= __gpio_pm(&s3c_gpio_pm_2bit),
 		.chip	= {
 			.base			= S3C2410_GPC0,
 			.owner			= THIS_MODULE,
@@ -107,6 +111,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
 	},
 	[3] = {
 		.base	= S3C24XX_GPIO_BASE(S3C2410_GPD0),
+		.pm	= __gpio_pm(&s3c_gpio_pm_2bit),
 		.chip	= {
 			.base			= S3C2410_GPD0,
 			.owner			= THIS_MODULE,
@@ -116,6 +121,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
 	},
 	[4] = {
 		.base	= S3C24XX_GPIO_BASE(S3C2410_GPE0),
+		.pm	= __gpio_pm(&s3c_gpio_pm_2bit),
 		.chip	= {
 			.base			= S3C2410_GPE0,
 			.label			= "GPIOE",
@@ -125,6 +131,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
 	},
 	[5] = {
 		.base	= S3C24XX_GPIO_BASE(S3C2410_GPF0),
+		.pm	= __gpio_pm(&s3c_gpio_pm_2bit),
 		.chip	= {
 			.base			= S3C2410_GPF0,
 			.owner			= THIS_MODULE,
@@ -135,12 +142,23 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
 	},
 	[6] = {
 		.base	= S3C24XX_GPIO_BASE(S3C2410_GPG0),
+		.pm	= __gpio_pm(&s3c_gpio_pm_2bit),
 		.chip	= {
 			.base			= S3C2410_GPG0,
 			.owner			= THIS_MODULE,
 			.label			= "GPIOG",
-			.ngpio			= 10,
 			.to_irq			= s3c24xx_gpiolib_bankg_toirq,
+			.ngpio			= 16,
+		},
+	},
+	[7] = {
+		.base	= S3C24XX_GPIO_BASE(S3C2410_GPH0),
+		.pm	= __gpio_pm(&s3c_gpio_pm_2bit),
+		.chip	= {
+			.base			= S3C2410_GPH0,
+			.owner			= THIS_MODULE,
+			.label			= "GPIOH",
+			.ngpio			= 11,
 		},
 	},
 };
--- a/arch/arm/plat-s3c24xx/include/plat/pm-core.h
+++ b/arch/arm/plat-s3c24xx/include/plat/pm-core.h
@@ -57,3 +57,8 @@ static inline void s3c_pm_arch_show_resu
 	s3c_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND),
 				s3c_irqwake_eintmask);
 }
+
+static inline void s3c_pm_arch_update_uart(void __iomem *regs,
+					   struct pm_uart_save *save)
+{
+}
--- a/arch/arm/plat-s3c24xx/irq-pm.c
+++ b/arch/arm/plat-s3c24xx/irq-pm.c
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/sysdev.h>
+#include <linux/irq.h>
 
 #include <plat/cpu.h>
 #include <plat/pm.h>
@@ -80,7 +81,9 @@ int s3c24xx_irq_suspend(struct sys_devic
 
 int s3c24xx_irq_resume(struct sys_device *dev)
 {
-	unsigned int i;
+	unsigned int i, irq;
+	unsigned long eintpnd;
+	struct irq_desc *desc;
 
 	for (i = 0; i < ARRAY_SIZE(save_extint); i++)
 		__raw_writel(save_extint[i], S3C24XX_EXTINT0 + (i*4));
@@ -91,5 +94,25 @@ int s3c24xx_irq_resume(struct sys_device
 	s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
 	__raw_writel(save_eintmask, S3C24XX_EINTMASK);
 
+	/*
+	 * ACK those interrupts which are now masked and pending.
+	 * Level interrupts if not ACKed here, create an interrupt storm
+	 * because they are not handled at all.
+	 */
+
+	eintpnd = __raw_readl(S3C24XX_EINTPEND);
+
+	eintpnd &= save_eintmask;
+	eintpnd &= ~0xff;	/* ignore lower irqs */
+
+	while (eintpnd) {
+		irq = __ffs(eintpnd);
+		eintpnd &= ~(1 << irq);
+
+		irq += (IRQ_EINT4 - 4);
+		desc = irq_to_desc(irq);
+		desc->chip->ack(irq);
+	}
+
 	return 0;
 }
--- a/arch/arm/plat-s3c24xx/pm.c
+++ b/arch/arm/plat-s3c24xx/pm.c
@@ -39,6 +39,7 @@
 #include <mach/regs-gpio.h>
 #include <mach/regs-mem.h>
 #include <mach/regs-irq.h>
+#include <mach/hardware.h>
 
 #include <asm/mach/time.h>
 
@@ -75,43 +76,10 @@ static struct sleep_save core_save[] = {
 	SAVE_ITEM(S3C2410_CLKSLOW),
 };
 
-static struct gpio_sleep {
-	void __iomem	*base;
-	unsigned int	 gpcon;
-	unsigned int	 gpdat;
-	unsigned int	 gpup;
-} gpio_save[] = {
-	[0] = {
-		.base	= S3C2410_GPACON,
-	},
-	[1] = {
-		.base	= S3C2410_GPBCON,
-	},
-	[2] = {
-		.base	= S3C2410_GPCCON,
-	},
-	[3] = {
-		.base	= S3C2410_GPDCON,
-	},
-	[4] = {
-		.base	= S3C2410_GPECON,
-	},
-	[5] = {
-		.base	= S3C2410_GPFCON,
-	},
-	[6] = {
-		.base	= S3C2410_GPGCON,
-	},
-	[7] = {
-		.base	= S3C2410_GPHCON,
-	},
-};
-
 static struct sleep_save misc_save[] = {
 	SAVE_ITEM(S3C2410_DCLKCON),
 };
 
-
 /* s3c_pm_check_resume_pin
  *
  * check to see if the pin is configured correctly for sleep mode, and
@@ -165,186 +133,6 @@ void s3c_pm_configure_extint(void)
 	}
 }
 
-/* offsets for CON/DAT/UP registers */
-
-#define OFFS_CON	(S3C2410_GPACON - S3C2410_GPACON)
-#define OFFS_DAT	(S3C2410_GPADAT - S3C2410_GPACON)
-#define OFFS_UP		(S3C2410_GPBUP  - S3C2410_GPBCON)
-
-/* s3c_pm_save_gpios()
- *
- * Save the state of the GPIOs
- */
-
-void s3c_pm_save_gpios(void)
-{
-	struct gpio_sleep *gps = gpio_save;
-	unsigned int gpio;
-
-	for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) {
-		void __iomem *base = gps->base;
-
-		gps->gpcon = __raw_readl(base + OFFS_CON);
-		gps->gpdat = __raw_readl(base + OFFS_DAT);
-
-		if (gpio > 0)
-			gps->gpup = __raw_readl(base + OFFS_UP);
-
-	}
-}
-
-/* Test whether the given masked+shifted bits of an GPIO configuration
- * are one of the SFN (special function) modes. */
-
-static inline int is_sfn(unsigned long con)
-{
-	return (con == 2 || con == 3);
-}
-
-/* Test if the given masked+shifted GPIO configuration is an input */
-
-static inline int is_in(unsigned long con)
-{
-	return con == 0;
-}
-
-/* Test if the given masked+shifted GPIO configuration is an output */
-
-static inline int is_out(unsigned long con)
-{
-	return con == 1;
-}
-
-/**
- * s3c2410_pm_restore_gpio() - restore the given GPIO bank
- * @index: The number of the GPIO bank being resumed.
- * @gps: The sleep confgiuration for the bank.
- *
- * Restore one of the GPIO banks that was saved during suspend. This is
- * not as simple as once thought, due to the possibility of glitches
- * from the order that the CON and DAT registers are set in.
- *
- * The three states the pin can be are {IN,OUT,SFN} which gives us 9
- * combinations of changes to check. Three of these, if the pin stays
- * in the same configuration can be discounted. This leaves us with
- * the following:
- *
- * { IN => OUT }  Change DAT first
- * { IN => SFN }  Change CON first
- * { OUT => SFN } Change CON first, so new data will not glitch
- * { OUT => IN }  Change CON first, so new data will not glitch
- * { SFN => IN }  Change CON first
- * { SFN => OUT } Change DAT first, so new data will not glitch [1]
- *
- * We do not currently deal with the UP registers as these control
- * weak resistors, so a small delay in change should not need to bring
- * these into the calculations.
- *
- * [1] this assumes that writing to a pin DAT whilst in SFN will set the
- *     state for when it is next output.
- */
-
-static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps)
-{
-	void __iomem *base = gps->base;
-	unsigned long gps_gpcon = gps->gpcon;
-	unsigned long gps_gpdat = gps->gpdat;
-	unsigned long old_gpcon;
-	unsigned long old_gpdat;
-	unsigned long old_gpup = 0x0;
-	unsigned long gpcon;
-	int nr;
-
-	old_gpcon = __raw_readl(base + OFFS_CON);
-	old_gpdat = __raw_readl(base + OFFS_DAT);
-
-	if (base == S3C2410_GPACON) {
-		/* GPACON only has one bit per control / data and no PULLUPs.
-		 * GPACON[x] = 0 => Output, 1 => SFN */
-
-		/* first set all SFN bits to SFN */
-
-		gpcon = old_gpcon | gps->gpcon;
-		__raw_writel(gpcon, base + OFFS_CON);
-
-		/* now set all the other bits */
-
-		__raw_writel(gps_gpdat, base + OFFS_DAT);
-		__raw_writel(gps_gpcon, base + OFFS_CON);
-	} else {
-		unsigned long old, new, mask;
-		unsigned long change_mask = 0x0;
-
-		old_gpup = __raw_readl(base + OFFS_UP);
-
-		/* Create a change_mask of all the items that need to have
-		 * their CON value changed before their DAT value, so that
-		 * we minimise the work between the two settings.
-		 */
-
-		for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) {
-			old = (old_gpcon & mask) >> nr;
-			new = (gps_gpcon & mask) >> nr;
-
-			/* If there is no change, then skip */
-
-			if (old == new)
-				continue;
-
-			/* If both are special function, then skip */
-
-			if (is_sfn(old) && is_sfn(new))
-				continue;
-
-			/* Change is IN => OUT, do not change now */
-
-			if (is_in(old) && is_out(new))
-				continue;
-
-			/* Change is SFN => OUT, do not change now */
-
-			if (is_sfn(old) && is_out(new))
-				continue;
-
-			/* We should now be at the case of IN=>SFN,
-			 * OUT=>SFN, OUT=>IN, SFN=>IN. */
-
-			change_mask |= mask;
-		}
-
-		/* Write the new CON settings */
-
-		gpcon = old_gpcon & ~change_mask;
-		gpcon |= gps_gpcon & change_mask;
-
-		__raw_writel(gpcon, base + OFFS_CON);
-
-		/* Now change any items that require DAT,CON */
-
-		__raw_writel(gps_gpdat, base + OFFS_DAT);
-		__raw_writel(gps_gpcon, base + OFFS_CON);
-		__raw_writel(gps->gpup, base + OFFS_UP);
-	}
-
-	S3C_PMDBG("GPIO[%d] CON %08lx => %08lx, DAT %08lx => %08lx\n",
-		  index, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
-}
-
-
-/** s3c2410_pm_restore_gpios()
- *
- * Restore the state of the GPIOs
- */
-
-void s3c_pm_restore_gpios(void)
-{
-	struct gpio_sleep *gps = gpio_save;
-	int gpio;
-
-	for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) {
-		s3c2410_pm_restore_gpio(gpio, gps);
-	}
-}
 
 void s3c_pm_restore_core(void)
 {
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -37,13 +37,6 @@ config MMC_SDHCI
 
 	  If unsure, say N.
 
-config MMC_SDHCI_IO_ACCESSORS
-	bool
-	depends on MMC_SDHCI
-	help
-	  This is silent Kconfig symbol that is selected by the drivers that
-	  need to overwrite SDHCI IO memory accessors.
-
 config MMC_SDHCI_PCI
 	tristate "SDHCI support on PCI bus"
 	depends on MMC_SDHCI && PCI
@@ -55,6 +48,18 @@ config MMC_SDHCI_PCI
 
 	  If unsure, say N.
 
+config MMC_SDHCI_S3C
+	tristate "SDHCI support on Samsung S3C SoC"
+	depends on MMC_SDHCI && PLAT_S3C24XX
+	help
+	  This selects the Secure Digital Host Controller Interface (SDHCI)
+	  often referrered to as the HSMMC block in some of the Samsung S3C
+	  range of SoC.
+
+	  If you have a controller with this interface, say Y or M here.
+
+	  If unsure, say N.
+
 config MMC_RICOH_MMC
 	tristate "Ricoh MMC Controller Disabler  (EXPERIMENTAL)"
 	depends on MMC_SDHCI_PCI
@@ -72,17 +77,6 @@ config MMC_RICOH_MMC
 
 	  If unsure, say Y.
 
-config MMC_SDHCI_OF
-	tristate "SDHCI support on OpenFirmware platforms"
-	depends on MMC_SDHCI && PPC_OF
-	select MMC_SDHCI_IO_ACCESSORS
-	help
-	  This selects the OF support for Secure Digital Host Controller
-	  Interfaces. So far, only the Freescale eSDHC controller is known
-	  to exist on OF platforms.
-
-	  If unsure, say N.
-
 config MMC_OMAP
 	tristate "TI OMAP Multimedia Card Interface support"
 	depends on ARCH_OMAP
@@ -163,16 +157,6 @@ config MMC_IMX
 
 	  If unsure, say N.
 
-config MMC_MXC
-	tristate "Freescale i.MX2/3 Multimedia Card Interface support"
-	depends on ARCH_MXC
-	help
-	  This selects the Freescale i.MX2/3 Multimedia card Interface.
-	  If you have a i.MX platform with a Multimedia Card slot,
-	  say Y or M here.
-
-	  If unsure, say N.
-
 config MMC_TIFM_SD
 	tristate "TI Flash Media MMC/SD Interface support  (EXPERIMENTAL)"
 	depends on EXPERIMENTAL && PCI
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -9,11 +9,10 @@ endif
 obj-$(CONFIG_MMC_ARMMMCI)	+= mmci.o
 obj-$(CONFIG_MMC_PXA)		+= pxamci.o
 obj-$(CONFIG_MMC_IMX)		+= imxmmc.o
-obj-$(CONFIG_MMC_MXC)		+= mxcmmc.o
 obj-$(CONFIG_MMC_SDHCI)		+= sdhci.o
 obj-$(CONFIG_MMC_SDHCI_PCI)	+= sdhci-pci.o
+obj-$(CONFIG_MMC_SDHCI_S3C)	+= sdhci-s3c.o
 obj-$(CONFIG_MMC_RICOH_MMC)	+= ricoh_mmc.o
-obj-$(CONFIG_MMC_SDHCI_OF)	+= sdhci-of.o
 obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
 obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
@@ -21,7 +20,6 @@ obj-$(CONFIG_MMC_OMAP_HS)	+= omap_hsmmc.
 obj-$(CONFIG_MMC_AT91)		+= at91_mci.o
 obj-$(CONFIG_MMC_ATMELMCI)	+= atmel-mci.o
 obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
-obj-$(CONFIG_MMC_MVSDIO)	+= mvsdio.o
 obj-$(CONFIG_MMC_SPI)		+= mmc_spi.o
 ifeq ($(CONFIG_OF),y)
 obj-$(CONFIG_MMC_SPI)		+= of_mmc_spi.o
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -2,6 +2,7 @@
  *  linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
  *
  *  Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
+ *  Copyright (C) 2007 Harald Welte <laforge@gnumonks.org>
  *
  * Current driver maintained by Ben Dooks and Simtec Electronics
  *  Copyright (C) 2008 Simtec Electronics <ben-linux@fluff.org>
@@ -24,9 +25,18 @@
 
 #include <mach/regs-sdi.h>
 #include <mach/regs-gpio.h>
+#include <mach/hardware.h>
 
 #include <plat/mci.h>
 
+#include <asm/dma.h>
+#include <asm/dma-mapping.h>
+
+#include <asm/io.h>
+#include <mach/regs-gpio.h>
+#include <mach/mci.h>
+#include <mach/dma.h>
+
 #include "s3cmci.h"
 
 #define DRIVER_NAME "s3c-mci"
@@ -47,6 +57,9 @@ static const int dbgmap_err   = dbg_fail
 static const int dbgmap_info  = dbg_info | dbg_conf;
 static const int dbgmap_debug = dbg_err | dbg_debug;
 
+static int f_max = -1; /* override maximum frequency limit */
+static int persist; /* keep interface alive across suspend/resume */
+
 #define dbg(host, channels, args...)		  \
 	do {					  \
 	if (dbgmap_err & channels) 		  \
@@ -280,8 +293,11 @@ static void do_pio_read(struct s3cmci_ho
 		 * an even multiple of 4. */
 		if (fifo >= host->pio_bytes)
 			fifo = host->pio_bytes;
-		else
+		else {
 			fifo -= fifo & 3;
+			if (!fifo)
+				break;
+		}
 
 		host->pio_bytes -= fifo;
 		host->pio_count += fifo;
@@ -329,7 +345,7 @@ static void do_pio_write(struct s3cmci_h
 
 	to_ptr = host->base + host->sdidata;
 
-	while ((fifo = fifo_free(host)) > 3) {
+	while ((fifo = fifo_free(host))) {
 		if (!host->pio_bytes) {
 			res = get_data_buffer(host, &host->pio_bytes,
 							&host->pio_ptr);
@@ -353,8 +369,11 @@ static void do_pio_write(struct s3cmci_h
 		 * words, so round down to an even multiple of 4. */
 		if (fifo >= host->pio_bytes)
 			fifo = host->pio_bytes;
-		else
+		else {
 			fifo -= fifo & 3;
+			if (!fifo)
+				break;
+		}
 
 		host->pio_bytes -= fifo;
 		host->pio_count += fifo;
@@ -373,7 +392,6 @@ static void pio_tasklet(unsigned long da
 {
 	struct s3cmci_host *host = (struct s3cmci_host *) data;
 
-
 	disable_irq(host->irq);
 
 	if (host->pio_active == XFER_WRITE)
@@ -614,7 +632,6 @@ irq_out:
 
 	spin_unlock_irqrestore(&host->complete_lock, iflags);
 	return IRQ_HANDLED;
-
 }
 
 /*
@@ -789,11 +806,11 @@ static void s3cmci_dma_setup(struct s3cm
 
 	last_source = source;
 
-	s3c2410_dma_devconfig(host->dma, source, 3,
+	s3c2410_dma_devconfig(host->dma, source,
 			      host->mem->start + host->sdidata);
 
 	if (!setup_ok) {
-		s3c2410_dma_config(host->dma, 4, 0);
+		s3c2410_dma_config(host->dma, 4);
 		s3c2410_dma_set_buffdone_fn(host->dma,
 					    s3cmci_dma_done_callback);
 		s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
@@ -1026,6 +1043,7 @@ static void s3cmci_send_request(struct m
 			dbg(host, dbg_err, "data prepare error %d\n", res);
 			cmd->error = res;
 			cmd->data->error = res;
+			cmd->data->error = -EIO;
 
 			mmc_request_done(mmc, mrq);
 			return;
@@ -1263,10 +1281,8 @@ static int __devinit s3cmci_probe(struct
 	host->is2440	= is2440;
 
 	host->pdata = pdev->dev.platform_data;
-	if (!host->pdata) {
-		pdev->dev.platform_data = &s3cmci_def_pdata;
+	if (!host->pdata)
 		host->pdata = &s3cmci_def_pdata;
-	}
 
 	spin_lock_init(&host->complete_lock);
 	tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
@@ -1379,6 +1395,18 @@ static int __devinit s3cmci_probe(struct
 	mmc->f_min 	= host->clk_rate / (host->clk_div * 256);
 	mmc->f_max 	= host->clk_rate / host->clk_div;
 
+	if (f_max >= 0) {
+		unsigned f = f_max;
+
+		if (f < mmc->f_min)
+			f = mmc->f_min;
+		if (mmc->f_max > f) {
+			dev_info(&pdev->dev, "f_max lowered from %u to %u Hz\n",
+			    mmc->f_max, f);
+			mmc->f_max = f;
+		}
+	}
+
 	if (host->pdata->ocr_avail)
 		mmc->ocr_avail = host->pdata->ocr_avail;
 
@@ -1491,18 +1519,60 @@ static int __devinit s3cmci_2440_probe(s
 
 #ifdef CONFIG_PM
 
+static int save_regs(struct mmc_host *mmc)
+{
+	struct s3cmci_host *host = mmc_priv(mmc);
+	unsigned long flags;
+	unsigned from;
+	u32 *to = host->saved;
+
+	mmc_flush_scheduled_work();
+
+	local_irq_save(flags);
+	for (from = S3C2410_SDICON; from != S3C2410_SDIIMSK+4; from += 4)
+		if (from != host->sdidata)
+			*to++ = readl(host->base + from);
+	BUG_ON(to-host->saved != ARRAY_SIZE(host->saved));
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static int restore_regs(struct mmc_host *mmc)
+{
+	struct s3cmci_host *host = mmc_priv(mmc);
+	unsigned long flags;
+	unsigned to;
+	u32 *from = host->saved;
+
+	/*
+	 * Before we begin with the necromancy, make sure we don't
+	 * inadvertently start something we'll regret microseconds later.
+	 */
+	from[S3C2410_SDICMDCON - S3C2410_SDICON] = 0;
+
+	local_irq_save(flags);
+	for (to = S3C2410_SDICON; to != S3C2410_SDIIMSK+4; to += 4)
+		if (to != host->sdidata)
+			writel(*from++, host->base + to);
+	BUG_ON(from-host->saved != ARRAY_SIZE(host->saved));
+	local_irq_restore(flags);
+
+	return 0;
+}
+
 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);
+	return persist ? save_regs(mmc) : 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);
+	return persist ? restore_regs(mmc) : mmc_resume_host(mmc);
 }
 
 #else /* CONFIG_PM */
@@ -1560,9 +1630,13 @@ static void __exit s3cmci_exit(void)
 module_init(s3cmci_init);
 module_exit(s3cmci_exit);
 
+module_param(f_max, int, 0644);
+module_param(persist, int, 0644);
+
 MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>, Ben Dooks <ben-linux@fluff.org>");
 MODULE_ALIAS("platform:s3c2410-sdi");
 MODULE_ALIAS("platform:s3c2412-sdi");
 MODULE_ALIAS("platform:s3c2440-sdi");
+
--- a/drivers/mmc/host/s3cmci.h
+++ b/drivers/mmc/host/s3cmci.h
@@ -8,6 +8,10 @@
  * published by the Free Software Foundation.
  */
 
+
+#include <mach/regs-sdi.h>
+#include <linux/regulator/consumer.h>
+
 /* FIXME: DMA Resource management ?! */
 #define S3CMCI_DMA 0
 
@@ -68,7 +72,16 @@ struct s3cmci_host {
 	unsigned int		ccnt, dcnt;
 	struct tasklet_struct	pio_tasklet;
 
+	/*
+	 * Here's where we save the registers during suspend. Note that we skip
+	 * SDIDATA, which is at different positions on 2410 and 2440, so
+	 * there's no "+1" in the array size.
+	 */
+	u32			saved[(S3C2410_SDIIMSK-S3C2410_SDICON)/4];
+
 #ifdef CONFIG_CPU_FREQ
 	struct notifier_block	freq_transition;
 #endif
+
+	struct regulator *regulator;
 };
--- /dev/null
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -0,0 +1,419 @@
+/* linux/drivers/mmc/host/sdhci-s3c.c
+ *
+ * Copyright 2008 Openmoko Inc.
+ * Copyright 2008 Simtec Electronics
+ *      Ben Dooks <ben@simtec.co.uk>
+ *      http://armlinux.simtec.co.uk/
+ *
+ * SDHCI (HSMMC) support for Samsung SoC
+ *
+ * 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/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <linux/mmc/host.h>
+
+#include <plat/regs-sdhci.h>
+#include <plat/sdhci.h>
+
+#include "sdhci.h"
+
+#define MAX_BUS_CLK	(4)
+
+struct sdhci_s3c {
+	struct sdhci_host	*host;
+	struct platform_device	*pdev;
+	struct resource		*ioarea;
+	struct s3c_sdhci_platdata *pdata;
+	unsigned int		cur_clk;
+
+	struct clk		*clk_io;	/* clock for io bus */
+	struct clk		*clk_bus[MAX_BUS_CLK];
+};
+
+static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
+{
+	return sdhci_priv(host);
+}
+
+static u32 get_curclk(u32 ctrl2)
+{
+	ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK;
+	ctrl2 >>= S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
+
+	return ctrl2;
+}
+
+static void sdhci_s3c_check_sclk(struct sdhci_host *host)
+{
+	struct sdhci_s3c *ourhost = to_s3c(host);
+	u32 tmp = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
+
+	if (get_curclk(tmp) != ourhost->cur_clk) {
+		dev_dbg(&ourhost->pdev->dev, "restored ctrl2 clock setting\n");
+
+		tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
+		tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
+		writel(tmp, host->ioaddr + 0x80);
+	}
+}
+
+static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host)
+{
+	struct sdhci_s3c *ourhost = to_s3c(host);
+	struct clk *busclk;
+	unsigned int rate, max;
+	int clk;
+
+	/* note, a reset will reset the clock source */
+
+	sdhci_s3c_check_sclk(host);
+
+	for (max = 0, clk = 0; clk < MAX_BUS_CLK; clk++) {
+		busclk = ourhost->clk_bus[clk];
+		if (!busclk)
+			continue;
+
+		rate = clk_get_rate(busclk);
+		if (rate > max)
+			max = rate;
+	}
+
+	return max;
+}
+
+static unsigned int sdhci_s3c_get_timeout_clk(struct sdhci_host *host)
+{
+	return sdhci_s3c_get_max_clk(host) / 1000000;
+}
+
+static void sdhci_s3c_set_ios(struct sdhci_host *host,
+			      struct mmc_ios *ios)
+{
+	struct sdhci_s3c *ourhost = to_s3c(host);
+	struct s3c_sdhci_platdata *pdata = ourhost->pdata;
+	int width;
+
+	sdhci_s3c_check_sclk(host);
+
+	if (ios->power_mode != MMC_POWER_OFF) {
+		switch (ios->bus_width) {
+		case MMC_BUS_WIDTH_4:
+			width = 4;
+			break;
+		case MMC_BUS_WIDTH_1:
+			width = 1;
+			break;
+		default:
+			BUG();
+		}
+
+		if (pdata->cfg_gpio)
+			pdata->cfg_gpio(ourhost->pdev, width);
+	}
+
+	if (pdata->cfg_card)
+		pdata->cfg_card(ourhost->pdev, host->ioaddr,
+				ios, host->mmc->card);
+}
+
+static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
+					     unsigned int src,
+					     unsigned int wanted)
+{
+	unsigned long rate;
+	struct clk *clksrc = ourhost->clk_bus[src];
+	int div;
+
+	if (!clksrc)
+		return UINT_MAX;
+
+	rate = clk_get_rate(clksrc);
+
+	for (div = 1; div < 256; div *= 2) {
+		if ((rate / div) <= wanted)
+			break;
+	}
+
+	dev_dbg(&ourhost->pdev->dev, "clk %d: rate %ld, want %d, got %ld\n",
+		src, rate, wanted, rate / div);
+
+	return (wanted - (rate / div));
+}
+
+static void sdhci_s3c_change_clock(struct sdhci_host *host, unsigned int clock)
+{
+	struct sdhci_s3c *ourhost = to_s3c(host);
+	unsigned int best = UINT_MAX;
+	unsigned int delta;
+	int best_src = 0;
+	int src;
+	u32 ctrl;
+
+	for (src = 0; src < MAX_BUS_CLK; src++) {
+		delta = sdhci_s3c_consider_clock(ourhost, src, clock);
+		if (delta < best) {
+			best = delta;
+			best_src = src;
+		}
+	}
+
+	dev_dbg(&ourhost->pdev->dev,
+		"selected source %d, clock %d, delta %d\n",
+		 best_src, clock, best);
+
+	/* turn clock off to card before changing clock source */
+	writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+	/* select the new clock source */
+
+	if (ourhost->cur_clk != best_src) {
+		struct clk *clk = ourhost->clk_bus[best_src];
+
+		ourhost->cur_clk = best_src;
+		host->max_clk = clk_get_rate(clk);
+		host->timeout_clk = host->max_clk / 1000000;
+
+		ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
+		ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
+		ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
+		writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
+	}
+
+	sdhci_change_clock(host, clock);
+}
+
+static struct sdhci_ops sdhci_s3c_ops = {
+	.get_max_clock		= sdhci_s3c_get_max_clk,
+	.get_timeout_clock	= sdhci_s3c_get_timeout_clk,
+	.change_clock		= sdhci_s3c_change_clock,
+	.set_ios		= sdhci_s3c_set_ios,
+};
+
+/*
+ * call this when you need sd stack to recognize insertion or removal of card
+ * that can't be told by SDHCI regs
+ */
+
+void sdhci_s3c_force_presence_change(struct platform_device *pdev)
+{
+	struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
+
+	dev_info(&pdev->dev, "sdhci_s3c_force_presence_change called\n");
+	mmc_detect_change(pdata->sdhci_host->mmc, msecs_to_jiffies(200));
+}
+EXPORT_SYMBOL_GPL(sdhci_s3c_force_presence_change);
+
+
+static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
+{
+	struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
+	struct device *dev = &pdev->dev;
+	struct sdhci_host *host;
+	struct sdhci_s3c *sc;
+	struct resource *res;
+	int ret, irq, ptr, clks;
+
+	if (!pdata) {
+		dev_err(dev, "no device data specified\n");
+		return -ENOENT;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "no irq specified\n");
+		return irq;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "no memory specified\n");
+		return -ENOENT;
+	}
+
+	host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c));
+	if (IS_ERR(host)) {
+		dev_err(dev, "sdhci_alloc_host() failed\n");
+		return PTR_ERR(host);
+	}
+
+	pdata->sdhci_host = host;
+
+	sc = sdhci_priv(host);
+
+	sc->host = host;
+	sc->pdev = pdev;
+	sc->pdata = pdata;
+
+	platform_set_drvdata(pdev, host);
+
+	sc->clk_io = clk_get(dev, "hsmmc");
+	if (IS_ERR(sc->clk_io)) {
+		dev_err(dev, "failed to get io clock\n");
+		ret = PTR_ERR(sc->clk_io);
+		goto err_io_clk;
+	}
+
+	/* enable the local io clock and keep it running for the moment. */
+	clk_enable(sc->clk_io);
+
+	for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
+		struct clk *clk;
+		char *name = pdata->clocks[ptr];
+
+		if (name == NULL)
+			continue;
+
+		clk = clk_get(dev, name);
+		if (IS_ERR(clk)) {
+			dev_err(dev, "failed to get clock %s\n", name);
+			continue;
+		}
+
+		clks++;
+		sc->clk_bus[ptr] = clk;
+		clk_enable(clk);
+
+		dev_info(dev, "clock source %d: %s (%ld Hz)\n",
+			 ptr, name, clk_get_rate(clk));
+	}
+
+	if (clks == 0) {
+		dev_err(dev, "failed to find any bus clocks\n");
+		ret = -ENOENT;
+		goto err_no_busclks;
+	}
+
+	sc->ioarea = request_mem_region(res->start, resource_size(res),
+					mmc_hostname(host->mmc));
+	if (!sc->ioarea) {
+		dev_err(dev, "failed to reserve register area\n");
+		ret = -ENXIO;
+		goto err_req_regs;
+	}
+
+	host->ioaddr = ioremap_nocache(res->start, resource_size(res));
+	if (!host->ioaddr) {
+		dev_err(dev, "failed to map registers\n");
+		ret = -ENXIO;
+		goto err_req_regs;
+	}
+
+	/* Ensure we have minimal gpio selected CMD/CLK/Detect */
+	if (pdata->cfg_gpio)
+		pdata->cfg_gpio(pdev, 0);
+
+	sdhci_s3c_check_sclk(host);
+
+	host->hw_name = "samsung-hsmmc";
+	host->ops = &sdhci_s3c_ops;
+	host->quirks = 0;
+	host->irq = irq;
+
+	/* Setup quirks for the controller */
+
+	/* Currently with ADMA enabled we are getting some length
+	 * interrupts that are not being dealt with, do disable
+	 * ADMA until this is sorted out. */
+	host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
+	host->quirks |= SDHCI_QUIRK_32BIT_ADMA_SIZE;
+
+	/* It seems we do not get an DATA transfer complete on non-busy
+	 * transfers, not sure if this is a problem with this specific
+	 * SDHCI block, or a missing configuration that needs to be set. */
+	host->quirks |= SDHCI_QUIRK_NO_TCIRQ_ON_NOT_BUSY;
+
+	host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR |
+			 SDHCI_QUIRK_32BIT_DMA_SIZE);
+
+	ret = sdhci_add_host(host);
+	if (ret) {
+		dev_err(dev, "sdhci_add_host() failed\n");
+		goto err_add_host;
+	}
+
+	return 0;
+
+ err_add_host:
+	release_resource(sc->ioarea);
+	kfree(sc->ioarea);
+
+ err_req_regs:
+	for (ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
+		clk_disable(sc->clk_bus[ptr]);
+		clk_put(sc->clk_bus[ptr]);
+	}
+
+ err_no_busclks:
+	clk_disable(sc->clk_io);
+	clk_put(sc->clk_io);
+
+ err_io_clk:
+	sdhci_free_host(host);
+
+	return ret;
+}
+
+static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int sdhci_s3c_suspend(struct platform_device *dev, pm_message_t pm)
+{
+	struct sdhci_host *host = platform_get_drvdata(dev);
+
+	sdhci_suspend_host(host, pm);
+	return 0;
+}
+
+static int sdhci_s3c_resume(struct platform_device *dev)
+{
+	struct sdhci_host *host = platform_get_drvdata(dev);
+
+	sdhci_resume_host(host);
+	return 0;
+}
+
+#else
+#define sdhci_s3c_suspend NULL
+#define sdhci_s3c_resume NULL
+#endif
+
+static struct platform_driver sdhci_s3c_driver = {
+	.probe		= sdhci_s3c_probe,
+	.remove		= __devexit_p(sdhci_s3c_remove),
+	.suspend	= sdhci_s3c_suspend,
+	.resume	        = sdhci_s3c_resume,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "s3c-sdhci",
+	},
+};
+
+static int __init sdhci_s3c_init(void)
+{
+	return platform_driver_register(&sdhci_s3c_driver);
+}
+
+static void __exit sdhci_s3c_exit(void)
+{
+	platform_driver_unregister(&sdhci_s3c_driver);
+}
+
+module_init(sdhci_s3c_init);
+module_exit(sdhci_s3c_exit);
+
+MODULE_DESCRIPTION("Samsung SDHCI (HSMMC) glue");
+MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:s3c-sdhci");
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -438,7 +438,7 @@ static int s3c2410_nand_correct_data(str
 	if ((diff0 & ~(1<<fls(diff0))) == 0)
 		return 1;
 
-	return -1;
+	return -EBADMSG;
 }
 
 /* ECC functions
@@ -530,7 +530,12 @@ static void s3c2410_nand_read_buf(struct
 static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 {
 	struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+	u8 *ptr = buf + (len & ~3);
+	int i;
+
 	readsl(info->regs + S3C2440_NFDATA, buf, len / 4);
+	for (i = 0; i != (len & 3); i++)
+		ptr[i] = readb(info->regs + S3C2440_NFDATA);
 }
 
 static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
@@ -645,17 +650,31 @@ static int s3c2410_nand_remove(struct pl
 }
 
 #ifdef CONFIG_MTD_PARTITIONS
+const char *part_probes[] = { "cmdlinepart", NULL };
 static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
 				      struct s3c2410_nand_mtd *mtd,
 				      struct s3c2410_nand_set *set)
 {
+	struct mtd_partition *part_info;
+	int nr_part = 0;
+
 	if (set == NULL)
 		return add_mtd_device(&mtd->mtd);
 
-	if (set->nr_partitions > 0 && set->partitions != NULL) {
-		return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
+	if (set->nr_partitions == 0) {
+		mtd->mtd.name = set->name;
+		nr_part = parse_mtd_partitions(&mtd->mtd, part_probes,
+						&part_info, 0);
+	} else {
+		if (set->nr_partitions > 0 && set->partitions != NULL) {
+			nr_part = set->nr_partitions;
+			part_info = set->partitions;
+		}
 	}
 
+	if (nr_part > 0 && part_info)
+		return add_mtd_partitions(&mtd->mtd, part_info, nr_part);
+
 	return add_mtd_device(&mtd->mtd);
 }
 #else
@@ -684,9 +703,13 @@ static void s3c2410_nand_init_chip(struc
 	chip->select_chip  = s3c2410_nand_select_chip;
 	chip->chip_delay   = 50;
 	chip->priv	   = nmtd;
-	chip->options	   = 0;
 	chip->controller   = &info->controller;
 
+	if (set->flags & S3C2410_NAND_BBT)
+		chip->options	   = NAND_USE_FLASH_BBT;
+	else
+		chip->options	   = 0;
+
 	switch (info->cpu_type) {
 	case TYPE_S3C2410:
 		chip->IO_ADDR_W = regs + S3C2410_NFDATA;
@@ -726,7 +749,7 @@ static void s3c2410_nand_init_chip(struc
 	nmtd->mtd.owner    = THIS_MODULE;
 	nmtd->set	   = set;
 
-	if (hardware_ecc) {
+	if (!info->platform->software_ecc && hardware_ecc) {
 		chip->ecc.calculate = s3c2410_nand_calculate_ecc;
 		chip->ecc.correct   = s3c2410_nand_correct_data;
 		chip->ecc.mode	    = NAND_ECC_HW;
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -59,10 +59,11 @@ static int mmc_schedule_delayed_work(str
 /*
  * Internal function. Flush all scheduled work from the MMC work queue.
  */
-static void mmc_flush_scheduled_work(void)
+void mmc_flush_scheduled_work(void)
 {
 	flush_workqueue(workqueue);
 }
+EXPORT_SYMBOL_GPL(mmc_flush_scheduled_work);
 
 /**
  *	mmc_request_done - finish processing an MMC request
