From 5c105d9f3fd086aff195d3849dcf847d6b0bd927 Mon Sep 17 00:00:00 2001 From: blogic Date: Fri, 5 Oct 2012 10:12:53 +0000 Subject: branch Attitude Adjustment git-svn-id: svn://svn.openwrt.org/openwrt/branches/attitude_adjustment@33625 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../patches-2.6.30/001-merge-openmoko.patch | 2207 ++++++++++++++++++++ 1 file changed, 2207 insertions(+) create mode 100644 target/linux/s3c24xx/patches-2.6.30/001-merge-openmoko.patch (limited to '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 index 000000000..1a515a489 --- /dev/null +++ b/target/linux/s3c24xx/patches-2.6.30/001-merge-openmoko.patch @@ -0,0 +1,2207 @@ +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 + #include ++#include + + 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 ++ * ++ * 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 ++ + /* 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 + #include ++#include + + #include + #include +-#include +-#include + #include + #include + +@@ -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 ++ * 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 ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* 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 + #include ++#include + + #include + #include +--- 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 + #include + +-#include ++#include + #include + #include ++#include + + #include + +@@ -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 + #include + #include ++#include + + #include + #include +@@ -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 + #include + #include ++#include + + #include + +@@ -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 ++ * Copyright (C) 2007 Harald Welte + * + * Current driver maintained by Ben Dooks and Simtec Electronics + * Copyright (C) 2008 Simtec Electronics +@@ -24,9 +25,18 @@ + + #include + #include ++#include + + #include + ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ + #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 , Ben Dooks "); + 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 ++#include ++ + /* 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 ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#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, "); ++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<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 -- cgit v1.2.3