[s3c24xx] bump to 2.6.30-rc6
[openwrt.git] / target / linux / s3c24xx / patches-2.6.30 / 001-merge-openmoko.patch
diff --git a/target/linux/s3c24xx/patches-2.6.30/001-merge-openmoko.patch b/target/linux/s3c24xx/patches-2.6.30/001-merge-openmoko.patch
new file mode 100644 (file)
index 0000000..0738a38
--- /dev/null
@@ -0,0 +1,2265 @@
+Merge OpenMoko kernel patches
+git://git.openmoko.org/git/kernel.git#(no
+
+lars@lars-laptop   Thu May 14 18:33:23 UTC 2009
+
+---
+
+Index: linux-2.6.30-rc6/arch/arm/mach-s3c2410/include/mach/mci.h
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30-rc6/arch/arm/mach-s3c2410/include/mach/mci.h  2009-05-18 19:07:48.000000000 +0200
+@@ -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 */
+Index: linux-2.6.30-rc6/arch/arm/mach-s3c2410/include/mach/regs-sdi.h
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/mach-s3c2410/include/mach/regs-sdi.h        2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/mach-s3c2410/include/mach/regs-sdi.h     2009-05-18 19:07:48.000000000 +0200
+@@ -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)
+Index: linux-2.6.30-rc6/arch/arm/mach-s3c2440/s3c2440.c
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/mach-s3c2440/s3c2440.c      2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/mach-s3c2440/s3c2440.c   2009-05-18 19:07:48.000000000 +0200
+@@ -46,6 +46,9 @@
+       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);
+Index: linux-2.6.30-rc6/arch/arm/mach-s3c2442/s3c2442.c
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/mach-s3c2442/s3c2442.c      2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/mach-s3c2442/s3c2442.c   2009-05-18 19:07:48.000000000 +0200
+@@ -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 @@
+ {
+       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);
+ }
+Index: linux-2.6.30-rc6/arch/arm/Makefile
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/Makefile    2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/Makefile 2009-05-18 19:07:48.000000000 +0200
+@@ -55,7 +55,8 @@
+ 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
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c/include/mach/cpu.h
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30-rc6/arch/arm/plat-s3c/include/mach/cpu.h      2009-05-18 19:07:48.000000000 +0200
+@@ -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
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/devs.h
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c/include/plat/devs.h        2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/devs.h     2009-05-18 19:07:48.000000000 +0200
+@@ -16,6 +16,10 @@
+       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[];
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/gpio-core.h
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c/include/plat/gpio-core.h   2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/gpio-core.h        2009-05-18 19:07:48.000000000 +0200
+@@ -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 @@
+  * @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_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 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 */
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/map-base.h
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c/include/plat/map-base.h    2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/map-base.h 2009-05-18 19:07:48.000000000 +0200
+@@ -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 */
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/nand.h
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c/include/plat/nand.h        2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/nand.h     2009-05-18 19:07:48.000000000 +0200
+@@ -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 @@
+       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);
+ };
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/pm.h
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c/include/plat/pm.h  2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/pm.h       2009-05-18 19:07:48.000000000 +0200
+@@ -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 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 @@
+       u32     ufcon;
+       u32     umcon;
+       u32     ubrdiv;
++      u32     udivslot;
+ };
+ /* helper functions to save/restore lists of registers. */
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/sdhci.h
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c/include/plat/sdhci.h       2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c/include/plat/sdhci.h    2009-05-18 19:07:48.000000000 +0200
+@@ -29,6 +29,7 @@
+  *            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 @@
+                           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.
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c/init.c
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c/init.c     2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c/init.c  2009-05-18 19:07:48.000000000 +0200
+@@ -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 @@
+               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) {
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c/Makefile
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c/Makefile   2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c/Makefile        2009-05-18 19:07:48.000000000 +0200
+@@ -21,6 +21,7 @@
+ # PM support
+ obj-$(CONFIG_PM)              += pm.o
++obj-$(CONFIG_PM)              += pm-gpio.o
+ obj-$(CONFIG_S3C2410_PM_CHECK)        += pm-check.o
+ # devices
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c/pm.c
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c/pm.c       2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c/pm.c    2009-05-18 19:07:48.000000000 +0200
+@@ -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 @@
+ /* 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 @@
+       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 @@
+ {
+       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 @@
+       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. */
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c/pm-gpio.c
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30-rc6/arch/arm/plat-s3c/pm-gpio.c       2009-05-18 19:07:48.000000000 +0200
+@@ -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;
++      }
++}
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c24xx/clock-dclk.c
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c24xx/clock-dclk.c   2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c24xx/clock-dclk.c        2009-05-18 19:07:48.000000000 +0200
+@@ -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>
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c24xx/cpu.c
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c24xx/cpu.c  2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c24xx/cpu.c       2009-05-18 19:07:48.000000000 +0200
+@@ -61,6 +61,7 @@
+ 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 @@
+               .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,
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c24xx/gpiolib.c
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c24xx/gpiolib.c      2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c24xx/gpiolib.c   2009-05-18 19:07:48.000000000 +0200
+@@ -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 @@
+ 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 @@
+       },
+       [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 @@
+       },
+       [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 @@
+       },
+       [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 @@
+       },
+       [4] = {
+               .base   = S3C24XX_GPIO_BASE(S3C2410_GPE0),
++              .pm     = __gpio_pm(&s3c_gpio_pm_2bit),
+               .chip   = {
+                       .base                   = S3C2410_GPE0,
+                       .label                  = "GPIOE",
+@@ -125,6 +131,7 @@
+       },
+       [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 @@
+       },
+       [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,
+               },
+       },
+ };
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c24xx/include/plat/pm-core.h
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c24xx/include/plat/pm-core.h 2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c24xx/include/plat/pm-core.h      2009-05-18 19:07:48.000000000 +0200
+@@ -57,3 +57,8 @@
+       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)
++{
++}
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c24xx/irq-pm.c
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c24xx/irq-pm.c       2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c24xx/irq-pm.c    2009-05-18 19:07:48.000000000 +0200
+@@ -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_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 @@
+       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;
+ }
+Index: linux-2.6.30-rc6/arch/arm/plat-s3c24xx/pm.c
+===================================================================
+--- linux-2.6.30-rc6.orig/arch/arm/plat-s3c24xx/pm.c   2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/arch/arm/plat-s3c24xx/pm.c        2009-05-18 19:07:48.000000000 +0200
+@@ -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 @@
+       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 @@
+       }
+ }
+-/* 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)
+ {
+Index: linux-2.6.30-rc6/drivers/mmc/host/Kconfig
+===================================================================
+--- linux-2.6.30-rc6.orig/drivers/mmc/host/Kconfig     2009-05-18 19:07:38.000000000 +0200
++++ linux-2.6.30-rc6/drivers/mmc/host/Kconfig  2009-05-18 19:07:48.000000000 +0200
+@@ -37,13 +37,6 @@
+         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 @@
+         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 @@
+         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 @@
+         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
+Index: linux-2.6.30-rc6/drivers/mmc/host/Makefile
+===================================================================
+--- linux-2.6.30-rc6.orig/drivers/mmc/host/Makefile    2009-05-18 19:07:38.000000000 +0200
++++ linux-2.6.30-rc6/drivers/mmc/host/Makefile 2009-05-18 19:07:48.000000000 +0200
+@@ -9,11 +9,10 @@
+ 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_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
+Index: linux-2.6.30-rc6/drivers/mmc/host/s3cmci.c
+===================================================================
+--- linux-2.6.30-rc6.orig/drivers/mmc/host/s3cmci.c    2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/drivers/mmc/host/s3cmci.c 2009-05-18 19:07:48.000000000 +0200
+@@ -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_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 @@
+                * 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 @@
+       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 @@
+                * 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 @@
+ {
+       struct s3cmci_host *host = (struct s3cmci_host *) data;
+-
+       disable_irq(host->irq);
+       if (host->pio_active == XFER_WRITE)
+@@ -614,7 +632,6 @@
+       spin_unlock_irqrestore(&host->complete_lock, iflags);
+       return IRQ_HANDLED;
+-
+ }
+ /*
+@@ -789,11 +806,11 @@
+       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 @@
+                       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 @@
+       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 @@
+       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 @@
+ #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 @@
+ 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");
++
+Index: linux-2.6.30-rc6/drivers/mmc/host/s3cmci.h
+===================================================================
+--- linux-2.6.30-rc6.orig/drivers/mmc/host/s3cmci.h    2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/drivers/mmc/host/s3cmci.h 2009-05-18 19:07:48.000000000 +0200
+@@ -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 @@
+       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;
+ };
+Index: linux-2.6.30-rc6/drivers/mmc/host/sdhci-s3c.c
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.30-rc6/drivers/mmc/host/sdhci-s3c.c      2009-05-18 19:07:48.000000000 +0200
+@@ -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");
+Index: linux-2.6.30-rc6/drivers/mtd/nand/s3c2410.c
+===================================================================
+--- linux-2.6.30-rc6.orig/drivers/mtd/nand/s3c2410.c   2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/drivers/mtd/nand/s3c2410.c        2009-05-18 19:07:48.000000000 +0200
+@@ -438,7 +438,7 @@
+       if ((diff0 & ~(1<<fls(diff0))) == 0)
+               return 1;
+-      return -1;
++      return -EBADMSG;
+ }
+ /* ECC functions
+@@ -530,7 +530,12 @@
+ 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 @@
+ }
+ #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 @@
+       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 @@
+       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;
+Index: linux-2.6.30-rc6/drivers/mmc/core/core.c
+===================================================================
+--- linux-2.6.30-rc6.orig/drivers/mmc/core/core.c      2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/drivers/mmc/core/core.c   2009-05-18 19:07:48.000000000 +0200
+@@ -59,10 +59,11 @@
+ /*
+  * 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