Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable:
  Btrfs: make sure fallocate properly starts a transaction
  Btrfs: make metadata chunks smaller
  Btrfs: Show discard option in /proc/mounts
  Btrfs: deny sys_link across subvolumes.
  Btrfs: fail mount on bad mount options
  Btrfs: don't add extent 0 to the free space cache v2
  Btrfs: Fix per root used space accounting
  Btrfs: Fix btrfs_drop_extent_cache for skip pinned case
  Btrfs: Add delayed iput
  Btrfs: Pass transaction handle to security and ACL initialization functions
  Btrfs: Make truncate(2) more ENOSPC friendly
  Btrfs: Make fallocate(2) more ENOSPC friendly
  Btrfs: Avoid orphan inodes cleanup during committing transaction
  Btrfs: Avoid orphan inodes cleanup while replaying log
  Btrfs: Fix disk_i_size update corner case
  Btrfs: Rewrite btrfs_drop_extents
  Btrfs: Add btrfs_duplicate_item
  Btrfs: Avoid superfluous tree-log writeout
diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c
index 5a375e5..bc90364 100644
--- a/arch/arm/common/dmabounce.c
+++ b/arch/arm/common/dmabounce.c
@@ -308,15 +308,11 @@
 			memcpy(ptr, buf->safe, size);
 
 			/*
-			 * DMA buffers must have the same cache properties
-			 * as if they were really used for DMA - which means
-			 * data must be written back to RAM.  Note that
-			 * we don't use dmac_flush_range() here for the
-			 * bidirectional case because we know the cache
-			 * lines will be coherent with the data written.
+			 * Since we may have written to a page cache page,
+			 * we need to ensure that the data will be coherent
+			 * with user mappings.
 			 */
-			dmac_clean_range(ptr, ptr + size);
-			outer_clean_range(__pa(ptr), __pa(ptr) + size);
+			__cpuc_flush_kernel_dcache_area(ptr, size);
 		}
 		free_safe_buffer(dev->archdata.dmabounce, buf);
 	}
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index 73eceb8..730aefc 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -211,7 +211,7 @@
 
 	void (*coherent_kern_range)(unsigned long, unsigned long);
 	void (*coherent_user_range)(unsigned long, unsigned long);
-	void (*flush_kern_dcache_page)(void *);
+	void (*flush_kern_dcache_area)(void *, size_t);
 
 	void (*dma_inv_range)(const void *, const void *);
 	void (*dma_clean_range)(const void *, const void *);
@@ -236,7 +236,7 @@
 #define __cpuc_flush_user_range		cpu_cache.flush_user_range
 #define __cpuc_coherent_kern_range	cpu_cache.coherent_kern_range
 #define __cpuc_coherent_user_range	cpu_cache.coherent_user_range
-#define __cpuc_flush_dcache_page	cpu_cache.flush_kern_dcache_page
+#define __cpuc_flush_dcache_area	cpu_cache.flush_kern_dcache_area
 
 /*
  * These are private to the dma-mapping API.  Do not use directly.
@@ -255,14 +255,14 @@
 #define __cpuc_flush_user_range		__glue(_CACHE,_flush_user_cache_range)
 #define __cpuc_coherent_kern_range	__glue(_CACHE,_coherent_kern_range)
 #define __cpuc_coherent_user_range	__glue(_CACHE,_coherent_user_range)
-#define __cpuc_flush_dcache_page	__glue(_CACHE,_flush_kern_dcache_page)
+#define __cpuc_flush_dcache_area	__glue(_CACHE,_flush_kern_dcache_area)
 
 extern void __cpuc_flush_kern_all(void);
 extern void __cpuc_flush_user_all(void);
 extern void __cpuc_flush_user_range(unsigned long, unsigned long, unsigned int);
 extern void __cpuc_coherent_kern_range(unsigned long, unsigned long);
 extern void __cpuc_coherent_user_range(unsigned long, unsigned long);
-extern void __cpuc_flush_dcache_page(void *);
+extern void __cpuc_flush_dcache_area(void *, size_t);
 
 /*
  * These are private to the dma-mapping API.  Do not use directly.
@@ -448,7 +448,7 @@
 {
 	/* highmem pages are always flushed upon kunmap already */
 	if ((cache_is_vivt() || cache_is_vipt_aliasing()) && !PageHighMem(page))
-		__cpuc_flush_dcache_page(page_address(page));
+		__cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
 }
 
 #define flush_dcache_mmap_lock(mapping) \
@@ -465,13 +465,6 @@
  */
 #define flush_icache_page(vma,page)	do { } while (0)
 
-static inline void flush_ioremap_region(unsigned long phys, void __iomem *virt,
-	unsigned offset, size_t size)
-{
-	const void *start = (void __force *)virt + offset;
-	dmac_inv_range(start, start + size);
-}
-
 /*
  * flush_cache_vmap() is used when creating mappings (eg, via vmap,
  * vmalloc, ioremap etc) in kernel space for pages.  On non-VIPT
diff --git a/arch/arm/mach-kirkwood/Kconfig b/arch/arm/mach-kirkwood/Kconfig
index 8bf09ae..f6c6196 100644
--- a/arch/arm/mach-kirkwood/Kconfig
+++ b/arch/arm/mach-kirkwood/Kconfig
@@ -52,6 +52,12 @@
 	  Say 'Y' here if you want your kernel to support the
 	  Marvell OpenRD Base Board.
 
+config MACH_NETSPACE_V2
+	bool "LaCie Network Space v2 NAS Board"
+	help
+	  Say 'Y' here if you want your kernel to support the
+	  LaCie Network Space v2 NAS.
+
 endmenu
 
 endif
diff --git a/arch/arm/mach-kirkwood/Makefile b/arch/arm/mach-kirkwood/Makefile
index 9f2f67b..d4d7f53 100644
--- a/arch/arm/mach-kirkwood/Makefile
+++ b/arch/arm/mach-kirkwood/Makefile
@@ -8,5 +8,6 @@
 obj-$(CONFIG_MACH_TS219)		+= ts219-setup.o tsx1x-common.o
 obj-$(CONFIG_MACH_TS41X)		+= ts41x-setup.o tsx1x-common.o
 obj-$(CONFIG_MACH_OPENRD_BASE)		+= openrd_base-setup.o
+obj-$(CONFIG_MACH_NETSPACE_V2)		+= netspace_v2-setup.o
 
 obj-$(CONFIG_CPU_IDLE)			+= cpuidle.o
diff --git a/arch/arm/mach-kirkwood/netspace_v2-setup.c b/arch/arm/mach-kirkwood/netspace_v2-setup.c
new file mode 100644
index 0000000..9a06406
--- /dev/null
+++ b/arch/arm/mach-kirkwood/netspace_v2-setup.c
@@ -0,0 +1,325 @@
+/*
+ * arch/arm/mach-kirkwood/netspace_v2-setup.c
+ *
+ * LaCie Network Space v2 board setup
+ *
+ * Copyright (C) 2009 Simon Guinot <sguinot@lacie.com>
+ * Copyright (C) 2009 Benoît Canet <benoit.canet@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/physmap.h>
+#include <linux/spi/flash.h>
+#include <linux/spi/spi.h>
+#include <linux/ata_platform.h>
+#include <linux/mv643xx_eth.h>
+#include <linux/i2c.h>
+#include <linux/i2c/at24.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/gpio_keys.h>
+#include <linux/leds.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/kirkwood.h>
+#include <plat/time.h>
+#include "common.h"
+#include "mpp.h"
+
+/*****************************************************************************
+ * 512KB SPI Flash on Boot Device (MACRONIX MX25L4005)
+ ****************************************************************************/
+
+static struct mtd_partition netspace_v2_flash_parts[] = {
+	{
+		.name = "u-boot",
+		.size = MTDPART_SIZ_FULL,
+		.offset = 0,
+		.mask_flags = MTD_WRITEABLE, /* force read-only */
+	},
+};
+
+static const struct flash_platform_data netspace_v2_flash = {
+	.type		= "mx25l4005a",
+	.name		= "spi_flash",
+	.parts		= netspace_v2_flash_parts,
+	.nr_parts	= ARRAY_SIZE(netspace_v2_flash_parts),
+};
+
+static struct spi_board_info __initdata netspace_v2_spi_slave_info[] = {
+	{
+		.modalias	= "m25p80",
+		.platform_data	= &netspace_v2_flash,
+		.irq		= -1,
+		.max_speed_hz	= 20000000,
+		.bus_num	= 0,
+		.chip_select	= 0,
+	},
+};
+
+/*****************************************************************************
+ * Ethernet
+ ****************************************************************************/
+
+static struct mv643xx_eth_platform_data netspace_v2_ge00_data = {
+	.phy_addr	= MV643XX_ETH_PHY_ADDR(8),
+};
+
+/*****************************************************************************
+ * I2C devices
+ ****************************************************************************/
+
+static struct at24_platform_data at24c04 = {
+	.byte_len	= SZ_4K / 8,
+	.page_size	= 16,
+};
+
+/*
+ * i2c addr | chip         | description
+ * 0x50     | HT24LC04     | eeprom (512B)
+ */
+
+static struct i2c_board_info __initdata netspace_v2_i2c_info[] = {
+	{
+		I2C_BOARD_INFO("24c04", 0x50),
+		.platform_data  = &at24c04,
+	}
+};
+
+/*****************************************************************************
+ * SATA
+ ****************************************************************************/
+
+static struct mv_sata_platform_data netspace_v2_sata_data = {
+	.n_ports	= 2,
+};
+
+#define NETSPACE_V2_GPIO_SATA0_POWER	16
+#define NETSPACE_V2_GPIO_SATA1_POWER	17
+
+static void __init netspace_v2_sata_power_init(void)
+{
+	int err;
+
+	err = gpio_request(NETSPACE_V2_GPIO_SATA0_POWER, "SATA0 power");
+	if (err == 0) {
+		err = gpio_direction_output(NETSPACE_V2_GPIO_SATA0_POWER, 1);
+		if (err)
+			gpio_free(NETSPACE_V2_GPIO_SATA0_POWER);
+	}
+	if (err)
+		pr_err("netspace_v2: failed to setup SATA0 power\n");
+}
+
+/*****************************************************************************
+ * GPIO keys
+ ****************************************************************************/
+
+#define NETSPACE_V2_PUSH_BUTTON		32
+
+static struct gpio_keys_button netspace_v2_buttons[] = {
+	[0] = {
+		.code		= KEY_POWER,
+		.gpio		= NETSPACE_V2_PUSH_BUTTON,
+		.desc		= "Power push button",
+		.active_low	= 0,
+	},
+};
+
+static struct gpio_keys_platform_data netspace_v2_button_data = {
+	.buttons	= netspace_v2_buttons,
+	.nbuttons	= ARRAY_SIZE(netspace_v2_buttons),
+};
+
+static struct platform_device netspace_v2_gpio_buttons = {
+	.name		= "gpio-keys",
+	.id		= -1,
+	.dev		= {
+		.platform_data 	= &netspace_v2_button_data,
+	},
+};
+
+/*****************************************************************************
+ * GPIO LEDs
+ ****************************************************************************/
+
+/*
+ * The blue front LED is wired to a CPLD and can blink in relation with the
+ * SATA activity.
+ *
+ * The following array detail the different LED registers and the combination
+ * of their possible values:
+ *
+ *  cmd_led   |  slow_led  | /SATA active | LED state
+ *            |            |              |
+ *     1      |     0      |      x       |  off
+ *     -      |     1      |      x       |  on
+ *     0      |     0      |      1       |  on
+ *     0      |     0      |      0       |  blink (rate 300ms)
+ */
+
+#define NETSPACE_V2_GPIO_RED_LED	12
+#define NETSPACE_V2_GPIO_BLUE_LED_SLOW	29
+#define NETSPACE_V2_GPIO_BLUE_LED_CMD	30
+
+
+static struct gpio_led netspace_v2_gpio_led_pins[] = {
+	{
+		.name	= "ns_v2:red:fail",
+		.gpio	= NETSPACE_V2_GPIO_RED_LED,
+	},
+};
+
+static struct gpio_led_platform_data netspace_v2_gpio_leds_data = {
+	.num_leds	= ARRAY_SIZE(netspace_v2_gpio_led_pins),
+	.leds		= netspace_v2_gpio_led_pins,
+};
+
+static struct platform_device netspace_v2_gpio_leds = {
+	.name		= "leds-gpio",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= &netspace_v2_gpio_leds_data,
+	},
+};
+
+static void __init netspace_v2_gpio_leds_init(void)
+{
+	platform_device_register(&netspace_v2_gpio_leds);
+
+	/*
+	 * Configure the front blue LED to blink in relation with the SATA
+	 * activity.
+	 */
+	if (gpio_request(NETSPACE_V2_GPIO_BLUE_LED_SLOW,
+			 "SATA blue LED slow") != 0)
+		return;
+	if (gpio_direction_output(NETSPACE_V2_GPIO_BLUE_LED_SLOW, 0) != 0)
+		goto err_free_1;
+	if (gpio_request(NETSPACE_V2_GPIO_BLUE_LED_CMD,
+			 "SATA blue LED command") != 0)
+		goto err_free_1;
+	if (gpio_direction_output(NETSPACE_V2_GPIO_BLUE_LED_CMD, 0) != 0)
+		goto err_free_2;
+
+	return;
+
+err_free_2:
+	gpio_free(NETSPACE_V2_GPIO_BLUE_LED_CMD);
+err_free_1:
+	gpio_free(NETSPACE_V2_GPIO_BLUE_LED_SLOW);
+	pr_err("netspace_v2: failed to configure SATA blue LED\n");
+}
+
+/*****************************************************************************
+ * Timer
+ ****************************************************************************/
+
+static void netspace_v2_timer_init(void)
+{
+	kirkwood_tclk = 166666667;
+	orion_time_init(IRQ_KIRKWOOD_BRIDGE, kirkwood_tclk);
+}
+
+struct sys_timer netspace_v2_timer = {
+	.init = netspace_v2_timer_init,
+};
+
+/*****************************************************************************
+ * General Setup
+ ****************************************************************************/
+
+static unsigned int netspace_v2_mpp_config[] __initdata = {
+	MPP0_SPI_SCn,
+	MPP1_SPI_MOSI,
+	MPP2_SPI_SCK,
+	MPP3_SPI_MISO,
+	MPP4_NF_IO6,
+	MPP5_NF_IO7,
+	MPP6_SYSRST_OUTn,
+	MPP8_TW_SDA,
+	MPP9_TW_SCK,
+	MPP10_UART0_TXD,
+	MPP11_UART0_RXD,
+	MPP12_GPO,		/* Red led */
+	MPP14_GPIO,		/* USB fuse */
+	MPP16_GPIO,		/* SATA 0 power */
+	MPP18_NF_IO0,
+	MPP19_NF_IO1,
+	MPP20_SATA1_ACTn,
+	MPP21_SATA0_ACTn,
+	MPP24_GPIO,		/* USB mode select */
+	MPP25_GPIO,		/* Fan rotation fail */
+	MPP26_GPIO,		/* USB device vbus */
+	MPP28_GPIO,		/* USB enable host vbus */
+	MPP29_GPIO,		/* Blue led (slow register) */
+	MPP30_GPIO,		/* Blue led (command register) */
+	MPP31_GPIO,		/* Board power off */
+	MPP32_GPIO, 		/* Power button (0 = Released, 1 = Pushed) */
+	0
+};
+
+#define NETSPACE_V2_GPIO_POWER_OFF	31
+
+static void netspace_v2_power_off(void)
+{
+	gpio_set_value(NETSPACE_V2_GPIO_POWER_OFF, 1);
+}
+
+static void __init netspace_v2_init(void)
+{
+	/*
+	 * Basic setup. Needs to be called early.
+	 */
+	kirkwood_init();
+	kirkwood_mpp_conf(netspace_v2_mpp_config);
+
+	netspace_v2_sata_power_init();
+
+	kirkwood_ehci_init();
+	kirkwood_ge00_init(&netspace_v2_ge00_data);
+	kirkwood_sata_init(&netspace_v2_sata_data);
+	kirkwood_uart0_init();
+	spi_register_board_info(netspace_v2_spi_slave_info,
+				ARRAY_SIZE(netspace_v2_spi_slave_info));
+	kirkwood_spi_init();
+	kirkwood_i2c_init();
+	i2c_register_board_info(0, netspace_v2_i2c_info,
+				ARRAY_SIZE(netspace_v2_i2c_info));
+
+	netspace_v2_gpio_leds_init();
+	platform_device_register(&netspace_v2_gpio_buttons);
+
+	if (gpio_request(NETSPACE_V2_GPIO_POWER_OFF, "power-off") == 0 &&
+	    gpio_direction_output(NETSPACE_V2_GPIO_POWER_OFF, 0) == 0)
+		pm_power_off = netspace_v2_power_off;
+	else
+		pr_err("netspace_v2: failed to configure power-off GPIO\n");
+}
+
+MACHINE_START(NETSPACE_V2, "LaCie Network Space v2")
+	.phys_io	= KIRKWOOD_REGS_PHYS_BASE,
+	.io_pg_offst	= ((KIRKWOOD_REGS_VIRT_BASE) >> 18) & 0xfffc,
+	.boot_params	= 0x00000100,
+	.init_machine	= netspace_v2_init,
+	.map_io		= kirkwood_map_io,
+	.init_irq	= kirkwood_init_irq,
+	.timer		= &netspace_v2_timer,
+MACHINE_END
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index e6d8e10..8a0837e 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -110,6 +110,8 @@
 	bool "CompuLab CM-X300 modules"
 	select PXA3xx
 	select CPU_PXA300
+	select CPU_PXA310
+	select HAVE_PWM
 
 config ARCH_GUMSTIX
 	bool "Gumstix XScale 255 boards"
@@ -240,7 +242,6 @@
 	select PXA3xx
 	select CPU_PXA300
 	select CPU_PXA310
-	select HAVE_PWM
 
 config MACH_COLIBRI320
 	bool "Toradex Colibri PXA320"
diff --git a/arch/arm/mach-pxa/devices.c b/arch/arm/mach-pxa/devices.c
index 3395463..8e10db1 100644
--- a/arch/arm/mach-pxa/devices.c
+++ b/arch/arm/mach-pxa/devices.c
@@ -4,7 +4,6 @@
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
-#include <mach/hardware.h>
 #include <mach/udc.h>
 #include <mach/pxafb.h>
 #include <mach/mmc.h>
@@ -14,6 +13,7 @@
 #include <mach/pxa2xx_spi.h>
 #include <mach/camera.h>
 #include <mach/audio.h>
+#include <mach/hardware.h>
 #include <plat/i2c.h>
 #include <plat/pxa3xx_nand.h>
 
diff --git a/arch/arm/mach-s3c2410/include/mach/spi.h b/arch/arm/mach-s3c2410/include/mach/spi.h
index 193b39d..4d95883 100644
--- a/arch/arm/mach-s3c2410/include/mach/spi.h
+++ b/arch/arm/mach-s3c2410/include/mach/spi.h
@@ -18,6 +18,8 @@
 	unsigned int		 num_cs;	/* total chipselects */
 	int			 bus_num;       /* bus number to use. */
 
+	unsigned int		 use_fiq:1;	/* use fiq */
+
 	void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable);
 	void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
 };
diff --git a/arch/arm/mm/cache-fa.S b/arch/arm/mm/cache-fa.S
index b63a8f7..a89444a 100644
--- a/arch/arm/mm/cache-fa.S
+++ b/arch/arm/mm/cache-fa.S
@@ -127,15 +127,16 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(kaddr)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure that the data held in the page kaddr is written back
  *	to the page in question.
  *
- *	- kaddr   - kernel address (guaranteed to be page aligned)
+ *	- addr	- kernel address
+ *	- size	- size of region
  */
-ENTRY(fa_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(fa_flush_kern_dcache_area)
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean & invalidate D line
 	add	r0, r0, #CACHE_DLINESIZE
 	cmp	r0, r1
@@ -213,7 +214,7 @@
 	.long	fa_flush_user_cache_range
 	.long	fa_coherent_kern_range
 	.long	fa_coherent_user_range
-	.long	fa_flush_kern_dcache_page
+	.long	fa_flush_kern_dcache_area
 	.long	fa_dma_inv_range
 	.long	fa_dma_clean_range
 	.long	fa_dma_flush_range
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 747f9a9..cb8fc65 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -28,69 +28,120 @@
 static void __iomem *l2x0_base;
 static DEFINE_SPINLOCK(l2x0_lock);
 
-static inline void sync_writel(unsigned long val, unsigned long reg,
-			       unsigned long complete_mask)
+static inline void cache_wait(void __iomem *reg, unsigned long mask)
 {
-	unsigned long flags;
-
-	spin_lock_irqsave(&l2x0_lock, flags);
-	writel(val, l2x0_base + reg);
 	/* wait for the operation to complete */
-	while (readl(l2x0_base + reg) & complete_mask)
+	while (readl(reg) & mask)
 		;
-	spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
 static inline void cache_sync(void)
 {
-	sync_writel(0, L2X0_CACHE_SYNC, 1);
+	void __iomem *base = l2x0_base;
+	writel(0, base + L2X0_CACHE_SYNC);
+	cache_wait(base + L2X0_CACHE_SYNC, 1);
 }
 
 static inline void l2x0_inv_all(void)
 {
+	unsigned long flags;
+
 	/* invalidate all ways */
-	sync_writel(0xff, L2X0_INV_WAY, 0xff);
+	spin_lock_irqsave(&l2x0_lock, flags);
+	writel(0xff, l2x0_base + L2X0_INV_WAY);
+	cache_wait(l2x0_base + L2X0_INV_WAY, 0xff);
 	cache_sync();
+	spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
 static void l2x0_inv_range(unsigned long start, unsigned long end)
 {
-	unsigned long addr;
+	void __iomem *base = l2x0_base;
+	unsigned long flags;
 
+	spin_lock_irqsave(&l2x0_lock, flags);
 	if (start & (CACHE_LINE_SIZE - 1)) {
 		start &= ~(CACHE_LINE_SIZE - 1);
-		sync_writel(start, L2X0_CLEAN_INV_LINE_PA, 1);
+		cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
+		writel(start, base + L2X0_CLEAN_INV_LINE_PA);
 		start += CACHE_LINE_SIZE;
 	}
 
 	if (end & (CACHE_LINE_SIZE - 1)) {
 		end &= ~(CACHE_LINE_SIZE - 1);
-		sync_writel(end, L2X0_CLEAN_INV_LINE_PA, 1);
+		cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
+		writel(end, base + L2X0_CLEAN_INV_LINE_PA);
 	}
 
-	for (addr = start; addr < end; addr += CACHE_LINE_SIZE)
-		sync_writel(addr, L2X0_INV_LINE_PA, 1);
+	while (start < end) {
+		unsigned long blk_end = start + min(end - start, 4096UL);
+
+		while (start < blk_end) {
+			cache_wait(base + L2X0_INV_LINE_PA, 1);
+			writel(start, base + L2X0_INV_LINE_PA);
+			start += CACHE_LINE_SIZE;
+		}
+
+		if (blk_end < end) {
+			spin_unlock_irqrestore(&l2x0_lock, flags);
+			spin_lock_irqsave(&l2x0_lock, flags);
+		}
+	}
+	cache_wait(base + L2X0_INV_LINE_PA, 1);
 	cache_sync();
+	spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
 static void l2x0_clean_range(unsigned long start, unsigned long end)
 {
-	unsigned long addr;
+	void __iomem *base = l2x0_base;
+	unsigned long flags;
 
+	spin_lock_irqsave(&l2x0_lock, flags);
 	start &= ~(CACHE_LINE_SIZE - 1);
-	for (addr = start; addr < end; addr += CACHE_LINE_SIZE)
-		sync_writel(addr, L2X0_CLEAN_LINE_PA, 1);
+	while (start < end) {
+		unsigned long blk_end = start + min(end - start, 4096UL);
+
+		while (start < blk_end) {
+			cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
+			writel(start, base + L2X0_CLEAN_LINE_PA);
+			start += CACHE_LINE_SIZE;
+		}
+
+		if (blk_end < end) {
+			spin_unlock_irqrestore(&l2x0_lock, flags);
+			spin_lock_irqsave(&l2x0_lock, flags);
+		}
+	}
+	cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
 	cache_sync();
+	spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
 static void l2x0_flush_range(unsigned long start, unsigned long end)
 {
-	unsigned long addr;
+	void __iomem *base = l2x0_base;
+	unsigned long flags;
 
+	spin_lock_irqsave(&l2x0_lock, flags);
 	start &= ~(CACHE_LINE_SIZE - 1);
-	for (addr = start; addr < end; addr += CACHE_LINE_SIZE)
-		sync_writel(addr, L2X0_CLEAN_INV_LINE_PA, 1);
+	while (start < end) {
+		unsigned long blk_end = start + min(end - start, 4096UL);
+
+		while (start < blk_end) {
+			cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
+			writel(start, base + L2X0_CLEAN_INV_LINE_PA);
+			start += CACHE_LINE_SIZE;
+		}
+
+		if (blk_end < end) {
+			spin_unlock_irqrestore(&l2x0_lock, flags);
+			spin_lock_irqsave(&l2x0_lock, flags);
+		}
+	}
+	cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
 	cache_sync();
+	spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
 void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
diff --git a/arch/arm/mm/cache-v3.S b/arch/arm/mm/cache-v3.S
index 8a4abeb..2a48273 100644
--- a/arch/arm/mm/cache-v3.S
+++ b/arch/arm/mm/cache-v3.S
@@ -72,14 +72,15 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *page, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(v3_flush_kern_dcache_page)
+ENTRY(v3_flush_kern_dcache_area)
 	/* FALLTHROUGH */
 
 /*
@@ -129,7 +130,7 @@
 	.long	v3_flush_user_cache_range
 	.long	v3_coherent_kern_range
 	.long	v3_coherent_user_range
-	.long	v3_flush_kern_dcache_page
+	.long	v3_flush_kern_dcache_area
 	.long	v3_dma_inv_range
 	.long	v3_dma_clean_range
 	.long	v3_dma_flush_range
diff --git a/arch/arm/mm/cache-v4.S b/arch/arm/mm/cache-v4.S
index 3668611..5c7da3e 100644
--- a/arch/arm/mm/cache-v4.S
+++ b/arch/arm/mm/cache-v4.S
@@ -82,14 +82,15 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(v4_flush_kern_dcache_page)
+ENTRY(v4_flush_kern_dcache_area)
 	/* FALLTHROUGH */
 
 /*
@@ -141,7 +142,7 @@
 	.long	v4_flush_user_cache_range
 	.long	v4_coherent_kern_range
 	.long	v4_coherent_user_range
-	.long	v4_flush_kern_dcache_page
+	.long	v4_flush_kern_dcache_area
 	.long	v4_dma_inv_range
 	.long	v4_dma_clean_range
 	.long	v4_dma_flush_range
diff --git a/arch/arm/mm/cache-v4wb.S b/arch/arm/mm/cache-v4wb.S
index 2ebc1b3..3dbedf1e 100644
--- a/arch/arm/mm/cache-v4wb.S
+++ b/arch/arm/mm/cache-v4wb.S
@@ -114,15 +114,16 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(v4wb_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(v4wb_flush_kern_dcache_area)
+	add	r1, r0, r1
 	/* fall through */
 
 /*
@@ -224,7 +225,7 @@
 	.long	v4wb_flush_user_cache_range
 	.long	v4wb_coherent_kern_range
 	.long	v4wb_coherent_user_range
-	.long	v4wb_flush_kern_dcache_page
+	.long	v4wb_flush_kern_dcache_area
 	.long	v4wb_dma_inv_range
 	.long	v4wb_dma_clean_range
 	.long	v4wb_dma_flush_range
diff --git a/arch/arm/mm/cache-v4wt.S b/arch/arm/mm/cache-v4wt.S
index c54fa2c..b3b7410 100644
--- a/arch/arm/mm/cache-v4wt.S
+++ b/arch/arm/mm/cache-v4wt.S
@@ -117,17 +117,18 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(v4wt_flush_kern_dcache_page)
+ENTRY(v4wt_flush_kern_dcache_area)
 	mov	r2, #0
 	mcr	p15, 0, r2, c7, c5, 0		@ invalidate I cache
-	add	r1, r0, #PAGE_SZ
+	add	r1, r0, r1
 	/* fallthrough */
 
 /*
@@ -180,7 +181,7 @@
 	.long	v4wt_flush_user_cache_range
 	.long	v4wt_coherent_kern_range
 	.long	v4wt_coherent_user_range
-	.long	v4wt_flush_kern_dcache_page
+	.long	v4wt_flush_kern_dcache_area
 	.long	v4wt_dma_inv_range
 	.long	v4wt_dma_clean_range
 	.long	v4wt_dma_flush_range
diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S
index 295e25d..4ba0a24 100644
--- a/arch/arm/mm/cache-v6.S
+++ b/arch/arm/mm/cache-v6.S
@@ -159,15 +159,16 @@
 ENDPROC(v6_coherent_kern_range)
 
 /*
- *	v6_flush_kern_dcache_page(kaddr)
+ *	v6_flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure that the data held in the page kaddr is written back
  *	to the page in question.
  *
- *	- kaddr   - kernel address (guaranteed to be page aligned)
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(v6_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(v6_flush_kern_dcache_area)
+	add	r1, r0, r1
 1:
 #ifdef HARVARD_CACHE
 	mcr	p15, 0, r0, c7, c14, 1		@ clean & invalidate D line
@@ -271,7 +272,7 @@
 	.long	v6_flush_user_cache_range
 	.long	v6_coherent_kern_range
 	.long	v6_coherent_user_range
-	.long	v6_flush_kern_dcache_page
+	.long	v6_flush_kern_dcache_area
 	.long	v6_dma_inv_range
 	.long	v6_dma_clean_range
 	.long	v6_dma_flush_range
diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S
index e1bd975..9073db8 100644
--- a/arch/arm/mm/cache-v7.S
+++ b/arch/arm/mm/cache-v7.S
@@ -186,16 +186,17 @@
 ENDPROC(v7_coherent_user_range)
 
 /*
- *	v7_flush_kern_dcache_page(kaddr)
+ *	v7_flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure that the data held in the page kaddr is written back
  *	to the page in question.
  *
- *	- kaddr   - kernel address (guaranteed to be page aligned)
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(v7_flush_kern_dcache_page)
+ENTRY(v7_flush_kern_dcache_area)
 	dcache_line_size r2, r3
-	add	r1, r0, #PAGE_SZ
+	add	r1, r0, r1
 1:
 	mcr	p15, 0, r0, c7, c14, 1		@ clean & invalidate D line / unified line
 	add	r0, r0, r2
@@ -203,7 +204,7 @@
 	blo	1b
 	dsb
 	mov	pc, lr
-ENDPROC(v7_flush_kern_dcache_page)
+ENDPROC(v7_flush_kern_dcache_area)
 
 /*
  *	v7_dma_inv_range(start,end)
@@ -279,7 +280,7 @@
 	.long	v7_flush_user_cache_range
 	.long	v7_coherent_kern_range
 	.long	v7_coherent_user_range
-	.long	v7_flush_kern_dcache_page
+	.long	v7_flush_kern_dcache_area
 	.long	v7_dma_inv_range
 	.long	v7_dma_clean_range
 	.long	v7_dma_flush_range
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index 329594e..6f3a4b7 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -131,7 +131,7 @@
 	 */
 	if (addr)
 #endif
-		__cpuc_flush_dcache_page(addr);
+		__cpuc_flush_dcache_area(addr, PAGE_SIZE);
 
 	/*
 	 * If this is a page cache page, and we have an aliasing VIPT cache,
@@ -258,5 +258,5 @@
 	 * in this mapping of the page.  FIXME: this is overkill
 	 * since we actually ask for a write-back and invalidate.
 	 */
-	__cpuc_flush_dcache_page(page_address(page));
+	__cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
 }
diff --git a/arch/arm/mm/highmem.c b/arch/arm/mm/highmem.c
index 30f82fb..2be1ec7 100644
--- a/arch/arm/mm/highmem.c
+++ b/arch/arm/mm/highmem.c
@@ -79,7 +79,7 @@
 	unsigned int idx = type + KM_TYPE_NR * smp_processor_id();
 
 	if (kvaddr >= (void *)FIXADDR_START) {
-		__cpuc_flush_dcache_page((void *)vaddr);
+		__cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE);
 #ifdef CONFIG_DEBUG_HIGHMEM
 		BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
 		set_pte_ext(TOP_PTE(vaddr), __pte(0), 0);
diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c
index 900811c..374a831 100644
--- a/arch/arm/mm/nommu.c
+++ b/arch/arm/mm/nommu.c
@@ -61,7 +61,7 @@
 
 void flush_dcache_page(struct page *page)
 {
-	__cpuc_flush_dcache_page(page_address(page));
+	__cpuc_flush_dcache_area(page_address(page), PAGE_SIZE);
 }
 EXPORT_SYMBOL(flush_dcache_page);
 
diff --git a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S
index d9fb4b9..8012e24 100644
--- a/arch/arm/mm/proc-arm1020.S
+++ b/arch/arm/mm/proc-arm1020.S
@@ -231,17 +231,18 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- page	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(arm1020_flush_kern_dcache_page)
+ENTRY(arm1020_flush_kern_dcache_area)
 	mov	ip, #0
 #ifndef CONFIG_CPU_DCACHE_DISABLE
-	add	r1, r0, #PAGE_SZ
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
 	mcr	p15, 0, ip, c7, c10, 4		@ drain WB
 	add	r0, r0, #CACHE_DLINESIZE
@@ -335,7 +336,7 @@
 	.long	arm1020_flush_user_cache_range
 	.long	arm1020_coherent_kern_range
 	.long	arm1020_coherent_user_range
-	.long	arm1020_flush_kern_dcache_page
+	.long	arm1020_flush_kern_dcache_area
 	.long	arm1020_dma_inv_range
 	.long	arm1020_dma_clean_range
 	.long	arm1020_dma_flush_range
diff --git a/arch/arm/mm/proc-arm1020e.S b/arch/arm/mm/proc-arm1020e.S
index 7453b75..41fe25d 100644
--- a/arch/arm/mm/proc-arm1020e.S
+++ b/arch/arm/mm/proc-arm1020e.S
@@ -225,17 +225,18 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- page	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(arm1020e_flush_kern_dcache_page)
+ENTRY(arm1020e_flush_kern_dcache_area)
 	mov	ip, #0
 #ifndef CONFIG_CPU_DCACHE_DISABLE
-	add	r1, r0, #PAGE_SZ
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
 	add	r0, r0, #CACHE_DLINESIZE
 	cmp	r0, r1
@@ -321,7 +322,7 @@
 	.long	arm1020e_flush_user_cache_range
 	.long	arm1020e_coherent_kern_range
 	.long	arm1020e_coherent_user_range
-	.long	arm1020e_flush_kern_dcache_page
+	.long	arm1020e_flush_kern_dcache_area
 	.long	arm1020e_dma_inv_range
 	.long	arm1020e_dma_clean_range
 	.long	arm1020e_dma_flush_range
diff --git a/arch/arm/mm/proc-arm1022.S b/arch/arm/mm/proc-arm1022.S
index 8eb72d7..20a5b1b 100644
--- a/arch/arm/mm/proc-arm1022.S
+++ b/arch/arm/mm/proc-arm1022.S
@@ -214,17 +214,18 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- page	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(arm1022_flush_kern_dcache_page)
+ENTRY(arm1022_flush_kern_dcache_area)
 	mov	ip, #0
 #ifndef CONFIG_CPU_DCACHE_DISABLE
-	add	r1, r0, #PAGE_SZ
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
 	add	r0, r0, #CACHE_DLINESIZE
 	cmp	r0, r1
@@ -310,7 +311,7 @@
 	.long	arm1022_flush_user_cache_range
 	.long	arm1022_coherent_kern_range
 	.long	arm1022_coherent_user_range
-	.long	arm1022_flush_kern_dcache_page
+	.long	arm1022_flush_kern_dcache_area
 	.long	arm1022_dma_inv_range
 	.long	arm1022_dma_clean_range
 	.long	arm1022_dma_flush_range
diff --git a/arch/arm/mm/proc-arm1026.S b/arch/arm/mm/proc-arm1026.S
index 3b59f0d..96aedb1 100644
--- a/arch/arm/mm/proc-arm1026.S
+++ b/arch/arm/mm/proc-arm1026.S
@@ -208,17 +208,18 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- page	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(arm1026_flush_kern_dcache_page)
+ENTRY(arm1026_flush_kern_dcache_area)
 	mov	ip, #0
 #ifndef CONFIG_CPU_DCACHE_DISABLE
-	add	r1, r0, #PAGE_SZ
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
 	add	r0, r0, #CACHE_DLINESIZE
 	cmp	r0, r1
@@ -304,7 +305,7 @@
 	.long	arm1026_flush_user_cache_range
 	.long	arm1026_coherent_kern_range
 	.long	arm1026_coherent_user_range
-	.long	arm1026_flush_kern_dcache_page
+	.long	arm1026_flush_kern_dcache_area
 	.long	arm1026_dma_inv_range
 	.long	arm1026_dma_clean_range
 	.long	arm1026_dma_flush_range
diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S
index 2b7c197..471669e 100644
--- a/arch/arm/mm/proc-arm920.S
+++ b/arch/arm/mm/proc-arm920.S
@@ -207,15 +207,16 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(arm920_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(arm920_flush_kern_dcache_area)
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
 	add	r0, r0, #CACHE_DLINESIZE
 	cmp	r0, r1
@@ -293,7 +294,7 @@
 	.long	arm920_flush_user_cache_range
 	.long	arm920_coherent_kern_range
 	.long	arm920_coherent_user_range
-	.long	arm920_flush_kern_dcache_page
+	.long	arm920_flush_kern_dcache_area
 	.long	arm920_dma_inv_range
 	.long	arm920_dma_clean_range
 	.long	arm920_dma_flush_range
diff --git a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S
index 06a1aa4..ee111b0 100644
--- a/arch/arm/mm/proc-arm922.S
+++ b/arch/arm/mm/proc-arm922.S
@@ -209,15 +209,16 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(arm922_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(arm922_flush_kern_dcache_area)
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
 	add	r0, r0, #CACHE_DLINESIZE
 	cmp	r0, r1
@@ -295,7 +296,7 @@
 	.long	arm922_flush_user_cache_range
 	.long	arm922_coherent_kern_range
 	.long	arm922_coherent_user_range
-	.long	arm922_flush_kern_dcache_page
+	.long	arm922_flush_kern_dcache_area
 	.long	arm922_dma_inv_range
 	.long	arm922_dma_clean_range
 	.long	arm922_dma_flush_range
diff --git a/arch/arm/mm/proc-arm925.S b/arch/arm/mm/proc-arm925.S
index cb53435..8deb5bd 100644
--- a/arch/arm/mm/proc-arm925.S
+++ b/arch/arm/mm/proc-arm925.S
@@ -251,15 +251,16 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(arm925_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(arm925_flush_kern_dcache_area)
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
 	add	r0, r0, #CACHE_DLINESIZE
 	cmp	r0, r1
@@ -346,7 +347,7 @@
 	.long	arm925_flush_user_cache_range
 	.long	arm925_coherent_kern_range
 	.long	arm925_coherent_user_range
-	.long	arm925_flush_kern_dcache_page
+	.long	arm925_flush_kern_dcache_area
 	.long	arm925_dma_inv_range
 	.long	arm925_dma_clean_range
 	.long	arm925_dma_flush_range
diff --git a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S
index 1c48487..64db6e2 100644
--- a/arch/arm/mm/proc-arm926.S
+++ b/arch/arm/mm/proc-arm926.S
@@ -214,15 +214,16 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(arm926_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(arm926_flush_kern_dcache_area)
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
 	add	r0, r0, #CACHE_DLINESIZE
 	cmp	r0, r1
@@ -309,7 +310,7 @@
 	.long	arm926_flush_user_cache_range
 	.long	arm926_coherent_kern_range
 	.long	arm926_coherent_user_range
-	.long	arm926_flush_kern_dcache_page
+	.long	arm926_flush_kern_dcache_area
 	.long	arm926_dma_inv_range
 	.long	arm926_dma_clean_range
 	.long	arm926_dma_flush_range
diff --git a/arch/arm/mm/proc-arm940.S b/arch/arm/mm/proc-arm940.S
index 5b0f846..8196b9f 100644
--- a/arch/arm/mm/proc-arm940.S
+++ b/arch/arm/mm/proc-arm940.S
@@ -141,14 +141,15 @@
 	/* FALLTHROUGH */
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(arm940_flush_kern_dcache_page)
+ENTRY(arm940_flush_kern_dcache_area)
 	mov	ip, #0
 	mov	r1, #(CACHE_DSEGMENTS - 1) << 4	@ 4 segments
 1:	orr	r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries
@@ -238,7 +239,7 @@
 	.long	arm940_flush_user_cache_range
 	.long	arm940_coherent_kern_range
 	.long	arm940_coherent_user_range
-	.long	arm940_flush_kern_dcache_page
+	.long	arm940_flush_kern_dcache_area
 	.long	arm940_dma_inv_range
 	.long	arm940_dma_clean_range
 	.long	arm940_dma_flush_range
diff --git a/arch/arm/mm/proc-arm946.S b/arch/arm/mm/proc-arm946.S
index 40c0449..9a95123 100644
--- a/arch/arm/mm/proc-arm946.S
+++ b/arch/arm/mm/proc-arm946.S
@@ -183,16 +183,17 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  * (same as arm926)
  */
-ENTRY(arm946_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(arm946_flush_kern_dcache_area)
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
 	add	r0, r0, #CACHE_DLINESIZE
 	cmp	r0, r1
@@ -280,7 +281,7 @@
 	.long	arm946_flush_user_cache_range
 	.long	arm946_coherent_kern_range
 	.long	arm946_coherent_user_range
-	.long	arm946_flush_kern_dcache_page
+	.long	arm946_flush_kern_dcache_area
 	.long	arm946_dma_inv_range
 	.long	arm946_dma_clean_range
 	.long	arm946_dma_flush_range
diff --git a/arch/arm/mm/proc-feroceon.S b/arch/arm/mm/proc-feroceon.S
index d0d7795..dbc3938 100644
--- a/arch/arm/mm/proc-feroceon.S
+++ b/arch/arm/mm/proc-feroceon.S
@@ -226,16 +226,17 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
 	.align	5
-ENTRY(feroceon_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(feroceon_flush_kern_dcache_area)
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
 	add	r0, r0, #CACHE_DLINESIZE
 	cmp	r0, r1
@@ -246,7 +247,7 @@
 	mov	pc, lr
 
 	.align	5
-ENTRY(feroceon_range_flush_kern_dcache_page)
+ENTRY(feroceon_range_flush_kern_dcache_area)
 	mrs	r2, cpsr
 	add	r1, r0, #PAGE_SZ - CACHE_DLINESIZE	@ top addr is inclusive
 	orr	r3, r2, #PSR_I_BIT
@@ -372,7 +373,7 @@
 	.long	feroceon_flush_user_cache_range
 	.long	feroceon_coherent_kern_range
 	.long	feroceon_coherent_user_range
-	.long	feroceon_flush_kern_dcache_page
+	.long	feroceon_flush_kern_dcache_area
 	.long	feroceon_dma_inv_range
 	.long	feroceon_dma_clean_range
 	.long	feroceon_dma_flush_range
@@ -383,7 +384,7 @@
 	.long	feroceon_flush_user_cache_range
 	.long	feroceon_coherent_kern_range
 	.long	feroceon_coherent_user_range
-	.long	feroceon_range_flush_kern_dcache_page
+	.long	feroceon_range_flush_kern_dcache_area
 	.long	feroceon_range_dma_inv_range
 	.long	feroceon_range_dma_clean_range
 	.long	feroceon_range_dma_flush_range
diff --git a/arch/arm/mm/proc-mohawk.S b/arch/arm/mm/proc-mohawk.S
index 52b5fd7..9674d36 100644
--- a/arch/arm/mm/proc-mohawk.S
+++ b/arch/arm/mm/proc-mohawk.S
@@ -186,15 +186,16 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(mohawk_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(mohawk_flush_kern_dcache_area)
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean+invalidate D entry
 	add	r0, r0, #CACHE_DLINESIZE
 	cmp	r0, r1
@@ -273,7 +274,7 @@
 	.long	mohawk_flush_user_cache_range
 	.long	mohawk_coherent_kern_range
 	.long	mohawk_coherent_user_range
-	.long	mohawk_flush_kern_dcache_page
+	.long	mohawk_flush_kern_dcache_area
 	.long	mohawk_dma_inv_range
 	.long	mohawk_dma_clean_range
 	.long	mohawk_dma_flush_range
diff --git a/arch/arm/mm/proc-syms.c b/arch/arm/mm/proc-syms.c
index ac5c800..3e6210b 100644
--- a/arch/arm/mm/proc-syms.c
+++ b/arch/arm/mm/proc-syms.c
@@ -27,8 +27,7 @@
 EXPORT_SYMBOL(__cpuc_flush_user_all);
 EXPORT_SYMBOL(__cpuc_flush_user_range);
 EXPORT_SYMBOL(__cpuc_coherent_kern_range);
-EXPORT_SYMBOL(__cpuc_flush_dcache_page);
-EXPORT_SYMBOL(dmac_inv_range);  /* because of flush_ioremap_region() */
+EXPORT_SYMBOL(__cpuc_flush_dcache_area);
 #else
 EXPORT_SYMBOL(cpu_cache);
 #endif
diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S
index 5485c82..395cc90 100644
--- a/arch/arm/mm/proc-v6.S
+++ b/arch/arm/mm/proc-v6.S
@@ -254,10 +254,9 @@
 	.long	0x560f5810
 	.long	0xff0ffff0
 	.long   PMD_TYPE_SECT | \
-		PMD_SECT_BUFFERABLE | \
-		PMD_SECT_CACHEABLE | \
 		PMD_SECT_AP_WRITE | \
-		PMD_SECT_AP_READ
+		PMD_SECT_AP_READ | \
+		PMD_FLAGS
 	.long   PMD_TYPE_SECT | \
 		PMD_SECT_XN | \
 		PMD_SECT_AP_WRITE | \
diff --git a/arch/arm/mm/proc-xsc3.S b/arch/arm/mm/proc-xsc3.S
index fab134e..96456f5 100644
--- a/arch/arm/mm/proc-xsc3.S
+++ b/arch/arm/mm/proc-xsc3.S
@@ -226,15 +226,16 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache.
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(xsc3_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(xsc3_flush_kern_dcache_area)
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c14, 1		@ clean/invalidate L1 D line
 	add	r0, r0, #CACHELINESIZE
 	cmp	r0, r1
@@ -309,7 +310,7 @@
 	.long	xsc3_flush_user_cache_range
 	.long	xsc3_coherent_kern_range
 	.long	xsc3_coherent_user_range
-	.long	xsc3_flush_kern_dcache_page
+	.long	xsc3_flush_kern_dcache_area
 	.long	xsc3_dma_inv_range
 	.long	xsc3_dma_clean_range
 	.long	xsc3_dma_flush_range
diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S
index f056c28..93df472 100644
--- a/arch/arm/mm/proc-xscale.S
+++ b/arch/arm/mm/proc-xscale.S
@@ -284,15 +284,16 @@
 	mov	pc, lr
 
 /*
- *	flush_kern_dcache_page(void *page)
+ *	flush_kern_dcache_area(void *addr, size_t size)
  *
  *	Ensure no D cache aliasing occurs, either with itself or
  *	the I cache
  *
- *	- addr	- page aligned address
+ *	- addr	- kernel address
+ *	- size	- region size
  */
-ENTRY(xscale_flush_kern_dcache_page)
-	add	r1, r0, #PAGE_SZ
+ENTRY(xscale_flush_kern_dcache_area)
+	add	r1, r0, r1
 1:	mcr	p15, 0, r0, c7, c10, 1		@ clean D entry
 	mcr	p15, 0, r0, c7, c6, 1		@ invalidate D entry
 	add	r0, r0, #CACHELINESIZE
@@ -368,7 +369,7 @@
 	.long	xscale_flush_user_cache_range
 	.long	xscale_coherent_kern_range
 	.long	xscale_coherent_user_range
-	.long	xscale_flush_kern_dcache_page
+	.long	xscale_flush_kern_dcache_area
 	.long	xscale_dma_inv_range
 	.long	xscale_dma_clean_range
 	.long	xscale_dma_flush_range
@@ -392,7 +393,7 @@
 	.long	xscale_flush_user_cache_range
 	.long	xscale_coherent_kern_range
 	.long	xscale_coherent_user_range
-	.long	xscale_flush_kern_dcache_page
+	.long	xscale_flush_kern_dcache_area
 	.long	xscale_dma_flush_range
 	.long	xscale_dma_clean_range
 	.long	xscale_dma_flush_range
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index 07b976d..c3a74ce 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -12,7 +12,7 @@
 #
 #   http://www.arm.linux.org.uk/developer/machines/?action=new
 #
-# Last update: Wed Nov 25 22:14:58 2009
+# Last update: Wed Dec 16 20:06:34 2009
 #
 # machine_is_xxx	CONFIG_xxxx		MACH_TYPE_xxx		number
 #
@@ -1776,6 +1776,7 @@
 wdg002			MACH_WDG002		WDG002			1785
 sg560adsl		MACH_SG560ADSL		SG560ADSL		1786
 nextio_n2800_ica	MACH_NEXTIO_N2800_ICA	NEXTIO_N2800_ICA	1787
+dove_db			MACH_DOVE_DB		DOVE_DB			1788
 marvell_newdb		MACH_MARVELL_NEWDB	MARVELL_NEWDB		1789
 vandihud		MACH_VANDIHUD		VANDIHUD		1790
 magx_e8			MACH_MAGX_E8		MAGX_E8			1791
@@ -2536,3 +2537,44 @@
 mxt_td60		MACH_MXT_TD60		MXT_TD60		2550
 esyx			MACH_ESYX		ESYX			2551
 bulldog			MACH_BULLDOG		BULLDOG			2553
+derell_me2000		MACH_DERELL_ME2000	DERELL_ME2000		2554
+bcmring_base		MACH_BCMRING_BASE	BCMRING_BASE		2555
+bcmring_evm		MACH_BCMRING_EVM	BCMRING_EVM		2556
+bcmring_evm_jazz	MACH_BCMRING_EVM_JAZZ	BCMRING_EVM_JAZZ	2557
+bcmring_sp		MACH_BCMRING_SP		BCMRING_SP		2558
+bcmring_sv		MACH_BCMRING_SV		BCMRING_SV		2559
+bcmring_sv_jazz		MACH_BCMRING_SV_JAZZ	BCMRING_SV_JAZZ		2560
+bcmring_tablet		MACH_BCMRING_TABLET	BCMRING_TABLET		2561
+bcmring_vp		MACH_BCMRING_VP		BCMRING_VP		2562
+bcmring_evm_seikor	MACH_BCMRING_EVM_SEIKOR	BCMRING_EVM_SEIKOR	2563
+bcmring_sp_wqvga	MACH_BCMRING_SP_WQVGA	BCMRING_SP_WQVGA	2564
+bcmring_custom		MACH_BCMRING_CUSTOM	BCMRING_CUSTOM		2565
+acer_s200		MACH_ACER_S200		ACER_S200		2566
+bt270			MACH_BT270		BT270			2567
+iseo			MACH_ISEO		ISEO			2568
+cezanne			MACH_CEZANNE		CEZANNE			2569
+lucca			MACH_LUCCA		LUCCA			2570
+supersmart		MACH_SUPERSMART		SUPERSMART		2571
+magnolia2		MACH_MAGNOLIA2		MAGNOLIA2		2573
+emxx			MACH_EMXX		EMXX			2574
+outlaw			MACH_OUTLAW		OUTLAW			2575
+riot_bei2		MACH_RIOT_BEI2		RIOT_BEI2		2576
+riot_vox		MACH_RIOT_VOX		RIOT_VOX		2577
+riot_x37		MACH_RIOT_X37		RIOT_X37		2578
+mega25mx		MACH_MEGA25MX		MEGA25MX		2579
+benzina2		MACH_BENZINA2		BENZINA2		2580
+ignite			MACH_IGNITE		IGNITE			2581
+foggia			MACH_FOGGIA		FOGGIA			2582
+arezzo			MACH_AREZZO		AREZZO			2583
+leica_skywalker		MACH_LEICA_SKYWALKER	LEICA_SKYWALKER		2584
+jacinto2_jamr		MACH_JACINTO2_JAMR	JACINTO2_JAMR		2585
+gts_nova		MACH_GTS_NOVA		GTS_NOVA		2586
+p3600			MACH_P3600		P3600			2587
+dlt2			MACH_DLT2		DLT2			2588
+df3120			MACH_DF3120		DF3120			2589
+ecucore_9g20		MACH_ECUCORE_9G20	ECUCORE_9G20		2590
+nautel_lpc3240		MACH_NAUTEL_LPC3240	NAUTEL_LPC3240		2591
+glacier			MACH_GLACIER		GLACIER			2592
+phrazer_bulldog		MACH_PHRAZER_BULLDOG	PHRAZER_BULLDOG		2593
+omap3_bulldog		MACH_OMAP3_BULLDOG	OMAP3_BULLDOG		2594
+pca101			MACH_PCA101		PCA101			2595
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 95ccbe3..bf28945 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -998,6 +998,23 @@
 	  will be called lis3lv02d and a specific module for the SPI transport
 	  is called lis3lv02d_spi.
 
+config SENSORS_LIS3_I2C
+	tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (I2C)"
+	depends on I2C && INPUT
+	select INPUT_POLLDEV
+	default n
+	help
+	  This driver provides support for the LIS3LV02Dx accelerometer connected
+	  via I2C. The accelerometer data is readable via
+	  /sys/devices/platform/lis3lv02d.
+
+	  This driver also provides an absolute input class device, allowing
+	  the device to act as a pinball machine-esque joystick.
+
+	  This driver can also be built as modules.  If so, the core module
+	  will be called lis3lv02d and a specific module for the I2C transport
+	  is called lis3lv02d_i2c.
+
 config SENSORS_APPLESMC
 	tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
 	depends on INPUT && X86
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 33c2ee1..4131e25 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -55,6 +55,7 @@
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
 obj-$(CONFIG_SENSORS_LIS3_SPI)	+= lis3lv02d.o lis3lv02d_spi.o
+obj-$(CONFIG_SENSORS_LIS3_I2C)	+= lis3lv02d.o lis3lv02d_i2c.o
 obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
 obj-$(CONFIG_SENSORS_LM70)	+= lm70.o
 obj-$(CONFIG_SENSORS_LM73)	+= lm73.o
diff --git a/drivers/hwmon/lis3lv02d_i2c.c b/drivers/hwmon/lis3lv02d_i2c.c
new file mode 100644
index 0000000..dc1f540
--- /dev/null
+++ b/drivers/hwmon/lis3lv02d_i2c.c
@@ -0,0 +1,183 @@
+/*
+ * drivers/hwmon/lis3lv02d_i2c.c
+ *
+ * Implements I2C interface for lis3lv02d (STMicroelectronics) accelerometer.
+ * Driver is based on corresponding SPI driver written by Daniel Mack
+ * (lis3lv02d_spi.c (C) 2009 Daniel Mack <daniel@caiaq.de> ).
+ *
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include "lis3lv02d.h"
+
+#define DRV_NAME 	"lis3lv02d_i2c"
+
+static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value)
+{
+	struct i2c_client *c = lis3->bus_priv;
+	return i2c_smbus_write_byte_data(c, reg, value);
+}
+
+static inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v)
+{
+	struct i2c_client *c = lis3->bus_priv;
+	*v = i2c_smbus_read_byte_data(c, reg);
+	return 0;
+}
+
+static int lis3_i2c_init(struct lis3lv02d *lis3)
+{
+	u8 reg;
+	int ret;
+
+	/* power up the device */
+	ret = lis3->read(lis3, CTRL_REG1, &reg);
+	if (ret < 0)
+		return ret;
+
+	reg |= CTRL1_PD0;
+	return lis3->write(lis3, CTRL_REG1, reg);
+}
+
+/* Default axis mapping but it can be overwritten by platform data */
+static struct axis_conversion lis3lv02d_axis_map = { LIS3_DEV_X,
+						     LIS3_DEV_Y,
+						     LIS3_DEV_Z };
+
+static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
+
+	if (pdata) {
+		if (pdata->axis_x)
+			lis3lv02d_axis_map.x = pdata->axis_x;
+
+		if (pdata->axis_y)
+			lis3lv02d_axis_map.y = pdata->axis_y;
+
+		if (pdata->axis_z)
+			lis3lv02d_axis_map.z = pdata->axis_z;
+
+		if (pdata->setup_resources)
+			ret = pdata->setup_resources();
+
+		if (ret)
+			goto fail;
+	}
+
+	lis3_dev.pdata	  = pdata;
+	lis3_dev.bus_priv = client;
+	lis3_dev.init	  = lis3_i2c_init;
+	lis3_dev.read	  = lis3_i2c_read;
+	lis3_dev.write	  = lis3_i2c_write;
+	lis3_dev.irq	  = client->irq;
+	lis3_dev.ac	  = lis3lv02d_axis_map;
+
+	i2c_set_clientdata(client, &lis3_dev);
+	ret = lis3lv02d_init_device(&lis3_dev);
+fail:
+	return ret;
+}
+
+static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client)
+{
+	struct lis3lv02d *lis3 = i2c_get_clientdata(client);
+	struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
+
+	if (pdata && pdata->release_resources)
+		pdata->release_resources();
+
+	lis3lv02d_joystick_disable();
+	lis3lv02d_poweroff(lis3);
+
+	return lis3lv02d_remove_fs(&lis3_dev);
+}
+
+#ifdef CONFIG_PM
+static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	struct lis3lv02d *lis3 = i2c_get_clientdata(client);
+
+	if (!lis3->pdata->wakeup_flags)
+		lis3lv02d_poweroff(lis3);
+	return 0;
+}
+
+static int lis3lv02d_i2c_resume(struct i2c_client *client)
+{
+	struct lis3lv02d *lis3 = i2c_get_clientdata(client);
+
+	if (!lis3->pdata->wakeup_flags)
+		lis3lv02d_poweron(lis3);
+	return 0;
+}
+
+static void lis3lv02d_i2c_shutdown(struct i2c_client *client)
+{
+	lis3lv02d_i2c_suspend(client, PMSG_SUSPEND);
+}
+#else
+#define lis3lv02d_i2c_suspend	NULL
+#define lis3lv02d_i2c_resume	NULL
+#define lis3lv02d_i2c_shutdown	NULL
+#endif
+
+static const struct i2c_device_id lis3lv02d_id[] = {
+	{"lis3lv02d", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, lis3lv02d_id);
+
+static struct i2c_driver lis3lv02d_i2c_driver = {
+	.driver	 = {
+		.name   = DRV_NAME,
+		.owner  = THIS_MODULE,
+	},
+	.suspend = lis3lv02d_i2c_suspend,
+	.shutdown = lis3lv02d_i2c_shutdown,
+	.resume = lis3lv02d_i2c_resume,
+	.probe	= lis3lv02d_i2c_probe,
+	.remove	= __devexit_p(lis3lv02d_i2c_remove),
+	.id_table = lis3lv02d_id,
+};
+
+static int __init lis3lv02d_init(void)
+{
+	return i2c_add_driver(&lis3lv02d_i2c_driver);
+}
+
+static void __exit lis3lv02d_exit(void)
+{
+	i2c_del_driver(&lis3lv02d_i2c_driver);
+}
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("lis3lv02d I2C interface");
+MODULE_LICENSE("GPL");
+
+module_init(lis3lv02d_init);
+module_exit(lis3lv02d_exit);
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index e4f599f..8a0e1ec 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -229,6 +229,12 @@
 	help
 	  This option enables support for pwm driven LEDs
 
+config LEDS_REGULATOR
+	tristate "REGULATOR driven LED support"
+	depends on LEDS_CLASS && REGULATOR
+	help
+	  This option enables support for regulator driven LEDs.
+
 config LEDS_BD2802
 	tristate "LED driver for BD2802 RGB LED"
 	depends on LEDS_CLASS && I2C
@@ -236,6 +242,33 @@
 	  This option enables support for BD2802GU RGB LED driver chips
 	  accessed via the I2C bus.
 
+config LEDS_INTEL_SS4200
+	tristate "LED driver for Intel NAS SS4200 series"
+	depends on LEDS_CLASS && PCI && DMI
+	help
+	  This option enables support for the Intel SS4200 series of
+	  Network Attached Storage servers.  You may control the hard
+	  drive or power LEDs on the front panel.  Using this driver
+	  can stop the front LED from blinking after startup.
+
+config LEDS_LT3593
+	tristate "LED driver for LT3593 controllers"
+	depends on LEDS_CLASS && GENERIC_GPIO
+	help
+	  This option enables support for LEDs driven by a Linear Technology
+	  LT3593 controller. This controller uses a special one-wire pulse
+	  coding protocol to set the brightness.
+
+config LEDS_ADP5520
+	tristate "LED Support for ADP5520/ADP5501 PMIC"
+	depends on LEDS_CLASS && PMIC_ADP5520
+	help
+	  This option enables support for on-chip LED drivers found
+	  on Analog Devices ADP5520/ADP5501 PMICs.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called leds-adp5520.
+
 comment "LED Triggers"
 
 config LEDS_TRIGGERS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 46d7270..9e63869 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -29,6 +29,10 @@
 obj-$(CONFIG_LEDS_WM831X_STATUS)	+= leds-wm831x-status.o
 obj-$(CONFIG_LEDS_WM8350)		+= leds-wm8350.o
 obj-$(CONFIG_LEDS_PWM)			+= leds-pwm.o
+obj-$(CONFIG_LEDS_REGULATOR)		+= leds-regulator.o
+obj-$(CONFIG_LEDS_INTEL_SS4200)		+= leds-ss4200.o
+obj-$(CONFIG_LEDS_LT3593)		+= leds-lt3593.o
+obj-$(CONFIG_LEDS_ADP5520)		+= leds-adp5520.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)		+= leds-dac124s085.o
diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c
new file mode 100644
index 0000000..a8f3159
--- /dev/null
+++ b/drivers/leds/leds-adp5520.c
@@ -0,0 +1,230 @@
+/*
+ * LEDs driver for Analog Devices ADP5520/ADP5501 MFD PMICs
+ *
+ * Copyright 2009 Analog Devices Inc.
+ *
+ * Loosely derived from leds-da903x:
+ * Copyright (C) 2008 Compulab, Ltd.
+ * 	Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * 	Eric Miao <eric.miao@marvell.com>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/adp5520.h>
+
+struct adp5520_led {
+	struct led_classdev	cdev;
+	struct work_struct	work;
+	struct device		*master;
+	enum led_brightness	new_brightness;
+	int			id;
+	int			flags;
+};
+
+static void adp5520_led_work(struct work_struct *work)
+{
+	struct adp5520_led *led = container_of(work, struct adp5520_led, work);
+	adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1,
+			 led->new_brightness >> 2);
+}
+
+static void adp5520_led_set(struct led_classdev *led_cdev,
+			   enum led_brightness value)
+{
+	struct adp5520_led *led;
+
+	led = container_of(led_cdev, struct adp5520_led, cdev);
+	led->new_brightness = value;
+	schedule_work(&led->work);
+}
+
+static int adp5520_led_setup(struct adp5520_led *led)
+{
+	struct device *dev = led->master;
+	int flags = led->flags;
+	int ret = 0;
+
+	switch (led->id) {
+	case FLAG_ID_ADP5520_LED1_ADP5501_LED0:
+		ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
+					(flags >> ADP5520_FLAG_OFFT_SHIFT) &
+					ADP5520_FLAG_OFFT_MASK);
+		ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
+					ADP5520_LED1_EN);
+		break;
+	case FLAG_ID_ADP5520_LED2_ADP5501_LED1:
+		ret |= adp5520_set_bits(dev,  ADP5520_LED_TIME,
+					((flags >> ADP5520_FLAG_OFFT_SHIFT) &
+					ADP5520_FLAG_OFFT_MASK) << 2);
+		ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
+					 ADP5520_R3_MODE);
+		ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
+					ADP5520_LED2_EN);
+		break;
+	case FLAG_ID_ADP5520_LED3_ADP5501_LED2:
+		ret |= adp5520_set_bits(dev,  ADP5520_LED_TIME,
+					((flags >> ADP5520_FLAG_OFFT_SHIFT) &
+					ADP5520_FLAG_OFFT_MASK) << 4);
+		ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
+					ADP5520_C3_MODE);
+		ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
+					ADP5520_LED3_EN);
+		break;
+	}
+
+	return ret;
+}
+
+static int __devinit adp5520_led_prepare(struct platform_device *pdev)
+{
+	struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	int ret = 0;
+
+	ret |= adp5520_write(dev, ADP5520_LED1_CURRENT, 0);
+	ret |= adp5520_write(dev, ADP5520_LED2_CURRENT, 0);
+	ret |= adp5520_write(dev, ADP5520_LED3_CURRENT, 0);
+	ret |= adp5520_write(dev, ADP5520_LED_TIME, pdata->led_on_time << 6);
+	ret |= adp5520_write(dev, ADP5520_LED_FADE, FADE_VAL(pdata->fade_in,
+		 pdata->fade_out));
+
+	return ret;
+}
+
+static int __devinit adp5520_led_probe(struct platform_device *pdev)
+{
+	struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+	struct adp5520_led *led, *led_dat;
+	struct led_info *cur_led;
+	int ret, i;
+
+	if (pdata == NULL) {
+		dev_err(&pdev->dev, "missing platform data\n");
+		return -ENODEV;
+	}
+
+	if (pdata->num_leds > ADP5520_01_MAXLEDS) {
+		dev_err(&pdev->dev, "can't handle more than %d LEDS\n",
+				 ADP5520_01_MAXLEDS);
+		return -EFAULT;
+	}
+
+	led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
+	if (led == NULL) {
+		dev_err(&pdev->dev, "failed to alloc memory\n");
+		return -ENOMEM;
+	}
+
+	ret = adp5520_led_prepare(pdev);
+
+	if (ret) {
+		dev_err(&pdev->dev, "failed to write\n");
+		goto err_free;
+	}
+
+	for (i = 0; i < pdata->num_leds; ++i) {
+		cur_led = &pdata->leds[i];
+		led_dat = &led[i];
+
+		led_dat->cdev.name = cur_led->name;
+		led_dat->cdev.default_trigger = cur_led->default_trigger;
+		led_dat->cdev.brightness_set = adp5520_led_set;
+		led_dat->cdev.brightness = LED_OFF;
+
+		if (cur_led->flags & ADP5520_FLAG_LED_MASK)
+			led_dat->flags = cur_led->flags;
+		else
+			led_dat->flags = i + 1;
+
+		led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK;
+
+		led_dat->master = pdev->dev.parent;
+		led_dat->new_brightness = LED_OFF;
+
+		INIT_WORK(&led_dat->work, adp5520_led_work);
+
+		ret = led_classdev_register(led_dat->master, &led_dat->cdev);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to register LED %d\n",
+				led_dat->id);
+			goto err;
+		}
+
+		ret = adp5520_led_setup(led_dat);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to write\n");
+			i++;
+			goto err;
+		}
+	}
+
+	platform_set_drvdata(pdev, led);
+	return 0;
+
+err:
+	if (i > 0) {
+		for (i = i - 1; i >= 0; i--) {
+			led_classdev_unregister(&led[i].cdev);
+			cancel_work_sync(&led[i].work);
+		}
+	}
+
+err_free:
+	kfree(led);
+	return ret;
+}
+
+static int __devexit adp5520_led_remove(struct platform_device *pdev)
+{
+	struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
+	struct adp5520_led *led;
+	int i;
+
+	led = platform_get_drvdata(pdev);
+
+	adp5520_clr_bits(led->master, ADP5520_LED_CONTROL,
+		 ADP5520_LED1_EN | ADP5520_LED2_EN | ADP5520_LED3_EN);
+
+	for (i = 0; i < pdata->num_leds; i++) {
+		led_classdev_unregister(&led[i].cdev);
+		cancel_work_sync(&led[i].work);
+	}
+
+	kfree(led);
+	return 0;
+}
+
+static struct platform_driver adp5520_led_driver = {
+	.driver	= {
+		.name	= "adp5520-led",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= adp5520_led_probe,
+	.remove		= __devexit_p(adp5520_led_remove),
+};
+
+static int __init adp5520_led_init(void)
+{
+	return platform_driver_register(&adp5520_led_driver);
+}
+module_init(adp5520_led_init);
+
+static void __exit adp5520_led_exit(void)
+{
+	platform_driver_unregister(&adp5520_led_driver);
+}
+module_exit(adp5520_led_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("LEDS ADP5520(01) Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:adp5520-led");
diff --git a/drivers/leds/leds-alix2.c b/drivers/leds/leds-alix2.c
index 731d4ee..f59ffad 100644
--- a/drivers/leds/leds-alix2.c
+++ b/drivers/leds/leds-alix2.c
@@ -11,11 +11,24 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/string.h>
+#include <linux/pci.h>
 
 static int force = 0;
 module_param(force, bool, 0444);
 MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs");
 
+#define MSR_LBAR_GPIO		0x5140000C
+#define CS5535_GPIO_SIZE	256
+
+static u32 gpio_base;
+
+static struct pci_device_id divil_pci[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_NS,  PCI_DEVICE_ID_NS_CS5535_ISA) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
+	{ } /* NULL entry */
+};
+MODULE_DEVICE_TABLE(pci, divil_pci);
+
 struct alix_led {
 	struct led_classdev cdev;
 	unsigned short port;
@@ -30,9 +43,9 @@
 		container_of(led_cdev, struct alix_led, cdev);
 
 	if (brightness)
-		outl(led_dev->on_value, led_dev->port);
+		outl(led_dev->on_value, gpio_base + led_dev->port);
 	else
-		outl(led_dev->off_value, led_dev->port);
+		outl(led_dev->off_value, gpio_base + led_dev->port);
 }
 
 static struct alix_led alix_leds[] = {
@@ -41,7 +54,7 @@
 			.name = "alix:1",
 			.brightness_set = alix_led_set,
 		},
-		.port = 0x6100,
+		.port = 0x00,
 		.on_value = 1 << 22,
 		.off_value = 1 << 6,
 	},
@@ -50,7 +63,7 @@
 			.name = "alix:2",
 			.brightness_set = alix_led_set,
 		},
-		.port = 0x6180,
+		.port = 0x80,
 		.on_value = 1 << 25,
 		.off_value = 1 << 9,
 	},
@@ -59,7 +72,7 @@
 			.name = "alix:3",
 			.brightness_set = alix_led_set,
 		},
-		.port = 0x6180,
+		.port = 0x80,
 		.on_value = 1 << 27,
 		.off_value = 1 << 11,
 	},
@@ -101,64 +114,104 @@
 	},
 };
 
-static int __init alix_present(void)
+static int __init alix_present(unsigned long bios_phys,
+				const char *alix_sig,
+				size_t alix_sig_len)
 {
-	const unsigned long bios_phys = 0x000f0000;
 	const size_t bios_len = 0x00010000;
-	const char alix_sig[] = "PC Engines ALIX.";
-	const size_t alix_sig_len = sizeof(alix_sig) - 1;
-
 	const char *bios_virt;
 	const char *scan_end;
 	const char *p;
-	int ret = 0;
+	char name[64];
 
 	if (force) {
 		printk(KERN_NOTICE "%s: forced to skip BIOS test, "
 		       "assume system has ALIX.2 style LEDs\n",
 		       KBUILD_MODNAME);
-		ret = 1;
-		goto out;
+		return 1;
 	}
 
 	bios_virt = phys_to_virt(bios_phys);
 	scan_end = bios_virt + bios_len - (alix_sig_len + 2);
 	for (p = bios_virt; p < scan_end; p++) {
 		const char *tail;
+		char *a;
 
-		if (memcmp(p, alix_sig, alix_sig_len) != 0) {
+		if (memcmp(p, alix_sig, alix_sig_len) != 0)
 			continue;
-		}
+
+		memcpy(name, p, sizeof(name));
+
+		/* remove the first \0 character from string */
+		a = strchr(name, '\0');
+		if (a)
+			*a = ' ';
+
+		/* cut the string at a newline */
+		a = strchr(name, '\r');
+		if (a)
+			*a = '\0';
 
 		tail = p + alix_sig_len;
-		if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') {
+		if ((tail[0] == '2' || tail[0] == '3')) {
 			printk(KERN_INFO
 			       "%s: system is recognized as \"%s\"\n",
-			       KBUILD_MODNAME, p);
-			ret = 1;
-			break;
+			       KBUILD_MODNAME, name);
+			return 1;
 		}
 	}
 
-out:
-	return ret;
+	return 0;
 }
 
 static struct platform_device *pdev;
 
-static int __init alix_led_init(void)
+static int __init alix_pci_led_init(void)
 {
-	int ret;
+	u32 low, hi;
 
-	if (!alix_present()) {
-		ret = -ENODEV;
-		goto out;
+	if (pci_dev_present(divil_pci) == 0) {
+		printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n");
+		return -ENODEV;
 	}
 
-	/* enable output on GPIO for LED 1,2,3 */
-	outl(1 << 6, 0x6104);
-	outl(1 << 9, 0x6184);
-	outl(1 << 11, 0x6184);
+	/* Grab the GPIO I/O range */
+	rdmsr(MSR_LBAR_GPIO, low, hi);
+
+	/* Check the mask and whether GPIO is enabled (sanity check) */
+	if (hi != 0x0000f001) {
+		printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n");
+		return -ENODEV;
+	}
+
+	/* Mask off the IO base address */
+	gpio_base = low & 0x0000ff00;
+
+	if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) {
+		printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n");
+		return -ENODEV;
+	}
+
+	/* Set GPIO function to output */
+	outl(1 << 6, gpio_base + 0x04);
+	outl(1 << 9, gpio_base + 0x84);
+	outl(1 << 11, gpio_base + 0x84);
+
+	return 0;
+}
+
+static int __init alix_led_init(void)
+{
+	int ret = -ENODEV;
+	const char tinybios_sig[] = "PC Engines ALIX.";
+	const char coreboot_sig[] = "PC Engines\0ALIX.";
+
+	if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) ||
+	    alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1))
+		ret = alix_pci_led_init();
+
+	if (ret < 0)
+		return ret;
 
 	pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
 	if (!IS_ERR(pdev)) {
@@ -168,7 +221,6 @@
 	} else
 		ret = PTR_ERR(pdev);
 
-out:
 	return ret;
 }
 
@@ -176,6 +228,7 @@
 {
 	platform_device_unregister(pdev);
 	platform_driver_unregister(&alix_led_driver);
+	release_region(gpio_base, CS5535_GPIO_SIZE);
 }
 
 module_init(alix_led_init);
diff --git a/drivers/leds/leds-cobalt-qube.c b/drivers/leds/leds-cobalt-qube.c
index 8816806..da5fb01 100644
--- a/drivers/leds/leds-cobalt-qube.c
+++ b/drivers/leds/leds-cobalt-qube.c
@@ -31,7 +31,7 @@
 	.name			= "qube::front",
 	.brightness		= LED_FULL,
 	.brightness_set		= qube_front_led_set,
-	.default_trigger	= "ide-disk",
+	.default_trigger	= "default-on",
 };
 
 static int __devinit cobalt_qube_led_probe(struct platform_device *pdev)
@@ -43,7 +43,7 @@
 	if (!res)
 		return -EBUSY;
 
-	led_port = ioremap(res->start, res->end - res->start + 1);
+	led_port = ioremap(res->start, resource_size(res));
 	if (!led_port)
 		return -ENOMEM;
 
diff --git a/drivers/leds/leds-cobalt-raq.c b/drivers/leds/leds-cobalt-raq.c
index defc212..438d483 100644
--- a/drivers/leds/leds-cobalt-raq.c
+++ b/drivers/leds/leds-cobalt-raq.c
@@ -84,7 +84,7 @@
 	if (!res)
 		return -EBUSY;
 
-	led_port = ioremap(res->start, res->end - res->start + 1);
+	led_port = ioremap(res->start, resource_size(res));
 	if (!led_port)
 		return -ENOMEM;
 
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c
new file mode 100644
index 0000000..fee40a8
--- /dev/null
+++ b/drivers/leds/leds-lt3593.c
@@ -0,0 +1,217 @@
+/*
+ * LEDs driver for LT3593 controllers
+ *
+ * See the datasheet at http://cds.linear.com/docs/Datasheet/3593f.pdf
+ *
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * Based on leds-gpio.c,
+ *
+ *   Copyright (C) 2007 8D Technologies inc.
+ *   Raphael Assenat <raph@8d.com>
+ *   Copyright (C) 2008 Freescale Semiconductor, Inc.
+ *
+ * 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/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+struct lt3593_led_data {
+	struct led_classdev cdev;
+	unsigned gpio;
+	struct work_struct work;
+	u8 new_level;
+};
+
+static void lt3593_led_work(struct work_struct *work)
+{
+	int pulses;
+	struct lt3593_led_data *led_dat =
+		container_of(work, struct lt3593_led_data, work);
+
+	/*
+	 * The LT3593 resets its internal current level register to the maximum
+	 * level on the first falling edge on the control pin. Each following
+	 * falling edge decreases the current level by 625uA. Up to 32 pulses
+	 * can be sent, so the maximum power reduction is 20mA.
+	 * After a timeout of 128us, the value is taken from the register and
+	 * applied is to the output driver.
+	 */
+
+	if (led_dat->new_level == 0) {
+		gpio_set_value_cansleep(led_dat->gpio, 0);
+		return;
+	}
+
+	pulses = 32 - (led_dat->new_level * 32) / 255;
+
+	if (pulses == 0) {
+		gpio_set_value_cansleep(led_dat->gpio, 0);
+		mdelay(1);
+		gpio_set_value_cansleep(led_dat->gpio, 1);
+		return;
+	}
+
+	gpio_set_value_cansleep(led_dat->gpio, 1);
+
+	while (pulses--) {
+		gpio_set_value_cansleep(led_dat->gpio, 0);
+		udelay(1);
+		gpio_set_value_cansleep(led_dat->gpio, 1);
+		udelay(1);
+	}
+}
+
+static void lt3593_led_set(struct led_classdev *led_cdev,
+	enum led_brightness value)
+{
+	struct lt3593_led_data *led_dat =
+		container_of(led_cdev, struct lt3593_led_data, cdev);
+
+	led_dat->new_level = value;
+	schedule_work(&led_dat->work);
+}
+
+static int __devinit create_lt3593_led(const struct gpio_led *template,
+	struct lt3593_led_data *led_dat, struct device *parent)
+{
+	int ret, state;
+
+	/* skip leds on GPIOs that aren't available */
+	if (!gpio_is_valid(template->gpio)) {
+		printk(KERN_INFO "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n",
+				KBUILD_MODNAME, template->gpio, template->name);
+		return 0;
+	}
+
+	ret = gpio_request(template->gpio, template->name);
+	if (ret < 0)
+		return ret;
+
+	led_dat->cdev.name = template->name;
+	led_dat->cdev.default_trigger = template->default_trigger;
+	led_dat->gpio = template->gpio;
+
+	led_dat->cdev.brightness_set = lt3593_led_set;
+
+	state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
+	led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
+
+	if (!template->retain_state_suspended)
+		led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+
+	ret = gpio_direction_output(led_dat->gpio, state);
+	if (ret < 0)
+		goto err;
+
+	INIT_WORK(&led_dat->work, lt3593_led_work);
+
+	ret = led_classdev_register(parent, &led_dat->cdev);
+	if (ret < 0)
+		goto err;
+
+	printk(KERN_INFO "%s: registered LT3593 LED '%s' at GPIO %d\n",
+		KBUILD_MODNAME, template->name, template->gpio);
+
+	return 0;
+
+err:
+	gpio_free(led_dat->gpio);
+	return ret;
+}
+
+static void delete_lt3593_led(struct lt3593_led_data *led)
+{
+	if (!gpio_is_valid(led->gpio))
+		return;
+
+	led_classdev_unregister(&led->cdev);
+	cancel_work_sync(&led->work);
+	gpio_free(led->gpio);
+}
+
+static int __devinit lt3593_led_probe(struct platform_device *pdev)
+{
+	struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+	struct lt3593_led_data *leds_data;
+	int i, ret = 0;
+
+	if (!pdata)
+		return -EBUSY;
+
+	leds_data = kzalloc(sizeof(struct lt3593_led_data) * pdata->num_leds,
+				GFP_KERNEL);
+	if (!leds_data)
+		return -ENOMEM;
+
+	for (i = 0; i < pdata->num_leds; i++) {
+		ret = create_lt3593_led(&pdata->leds[i], &leds_data[i],
+				      &pdev->dev);
+		if (ret < 0)
+			goto err;
+	}
+
+	platform_set_drvdata(pdev, leds_data);
+
+	return 0;
+
+err:
+	for (i = i - 1; i >= 0; i--)
+		delete_lt3593_led(&leds_data[i]);
+
+	kfree(leds_data);
+
+	return ret;
+}
+
+static int __devexit lt3593_led_remove(struct platform_device *pdev)
+{
+	int i;
+	struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
+	struct lt3593_led_data *leds_data;
+
+	leds_data = platform_get_drvdata(pdev);
+
+	for (i = 0; i < pdata->num_leds; i++)
+		delete_lt3593_led(&leds_data[i]);
+
+	kfree(leds_data);
+
+	return 0;
+}
+
+static struct platform_driver lt3593_led_driver = {
+	.probe		= lt3593_led_probe,
+	.remove		= __devexit_p(lt3593_led_remove),
+	.driver		= {
+		.name	= "leds-lt3593",
+		.owner	= THIS_MODULE,
+	},
+};
+
+MODULE_ALIAS("platform:leds-lt3593");
+
+static int __init lt3593_led_init(void)
+{
+	return platform_driver_register(&lt3593_led_driver);
+}
+
+static void __exit lt3593_led_exit(void)
+{
+	platform_driver_unregister(&lt3593_led_driver);
+}
+
+module_init(lt3593_led_init);
+module_exit(lt3593_led_exit);
+
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_DESCRIPTION("LED driver for LT3593 controllers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index cdfdc87..88b1dd0 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -27,7 +27,6 @@
 	struct pwm_device	*pwm;
 	unsigned int 		active_low;
 	unsigned int		period;
-	unsigned int		max_brightness;
 };
 
 static void led_pwm_set(struct led_classdev *led_cdev,
@@ -35,7 +34,7 @@
 {
 	struct led_pwm_data *led_dat =
 		container_of(led_cdev, struct led_pwm_data, cdev);
-	unsigned int max = led_dat->max_brightness;
+	unsigned int max = led_dat->cdev.max_brightness;
 	unsigned int period =  led_dat->period;
 
 	if (brightness == 0) {
@@ -77,10 +76,10 @@
 		led_dat->cdev.name = cur_led->name;
 		led_dat->cdev.default_trigger = cur_led->default_trigger;
 		led_dat->active_low = cur_led->active_low;
-		led_dat->max_brightness = cur_led->max_brightness;
 		led_dat->period = cur_led->pwm_period_ns;
 		led_dat->cdev.brightness_set = led_pwm_set;
 		led_dat->cdev.brightness = LED_OFF;
+		led_dat->cdev.max_brightness = cur_led->max_brightness;
 		led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
 
 		ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c
new file mode 100644
index 0000000..7f00de3
--- /dev/null
+++ b/drivers/leds/leds-regulator.c
@@ -0,0 +1,242 @@
+/*
+ * leds-regulator.c - LED class driver for regulator driven LEDs.
+ *
+ * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it>
+ *
+ * Inspired by leds-wm8350 driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+#include <linux/leds-regulator.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define to_regulator_led(led_cdev) \
+	container_of(led_cdev, struct regulator_led, cdev)
+
+struct regulator_led {
+	struct led_classdev cdev;
+	enum led_brightness value;
+	int enabled;
+	struct mutex mutex;
+	struct work_struct work;
+
+	struct regulator *vcc;
+};
+
+static inline int led_regulator_get_max_brightness(struct regulator *supply)
+{
+	int ret;
+	int voltage = regulator_list_voltage(supply, 0);
+
+	if (voltage <= 0)
+		return 1;
+
+	/* even if regulator can't change voltages,
+	 * we still assume it can change status
+	 * and the LED can be turned on and off.
+	 */
+	ret = regulator_set_voltage(supply, voltage, voltage);
+	if (ret < 0)
+		return 1;
+
+	return regulator_count_voltages(supply);
+}
+
+static int led_regulator_get_voltage(struct regulator *supply,
+		enum led_brightness brightness)
+{
+	if (brightness == 0)
+		return -EINVAL;
+
+	return regulator_list_voltage(supply, brightness - 1);
+}
+
+
+static void regulator_led_enable(struct regulator_led *led)
+{
+	int ret;
+
+	if (led->enabled)
+		return;
+
+	ret = regulator_enable(led->vcc);
+	if (ret != 0) {
+		dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret);
+		return;
+	}
+
+	led->enabled = 1;
+}
+
+static void regulator_led_disable(struct regulator_led *led)
+{
+	int ret;
+
+	if (!led->enabled)
+		return;
+
+	ret = regulator_disable(led->vcc);
+	if (ret != 0) {
+		dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret);
+		return;
+	}
+
+	led->enabled = 0;
+}
+
+static void regulator_led_set_value(struct regulator_led *led)
+{
+	int voltage;
+	int ret;
+
+	mutex_lock(&led->mutex);
+
+	if (led->value == LED_OFF) {
+		regulator_led_disable(led);
+		goto out;
+	}
+
+	if (led->cdev.max_brightness > 1) {
+		voltage = led_regulator_get_voltage(led->vcc, led->value);
+		dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n",
+				led->value, voltage);
+
+		ret = regulator_set_voltage(led->vcc, voltage, voltage);
+		if (ret != 0)
+			dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n",
+				voltage, ret);
+	}
+
+	regulator_led_enable(led);
+
+out:
+	mutex_unlock(&led->mutex);
+}
+
+static void led_work(struct work_struct *work)
+{
+	struct regulator_led *led;
+
+	led = container_of(work, struct regulator_led, work);
+	regulator_led_set_value(led);
+}
+
+static void regulator_led_brightness_set(struct led_classdev *led_cdev,
+			   enum led_brightness value)
+{
+	struct regulator_led *led = to_regulator_led(led_cdev);
+
+	led->value = value;
+	schedule_work(&led->work);
+}
+
+static int __devinit regulator_led_probe(struct platform_device *pdev)
+{
+	struct led_regulator_platform_data *pdata = pdev->dev.platform_data;
+	struct regulator_led *led;
+	struct regulator *vcc;
+	int ret = 0;
+
+	if (pdata == NULL) {
+		dev_err(&pdev->dev, "no platform data\n");
+		return -ENODEV;
+	}
+
+	vcc = regulator_get_exclusive(&pdev->dev, "vled");
+	if (IS_ERR(vcc)) {
+		dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name);
+		return PTR_ERR(vcc);
+	}
+
+	led = kzalloc(sizeof(*led), GFP_KERNEL);
+	if (led == NULL) {
+		ret = -ENOMEM;
+		goto err_vcc;
+	}
+
+	led->cdev.max_brightness = led_regulator_get_max_brightness(vcc);
+	if (pdata->brightness > led->cdev.max_brightness) {
+		dev_err(&pdev->dev, "Invalid default brightness %d\n",
+				pdata->brightness);
+		ret = -EINVAL;
+		goto err_led;
+	}
+	led->value = pdata->brightness;
+
+	led->cdev.brightness_set = regulator_led_brightness_set;
+	led->cdev.name = pdata->name;
+	led->cdev.flags |= LED_CORE_SUSPENDRESUME;
+	led->vcc = vcc;
+
+	mutex_init(&led->mutex);
+	INIT_WORK(&led->work, led_work);
+
+	platform_set_drvdata(pdev, led);
+
+	ret = led_classdev_register(&pdev->dev, &led->cdev);
+	if (ret < 0) {
+		cancel_work_sync(&led->work);
+		goto err_led;
+	}
+
+	/* to expose the default value to userspace */
+	led->cdev.brightness = led->value;
+
+	/* Set the default led status */
+	regulator_led_set_value(led);
+
+	return 0;
+
+err_led:
+	kfree(led);
+err_vcc:
+	regulator_put(vcc);
+	return ret;
+}
+
+static int __devexit regulator_led_remove(struct platform_device *pdev)
+{
+	struct regulator_led *led = platform_get_drvdata(pdev);
+
+	led_classdev_unregister(&led->cdev);
+	cancel_work_sync(&led->work);
+	regulator_led_disable(led);
+	regulator_put(led->vcc);
+	kfree(led);
+	return 0;
+}
+
+static struct platform_driver regulator_led_driver = {
+	.driver = {
+		   .name  = "leds-regulator",
+		   .owner = THIS_MODULE,
+		   },
+	.probe  = regulator_led_probe,
+	.remove = __devexit_p(regulator_led_remove),
+};
+
+static int __init regulator_led_init(void)
+{
+	return platform_driver_register(&regulator_led_driver);
+}
+module_init(regulator_led_init);
+
+static void __exit regulator_led_exit(void)
+{
+	platform_driver_unregister(&regulator_led_driver);
+}
+module_exit(regulator_led_exit);
+
+MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
+MODULE_DESCRIPTION("Regulator driven LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:leds-regulator");
diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c
new file mode 100644
index 0000000..97f0498
--- /dev/null
+++ b/drivers/leds/leds-ss4200.c
@@ -0,0 +1,556 @@
+/*
+ * SS4200-E Hardware API
+ * Copyright (c) 2009, Intel Corporation.
+ * Copyright IBM Corporation, 2009
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Dave Hansen <dave@sr71.net>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+MODULE_AUTHOR("Rodney Girod <rgirod@confocus.com>, Dave Hansen <dave@sr71.net>");
+MODULE_DESCRIPTION("Intel NAS/Home Server ICH7 GPIO Driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * ICH7 LPC/GPIO PCI Config register offsets
+ */
+#define PMBASE		0x040
+#define GPIO_BASE	0x048
+#define GPIO_CTRL	0x04c
+#define GPIO_EN		0x010
+
+/*
+ * The ICH7 GPIO register block is 64 bytes in size.
+ */
+#define ICH7_GPIO_SIZE	64
+
+/*
+ * Define register offsets within the ICH7 register block.
+ */
+#define GPIO_USE_SEL	0x000
+#define GP_IO_SEL	0x004
+#define GP_LVL		0x00c
+#define GPO_BLINK	0x018
+#define GPI_INV		0x030
+#define GPIO_USE_SEL2	0x034
+#define GP_IO_SEL2	0x038
+#define GP_LVL2		0x03c
+
+/*
+ * PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives.
+ */
+static struct pci_device_id ich7_lpc_pci_id[] =
+{
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) },
+	{ } /* NULL entry */
+};
+
+MODULE_DEVICE_TABLE(pci, ich7_lpc_pci_id);
+
+static int __init ss4200_led_dmi_callback(const struct dmi_system_id *id)
+{
+	pr_info("detected '%s'\n", id->ident);
+	return 1;
+}
+
+static unsigned int __initdata nodetect;
+module_param_named(nodetect, nodetect, bool, 0);
+MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection");
+
+/*
+ * struct nas_led_whitelist - List of known good models
+ *
+ * Contains the known good models this driver is compatible with.
+ * When adding a new model try to be as strict as possible. This
+ * makes it possible to keep the false positives (the model is
+ * detected as working, but in reality it is not) as low as
+ * possible.
+ */
+static struct dmi_system_id __initdata nas_led_whitelist[] = {
+	{
+		.callback = ss4200_led_dmi_callback,
+		.ident = "Intel SS4200-E",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "SS4200-E"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "1.00.00")
+		}
+	},
+};
+
+/*
+ * Base I/O address assigned to the Power Management register block
+ */
+static u32 g_pm_io_base;
+
+/*
+ * Base I/O address assigned to the ICH7 GPIO register block
+ */
+static u32 nas_gpio_io_base;
+
+/*
+ * When we successfully register a region, we are returned a resource.
+ * We use these to identify which regions we need to release on our way
+ * back out.
+ */
+static struct resource *gp_gpio_resource;
+
+struct nasgpio_led {
+	char *name;
+	u32 gpio_bit;
+	struct led_classdev led_cdev;
+};
+
+/*
+ * gpio_bit(s) are the ICH7 GPIO bit assignments
+ */
+static struct nasgpio_led nasgpio_leds[] = {
+	{ .name = "hdd1:blue:sata",	.gpio_bit = 0 },
+	{ .name = "hdd1:amber:sata",	.gpio_bit = 1 },
+	{ .name = "hdd2:blue:sata",	.gpio_bit = 2 },
+	{ .name = "hdd2:amber:sata",	.gpio_bit = 3 },
+	{ .name = "hdd3:blue:sata",	.gpio_bit = 4 },
+	{ .name = "hdd3:amber:sata",	.gpio_bit = 5 },
+	{ .name = "hdd4:blue:sata",	.gpio_bit = 6 },
+	{ .name = "hdd4:amber:sata",	.gpio_bit = 7 },
+	{ .name = "power:blue:power",	.gpio_bit = 27},
+	{ .name = "power:amber:power",  .gpio_bit = 28},
+};
+
+#define NAS_RECOVERY	0x00000400	/* GPIO10 */
+
+static struct nasgpio_led *
+led_classdev_to_nasgpio_led(struct led_classdev *led_cdev)
+{
+	return container_of(led_cdev, struct nasgpio_led, led_cdev);
+}
+
+static struct nasgpio_led *get_led_named(char *name)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) {
+		if (strcmp(nasgpio_leds[i].name, name))
+			continue;
+		return &nasgpio_leds[i];
+	}
+	return NULL;
+}
+
+/*
+ * This protects access to the gpio ports.
+ */
+static DEFINE_SPINLOCK(nasgpio_gpio_lock);
+
+/*
+ * There are two gpio ports, one for blinking and the other
+ * for power.  @port tells us if we're doing blinking or
+ * power control.
+ *
+ * Caller must hold nasgpio_gpio_lock
+ */
+static void __nasgpio_led_set_attr(struct led_classdev *led_cdev,
+				   u32 port, u32 value)
+{
+	struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev);
+	u32 gpio_out;
+
+	gpio_out = inl(nas_gpio_io_base + port);
+	if (value)
+		gpio_out |= (1<<led->gpio_bit);
+	else
+		gpio_out &= ~(1<<led->gpio_bit);
+
+	outl(gpio_out, nas_gpio_io_base + port);
+}
+
+static void nasgpio_led_set_attr(struct led_classdev *led_cdev,
+				 u32 port, u32 value)
+{
+	spin_lock(&nasgpio_gpio_lock);
+	__nasgpio_led_set_attr(led_cdev, port, value);
+	spin_unlock(&nasgpio_gpio_lock);
+}
+
+u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port)
+{
+	struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev);
+	u32 gpio_in;
+
+	spin_lock(&nasgpio_gpio_lock);
+	gpio_in = inl(nas_gpio_io_base + port);
+	spin_unlock(&nasgpio_gpio_lock);
+	if (gpio_in & (1<<led->gpio_bit))
+		return 1;
+	return 0;
+}
+
+/*
+ * There is actual brightness control in the hardware,
+ * but it is via smbus commands and not implemented
+ * in this driver.
+ */
+static void nasgpio_led_set_brightness(struct led_classdev *led_cdev,
+				       enum led_brightness brightness)
+{
+	u32 setting = 0;
+	if (brightness >= LED_HALF)
+		setting = 1;
+	/*
+	 * Hold the lock across both operations.  This ensures
+	 * consistency so that both the "turn off blinking"
+	 * and "turn light off" operations complete as a set.
+	 */
+	spin_lock(&nasgpio_gpio_lock);
+	/*
+	 * LED class documentation asks that past blink state
+	 * be disabled when brightness is turned to zero.
+	 */
+	if (brightness == 0)
+		__nasgpio_led_set_attr(led_cdev, GPO_BLINK, 0);
+	__nasgpio_led_set_attr(led_cdev, GP_LVL, setting);
+	spin_unlock(&nasgpio_gpio_lock);
+}
+
+static int nasgpio_led_set_blink(struct led_classdev *led_cdev,
+				 unsigned long *delay_on,
+				 unsigned long *delay_off)
+{
+	u32 setting = 1;
+	if (!(*delay_on == 0 && *delay_off == 0) &&
+	    !(*delay_on == 500 && *delay_off == 500))
+		return -EINVAL;
+	/*
+	 * These are very approximate.
+	 */
+	*delay_on = 500;
+	*delay_off = 500;
+
+	nasgpio_led_set_attr(led_cdev, GPO_BLINK, setting);
+
+	return 0;
+}
+
+
+/*
+ * Initialize the ICH7 GPIO registers for NAS usage.  The BIOS should have
+ * already taken care of this, but we will do so in a non destructive manner
+ * so that we have what we need whether the BIOS did it or not.
+ */
+static int __devinit ich7_gpio_init(struct device *dev)
+{
+	int i;
+	u32 config_data = 0;
+	u32 all_nas_led = 0;
+
+	for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++)
+		all_nas_led |= (1<<nasgpio_leds[i].gpio_bit);
+
+	spin_lock(&nasgpio_gpio_lock);
+	/*
+	 * We need to enable all of the GPIO lines used by the NAS box,
+	 * so we will read the current Use Selection and add our usage
+	 * to it.  This should be benign with regard to the original
+	 * BIOS configuration.
+	 */
+	config_data = inl(nas_gpio_io_base + GPIO_USE_SEL);
+	dev_dbg(dev, ": Data read from GPIO_USE_SEL = 0x%08x\n", config_data);
+	config_data |= all_nas_led + NAS_RECOVERY;
+	outl(config_data, nas_gpio_io_base + GPIO_USE_SEL);
+	config_data = inl(nas_gpio_io_base + GPIO_USE_SEL);
+	dev_dbg(dev, ": GPIO_USE_SEL = 0x%08x\n\n", config_data);
+
+	/*
+	 * The LED GPIO outputs need to be configured for output, so we
+	 * will ensure that all LED lines are cleared for output and the
+	 * RECOVERY line ready for input.  This too should be benign with
+	 * regard to BIOS configuration.
+	 */
+	config_data = inl(nas_gpio_io_base + GP_IO_SEL);
+	dev_dbg(dev, ": Data read from GP_IO_SEL = 0x%08x\n",
+					config_data);
+	config_data &= ~all_nas_led;
+	config_data |= NAS_RECOVERY;
+	outl(config_data, nas_gpio_io_base + GP_IO_SEL);
+	config_data = inl(nas_gpio_io_base + GP_IO_SEL);
+	dev_dbg(dev, ": GP_IO_SEL = 0x%08x\n", config_data);
+
+	/*
+	 * In our final system, the BIOS will initialize the state of all
+	 * of the LEDs.  For now, we turn them all off (or Low).
+	 */
+	config_data = inl(nas_gpio_io_base + GP_LVL);
+	dev_dbg(dev, ": Data read from GP_LVL = 0x%08x\n", config_data);
+	/*
+	 * In our final system, the BIOS will initialize the blink state of all
+	 * of the LEDs.  For now, we turn blink off for all of them.
+	 */
+	config_data = inl(nas_gpio_io_base + GPO_BLINK);
+	dev_dbg(dev, ": Data read from GPO_BLINK = 0x%08x\n", config_data);
+
+	/*
+	 * At this moment, I am unsure if anything needs to happen with GPI_INV
+	 */
+	config_data = inl(nas_gpio_io_base + GPI_INV);
+	dev_dbg(dev, ": Data read from GPI_INV = 0x%08x\n", config_data);
+
+	spin_unlock(&nasgpio_gpio_lock);
+	return 0;
+}
+
+static void ich7_lpc_cleanup(struct device *dev)
+{
+	/*
+	 * If we were given exclusive use of the GPIO
+	 * I/O Address range, we must return it.
+	 */
+	if (gp_gpio_resource) {
+		dev_dbg(dev, ": Releasing GPIO I/O addresses\n");
+		release_region(nas_gpio_io_base, ICH7_GPIO_SIZE);
+		gp_gpio_resource = NULL;
+	}
+}
+
+/*
+ * The OS has determined that the LPC of the Intel ICH7 Southbridge is present
+ * so we can retrive the required operational information and prepare the GPIO.
+ */
+static struct pci_dev *nas_gpio_pci_dev;
+static int __devinit ich7_lpc_probe(struct pci_dev *dev,
+				    const struct pci_device_id *id)
+{
+	int status;
+	u32 gc = 0;
+
+	status = pci_enable_device(dev);
+	if (status) {
+		dev_err(&dev->dev, "pci_enable_device failed\n");
+		return -EIO;
+	}
+
+	nas_gpio_pci_dev = dev;
+	status = pci_read_config_dword(dev, PMBASE, &g_pm_io_base);
+	if (status)
+		goto out;
+	g_pm_io_base &= 0x00000ff80;
+
+	status = pci_read_config_dword(dev, GPIO_CTRL, &gc);
+	if (!(GPIO_EN & gc)) {
+		status = -EEXIST;
+		dev_info(&dev->dev,
+			   "ERROR: The LPC GPIO Block has not been enabled.\n");
+		goto out;
+	}
+
+	status = pci_read_config_dword(dev, GPIO_BASE, &nas_gpio_io_base);
+	if (0 > status) {
+		dev_info(&dev->dev, "Unable to read GPIOBASE.\n");
+		goto out;
+	}
+	dev_dbg(&dev->dev, ": GPIOBASE = 0x%08x\n", nas_gpio_io_base);
+	nas_gpio_io_base &= 0x00000ffc0;
+
+	/*
+	 * Insure that we have exclusive access to the GPIO I/O address range.
+	 */
+	gp_gpio_resource = request_region(nas_gpio_io_base, ICH7_GPIO_SIZE,
+					  KBUILD_MODNAME);
+	if (NULL == gp_gpio_resource) {
+		dev_info(&dev->dev,
+			 "ERROR Unable to register GPIO I/O addresses.\n");
+		status = -1;
+		goto out;
+	}
+
+	/*
+	 * Initialize the GPIO for NAS/Home Server Use
+	 */
+	ich7_gpio_init(&dev->dev);
+
+out:
+	if (status) {
+		ich7_lpc_cleanup(&dev->dev);
+		pci_disable_device(dev);
+	}
+	return status;
+}
+
+static void ich7_lpc_remove(struct pci_dev *dev)
+{
+	ich7_lpc_cleanup(&dev->dev);
+	pci_disable_device(dev);
+}
+
+/*
+ * pci_driver structure passed to the PCI modules
+ */
+static struct pci_driver nas_gpio_pci_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = ich7_lpc_pci_id,
+	.probe = ich7_lpc_probe,
+	.remove = ich7_lpc_remove,
+};
+
+static struct led_classdev *get_classdev_for_led_nr(int nr)
+{
+	struct nasgpio_led *nas_led = &nasgpio_leds[nr];
+	struct led_classdev *led = &nas_led->led_cdev;
+	return led;
+}
+
+
+static void set_power_light_amber_noblink(void)
+{
+	struct nasgpio_led *amber = get_led_named("power:amber:power");
+	struct nasgpio_led *blue = get_led_named("power:blue:power");
+
+	if (!amber || !blue)
+		return;
+	/*
+	 * LED_OFF implies disabling future blinking
+	 */
+	pr_debug("setting blue off and amber on\n");
+
+	nasgpio_led_set_brightness(&blue->led_cdev, LED_OFF);
+	nasgpio_led_set_brightness(&amber->led_cdev, LED_FULL);
+}
+
+static ssize_t nas_led_blink_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led = dev_get_drvdata(dev);
+	int blinking = 0;
+	if (nasgpio_led_get_attr(led, GPO_BLINK))
+		blinking = 1;
+	return sprintf(buf, "%u\n", blinking);
+}
+
+static ssize_t nas_led_blink_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t size)
+{
+	int ret;
+	struct led_classdev *led = dev_get_drvdata(dev);
+	unsigned long blink_state;
+
+	ret = strict_strtoul(buf, 10, &blink_state);
+	if (ret)
+		return ret;
+
+	nasgpio_led_set_attr(led, GPO_BLINK, blink_state);
+
+	return size;
+}
+
+static DEVICE_ATTR(blink, 0644, nas_led_blink_show, nas_led_blink_store);
+
+static int register_nasgpio_led(int led_nr)
+{
+	int ret;
+	struct nasgpio_led *nas_led = &nasgpio_leds[led_nr];
+	struct led_classdev *led = get_classdev_for_led_nr(led_nr);
+
+	led->name = nas_led->name;
+	led->brightness = LED_OFF;
+	if (nasgpio_led_get_attr(led, GP_LVL))
+		led->brightness = LED_FULL;
+	led->brightness_set = nasgpio_led_set_brightness;
+	led->blink_set = nasgpio_led_set_blink;
+	ret = led_classdev_register(&nas_gpio_pci_dev->dev, led);
+	if (ret)
+		return ret;
+	ret = device_create_file(led->dev, &dev_attr_blink);
+	if (ret)
+		led_classdev_unregister(led);
+	return ret;
+}
+
+static void unregister_nasgpio_led(int led_nr)
+{
+	struct led_classdev *led = get_classdev_for_led_nr(led_nr);
+	led_classdev_unregister(led);
+	device_remove_file(led->dev, &dev_attr_blink);
+}
+/*
+ * module load/initialization
+ */
+static int __init nas_gpio_init(void)
+{
+	int i;
+	int ret = 0;
+	int nr_devices = 0;
+
+	nr_devices = dmi_check_system(nas_led_whitelist);
+	if (nodetect) {
+		pr_info("skipping hardware autodetection\n");
+		pr_info("Please send 'dmidecode' output to dave@sr71.net\n");
+		nr_devices++;
+	}
+
+	if (nr_devices <= 0) {
+		pr_info("no LED devices found\n");
+		return -ENODEV;
+	}
+
+	pr_info("registering PCI driver\n");
+	ret = pci_register_driver(&nas_gpio_pci_driver);
+	if (ret)
+		return ret;
+	for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) {
+		ret = register_nasgpio_led(i);
+		if (ret)
+			goto out_err;
+	}
+	/*
+	 * When the system powers on, the BIOS leaves the power
+	 * light blue and blinking.  This will turn it solid
+	 * amber once the driver is loaded.
+	 */
+	set_power_light_amber_noblink();
+	return 0;
+out_err:
+	for (; i >= 0; i--)
+		unregister_nasgpio_led(i);
+	pci_unregister_driver(&nas_gpio_pci_driver);
+	return ret;
+}
+
+/*
+ * module unload
+ */
+static void __exit nas_gpio_exit(void)
+{
+	int i;
+	pr_info("Unregistering driver\n");
+	for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++)
+		unregister_nasgpio_led(i);
+	pci_unregister_driver(&nas_gpio_pci_driver);
+}
+
+module_init(nas_gpio_init);
+module_exit(nas_gpio_exit);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 1a7a9fc..e3551d2 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -203,6 +203,7 @@
 
 config CS5535_MFGPT_DEFAULT_IRQ
 	int
+	depends on CS5535_MFGPT
 	default 7
 	help
 	  MFGPTs on the CS5535 require an interrupt.  The selected IRQ
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index cdb845b..06b6408 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -516,7 +516,8 @@
 	 * The number of functions on the card is encoded inside
 	 * the ocr.
 	 */
-	card->sdio_funcs = funcs = (ocr & 0x70000000) >> 28;
+	funcs = (ocr & 0x70000000) >> 28;
+	card->sdio_funcs = 0;
 
 	/*
 	 * If needed, disconnect card detection pull-up resistor.
@@ -528,7 +529,7 @@
 	/*
 	 * Initialize (but don't add) all present functions.
 	 */
-	for (i = 0;i < funcs;i++) {
+	for (i = 0; i < funcs; i++, card->sdio_funcs++) {
 		err = sdio_init_func(host->card, i + 1);
 		if (err)
 			goto remove;
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index d37464e..9e060c8 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -248,12 +248,15 @@
 /*
  * Unregister a SDIO function with the driver model, and
  * (eventually) free it.
+ * This function can be called through error paths where sdio_add_func() was
+ * never executed (because a failure occurred at an earlier point).
  */
 void sdio_remove_func(struct sdio_func *func)
 {
-	if (sdio_func_present(func))
-		device_del(&func->dev);
+	if (!sdio_func_present(func))
+		return;
 
+	device_del(&func->dev);
 	put_device(&func->dev);
 }
 
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 9d405b1..ce1d288 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -44,6 +44,19 @@
 	  This is silent Kconfig symbol that is selected by the drivers that
 	  need to overwrite SDHCI IO memory accessors.
 
+config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
+	bool
+	select MMC_SDHCI_IO_ACCESSORS
+	help
+	  This option is selected by drivers running on big endian hosts
+	  and performing I/O to a SDHCI controller through a bus that
+	  implements a hardware byte swapper using a 32-bit datum.
+	  This endian mapping mode is called "data invariance" and
+	  has the effect of scrambling the addresses and formats of data
+	  accessed in sizes other than the datum size.
+
+	  This is the case for the Freescale eSDHC and Nintendo Wii SDHCI.
+
 config MMC_SDHCI_PCI
 	tristate "SDHCI support on PCI bus"
 	depends on MMC_SDHCI && PCI
@@ -75,11 +88,29 @@
 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.
+	  Interfaces.
+
+	  If unsure, say N.
+
+config MMC_SDHCI_OF_ESDHC
+	bool "SDHCI OF support for the Freescale eSDHC controller"
+	depends on MMC_SDHCI_OF
+	select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
+	help
+	  This selects the Freescale eSDHC controller support.
+
+	  If unsure, say N.
+
+config MMC_SDHCI_OF_HLWD
+	bool "SDHCI OF support for the Nintendo Wii SDHCI controllers"
+	depends on MMC_SDHCI_OF
+	select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
+	help
+	  This selects the Secure Digital Host Controller Interface (SDHCI)
+	  found in the "Hollywood" chipset of the Nintendo Wii video game
+	  console.
 
 	  If unsure, say N.
 
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index ded4d8cd..3d253dd 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -13,7 +13,6 @@
 obj-$(CONFIG_MMC_SDHCI)		+= sdhci.o
 obj-$(CONFIG_MMC_SDHCI_PCI)	+= sdhci-pci.o
 obj-$(CONFIG_MMC_RICOH_MMC)	+= ricoh_mmc.o
-obj-$(CONFIG_MMC_SDHCI_OF)	+= sdhci-of.o
 obj-$(CONFIG_MMC_SDHCI_PLTFM)	+= sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_S3C)	+= sdhci-s3c.o
 obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
@@ -37,6 +36,11 @@
 obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o
 obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o
 
+obj-$(CONFIG_MMC_SDHCI_OF)	+= sdhci-of.o
+sdhci-of-y				:= sdhci-of-core.o
+sdhci-of-$(CONFIG_MMC_SDHCI_OF_ESDHC)	+= sdhci-of-esdhc.o
+sdhci-of-$(CONFIG_MMC_SDHCI_OF_HLWD)	+= sdhci-of-hlwd.o
+
 ifeq ($(CONFIG_CB710_DEBUG),y)
 	CFLAGS-cb710-mmc	+= -DDEBUG
 endif
diff --git a/drivers/mmc/host/sdhci-of-core.c b/drivers/mmc/host/sdhci-of-core.c
new file mode 100644
index 0000000..55e3313
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-core.c
@@ -0,0 +1,231 @@
+/*
+ * OpenFirmware bindings for Secure Digital Host Controller Interface.
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ *	    Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mmc/host.h>
+#include <asm/machdep.h>
+#include "sdhci-of.h"
+#include "sdhci.h"
+
+#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
+
+/*
+ * These accessors are designed for big endian hosts doing I/O to
+ * little endian controllers incorporating a 32-bit hardware byte swapper.
+ */
+
+u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg)
+{
+	return in_be32(host->ioaddr + reg);
+}
+
+u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg)
+{
+	return in_be16(host->ioaddr + (reg ^ 0x2));
+}
+
+u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg)
+{
+	return in_8(host->ioaddr + (reg ^ 0x3));
+}
+
+void sdhci_be32bs_writel(struct sdhci_host *host, u32 val, int reg)
+{
+	out_be32(host->ioaddr + reg, val);
+}
+
+void sdhci_be32bs_writew(struct sdhci_host *host, u16 val, int reg)
+{
+	struct sdhci_of_host *of_host = sdhci_priv(host);
+	int base = reg & ~0x3;
+	int shift = (reg & 0x2) * 8;
+
+	switch (reg) {
+	case SDHCI_TRANSFER_MODE:
+		/*
+		 * Postpone this write, we must do it together with a
+		 * command write that is down below.
+		 */
+		of_host->xfer_mode_shadow = val;
+		return;
+	case SDHCI_COMMAND:
+		sdhci_be32bs_writel(host, val << 16 | of_host->xfer_mode_shadow,
+				    SDHCI_TRANSFER_MODE);
+		return;
+	}
+	clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
+}
+
+void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	int base = reg & ~0x3;
+	int shift = (reg & 0x3) * 8;
+
+	clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
+}
+#endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */
+
+#ifdef CONFIG_PM
+
+static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state)
+{
+	struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+	return mmc_suspend_host(host->mmc, state);
+}
+
+static int sdhci_of_resume(struct of_device *ofdev)
+{
+	struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+	return mmc_resume_host(host->mmc);
+}
+
+#else
+
+#define sdhci_of_suspend NULL
+#define sdhci_of_resume NULL
+
+#endif
+
+static bool __devinit sdhci_of_wp_inverted(struct device_node *np)
+{
+	if (of_get_property(np, "sdhci,wp-inverted", NULL))
+		return true;
+
+	/* Old device trees don't have the wp-inverted property. */
+	return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds);
+}
+
+static int __devinit sdhci_of_probe(struct of_device *ofdev,
+				 const struct of_device_id *match)
+{
+	struct device_node *np = ofdev->node;
+	struct sdhci_of_data *sdhci_of_data = match->data;
+	struct sdhci_host *host;
+	struct sdhci_of_host *of_host;
+	const u32 *clk;
+	int size;
+	int ret;
+
+	if (!of_device_is_available(np))
+		return -ENODEV;
+
+	host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host));
+	if (IS_ERR(host))
+		return -ENOMEM;
+
+	of_host = sdhci_priv(host);
+	dev_set_drvdata(&ofdev->dev, host);
+
+	host->ioaddr = of_iomap(np, 0);
+	if (!host->ioaddr) {
+		ret = -ENOMEM;
+		goto err_addr_map;
+	}
+
+	host->irq = irq_of_parse_and_map(np, 0);
+	if (!host->irq) {
+		ret = -EINVAL;
+		goto err_no_irq;
+	}
+
+	host->hw_name = dev_name(&ofdev->dev);
+	if (sdhci_of_data) {
+		host->quirks = sdhci_of_data->quirks;
+		host->ops = &sdhci_of_data->ops;
+	}
+
+	if (of_get_property(np, "sdhci,1-bit-only", NULL))
+		host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
+
+	if (sdhci_of_wp_inverted(np))
+		host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
+
+	clk = of_get_property(np, "clock-frequency", &size);
+	if (clk && size == sizeof(*clk) && *clk)
+		of_host->clock = *clk;
+
+	ret = sdhci_add_host(host);
+	if (ret)
+		goto err_add_host;
+
+	return 0;
+
+err_add_host:
+	irq_dispose_mapping(host->irq);
+err_no_irq:
+	iounmap(host->ioaddr);
+err_addr_map:
+	sdhci_free_host(host);
+	return ret;
+}
+
+static int __devexit sdhci_of_remove(struct of_device *ofdev)
+{
+	struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+	sdhci_remove_host(host, 0);
+	sdhci_free_host(host);
+	irq_dispose_mapping(host->irq);
+	iounmap(host->ioaddr);
+	return 0;
+}
+
+static const struct of_device_id sdhci_of_match[] = {
+#ifdef CONFIG_MMC_SDHCI_OF_ESDHC
+	{ .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, },
+	{ .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, },
+	{ .compatible = "fsl,esdhc", .data = &sdhci_esdhc, },
+#endif
+#ifdef CONFIG_MMC_SDHCI_OF_HLWD
+	{ .compatible = "nintendo,hollywood-sdhci", .data = &sdhci_hlwd, },
+#endif
+	{ .compatible = "generic-sdhci", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sdhci_of_match);
+
+static struct of_platform_driver sdhci_of_driver = {
+	.driver.name = "sdhci-of",
+	.match_table = sdhci_of_match,
+	.probe = sdhci_of_probe,
+	.remove = __devexit_p(sdhci_of_remove),
+	.suspend = sdhci_of_suspend,
+	.resume	= sdhci_of_resume,
+};
+
+static int __init sdhci_of_init(void)
+{
+	return of_register_platform_driver(&sdhci_of_driver);
+}
+module_init(sdhci_of_init);
+
+static void __exit sdhci_of_exit(void)
+{
+	of_unregister_platform_driver(&sdhci_of_driver);
+}
+module_exit(sdhci_of_exit);
+
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver");
+MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
+	      "Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
new file mode 100644
index 0000000..d5b11a1
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -0,0 +1,143 @@
+/*
+ * Freescale eSDHC controller driver.
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ *	    Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * 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.
+ */
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include "sdhci-of.h"
+#include "sdhci.h"
+
+/*
+ * Ops and quirks for the Freescale eSDHC controller.
+ */
+
+#define ESDHC_DMA_SYSCTL	0x40c
+#define ESDHC_DMA_SNOOP		0x00000040
+
+#define ESDHC_SYSTEM_CONTROL	0x2c
+#define ESDHC_CLOCK_MASK	0x0000fff0
+#define ESDHC_PREDIV_SHIFT	8
+#define ESDHC_DIVIDER_SHIFT	4
+#define ESDHC_CLOCK_PEREN	0x00000004
+#define ESDHC_CLOCK_HCKEN	0x00000002
+#define ESDHC_CLOCK_IPGEN	0x00000001
+
+#define ESDHC_HOST_CONTROL_RES	0x05
+
+static u16 esdhc_readw(struct sdhci_host *host, int reg)
+{
+	u16 ret;
+
+	if (unlikely(reg == SDHCI_HOST_VERSION))
+		ret = in_be16(host->ioaddr + reg);
+	else
+		ret = sdhci_be32bs_readw(host, reg);
+	return ret;
+}
+
+static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
+{
+	if (reg == SDHCI_BLOCK_SIZE) {
+		/*
+		 * Two last DMA bits are reserved, and first one is used for
+		 * non-standard blksz of 4096 bytes that we don't support
+		 * yet. So clear the DMA boundary bits.
+		 */
+		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
+	}
+	sdhci_be32bs_writew(host, val, reg);
+}
+
+static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	/* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */
+	if (reg == SDHCI_HOST_CONTROL)
+		val &= ~ESDHC_HOST_CONTROL_RES;
+	sdhci_be32bs_writeb(host, val, reg);
+}
+
+static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	int pre_div = 2;
+	int div = 1;
+
+	clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
+		  ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
+
+	if (clock == 0)
+		goto out;
+
+	while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
+		pre_div *= 2;
+
+	while (host->max_clk / pre_div / div > clock && div < 16)
+		div++;
+
+	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
+		clock, host->max_clk / pre_div / div);
+
+	pre_div >>= 1;
+	div--;
+
+	setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
+		  ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN |
+		  div << ESDHC_DIVIDER_SHIFT | pre_div << ESDHC_PREDIV_SHIFT);
+	mdelay(100);
+out:
+	host->clock = clock;
+}
+
+static int esdhc_enable_dma(struct sdhci_host *host)
+{
+	setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
+	return 0;
+}
+
+static unsigned int esdhc_get_max_clock(struct sdhci_host *host)
+{
+	struct sdhci_of_host *of_host = sdhci_priv(host);
+
+	return of_host->clock;
+}
+
+static unsigned int esdhc_get_min_clock(struct sdhci_host *host)
+{
+	struct sdhci_of_host *of_host = sdhci_priv(host);
+
+	return of_host->clock / 256 / 16;
+}
+
+struct sdhci_of_data sdhci_esdhc = {
+	.quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
+		  SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+		  SDHCI_QUIRK_NO_BUSY_IRQ |
+		  SDHCI_QUIRK_NONSTANDARD_CLOCK |
+		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+		  SDHCI_QUIRK_PIO_NEEDS_DELAY |
+		  SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET |
+		  SDHCI_QUIRK_NO_CARD_NO_RESET,
+	.ops = {
+		.readl = sdhci_be32bs_readl,
+		.readw = esdhc_readw,
+		.readb = sdhci_be32bs_readb,
+		.writel = sdhci_be32bs_writel,
+		.writew = esdhc_writew,
+		.writeb = esdhc_writeb,
+		.set_clock = esdhc_set_clock,
+		.enable_dma = esdhc_enable_dma,
+		.get_max_clock = esdhc_get_max_clock,
+		.get_min_clock = esdhc_get_min_clock,
+	},
+};
diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c
new file mode 100644
index 0000000..35117f3
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-hlwd.c
@@ -0,0 +1,65 @@
+/*
+ * drivers/mmc/host/sdhci-of-hlwd.c
+ *
+ * Nintendo Wii Secure Digital Host Controller Interface.
+ * Copyright (C) 2009 The GameCube Linux Team
+ * Copyright (C) 2009 Albert Herranz
+ *
+ * Based on sdhci-of-esdhc.c
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ *	    Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include "sdhci-of.h"
+#include "sdhci.h"
+
+/*
+ * Ops and quirks for the Nintendo Wii SDHCI controllers.
+ */
+
+/*
+ * We need a small delay after each write, or things go horribly wrong.
+ */
+#define SDHCI_HLWD_WRITE_DELAY	5 /* usecs */
+
+static void sdhci_hlwd_writel(struct sdhci_host *host, u32 val, int reg)
+{
+	sdhci_be32bs_writel(host, val, reg);
+	udelay(SDHCI_HLWD_WRITE_DELAY);
+}
+
+static void sdhci_hlwd_writew(struct sdhci_host *host, u16 val, int reg)
+{
+	sdhci_be32bs_writew(host, val, reg);
+	udelay(SDHCI_HLWD_WRITE_DELAY);
+}
+
+static void sdhci_hlwd_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	sdhci_be32bs_writeb(host, val, reg);
+	udelay(SDHCI_HLWD_WRITE_DELAY);
+}
+
+struct sdhci_of_data sdhci_hlwd = {
+	.quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
+		  SDHCI_QUIRK_32BIT_DMA_SIZE,
+	.ops = {
+		.readl = sdhci_be32bs_readl,
+		.readw = sdhci_be32bs_readw,
+		.readb = sdhci_be32bs_readb,
+		.writel = sdhci_hlwd_writel,
+		.writew = sdhci_hlwd_writew,
+		.writeb = sdhci_hlwd_writeb,
+	},
+};
diff --git a/drivers/mmc/host/sdhci-of.c b/drivers/mmc/host/sdhci-of.c
deleted file mode 100644
index 01ab916..0000000
--- a/drivers/mmc/host/sdhci-of.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * OpenFirmware bindings for Secure Digital Host Controller Interface.
- *
- * Copyright (c) 2007 Freescale Semiconductor, Inc.
- * Copyright (c) 2009 MontaVista Software, Inc.
- *
- * Authors: Xiaobo Xie <X.Xie@freescale.com>
- *	    Anton Vorontsov <avorontsov@ru.mvista.com>
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/mmc/host.h>
-#include <asm/machdep.h>
-#include "sdhci.h"
-
-struct sdhci_of_data {
-	unsigned int quirks;
-	struct sdhci_ops ops;
-};
-
-struct sdhci_of_host {
-	unsigned int clock;
-	u16 xfer_mode_shadow;
-};
-
-/*
- * Ops and quirks for the Freescale eSDHC controller.
- */
-
-#define ESDHC_DMA_SYSCTL	0x40c
-#define ESDHC_DMA_SNOOP		0x00000040
-
-#define ESDHC_SYSTEM_CONTROL	0x2c
-#define ESDHC_CLOCK_MASK	0x0000fff0
-#define ESDHC_PREDIV_SHIFT	8
-#define ESDHC_DIVIDER_SHIFT	4
-#define ESDHC_CLOCK_PEREN	0x00000004
-#define ESDHC_CLOCK_HCKEN	0x00000002
-#define ESDHC_CLOCK_IPGEN	0x00000001
-
-#define ESDHC_HOST_CONTROL_RES	0x05
-
-static u32 esdhc_readl(struct sdhci_host *host, int reg)
-{
-	return in_be32(host->ioaddr + reg);
-}
-
-static u16 esdhc_readw(struct sdhci_host *host, int reg)
-{
-	u16 ret;
-
-	if (unlikely(reg == SDHCI_HOST_VERSION))
-		ret = in_be16(host->ioaddr + reg);
-	else
-		ret = in_be16(host->ioaddr + (reg ^ 0x2));
-	return ret;
-}
-
-static u8 esdhc_readb(struct sdhci_host *host, int reg)
-{
-	return in_8(host->ioaddr + (reg ^ 0x3));
-}
-
-static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
-{
-	out_be32(host->ioaddr + reg, val);
-}
-
-static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
-{
-	struct sdhci_of_host *of_host = sdhci_priv(host);
-	int base = reg & ~0x3;
-	int shift = (reg & 0x2) * 8;
-
-	switch (reg) {
-	case SDHCI_TRANSFER_MODE:
-		/*
-		 * Postpone this write, we must do it together with a
-		 * command write that is down below.
-		 */
-		of_host->xfer_mode_shadow = val;
-		return;
-	case SDHCI_COMMAND:
-		esdhc_writel(host, val << 16 | of_host->xfer_mode_shadow,
-			     SDHCI_TRANSFER_MODE);
-		return;
-	case SDHCI_BLOCK_SIZE:
-		/*
-		 * Two last DMA bits are reserved, and first one is used for
-		 * non-standard blksz of 4096 bytes that we don't support
-		 * yet. So clear the DMA boundary bits.
-		 */
-		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
-		/* fall through */
-	}
-	clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
-}
-
-static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
-{
-	int base = reg & ~0x3;
-	int shift = (reg & 0x3) * 8;
-
-	/* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */
-	if (reg == SDHCI_HOST_CONTROL)
-		val &= ~ESDHC_HOST_CONTROL_RES;
-
-	clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
-}
-
-static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
-{
-	int pre_div = 2;
-	int div = 1;
-
-	clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
-		  ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
-
-	if (clock == 0)
-		goto out;
-
-	while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
-		pre_div *= 2;
-
-	while (host->max_clk / pre_div / div > clock && div < 16)
-		div++;
-
-	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
-		clock, host->max_clk / pre_div / div);
-
-	pre_div >>= 1;
-	div--;
-
-	setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
-		  ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN |
-		  div << ESDHC_DIVIDER_SHIFT | pre_div << ESDHC_PREDIV_SHIFT);
-	mdelay(100);
-out:
-	host->clock = clock;
-}
-
-static int esdhc_enable_dma(struct sdhci_host *host)
-{
-	setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
-	return 0;
-}
-
-static unsigned int esdhc_get_max_clock(struct sdhci_host *host)
-{
-	struct sdhci_of_host *of_host = sdhci_priv(host);
-
-	return of_host->clock;
-}
-
-static unsigned int esdhc_get_min_clock(struct sdhci_host *host)
-{
-	struct sdhci_of_host *of_host = sdhci_priv(host);
-
-	return of_host->clock / 256 / 16;
-}
-
-static struct sdhci_of_data sdhci_esdhc = {
-	.quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
-		  SDHCI_QUIRK_BROKEN_CARD_DETECTION |
-		  SDHCI_QUIRK_NO_BUSY_IRQ |
-		  SDHCI_QUIRK_NONSTANDARD_CLOCK |
-		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
-		  SDHCI_QUIRK_PIO_NEEDS_DELAY |
-		  SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET |
-		  SDHCI_QUIRK_NO_CARD_NO_RESET,
-	.ops = {
-		.readl = esdhc_readl,
-		.readw = esdhc_readw,
-		.readb = esdhc_readb,
-		.writel = esdhc_writel,
-		.writew = esdhc_writew,
-		.writeb = esdhc_writeb,
-		.set_clock = esdhc_set_clock,
-		.enable_dma = esdhc_enable_dma,
-		.get_max_clock = esdhc_get_max_clock,
-		.get_min_clock = esdhc_get_min_clock,
-	},
-};
-
-#ifdef CONFIG_PM
-
-static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state)
-{
-	struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
-
-	return mmc_suspend_host(host->mmc, state);
-}
-
-static int sdhci_of_resume(struct of_device *ofdev)
-{
-	struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
-
-	return mmc_resume_host(host->mmc);
-}
-
-#else
-
-#define sdhci_of_suspend NULL
-#define sdhci_of_resume NULL
-
-#endif
-
-static bool __devinit sdhci_of_wp_inverted(struct device_node *np)
-{
-	if (of_get_property(np, "sdhci,wp-inverted", NULL))
-		return true;
-
-	/* Old device trees don't have the wp-inverted property. */
-	return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds);
-}
-
-static int __devinit sdhci_of_probe(struct of_device *ofdev,
-				 const struct of_device_id *match)
-{
-	struct device_node *np = ofdev->node;
-	struct sdhci_of_data *sdhci_of_data = match->data;
-	struct sdhci_host *host;
-	struct sdhci_of_host *of_host;
-	const u32 *clk;
-	int size;
-	int ret;
-
-	if (!of_device_is_available(np))
-		return -ENODEV;
-
-	host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host));
-	if (IS_ERR(host))
-		return -ENOMEM;
-
-	of_host = sdhci_priv(host);
-	dev_set_drvdata(&ofdev->dev, host);
-
-	host->ioaddr = of_iomap(np, 0);
-	if (!host->ioaddr) {
-		ret = -ENOMEM;
-		goto err_addr_map;
-	}
-
-	host->irq = irq_of_parse_and_map(np, 0);
-	if (!host->irq) {
-		ret = -EINVAL;
-		goto err_no_irq;
-	}
-
-	host->hw_name = dev_name(&ofdev->dev);
-	if (sdhci_of_data) {
-		host->quirks = sdhci_of_data->quirks;
-		host->ops = &sdhci_of_data->ops;
-	}
-
-	if (of_get_property(np, "sdhci,1-bit-only", NULL))
-		host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
-
-	if (sdhci_of_wp_inverted(np))
-		host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
-
-	clk = of_get_property(np, "clock-frequency", &size);
-	if (clk && size == sizeof(*clk) && *clk)
-		of_host->clock = *clk;
-
-	ret = sdhci_add_host(host);
-	if (ret)
-		goto err_add_host;
-
-	return 0;
-
-err_add_host:
-	irq_dispose_mapping(host->irq);
-err_no_irq:
-	iounmap(host->ioaddr);
-err_addr_map:
-	sdhci_free_host(host);
-	return ret;
-}
-
-static int __devexit sdhci_of_remove(struct of_device *ofdev)
-{
-	struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
-
-	sdhci_remove_host(host, 0);
-	sdhci_free_host(host);
-	irq_dispose_mapping(host->irq);
-	iounmap(host->ioaddr);
-	return 0;
-}
-
-static const struct of_device_id sdhci_of_match[] = {
-	{ .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, },
-	{ .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, },
-	{ .compatible = "fsl,esdhc", .data = &sdhci_esdhc, },
-	{ .compatible = "generic-sdhci", },
-	{},
-};
-MODULE_DEVICE_TABLE(of, sdhci_of_match);
-
-static struct of_platform_driver sdhci_of_driver = {
-	.driver.name = "sdhci-of",
-	.match_table = sdhci_of_match,
-	.probe = sdhci_of_probe,
-	.remove = __devexit_p(sdhci_of_remove),
-	.suspend = sdhci_of_suspend,
-	.resume	= sdhci_of_resume,
-};
-
-static int __init sdhci_of_init(void)
-{
-	return of_register_platform_driver(&sdhci_of_driver);
-}
-module_init(sdhci_of_init);
-
-static void __exit sdhci_of_exit(void)
-{
-	of_unregister_platform_driver(&sdhci_of_driver);
-}
-module_exit(sdhci_of_exit);
-
-MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver");
-MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
-	      "Anton Vorontsov <avorontsov@ru.mvista.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci-of.h b/drivers/mmc/host/sdhci-of.h
new file mode 100644
index 0000000..ad09ad9
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of.h
@@ -0,0 +1,42 @@
+/*
+ * OpenFirmware bindings for Secure Digital Host Controller Interface.
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ *	    Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * 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.
+ */
+
+#ifndef __SDHCI_OF_H
+#define __SDHCI_OF_H
+
+#include <linux/types.h>
+#include "sdhci.h"
+
+struct sdhci_of_data {
+	unsigned int quirks;
+	struct sdhci_ops ops;
+};
+
+struct sdhci_of_host {
+	unsigned int clock;
+	u16 xfer_mode_shadow;
+};
+
+extern u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg);
+extern u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg);
+extern u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg);
+extern void sdhci_be32bs_writel(struct sdhci_host *host, u32 val, int reg);
+extern void sdhci_be32bs_writew(struct sdhci_host *host, u16 val, int reg);
+extern void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg);
+
+extern struct sdhci_of_data sdhci_esdhc;
+extern struct sdhci_of_data sdhci_hlwd;
+
+#endif /* __SDHCI_OF_H */
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index ce5f1d7..842f46f 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -8,6 +8,8 @@
  * the Free Software Foundation; either version 2 of the License, or (at
  * your option) any later version.
  */
+#ifndef __SDHCI_H
+#define __SDHCI_H
 
 #include <linux/scatterlist.h>
 #include <linux/compiler.h>
@@ -408,3 +410,5 @@
 extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state);
 extern int sdhci_resume_host(struct sdhci_host *host);
 #endif
+
+#endif /* __SDHCI_H */
diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c
index 74fa075..b13f641 100644
--- a/drivers/mtd/maps/pxa2xx-flash.c
+++ b/drivers/mtd/maps/pxa2xx-flash.c
@@ -20,14 +20,23 @@
 
 #include <asm/io.h>
 #include <mach/hardware.h>
-#include <asm/cacheflush.h>
 
 #include <asm/mach/flash.h>
 
+#define CACHELINESIZE	32
+
 static void pxa2xx_map_inval_cache(struct map_info *map, unsigned long from,
 				      ssize_t len)
 {
-	flush_ioremap_region(map->phys, map->cached, from, len);
+	unsigned long start = (unsigned long)map->cached + from;
+	unsigned long end = start + len;
+
+	start &= ~(CACHELINESIZE - 1);
+	while (start < end) {
+		/* invalidate D cache line */
+		asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start));
+		start += CACHELINESIZE;
+	}
 }
 
 struct pxa2xx_flash_info {
diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c
index 3aabf1e..76e640b 100644
--- a/drivers/pcmcia/pxa2xx_base.c
+++ b/drivers/pcmcia/pxa2xx_base.c
@@ -291,7 +291,7 @@
 		skt->nr = ops->first + i;
 		skt->ops = ops;
 		skt->socket.owner = ops->owner;
-		skt->socket.dev.parent = dev;
+		skt->socket.dev.parent = &dev->dev;
 		skt->socket.pci_irq = NO_IRQ;
 
 		ret = pxa2xx_drv_pcmcia_add_one(skt);
@@ -304,8 +304,8 @@
 			soc_pcmcia_remove_one(&sinfo->skt[i]);
 		kfree(sinfo);
 	} else {
-		pxa2xx_configure_sockets(dev);
-		dev_set_drvdata(dev, sinfo);
+		pxa2xx_configure_sockets(&dev->dev);
+		dev_set_drvdata(&dev->dev, sinfo);
 	}
 
 	return ret;
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c
index 259db7f..9630e7d 100644
--- a/drivers/rtc/rtc-ds1305.c
+++ b/drivers/rtc/rtc-ds1305.c
@@ -778,6 +778,8 @@
 					spi->irq, status);
 			goto fail1;
 		}
+
+		device_set_wakeup_capable(&spi->dev, 1);
 	}
 
 	/* export NVRAM */
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 8a99da6..c4ec5c1 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -881,6 +881,8 @@
 				"unable to request IRQ!\n");
 			goto exit_irq;
 		}
+
+		device_set_wakeup_capable(&client->dev, 1);
 		set_bit(HAS_ALARM, &ds1307->flags);
 		dev_dbg(&client->dev, "got IRQ %d\n", client->irq);
 	}
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index 713f7bf..5317bbc 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -383,6 +383,8 @@
 			dev_err(&client->dev, "unable to request IRQ\n");
 			goto out_free;
 		}
+
+		device_set_wakeup_capable(&client->dev, 1);
 	}
 
 	ds1374->rtc = rtc_device_register(client->name, &client->dev,
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 2d9d703..f55eb01 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -216,6 +216,17 @@
 	help
 	  SPI driver for Samsung S3C24XX series ARM SoCs
 
+config SPI_S3C24XX_FIQ
+	bool "S3C24XX driver with FIQ pseudo-DMA"
+	depends on SPI_S3C24XX
+	select FIQ
+	help
+	  Enable FIQ support for the S3C24XX SPI driver to provide pseudo
+	  DMA by using the fast-interrupt request framework, This allows
+	  the driver to get DMA-like performance when there are either
+	  no free DMA channels, or when doing transfers that required both
+	  TX and RX data paths.
+
 config SPI_S3C24XX_GPIO
 	tristate "Samsung S3C24XX series SPI by GPIO"
 	depends on ARCH_S3C2410 && EXPERIMENTAL
@@ -226,6 +237,13 @@
 	  the inbuilt hardware cannot provide the transfer mode, or
 	  where the board is using non hardware connected pins.
 
+config SPI_S3C64XX
+	tristate "Samsung S3C64XX series type SPI"
+	depends on ARCH_S3C64XX && EXPERIMENTAL
+	select S3C64XX_DMA
+	help
+	  SPI driver for Samsung S3C64XX and newer SoCs.
+
 config SPI_SH_MSIOF
 	tristate "SuperH MSIOF SPI controller"
 	depends on SUPERH && HAVE_CLK
@@ -289,6 +307,16 @@
 # Add new SPI master controllers in alphabetical order above this line
 #
 
+config SPI_DESIGNWARE
+	bool "DesignWare SPI controller core support"
+	depends on SPI_MASTER
+	help
+	  general driver for SPI controller core from DesignWare
+
+config SPI_DW_PCI
+	tristate "PCI interface driver for DW SPI core"
+	depends on SPI_DESIGNWARE && PCI
+
 #
 # There are lots of SPI device types, with sensors and memory
 # being probably the most widely used ones.
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index ed8c167..f3d2810 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -16,6 +16,8 @@
 obj-$(CONFIG_SPI_BITBANG)		+= spi_bitbang.o
 obj-$(CONFIG_SPI_AU1550)		+= au1550_spi.o
 obj-$(CONFIG_SPI_BUTTERFLY)		+= spi_butterfly.o
+obj-$(CONFIG_SPI_DESIGNWARE)		+= dw_spi.o
+obj-$(CONFIG_SPI_DW_PCI)		+= dw_spi_pci.o
 obj-$(CONFIG_SPI_GPIO)			+= spi_gpio.o
 obj-$(CONFIG_SPI_IMX)			+= spi_imx.o
 obj-$(CONFIG_SPI_LM70_LLP)		+= spi_lm70llp.o
@@ -30,7 +32,8 @@
 obj-$(CONFIG_SPI_MPC8xxx)		+= spi_mpc8xxx.o
 obj-$(CONFIG_SPI_PPC4xx)		+= spi_ppc4xx.o
 obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
-obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx.o
+obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx_hw.o
+obj-$(CONFIG_SPI_S3C64XX)		+= spi_s3c64xx.o
 obj-$(CONFIG_SPI_TXX9)			+= spi_txx9.o
 obj-$(CONFIG_SPI_XILINX)		+= xilinx_spi.o
 obj-$(CONFIG_SPI_XILINX_OF)		+= xilinx_spi_of.o
@@ -39,6 +42,11 @@
 obj-$(CONFIG_SPI_SH_MSIOF)		+= spi_sh_msiof.o
 obj-$(CONFIG_SPI_STMP3XXX)		+= spi_stmp.o
 obj-$(CONFIG_SPI_NUC900)		+= spi_nuc900.o
+
+# special build for s3c24xx spi driver with fiq support
+spi_s3c24xx_hw-y			:= spi_s3c24xx.o
+spi_s3c24xx_hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi_s3c24xx_fiq.o
+
 # 	... add above this line ...
 
 # SPI protocol drivers (device/link on bus)
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index f5b3fdb..d21c24e 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -189,14 +189,14 @@
 
 	/* use scratch buffer only when rx or tx data is unspecified */
 	if (xfer->rx_buf)
-		*rx_dma = xfer->rx_dma + xfer->len - len;
+		*rx_dma = xfer->rx_dma + xfer->len - *plen;
 	else {
 		*rx_dma = as->buffer_dma;
 		if (len > BUFFER_SIZE)
 			len = BUFFER_SIZE;
 	}
 	if (xfer->tx_buf)
-		*tx_dma = xfer->tx_dma + xfer->len - len;
+		*tx_dma = xfer->tx_dma + xfer->len - *plen;
 	else {
 		*tx_dma = as->buffer_dma;
 		if (len > BUFFER_SIZE)
@@ -788,7 +788,7 @@
 	spin_lock_init(&as->lock);
 	INIT_LIST_HEAD(&as->queue);
 	as->pdev = pdev;
-	as->regs = ioremap(regs->start, (regs->end - regs->start) + 1);
+	as->regs = ioremap(regs->start, resource_size(regs));
 	if (!as->regs)
 		goto out_free_buffer;
 	as->irq = irq;
diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c
new file mode 100644
index 0000000..31620fa
--- /dev/null
+++ b/drivers/spi/dw_spi.c
@@ -0,0 +1,944 @@
+/*
+ * dw_spi.c - Designware SPI core controller driver (refer pxa2xx_spi.c)
+ *
+ * Copyright (c) 2009, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+
+#include <linux/spi/dw_spi.h>
+#include <linux/spi/spi.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
+
+#define START_STATE	((void *)0)
+#define RUNNING_STATE	((void *)1)
+#define DONE_STATE	((void *)2)
+#define ERROR_STATE	((void *)-1)
+
+#define QUEUE_RUNNING	0
+#define QUEUE_STOPPED	1
+
+#define MRST_SPI_DEASSERT	0
+#define MRST_SPI_ASSERT		1
+
+/* Slave spi_dev related */
+struct chip_data {
+	u16 cr0;
+	u8 cs;			/* chip select pin */
+	u8 n_bytes;		/* current is a 1/2/4 byte op */
+	u8 tmode;		/* TR/TO/RO/EEPROM */
+	u8 type;		/* SPI/SSP/MicroWire */
+
+	u8 poll_mode;		/* 1 means use poll mode */
+
+	u32 dma_width;
+	u32 rx_threshold;
+	u32 tx_threshold;
+	u8 enable_dma;
+	u8 bits_per_word;
+	u16 clk_div;		/* baud rate divider */
+	u32 speed_hz;		/* baud rate */
+	int (*write)(struct dw_spi *dws);
+	int (*read)(struct dw_spi *dws);
+	void (*cs_control)(u32 command);
+};
+
+#ifdef CONFIG_DEBUG_FS
+static int spi_show_regs_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+#define SPI_REGS_BUFSIZE	1024
+static ssize_t  spi_show_regs(struct file *file, char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct dw_spi *dws;
+	char *buf;
+	u32 len = 0;
+	ssize_t ret;
+
+	dws = file->private_data;
+
+	buf = kzalloc(SPI_REGS_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return 0;
+
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"MRST SPI0 registers:\n");
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"=================================\n");
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"CTRL0: \t\t0x%08x\n", dw_readl(dws, ctrl0));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"CTRL1: \t\t0x%08x\n", dw_readl(dws, ctrl1));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"SSIENR: \t0x%08x\n", dw_readl(dws, ssienr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"SER: \t\t0x%08x\n", dw_readl(dws, ser));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"BAUDR: \t\t0x%08x\n", dw_readl(dws, baudr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"TXFTLR: \t0x%08x\n", dw_readl(dws, txfltr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"RXFTLR: \t0x%08x\n", dw_readl(dws, rxfltr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"TXFLR: \t\t0x%08x\n", dw_readl(dws, txflr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"RXFLR: \t\t0x%08x\n", dw_readl(dws, rxflr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"SR: \t\t0x%08x\n", dw_readl(dws, sr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"IMR: \t\t0x%08x\n", dw_readl(dws, imr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"ISR: \t\t0x%08x\n", dw_readl(dws, isr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"DMACR: \t\t0x%08x\n", dw_readl(dws, dmacr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"DMATDLR: \t0x%08x\n", dw_readl(dws, dmatdlr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"DMARDLR: \t0x%08x\n", dw_readl(dws, dmardlr));
+	len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+			"=================================\n");
+
+	ret =  simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations mrst_spi_regs_ops = {
+	.owner		= THIS_MODULE,
+	.open		= spi_show_regs_open,
+	.read		= spi_show_regs,
+};
+
+static int mrst_spi_debugfs_init(struct dw_spi *dws)
+{
+	dws->debugfs = debugfs_create_dir("mrst_spi", NULL);
+	if (!dws->debugfs)
+		return -ENOMEM;
+
+	debugfs_create_file("registers", S_IFREG | S_IRUGO,
+		dws->debugfs, (void *)dws, &mrst_spi_regs_ops);
+	return 0;
+}
+
+static void mrst_spi_debugfs_remove(struct dw_spi *dws)
+{
+	if (dws->debugfs)
+		debugfs_remove_recursive(dws->debugfs);
+}
+
+#else
+static inline int mrst_spi_debugfs_init(struct dw_spi *dws)
+{
+}
+
+static inline void mrst_spi_debugfs_remove(struct dw_spi *dws)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static void wait_till_not_busy(struct dw_spi *dws)
+{
+	unsigned long end = jiffies + usecs_to_jiffies(1000);
+
+	while (time_before(jiffies, end)) {
+		if (!(dw_readw(dws, sr) & SR_BUSY))
+			return;
+	}
+	dev_err(&dws->master->dev,
+		"DW SPI: Stutus keeps busy for 1000us after a read/write!\n");
+}
+
+static void flush(struct dw_spi *dws)
+{
+	while (dw_readw(dws, sr) & SR_RF_NOT_EMPT)
+		dw_readw(dws, dr);
+
+	wait_till_not_busy(dws);
+}
+
+static void null_cs_control(u32 command)
+{
+}
+
+static int null_writer(struct dw_spi *dws)
+{
+	u8 n_bytes = dws->n_bytes;
+
+	if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
+		|| (dws->tx == dws->tx_end))
+		return 0;
+	dw_writew(dws, dr, 0);
+	dws->tx += n_bytes;
+
+	wait_till_not_busy(dws);
+	return 1;
+}
+
+static int null_reader(struct dw_spi *dws)
+{
+	u8 n_bytes = dws->n_bytes;
+
+	while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
+		&& (dws->rx < dws->rx_end)) {
+		dw_readw(dws, dr);
+		dws->rx += n_bytes;
+	}
+	wait_till_not_busy(dws);
+	return dws->rx == dws->rx_end;
+}
+
+static int u8_writer(struct dw_spi *dws)
+{
+	if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
+		|| (dws->tx == dws->tx_end))
+		return 0;
+
+	dw_writew(dws, dr, *(u8 *)(dws->tx));
+	++dws->tx;
+
+	wait_till_not_busy(dws);
+	return 1;
+}
+
+static int u8_reader(struct dw_spi *dws)
+{
+	while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
+		&& (dws->rx < dws->rx_end)) {
+		*(u8 *)(dws->rx) = dw_readw(dws, dr);
+		++dws->rx;
+	}
+
+	wait_till_not_busy(dws);
+	return dws->rx == dws->rx_end;
+}
+
+static int u16_writer(struct dw_spi *dws)
+{
+	if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
+		|| (dws->tx == dws->tx_end))
+		return 0;
+
+	dw_writew(dws, dr, *(u16 *)(dws->tx));
+	dws->tx += 2;
+
+	wait_till_not_busy(dws);
+	return 1;
+}
+
+static int u16_reader(struct dw_spi *dws)
+{
+	u16 temp;
+
+	while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
+		&& (dws->rx < dws->rx_end)) {
+		temp = dw_readw(dws, dr);
+		*(u16 *)(dws->rx) = temp;
+		dws->rx += 2;
+	}
+
+	wait_till_not_busy(dws);
+	return dws->rx == dws->rx_end;
+}
+
+static void *next_transfer(struct dw_spi *dws)
+{
+	struct spi_message *msg = dws->cur_msg;
+	struct spi_transfer *trans = dws->cur_transfer;
+
+	/* Move to next transfer */
+	if (trans->transfer_list.next != &msg->transfers) {
+		dws->cur_transfer =
+			list_entry(trans->transfer_list.next,
+					struct spi_transfer,
+					transfer_list);
+		return RUNNING_STATE;
+	} else
+		return DONE_STATE;
+}
+
+/*
+ * Note: first step is the protocol driver prepares
+ * a dma-capable memory, and this func just need translate
+ * the virt addr to physical
+ */
+static int map_dma_buffers(struct dw_spi *dws)
+{
+	if (!dws->cur_msg->is_dma_mapped || !dws->dma_inited
+		|| !dws->cur_chip->enable_dma)
+		return 0;
+
+	if (dws->cur_transfer->tx_dma)
+		dws->tx_dma = dws->cur_transfer->tx_dma;
+
+	if (dws->cur_transfer->rx_dma)
+		dws->rx_dma = dws->cur_transfer->rx_dma;
+
+	return 1;
+}
+
+/* Caller already set message->status; dma and pio irqs are blocked */
+static void giveback(struct dw_spi *dws)
+{
+	struct spi_transfer *last_transfer;
+	unsigned long flags;
+	struct spi_message *msg;
+
+	spin_lock_irqsave(&dws->lock, flags);
+	msg = dws->cur_msg;
+	dws->cur_msg = NULL;
+	dws->cur_transfer = NULL;
+	dws->prev_chip = dws->cur_chip;
+	dws->cur_chip = NULL;
+	dws->dma_mapped = 0;
+	queue_work(dws->workqueue, &dws->pump_messages);
+	spin_unlock_irqrestore(&dws->lock, flags);
+
+	last_transfer = list_entry(msg->transfers.prev,
+					struct spi_transfer,
+					transfer_list);
+
+	if (!last_transfer->cs_change)
+		dws->cs_control(MRST_SPI_DEASSERT);
+
+	msg->state = NULL;
+	if (msg->complete)
+		msg->complete(msg->context);
+}
+
+static void int_error_stop(struct dw_spi *dws, const char *msg)
+{
+	/* Stop and reset hw */
+	flush(dws);
+	spi_enable_chip(dws, 0);
+
+	dev_err(&dws->master->dev, "%s\n", msg);
+	dws->cur_msg->state = ERROR_STATE;
+	tasklet_schedule(&dws->pump_transfers);
+}
+
+static void transfer_complete(struct dw_spi *dws)
+{
+	/* Update total byte transfered return count actual bytes read */
+	dws->cur_msg->actual_length += dws->len;
+
+	/* Move to next transfer */
+	dws->cur_msg->state = next_transfer(dws);
+
+	/* Handle end of message */
+	if (dws->cur_msg->state == DONE_STATE) {
+		dws->cur_msg->status = 0;
+		giveback(dws);
+	} else
+		tasklet_schedule(&dws->pump_transfers);
+}
+
+static irqreturn_t interrupt_transfer(struct dw_spi *dws)
+{
+	u16 irq_status, irq_mask = 0x3f;
+
+	irq_status = dw_readw(dws, isr) & irq_mask;
+	/* Error handling */
+	if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) {
+		dw_readw(dws, txoicr);
+		dw_readw(dws, rxoicr);
+		dw_readw(dws, rxuicr);
+		int_error_stop(dws, "interrupt_transfer: fifo overrun");
+		return IRQ_HANDLED;
+	}
+
+	/* INT comes from tx */
+	if (dws->tx && (irq_status & SPI_INT_TXEI)) {
+		while (dws->tx < dws->tx_end)
+			dws->write(dws);
+
+		if (dws->tx == dws->tx_end) {
+			spi_mask_intr(dws, SPI_INT_TXEI);
+			transfer_complete(dws);
+		}
+	}
+
+	/* INT comes from rx */
+	if (dws->rx && (irq_status & SPI_INT_RXFI)) {
+		if (dws->read(dws))
+			transfer_complete(dws);
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dw_spi_irq(int irq, void *dev_id)
+{
+	struct dw_spi *dws = dev_id;
+
+	if (!dws->cur_msg) {
+		spi_mask_intr(dws, SPI_INT_TXEI);
+		/* Never fail */
+		return IRQ_HANDLED;
+	}
+
+	return dws->transfer_handler(dws);
+}
+
+/* Must be called inside pump_transfers() */
+static void poll_transfer(struct dw_spi *dws)
+{
+	if (dws->tx) {
+		while (dws->write(dws))
+			dws->read(dws);
+	}
+
+	dws->read(dws);
+	transfer_complete(dws);
+}
+
+static void dma_transfer(struct dw_spi *dws, int cs_change)
+{
+}
+
+static void pump_transfers(unsigned long data)
+{
+	struct dw_spi *dws = (struct dw_spi *)data;
+	struct spi_message *message = NULL;
+	struct spi_transfer *transfer = NULL;
+	struct spi_transfer *previous = NULL;
+	struct spi_device *spi = NULL;
+	struct chip_data *chip = NULL;
+	u8 bits = 0;
+	u8 imask = 0;
+	u8 cs_change = 0;
+	u16 clk_div = 0;
+	u32 speed = 0;
+	u32 cr0 = 0;
+
+	/* Get current state information */
+	message = dws->cur_msg;
+	transfer = dws->cur_transfer;
+	chip = dws->cur_chip;
+	spi = message->spi;
+
+	if (message->state == ERROR_STATE) {
+		message->status = -EIO;
+		goto early_exit;
+	}
+
+	/* Handle end of message */
+	if (message->state == DONE_STATE) {
+		message->status = 0;
+		goto early_exit;
+	}
+
+	/* Delay if requested at end of transfer*/
+	if (message->state == RUNNING_STATE) {
+		previous = list_entry(transfer->transfer_list.prev,
+					struct spi_transfer,
+					transfer_list);
+		if (previous->delay_usecs)
+			udelay(previous->delay_usecs);
+	}
+
+	dws->n_bytes = chip->n_bytes;
+	dws->dma_width = chip->dma_width;
+	dws->cs_control = chip->cs_control;
+
+	dws->rx_dma = transfer->rx_dma;
+	dws->tx_dma = transfer->tx_dma;
+	dws->tx = (void *)transfer->tx_buf;
+	dws->tx_end = dws->tx + transfer->len;
+	dws->rx = transfer->rx_buf;
+	dws->rx_end = dws->rx + transfer->len;
+	dws->write = dws->tx ? chip->write : null_writer;
+	dws->read = dws->rx ? chip->read : null_reader;
+	dws->cs_change = transfer->cs_change;
+	dws->len = dws->cur_transfer->len;
+	if (chip != dws->prev_chip)
+		cs_change = 1;
+
+	cr0 = chip->cr0;
+
+	/* Handle per transfer options for bpw and speed */
+	if (transfer->speed_hz) {
+		speed = chip->speed_hz;
+
+		if (transfer->speed_hz != speed) {
+			speed = transfer->speed_hz;
+			if (speed > dws->max_freq) {
+				printk(KERN_ERR "MRST SPI0: unsupported"
+					"freq: %dHz\n", speed);
+				message->status = -EIO;
+				goto early_exit;
+			}
+
+			/* clk_div doesn't support odd number */
+			clk_div = dws->max_freq / speed;
+			clk_div = (clk_div >> 1) << 1;
+
+			chip->speed_hz = speed;
+			chip->clk_div = clk_div;
+		}
+	}
+	if (transfer->bits_per_word) {
+		bits = transfer->bits_per_word;
+
+		switch (bits) {
+		case 8:
+			dws->n_bytes = 1;
+			dws->dma_width = 1;
+			dws->read = (dws->read != null_reader) ?
+					u8_reader : null_reader;
+			dws->write = (dws->write != null_writer) ?
+					u8_writer : null_writer;
+			break;
+		case 16:
+			dws->n_bytes = 2;
+			dws->dma_width = 2;
+			dws->read = (dws->read != null_reader) ?
+					u16_reader : null_reader;
+			dws->write = (dws->write != null_writer) ?
+					u16_writer : null_writer;
+			break;
+		default:
+			printk(KERN_ERR "MRST SPI0: unsupported bits:"
+				"%db\n", bits);
+			message->status = -EIO;
+			goto early_exit;
+		}
+
+		cr0 = (bits - 1)
+			| (chip->type << SPI_FRF_OFFSET)
+			| (spi->mode << SPI_MODE_OFFSET)
+			| (chip->tmode << SPI_TMOD_OFFSET);
+	}
+	message->state = RUNNING_STATE;
+
+	/* Check if current transfer is a DMA transaction */
+	dws->dma_mapped = map_dma_buffers(dws);
+
+	if (!dws->dma_mapped && !chip->poll_mode) {
+		if (dws->rx)
+			imask |= SPI_INT_RXFI;
+		if (dws->tx)
+			imask |= SPI_INT_TXEI;
+		dws->transfer_handler = interrupt_transfer;
+	}
+
+	/*
+	 * Reprogram registers only if
+	 *	1. chip select changes
+	 *	2. clk_div is changed
+	 *	3. control value changes
+	 */
+	if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div) {
+		spi_enable_chip(dws, 0);
+
+		if (dw_readw(dws, ctrl0) != cr0)
+			dw_writew(dws, ctrl0, cr0);
+
+		/* Set the interrupt mask, for poll mode just diable all int */
+		spi_mask_intr(dws, 0xff);
+		if (!chip->poll_mode)
+			spi_umask_intr(dws, imask);
+
+		spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
+		spi_chip_sel(dws, spi->chip_select);
+		spi_enable_chip(dws, 1);
+
+		if (cs_change)
+			dws->prev_chip = chip;
+	}
+
+	if (dws->dma_mapped)
+		dma_transfer(dws, cs_change);
+
+	if (chip->poll_mode)
+		poll_transfer(dws);
+
+	return;
+
+early_exit:
+	giveback(dws);
+	return;
+}
+
+static void pump_messages(struct work_struct *work)
+{
+	struct dw_spi *dws =
+		container_of(work, struct dw_spi, pump_messages);
+	unsigned long flags;
+
+	/* Lock queue and check for queue work */
+	spin_lock_irqsave(&dws->lock, flags);
+	if (list_empty(&dws->queue) || dws->run == QUEUE_STOPPED) {
+		dws->busy = 0;
+		spin_unlock_irqrestore(&dws->lock, flags);
+		return;
+	}
+
+	/* Make sure we are not already running a message */
+	if (dws->cur_msg) {
+		spin_unlock_irqrestore(&dws->lock, flags);
+		return;
+	}
+
+	/* Extract head of queue */
+	dws->cur_msg = list_entry(dws->queue.next, struct spi_message, queue);
+	list_del_init(&dws->cur_msg->queue);
+
+	/* Initial message state*/
+	dws->cur_msg->state = START_STATE;
+	dws->cur_transfer = list_entry(dws->cur_msg->transfers.next,
+						struct spi_transfer,
+						transfer_list);
+	dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi);
+
+	/* Mark as busy and launch transfers */
+	tasklet_schedule(&dws->pump_transfers);
+
+	dws->busy = 1;
+	spin_unlock_irqrestore(&dws->lock, flags);
+}
+
+/* spi_device use this to queue in their spi_msg */
+static int dw_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+	struct dw_spi *dws = spi_master_get_devdata(spi->master);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dws->lock, flags);
+
+	if (dws->run == QUEUE_STOPPED) {
+		spin_unlock_irqrestore(&dws->lock, flags);
+		return -ESHUTDOWN;
+	}
+
+	msg->actual_length = 0;
+	msg->status = -EINPROGRESS;
+	msg->state = START_STATE;
+
+	list_add_tail(&msg->queue, &dws->queue);
+
+	if (dws->run == QUEUE_RUNNING && !dws->busy) {
+
+		if (dws->cur_transfer || dws->cur_msg)
+			queue_work(dws->workqueue,
+					&dws->pump_messages);
+		else {
+			/* If no other data transaction in air, just go */
+			spin_unlock_irqrestore(&dws->lock, flags);
+			pump_messages(&dws->pump_messages);
+			return 0;
+		}
+	}
+
+	spin_unlock_irqrestore(&dws->lock, flags);
+	return 0;
+}
+
+/* This may be called twice for each spi dev */
+static int dw_spi_setup(struct spi_device *spi)
+{
+	struct dw_spi_chip *chip_info = NULL;
+	struct chip_data *chip;
+
+	if (spi->bits_per_word != 8 && spi->bits_per_word != 16)
+		return -EINVAL;
+
+	/* Only alloc on first setup */
+	chip = spi_get_ctldata(spi);
+	if (!chip) {
+		chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
+		if (!chip)
+			return -ENOMEM;
+
+		chip->cs_control = null_cs_control;
+		chip->enable_dma = 0;
+	}
+
+	/*
+	 * Protocol drivers may change the chip settings, so...
+	 * if chip_info exists, use it
+	 */
+	chip_info = spi->controller_data;
+
+	/* chip_info doesn't always exist */
+	if (chip_info) {
+		if (chip_info->cs_control)
+			chip->cs_control = chip_info->cs_control;
+
+		chip->poll_mode = chip_info->poll_mode;
+		chip->type = chip_info->type;
+
+		chip->rx_threshold = 0;
+		chip->tx_threshold = 0;
+
+		chip->enable_dma = chip_info->enable_dma;
+	}
+
+	if (spi->bits_per_word <= 8) {
+		chip->n_bytes = 1;
+		chip->dma_width = 1;
+		chip->read = u8_reader;
+		chip->write = u8_writer;
+	} else if (spi->bits_per_word <= 16) {
+		chip->n_bytes = 2;
+		chip->dma_width = 2;
+		chip->read = u16_reader;
+		chip->write = u16_writer;
+	} else {
+		/* Never take >16b case for MRST SPIC */
+		dev_err(&spi->dev, "invalid wordsize\n");
+		return -EINVAL;
+	}
+	chip->bits_per_word = spi->bits_per_word;
+
+	chip->speed_hz = spi->max_speed_hz;
+	if (chip->speed_hz)
+		chip->clk_div = 25000000 / chip->speed_hz;
+	else
+		chip->clk_div = 8;	/* default value */
+
+	chip->tmode = 0; /* Tx & Rx */
+	/* Default SPI mode is SCPOL = 0, SCPH = 0 */
+	chip->cr0 = (chip->bits_per_word - 1)
+			| (chip->type << SPI_FRF_OFFSET)
+			| (spi->mode  << SPI_MODE_OFFSET)
+			| (chip->tmode << SPI_TMOD_OFFSET);
+
+	spi_set_ctldata(spi, chip);
+	return 0;
+}
+
+static void dw_spi_cleanup(struct spi_device *spi)
+{
+	struct chip_data *chip = spi_get_ctldata(spi);
+	kfree(chip);
+}
+
+static int __init init_queue(struct dw_spi *dws)
+{
+	INIT_LIST_HEAD(&dws->queue);
+	spin_lock_init(&dws->lock);
+
+	dws->run = QUEUE_STOPPED;
+	dws->busy = 0;
+
+	tasklet_init(&dws->pump_transfers,
+			pump_transfers,	(unsigned long)dws);
+
+	INIT_WORK(&dws->pump_messages, pump_messages);
+	dws->workqueue = create_singlethread_workqueue(
+					dev_name(dws->master->dev.parent));
+	if (dws->workqueue == NULL)
+		return -EBUSY;
+
+	return 0;
+}
+
+static int start_queue(struct dw_spi *dws)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dws->lock, flags);
+
+	if (dws->run == QUEUE_RUNNING || dws->busy) {
+		spin_unlock_irqrestore(&dws->lock, flags);
+		return -EBUSY;
+	}
+
+	dws->run = QUEUE_RUNNING;
+	dws->cur_msg = NULL;
+	dws->cur_transfer = NULL;
+	dws->cur_chip = NULL;
+	dws->prev_chip = NULL;
+	spin_unlock_irqrestore(&dws->lock, flags);
+
+	queue_work(dws->workqueue, &dws->pump_messages);
+
+	return 0;
+}
+
+static int stop_queue(struct dw_spi *dws)
+{
+	unsigned long flags;
+	unsigned limit = 50;
+	int status = 0;
+
+	spin_lock_irqsave(&dws->lock, flags);
+	dws->run = QUEUE_STOPPED;
+	while (!list_empty(&dws->queue) && dws->busy && limit--) {
+		spin_unlock_irqrestore(&dws->lock, flags);
+		msleep(10);
+		spin_lock_irqsave(&dws->lock, flags);
+	}
+
+	if (!list_empty(&dws->queue) || dws->busy)
+		status = -EBUSY;
+	spin_unlock_irqrestore(&dws->lock, flags);
+
+	return status;
+}
+
+static int destroy_queue(struct dw_spi *dws)
+{
+	int status;
+
+	status = stop_queue(dws);
+	if (status != 0)
+		return status;
+	destroy_workqueue(dws->workqueue);
+	return 0;
+}
+
+/* Restart the controller, disable all interrupts, clean rx fifo */
+static void spi_hw_init(struct dw_spi *dws)
+{
+	spi_enable_chip(dws, 0);
+	spi_mask_intr(dws, 0xff);
+	spi_enable_chip(dws, 1);
+	flush(dws);
+}
+
+int __devinit dw_spi_add_host(struct dw_spi *dws)
+{
+	struct spi_master *master;
+	int ret;
+
+	BUG_ON(dws == NULL);
+
+	master = spi_alloc_master(dws->parent_dev, 0);
+	if (!master) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	dws->master = master;
+	dws->type = SSI_MOTO_SPI;
+	dws->prev_chip = NULL;
+	dws->dma_inited = 0;
+	dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60);
+
+	ret = request_irq(dws->irq, dw_spi_irq, 0,
+			"dw_spi", dws);
+	if (ret < 0) {
+		dev_err(&master->dev, "can not get IRQ\n");
+		goto err_free_master;
+	}
+
+	master->mode_bits = SPI_CPOL | SPI_CPHA;
+	master->bus_num = dws->bus_num;
+	master->num_chipselect = dws->num_cs;
+	master->cleanup = dw_spi_cleanup;
+	master->setup = dw_spi_setup;
+	master->transfer = dw_spi_transfer;
+
+	dws->dma_inited = 0;
+
+	/* Basic HW init */
+	spi_hw_init(dws);
+
+	/* Initial and start queue */
+	ret = init_queue(dws);
+	if (ret) {
+		dev_err(&master->dev, "problem initializing queue\n");
+		goto err_diable_hw;
+	}
+	ret = start_queue(dws);
+	if (ret) {
+		dev_err(&master->dev, "problem starting queue\n");
+		goto err_diable_hw;
+	}
+
+	spi_master_set_devdata(master, dws);
+	ret = spi_register_master(master);
+	if (ret) {
+		dev_err(&master->dev, "problem registering spi master\n");
+		goto err_queue_alloc;
+	}
+
+	mrst_spi_debugfs_init(dws);
+	return 0;
+
+err_queue_alloc:
+	destroy_queue(dws);
+err_diable_hw:
+	spi_enable_chip(dws, 0);
+	free_irq(dws->irq, dws);
+err_free_master:
+	spi_master_put(master);
+exit:
+	return ret;
+}
+EXPORT_SYMBOL(dw_spi_add_host);
+
+void __devexit dw_spi_remove_host(struct dw_spi *dws)
+{
+	int status = 0;
+
+	if (!dws)
+		return;
+	mrst_spi_debugfs_remove(dws);
+
+	/* Remove the queue */
+	status = destroy_queue(dws);
+	if (status != 0)
+		dev_err(&dws->master->dev, "dw_spi_remove: workqueue will not "
+			"complete, message memory not freed\n");
+
+	spi_enable_chip(dws, 0);
+	/* Disable clk */
+	spi_set_clk(dws, 0);
+	free_irq(dws->irq, dws);
+
+	/* Disconnect from the SPI framework */
+	spi_unregister_master(dws->master);
+}
+
+int dw_spi_suspend_host(struct dw_spi *dws)
+{
+	int ret = 0;
+
+	ret = stop_queue(dws);
+	if (ret)
+		return ret;
+	spi_enable_chip(dws, 0);
+	spi_set_clk(dws, 0);
+	return ret;
+}
+EXPORT_SYMBOL(dw_spi_suspend_host);
+
+int dw_spi_resume_host(struct dw_spi *dws)
+{
+	int ret;
+
+	spi_hw_init(dws);
+	ret = start_queue(dws);
+	if (ret)
+		dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret);
+	return ret;
+}
+EXPORT_SYMBOL(dw_spi_resume_host);
+
+MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
+MODULE_DESCRIPTION("Driver for DesignWare SPI controller core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/dw_spi_pci.c b/drivers/spi/dw_spi_pci.c
new file mode 100644
index 0000000..34ba691
--- /dev/null
+++ b/drivers/spi/dw_spi_pci.c
@@ -0,0 +1,169 @@
+/*
+ * mrst_spi_pci.c - PCI interface driver for DW SPI Core
+ *
+ * Copyright (c) 2009, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/spi/dw_spi.h>
+#include <linux/spi/spi.h>
+
+#define DRIVER_NAME "dw_spi_pci"
+
+struct dw_spi_pci {
+	struct pci_dev		*pdev;
+	struct dw_spi		dws;
+};
+
+static int __devinit spi_pci_probe(struct pci_dev *pdev,
+	const struct pci_device_id *ent)
+{
+	struct dw_spi_pci *dwpci;
+	struct dw_spi *dws;
+	int pci_bar = 0;
+	int ret;
+
+	printk(KERN_INFO "DW: found PCI SPI controller(ID: %04x:%04x)\n",
+		pdev->vendor, pdev->device);
+
+	ret = pci_enable_device(pdev);
+	if (ret)
+		return ret;
+
+	dwpci = kzalloc(sizeof(struct dw_spi_pci), GFP_KERNEL);
+	if (!dwpci) {
+		ret = -ENOMEM;
+		goto err_disable;
+	}
+
+	dwpci->pdev = pdev;
+	dws = &dwpci->dws;
+
+	/* Get basic io resource and map it */
+	dws->paddr = pci_resource_start(pdev, pci_bar);
+	dws->iolen = pci_resource_len(pdev, pci_bar);
+
+	ret = pci_request_region(pdev, pci_bar, dev_name(&pdev->dev));
+	if (ret)
+		goto err_kfree;
+
+	dws->regs = ioremap_nocache((unsigned long)dws->paddr,
+				pci_resource_len(pdev, pci_bar));
+	if (!dws->regs) {
+		ret = -ENOMEM;
+		goto err_release_reg;
+	}
+
+	dws->parent_dev = &pdev->dev;
+	dws->bus_num = 0;
+	dws->num_cs = 4;
+	dws->max_freq = 25000000;	/* for Moorestwon */
+	dws->irq = pdev->irq;
+
+	ret = dw_spi_add_host(dws);
+	if (ret)
+		goto err_unmap;
+
+	/* PCI hook and SPI hook use the same drv data */
+	pci_set_drvdata(pdev, dwpci);
+	return 0;
+
+err_unmap:
+	iounmap(dws->regs);
+err_release_reg:
+	pci_release_region(pdev, pci_bar);
+err_kfree:
+	kfree(dwpci);
+err_disable:
+	pci_disable_device(pdev);
+	return ret;
+}
+
+static void __devexit spi_pci_remove(struct pci_dev *pdev)
+{
+	struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
+
+	pci_set_drvdata(pdev, NULL);
+	iounmap(dwpci->dws.regs);
+	pci_release_region(pdev, 0);
+	kfree(dwpci);
+	pci_disable_device(pdev);
+}
+
+#ifdef CONFIG_PM
+static int spi_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
+	int ret;
+
+	ret = dw_spi_suspend_host(&dwpci->dws);
+	if (ret)
+		return ret;
+	pci_save_state(pdev);
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	return ret;
+}
+
+static int spi_resume(struct pci_dev *pdev)
+{
+	struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	ret = pci_enable_device(pdev);
+	if (ret)
+		return ret;
+	return dw_spi_resume_host(&dwpci->dws);
+}
+#else
+#define spi_suspend	NULL
+#define spi_resume	NULL
+#endif
+
+static const struct pci_device_id pci_ids[] __devinitdata = {
+	/* Intel Moorestown platform SPI controller 0 */
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) },
+	{},
+};
+
+static struct pci_driver dw_spi_driver = {
+	.name =		DRIVER_NAME,
+	.id_table =	pci_ids,
+	.probe =	spi_pci_probe,
+	.remove =	__devexit_p(spi_pci_remove),
+	.suspend =	spi_suspend,
+	.resume	=	spi_resume,
+};
+
+static int __init mrst_spi_init(void)
+{
+	return pci_register_driver(&dw_spi_driver);
+}
+
+static void __exit mrst_spi_exit(void)
+{
+	pci_unregister_driver(&dw_spi_driver);
+}
+
+module_init(mrst_spi_init);
+module_exit(mrst_spi_exit);
+
+MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
+MODULE_DESCRIPTION("PCI interface driver for DW SPI Core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c
index 73e24ef..1d41058 100644
--- a/drivers/spi/spi_bfin5xx.c
+++ b/drivers/spi/spi_bfin5xx.c
@@ -1294,7 +1294,7 @@
 		goto out_error_get_res;
 	}
 
-	drv_data->regs_base = ioremap(res->start, (res->end - res->start + 1));
+	drv_data->regs_base = ioremap(res->start, resource_size(res));
 	if (drv_data->regs_base == NULL) {
 		dev_err(dev, "Cannot map IO\n");
 		status = -ENXIO;
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index e9390d7..1fb2a6e 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -1013,7 +1013,7 @@
 
 	init_completion(&mpc8xxx_spi->done);
 
-	mpc8xxx_spi->base = ioremap(mem->start, mem->end - mem->start + 1);
+	mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem));
 	if (mpc8xxx_spi->base == NULL) {
 		ret = -ENOMEM;
 		goto err_ioremap;
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c
index 2765915..c010733 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi_s3c24xx.c
@@ -1,7 +1,7 @@
 /* linux/drivers/spi/spi_s3c24xx.c
  *
  * Copyright (c) 2006 Ben Dooks
- * Copyright (c) 2006 Simtec Electronics
+ * Copyright 2006-2009 Simtec Electronics
  *	Ben Dooks <ben@simtec.co.uk>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -28,6 +28,11 @@
 #include <plat/regs-spi.h>
 #include <mach/spi.h>
 
+#include <plat/fiq.h>
+#include <asm/fiq.h>
+
+#include "spi_s3c24xx_fiq.h"
+
 /**
  * s3c24xx_spi_devstate - per device data
  * @hz: Last frequency calculated for @sppre field.
@@ -42,6 +47,13 @@
 	u8		sppre;
 };
 
+enum spi_fiq_mode {
+	FIQ_MODE_NONE	= 0,
+	FIQ_MODE_TX	= 1,
+	FIQ_MODE_RX	= 2,
+	FIQ_MODE_TXRX	= 3,
+};
+
 struct s3c24xx_spi {
 	/* bitbang has to be first */
 	struct spi_bitbang	 bitbang;
@@ -52,6 +64,11 @@
 	int			 len;
 	int			 count;
 
+	struct fiq_handler	 fiq_handler;
+	enum spi_fiq_mode	 fiq_mode;
+	unsigned char		 fiq_inuse;
+	unsigned char		 fiq_claimed;
+
 	void			(*set_cs)(struct s3c2410_spi_info *spi,
 					  int cs, int pol);
 
@@ -67,6 +84,7 @@
 	struct s3c2410_spi_info *pdata;
 };
 
+
 #define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT)
 #define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP)
 
@@ -127,7 +145,7 @@
 	}
 
 	if (spi->mode != cs->mode) {
-		u8 spcon = SPCON_DEFAULT;
+		u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK;
 
 		if (spi->mode & SPI_CPHA)
 			spcon |= S3C2410_SPCON_CPHA_FMTB;
@@ -214,13 +232,196 @@
 	return hw->tx ? hw->tx[count] : 0;
 }
 
+#ifdef CONFIG_SPI_S3C24XX_FIQ
+/* Support for FIQ based pseudo-DMA to improve the transfer speed.
+ *
+ * This code uses the assembly helper in spi_s3c24xx_spi.S which is
+ * used by the FIQ core to move data between main memory and the peripheral
+ * block. Since this is code running on the processor, there is no problem
+ * with cache coherency of the buffers, so we can use any buffer we like.
+ */
+
+/**
+ * struct spi_fiq_code - FIQ code and header
+ * @length: The length of the code fragment, excluding this header.
+ * @ack_offset: The offset from @data to the word to place the IRQ ACK bit at.
+ * @data: The code itself to install as a FIQ handler.
+ */
+struct spi_fiq_code {
+	u32	length;
+	u32	ack_offset;
+	u8	data[0];
+};
+
+extern struct spi_fiq_code s3c24xx_spi_fiq_txrx;
+extern struct spi_fiq_code s3c24xx_spi_fiq_tx;
+extern struct spi_fiq_code s3c24xx_spi_fiq_rx;
+
+/**
+ * ack_bit - turn IRQ into IRQ acknowledgement bit
+ * @irq: The interrupt number
+ *
+ * Returns the bit to write to the interrupt acknowledge register.
+ */
+static inline u32 ack_bit(unsigned int irq)
+{
+	return 1 << (irq - IRQ_EINT0);
+}
+
+/**
+ * s3c24xx_spi_tryfiq - attempt to claim and setup FIQ for transfer
+ * @hw: The hardware state.
+ *
+ * Claim the FIQ handler (only one can be active at any one time) and
+ * then setup the correct transfer code for this transfer.
+ *
+ * This call updates all the necessary state information if sucessful,
+ * so the caller does not need to do anything more than start the transfer
+ * as normal, since the IRQ will have been re-routed to the FIQ handler.
+*/
+void s3c24xx_spi_tryfiq(struct s3c24xx_spi *hw)
+{
+	struct pt_regs regs;
+	enum spi_fiq_mode mode;
+	struct spi_fiq_code *code;
+	int ret;
+
+	if (!hw->fiq_claimed) {
+		/* try and claim fiq if we haven't got it, and if not
+		 * then return and simply use another transfer method */
+
+		ret = claim_fiq(&hw->fiq_handler);
+		if (ret)
+			return;
+	}
+
+	if (hw->tx && !hw->rx)
+		mode = FIQ_MODE_TX;
+	else if (hw->rx && !hw->tx)
+		mode = FIQ_MODE_RX;
+	else
+		mode = FIQ_MODE_TXRX;
+
+	regs.uregs[fiq_rspi] = (long)hw->regs;
+	regs.uregs[fiq_rrx]  = (long)hw->rx;
+	regs.uregs[fiq_rtx]  = (long)hw->tx + 1;
+	regs.uregs[fiq_rcount] = hw->len - 1;
+	regs.uregs[fiq_rirq] = (long)S3C24XX_VA_IRQ;
+
+	set_fiq_regs(&regs);
+
+	if (hw->fiq_mode != mode) {
+		u32 *ack_ptr;
+
+		hw->fiq_mode = mode;
+
+		switch (mode) {
+		case FIQ_MODE_TX:
+			code = &s3c24xx_spi_fiq_tx;
+			break;
+		case FIQ_MODE_RX:
+			code = &s3c24xx_spi_fiq_rx;
+			break;
+		case FIQ_MODE_TXRX:
+			code = &s3c24xx_spi_fiq_txrx;
+			break;
+		default:
+			code = NULL;
+		}
+
+		BUG_ON(!code);
+
+		ack_ptr = (u32 *)&code->data[code->ack_offset];
+		*ack_ptr = ack_bit(hw->irq);
+
+		set_fiq_handler(&code->data, code->length);
+	}
+
+	s3c24xx_set_fiq(hw->irq, true);
+
+	hw->fiq_mode = mode;
+	hw->fiq_inuse = 1;
+}
+
+/**
+ * s3c24xx_spi_fiqop - FIQ core code callback
+ * @pw: Data registered with the handler
+ * @release: Whether this is a release or a return.
+ *
+ * Called by the FIQ code when another module wants to use the FIQ, so
+ * return whether we are currently using this or not and then update our
+ * internal state.
+ */
+static int s3c24xx_spi_fiqop(void *pw, int release)
+{
+	struct s3c24xx_spi *hw = pw;
+	int ret = 0;
+
+	if (release) {
+		if (hw->fiq_inuse)
+			ret = -EBUSY;
+
+		/* note, we do not need to unroute the FIQ, as the FIQ
+		 * vector code de-routes it to signal the end of transfer */
+
+		hw->fiq_mode = FIQ_MODE_NONE;
+		hw->fiq_claimed = 0;
+	} else {
+		hw->fiq_claimed = 1;
+	}
+
+	return ret;
+}
+
+/**
+ * s3c24xx_spi_initfiq - setup the information for the FIQ core
+ * @hw: The hardware state.
+ *
+ * Setup the fiq_handler block to pass to the FIQ core.
+ */
+static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *hw)
+{
+	hw->fiq_handler.dev_id = hw;
+	hw->fiq_handler.name = dev_name(hw->dev);
+	hw->fiq_handler.fiq_op = s3c24xx_spi_fiqop;
+}
+
+/**
+ * s3c24xx_spi_usefiq - return if we should be using FIQ.
+ * @hw: The hardware state.
+ *
+ * Return true if the platform data specifies whether this channel is
+ * allowed to use the FIQ.
+ */
+static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *hw)
+{
+	return hw->pdata->use_fiq;
+}
+
+/**
+ * s3c24xx_spi_usingfiq - return if channel is using FIQ
+ * @spi: The hardware state.
+ *
+ * Return whether the channel is currently using the FIQ (separate from
+ * whether the FIQ is claimed).
+ */
+static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *spi)
+{
+	return spi->fiq_inuse;
+}
+#else
+
+static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *s) { }
+static inline void s3c24xx_spi_tryfiq(struct s3c24xx_spi *s) { }
+static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *s) { return false; }
+static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *s) { return false; }
+
+#endif /* CONFIG_SPI_S3C24XX_FIQ */
+
 static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
 {
 	struct s3c24xx_spi *hw = to_hw(spi);
 
-	dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
-		t->tx_buf, t->rx_buf, t->len);
-
 	hw->tx = t->tx_buf;
 	hw->rx = t->rx_buf;
 	hw->len = t->len;
@@ -228,11 +429,14 @@
 
 	init_completion(&hw->done);
 
+	hw->fiq_inuse = 0;
+	if (s3c24xx_spi_usefiq(hw) && t->len >= 3)
+		s3c24xx_spi_tryfiq(hw);
+
 	/* send the first byte */
 	writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
 
 	wait_for_completion(&hw->done);
-
 	return hw->count;
 }
 
@@ -254,17 +458,27 @@
 		goto irq_done;
 	}
 
-	hw->count++;
+	if (!s3c24xx_spi_usingfiq(hw)) {
+		hw->count++;
 
-	if (hw->rx)
-		hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
+		if (hw->rx)
+			hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
 
-	count++;
+		count++;
 
-	if (count < hw->len)
-		writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
-	else
+		if (count < hw->len)
+			writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
+		else
+			complete(&hw->done);
+	} else {
+		hw->count = hw->len;
+		hw->fiq_inuse = 0;
+
+		if (hw->rx)
+			hw->rx[hw->len-1] = readb(hw->regs + S3C2410_SPRDAT);
+
 		complete(&hw->done);
+	}
 
  irq_done:
 	return IRQ_HANDLED;
@@ -322,6 +536,10 @@
 	platform_set_drvdata(pdev, hw);
 	init_completion(&hw->done);
 
+	/* initialise fiq handler */
+
+	s3c24xx_spi_initfiq(hw);
+
 	/* setup the master state. */
 
 	/* the spi->mode bits understood by this driver: */
diff --git a/drivers/spi/spi_s3c24xx_fiq.S b/drivers/spi/spi_s3c24xx_fiq.S
new file mode 100644
index 0000000..3793cae
--- /dev/null
+++ b/drivers/spi/spi_s3c24xx_fiq.S
@@ -0,0 +1,116 @@
+/* linux/drivers/spi/spi_s3c24xx_fiq.S
+ *
+ * Copyright 2009 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX SPI - FIQ pseudo-DMA transfer 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/linkage.h>
+#include <asm/assembler.h>
+
+#include <mach/map.h>
+#include <mach/regs-irq.h>
+#include <plat/regs-spi.h>
+
+#include "spi_s3c24xx_fiq.h"
+
+	.text
+
+	@ entry to these routines is as follows, with the register names
+	@ defined in fiq.h so that they can be shared with the C files which
+	@ setup the calling registers.
+	@
+	@ fiq_rirq	The base of the IRQ registers to find S3C2410_SRCPND
+	@ fiq_rtmp	Temporary register to hold tx/rx data
+	@ fiq_rspi	The base of the SPI register block
+	@ fiq_rtx	The tx buffer pointer
+	@ fiq_rrx	The rx buffer pointer
+	@ fiq_rcount	The number of bytes to move
+
+	@ each entry starts with a word entry of how long it is
+	@ and an offset to the irq acknowledgment word
+
+ENTRY(s3c24xx_spi_fiq_rx)
+s3c24xx_spi_fix_rx:
+	.word	fiq_rx_end - fiq_rx_start
+	.word	fiq_rx_irq_ack - fiq_rx_start
+fiq_rx_start:
+	ldr	fiq_rtmp, fiq_rx_irq_ack
+	str	fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
+
+	ldrb	fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]
+	strb	fiq_rtmp, [ fiq_rrx ], #1
+
+	mov	fiq_rtmp, #0xff
+	strb	fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
+
+	subs	fiq_rcount, fiq_rcount, #1
+	subnes	pc, lr, #4		@@ return, still have work to do
+
+	@@ set IRQ controller so that next op will trigger IRQ
+	mov	fiq_rtmp, #0
+	str	fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
+	subs	pc, lr, #4
+
+fiq_rx_irq_ack:
+	.word	0
+fiq_rx_end:
+
+ENTRY(s3c24xx_spi_fiq_txrx)
+s3c24xx_spi_fiq_txrx:
+	.word	fiq_txrx_end - fiq_txrx_start
+	.word	fiq_txrx_irq_ack - fiq_txrx_start
+fiq_txrx_start:
+
+	ldrb	fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]
+	strb	fiq_rtmp, [ fiq_rrx ], #1
+
+	ldr	fiq_rtmp, fiq_txrx_irq_ack
+	str	fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
+
+	ldrb	fiq_rtmp, [ fiq_rtx ], #1
+	strb	fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
+
+	subs	fiq_rcount, fiq_rcount, #1
+	subnes	pc, lr, #4		@@ return, still have work to do
+
+	mov	fiq_rtmp, #0
+	str	fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
+	subs	pc, lr, #4
+
+fiq_txrx_irq_ack:
+	.word	0
+
+fiq_txrx_end:
+
+ENTRY(s3c24xx_spi_fiq_tx)
+s3c24xx_spi_fix_tx:
+	.word	fiq_tx_end - fiq_tx_start
+	.word	fiq_tx_irq_ack - fiq_tx_start
+fiq_tx_start:
+	ldrb	fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]
+
+	ldr	fiq_rtmp, fiq_tx_irq_ack
+	str	fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]
+
+	ldrb	fiq_rtmp, [ fiq_rtx ], #1
+	strb	fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]
+
+	subs	fiq_rcount, fiq_rcount, #1
+	subnes	pc, lr, #4		@@ return, still have work to do
+
+	mov	fiq_rtmp, #0
+	str	fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
+	subs	pc, lr, #4
+
+fiq_tx_irq_ack:
+	.word	0
+
+fiq_tx_end:
+
+	.end
diff --git a/drivers/spi/spi_s3c24xx_fiq.h b/drivers/spi/spi_s3c24xx_fiq.h
new file mode 100644
index 0000000..a5950bb
--- /dev/null
+++ b/drivers/spi/spi_s3c24xx_fiq.h
@@ -0,0 +1,26 @@
+/* linux/drivers/spi/spi_s3c24xx_fiq.h
+ *
+ * Copyright 2009 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX SPI - FIQ pseudo-DMA transfer support
+ *
+ * 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.
+*/
+
+/* We have R8 through R13 to play with */
+
+#ifdef __ASSEMBLY__
+#define __REG_NR(x)     r##x
+#else
+#define __REG_NR(x)     (x)
+#endif
+
+#define fiq_rspi	__REG_NR(8)
+#define fiq_rtmp	__REG_NR(9)
+#define fiq_rrx		__REG_NR(10)
+#define fiq_rtx		__REG_NR(11)
+#define fiq_rcount	__REG_NR(12)
+#define fiq_rirq	__REG_NR(13)
diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c
new file mode 100644
index 0000000..88a456d
--- /dev/null
+++ b/drivers/spi/spi_s3c64xx.c
@@ -0,0 +1,1196 @@
+/* linux/drivers/spi/spi_s3c64xx.c
+ *
+ * Copyright (C) 2009 Samsung Electronics Ltd.
+ *	Jaswinder Singh <jassi.brar@samsung.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#include <mach/dma.h>
+#include <plat/spi.h>
+
+/* Registers and bit-fields */
+
+#define S3C64XX_SPI_CH_CFG		0x00
+#define S3C64XX_SPI_CLK_CFG		0x04
+#define S3C64XX_SPI_MODE_CFG	0x08
+#define S3C64XX_SPI_SLAVE_SEL	0x0C
+#define S3C64XX_SPI_INT_EN		0x10
+#define S3C64XX_SPI_STATUS		0x14
+#define S3C64XX_SPI_TX_DATA		0x18
+#define S3C64XX_SPI_RX_DATA		0x1C
+#define S3C64XX_SPI_PACKET_CNT	0x20
+#define S3C64XX_SPI_PENDING_CLR	0x24
+#define S3C64XX_SPI_SWAP_CFG	0x28
+#define S3C64XX_SPI_FB_CLK		0x2C
+
+#define S3C64XX_SPI_CH_HS_EN		(1<<6)	/* High Speed Enable */
+#define S3C64XX_SPI_CH_SW_RST		(1<<5)
+#define S3C64XX_SPI_CH_SLAVE		(1<<4)
+#define S3C64XX_SPI_CPOL_L		(1<<3)
+#define S3C64XX_SPI_CPHA_B		(1<<2)
+#define S3C64XX_SPI_CH_RXCH_ON		(1<<1)
+#define S3C64XX_SPI_CH_TXCH_ON		(1<<0)
+
+#define S3C64XX_SPI_CLKSEL_SRCMSK	(3<<9)
+#define S3C64XX_SPI_CLKSEL_SRCSHFT	9
+#define S3C64XX_SPI_ENCLK_ENABLE	(1<<8)
+#define S3C64XX_SPI_PSR_MASK 		0xff
+
+#define S3C64XX_SPI_MODE_CH_TSZ_BYTE		(0<<29)
+#define S3C64XX_SPI_MODE_CH_TSZ_HALFWORD	(1<<29)
+#define S3C64XX_SPI_MODE_CH_TSZ_WORD		(2<<29)
+#define S3C64XX_SPI_MODE_CH_TSZ_MASK		(3<<29)
+#define S3C64XX_SPI_MODE_BUS_TSZ_BYTE		(0<<17)
+#define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD	(1<<17)
+#define S3C64XX_SPI_MODE_BUS_TSZ_WORD		(2<<17)
+#define S3C64XX_SPI_MODE_BUS_TSZ_MASK		(3<<17)
+#define S3C64XX_SPI_MODE_RXDMA_ON		(1<<2)
+#define S3C64XX_SPI_MODE_TXDMA_ON		(1<<1)
+#define S3C64XX_SPI_MODE_4BURST			(1<<0)
+
+#define S3C64XX_SPI_SLAVE_AUTO			(1<<1)
+#define S3C64XX_SPI_SLAVE_SIG_INACT		(1<<0)
+
+#define S3C64XX_SPI_ACT(c) writel(0, (c)->regs + S3C64XX_SPI_SLAVE_SEL)
+
+#define S3C64XX_SPI_DEACT(c) writel(S3C64XX_SPI_SLAVE_SIG_INACT, \
+					(c)->regs + S3C64XX_SPI_SLAVE_SEL)
+
+#define S3C64XX_SPI_INT_TRAILING_EN		(1<<6)
+#define S3C64XX_SPI_INT_RX_OVERRUN_EN		(1<<5)
+#define S3C64XX_SPI_INT_RX_UNDERRUN_EN		(1<<4)
+#define S3C64XX_SPI_INT_TX_OVERRUN_EN		(1<<3)
+#define S3C64XX_SPI_INT_TX_UNDERRUN_EN		(1<<2)
+#define S3C64XX_SPI_INT_RX_FIFORDY_EN		(1<<1)
+#define S3C64XX_SPI_INT_TX_FIFORDY_EN		(1<<0)
+
+#define S3C64XX_SPI_ST_RX_OVERRUN_ERR		(1<<5)
+#define S3C64XX_SPI_ST_RX_UNDERRUN_ERR	(1<<4)
+#define S3C64XX_SPI_ST_TX_OVERRUN_ERR		(1<<3)
+#define S3C64XX_SPI_ST_TX_UNDERRUN_ERR	(1<<2)
+#define S3C64XX_SPI_ST_RX_FIFORDY		(1<<1)
+#define S3C64XX_SPI_ST_TX_FIFORDY		(1<<0)
+
+#define S3C64XX_SPI_PACKET_CNT_EN		(1<<16)
+
+#define S3C64XX_SPI_PND_TX_UNDERRUN_CLR		(1<<4)
+#define S3C64XX_SPI_PND_TX_OVERRUN_CLR		(1<<3)
+#define S3C64XX_SPI_PND_RX_UNDERRUN_CLR		(1<<2)
+#define S3C64XX_SPI_PND_RX_OVERRUN_CLR		(1<<1)
+#define S3C64XX_SPI_PND_TRAILING_CLR		(1<<0)
+
+#define S3C64XX_SPI_SWAP_RX_HALF_WORD		(1<<7)
+#define S3C64XX_SPI_SWAP_RX_BYTE		(1<<6)
+#define S3C64XX_SPI_SWAP_RX_BIT			(1<<5)
+#define S3C64XX_SPI_SWAP_RX_EN			(1<<4)
+#define S3C64XX_SPI_SWAP_TX_HALF_WORD		(1<<3)
+#define S3C64XX_SPI_SWAP_TX_BYTE		(1<<2)
+#define S3C64XX_SPI_SWAP_TX_BIT			(1<<1)
+#define S3C64XX_SPI_SWAP_TX_EN			(1<<0)
+
+#define S3C64XX_SPI_FBCLK_MSK		(3<<0)
+
+#define S3C64XX_SPI_ST_TRLCNTZ(v, i) ((((v) >> (i)->rx_lvl_offset) & \
+					(((i)->fifo_lvl_mask + 1))) \
+					? 1 : 0)
+
+#define S3C64XX_SPI_ST_TX_DONE(v, i) ((((v) >> (i)->rx_lvl_offset) & \
+					(((i)->fifo_lvl_mask + 1) << 1)) \
+					? 1 : 0)
+#define TX_FIFO_LVL(v, i) (((v) >> 6) & (i)->fifo_lvl_mask)
+#define RX_FIFO_LVL(v, i) (((v) >> (i)->rx_lvl_offset) & (i)->fifo_lvl_mask)
+
+#define S3C64XX_SPI_MAX_TRAILCNT	0x3ff
+#define S3C64XX_SPI_TRAILCNT_OFF	19
+
+#define S3C64XX_SPI_TRAILCNT		S3C64XX_SPI_MAX_TRAILCNT
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+
+#define SUSPND    (1<<0)
+#define SPIBUSY   (1<<1)
+#define RXBUSY    (1<<2)
+#define TXBUSY    (1<<3)
+
+/**
+ * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver.
+ * @clk: Pointer to the spi clock.
+ * @master: Pointer to the SPI Protocol master.
+ * @workqueue: Work queue for the SPI xfer requests.
+ * @cntrlr_info: Platform specific data for the controller this driver manages.
+ * @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint.
+ * @work: Work
+ * @queue: To log SPI xfer requests.
+ * @lock: Controller specific lock.
+ * @state: Set of FLAGS to indicate status.
+ * @rx_dmach: Controller's DMA channel for Rx.
+ * @tx_dmach: Controller's DMA channel for Tx.
+ * @sfr_start: BUS address of SPI controller regs.
+ * @regs: Pointer to ioremap'ed controller registers.
+ * @xfer_completion: To indicate completion of xfer task.
+ * @cur_mode: Stores the active configuration of the controller.
+ * @cur_bpw: Stores the active bits per word settings.
+ * @cur_speed: Stores the active xfer clock speed.
+ */
+struct s3c64xx_spi_driver_data {
+	void __iomem                    *regs;
+	struct clk                      *clk;
+	struct platform_device          *pdev;
+	struct spi_master               *master;
+	struct workqueue_struct	        *workqueue;
+	struct s3c64xx_spi_cntrlr_info  *cntrlr_info;
+	struct spi_device               *tgl_spi;
+	struct work_struct              work;
+	struct list_head                queue;
+	spinlock_t                      lock;
+	enum dma_ch                     rx_dmach;
+	enum dma_ch                     tx_dmach;
+	unsigned long                   sfr_start;
+	struct completion               xfer_completion;
+	unsigned                        state;
+	unsigned                        cur_mode, cur_bpw;
+	unsigned                        cur_speed;
+};
+
+static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
+	.name = "samsung-spi-dma",
+};
+
+static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
+{
+	struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+	void __iomem *regs = sdd->regs;
+	unsigned long loops;
+	u32 val;
+
+	writel(0, regs + S3C64XX_SPI_PACKET_CNT);
+
+	val = readl(regs + S3C64XX_SPI_CH_CFG);
+	val |= S3C64XX_SPI_CH_SW_RST;
+	val &= ~S3C64XX_SPI_CH_HS_EN;
+	writel(val, regs + S3C64XX_SPI_CH_CFG);
+
+	/* Flush TxFIFO*/
+	loops = msecs_to_loops(1);
+	do {
+		val = readl(regs + S3C64XX_SPI_STATUS);
+	} while (TX_FIFO_LVL(val, sci) && loops--);
+
+	/* Flush RxFIFO*/
+	loops = msecs_to_loops(1);
+	do {
+		val = readl(regs + S3C64XX_SPI_STATUS);
+		if (RX_FIFO_LVL(val, sci))
+			readl(regs + S3C64XX_SPI_RX_DATA);
+		else
+			break;
+	} while (loops--);
+
+	val = readl(regs + S3C64XX_SPI_CH_CFG);
+	val &= ~S3C64XX_SPI_CH_SW_RST;
+	writel(val, regs + S3C64XX_SPI_CH_CFG);
+
+	val = readl(regs + S3C64XX_SPI_MODE_CFG);
+	val &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON);
+	writel(val, regs + S3C64XX_SPI_MODE_CFG);
+
+	val = readl(regs + S3C64XX_SPI_CH_CFG);
+	val &= ~(S3C64XX_SPI_CH_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON);
+	writel(val, regs + S3C64XX_SPI_CH_CFG);
+}
+
+static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
+				struct spi_device *spi,
+				struct spi_transfer *xfer, int dma_mode)
+{
+	struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+	void __iomem *regs = sdd->regs;
+	u32 modecfg, chcfg;
+
+	modecfg = readl(regs + S3C64XX_SPI_MODE_CFG);
+	modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON);
+
+	chcfg = readl(regs + S3C64XX_SPI_CH_CFG);
+	chcfg &= ~S3C64XX_SPI_CH_TXCH_ON;
+
+	if (dma_mode) {
+		chcfg &= ~S3C64XX_SPI_CH_RXCH_ON;
+	} else {
+		/* Always shift in data in FIFO, even if xfer is Tx only,
+		 * this helps setting PCKT_CNT value for generating clocks
+		 * as exactly needed.
+		 */
+		chcfg |= S3C64XX_SPI_CH_RXCH_ON;
+		writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
+					| S3C64XX_SPI_PACKET_CNT_EN,
+					regs + S3C64XX_SPI_PACKET_CNT);
+	}
+
+	if (xfer->tx_buf != NULL) {
+		sdd->state |= TXBUSY;
+		chcfg |= S3C64XX_SPI_CH_TXCH_ON;
+		if (dma_mode) {
+			modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
+			s3c2410_dma_config(sdd->tx_dmach, 1);
+			s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd,
+						xfer->tx_dma, xfer->len);
+			s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START);
+		} else {
+			unsigned char *buf = (unsigned char *) xfer->tx_buf;
+			int i = 0;
+			while (i < xfer->len)
+				writeb(buf[i++], regs + S3C64XX_SPI_TX_DATA);
+		}
+	}
+
+	if (xfer->rx_buf != NULL) {
+		sdd->state |= RXBUSY;
+
+		if (sci->high_speed && sdd->cur_speed >= 30000000UL
+					&& !(sdd->cur_mode & SPI_CPHA))
+			chcfg |= S3C64XX_SPI_CH_HS_EN;
+
+		if (dma_mode) {
+			modecfg |= S3C64XX_SPI_MODE_RXDMA_ON;
+			chcfg |= S3C64XX_SPI_CH_RXCH_ON;
+			writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
+					| S3C64XX_SPI_PACKET_CNT_EN,
+					regs + S3C64XX_SPI_PACKET_CNT);
+			s3c2410_dma_config(sdd->rx_dmach, 1);
+			s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd,
+						xfer->rx_dma, xfer->len);
+			s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START);
+		}
+	}
+
+	writel(modecfg, regs + S3C64XX_SPI_MODE_CFG);
+	writel(chcfg, regs + S3C64XX_SPI_CH_CFG);
+}
+
+static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd,
+						struct spi_device *spi)
+{
+	struct s3c64xx_spi_csinfo *cs;
+
+	if (sdd->tgl_spi != NULL) { /* If last device toggled after mssg */
+		if (sdd->tgl_spi != spi) { /* if last mssg on diff device */
+			/* Deselect the last toggled device */
+			cs = sdd->tgl_spi->controller_data;
+			cs->set_level(spi->mode & SPI_CS_HIGH ? 0 : 1);
+		}
+		sdd->tgl_spi = NULL;
+	}
+
+	cs = spi->controller_data;
+	cs->set_level(spi->mode & SPI_CS_HIGH ? 1 : 0);
+}
+
+static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
+				struct spi_transfer *xfer, int dma_mode)
+{
+	struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+	void __iomem *regs = sdd->regs;
+	unsigned long val;
+	int ms;
+
+	/* millisecs to xfer 'len' bytes @ 'cur_speed' */
+	ms = xfer->len * 8 * 1000 / sdd->cur_speed;
+	ms += 5; /* some tolerance */
+
+	if (dma_mode) {
+		val = msecs_to_jiffies(ms) + 10;
+		val = wait_for_completion_timeout(&sdd->xfer_completion, val);
+	} else {
+		val = msecs_to_loops(ms);
+		do {
+			val = readl(regs + S3C64XX_SPI_STATUS);
+		} while (RX_FIFO_LVL(val, sci) < xfer->len && --val);
+	}
+
+	if (!val)
+		return -EIO;
+
+	if (dma_mode) {
+		u32 status;
+
+		/*
+		 * DmaTx returns after simply writing data in the FIFO,
+		 * w/o waiting for real transmission on the bus to finish.
+		 * DmaRx returns only after Dma read data from FIFO which
+		 * needs bus transmission to finish, so we don't worry if
+		 * Xfer involved Rx(with or without Tx).
+		 */
+		if (xfer->rx_buf == NULL) {
+			val = msecs_to_loops(10);
+			status = readl(regs + S3C64XX_SPI_STATUS);
+			while ((TX_FIFO_LVL(status, sci)
+				|| !S3C64XX_SPI_ST_TX_DONE(status, sci))
+					&& --val) {
+				cpu_relax();
+				status = readl(regs + S3C64XX_SPI_STATUS);
+			}
+
+			if (!val)
+				return -EIO;
+		}
+	} else {
+		unsigned char *buf;
+		int i;
+
+		/* If it was only Tx */
+		if (xfer->rx_buf == NULL) {
+			sdd->state &= ~TXBUSY;
+			return 0;
+		}
+
+		i = 0;
+		buf = xfer->rx_buf;
+		while (i < xfer->len)
+			buf[i++] = readb(regs + S3C64XX_SPI_RX_DATA);
+
+		sdd->state &= ~RXBUSY;
+	}
+
+	return 0;
+}
+
+static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd,
+						struct spi_device *spi)
+{
+	struct s3c64xx_spi_csinfo *cs = spi->controller_data;
+
+	if (sdd->tgl_spi == spi)
+		sdd->tgl_spi = NULL;
+
+	cs->set_level(spi->mode & SPI_CS_HIGH ? 0 : 1);
+}
+
+static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
+{
+	struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+	void __iomem *regs = sdd->regs;
+	u32 val;
+
+	/* Disable Clock */
+	val = readl(regs + S3C64XX_SPI_CLK_CFG);
+	val &= ~S3C64XX_SPI_ENCLK_ENABLE;
+	writel(val, regs + S3C64XX_SPI_CLK_CFG);
+
+	/* Set Polarity and Phase */
+	val = readl(regs + S3C64XX_SPI_CH_CFG);
+	val &= ~(S3C64XX_SPI_CH_SLAVE |
+			S3C64XX_SPI_CPOL_L |
+			S3C64XX_SPI_CPHA_B);
+
+	if (sdd->cur_mode & SPI_CPOL)
+		val |= S3C64XX_SPI_CPOL_L;
+
+	if (sdd->cur_mode & SPI_CPHA)
+		val |= S3C64XX_SPI_CPHA_B;
+
+	writel(val, regs + S3C64XX_SPI_CH_CFG);
+
+	/* Set Channel & DMA Mode */
+	val = readl(regs + S3C64XX_SPI_MODE_CFG);
+	val &= ~(S3C64XX_SPI_MODE_BUS_TSZ_MASK
+			| S3C64XX_SPI_MODE_CH_TSZ_MASK);
+
+	switch (sdd->cur_bpw) {
+	case 32:
+		val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD;
+		break;
+	case 16:
+		val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD;
+		break;
+	default:
+		val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE;
+		break;
+	}
+	val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; /* Always 8bits wide */
+
+	writel(val, regs + S3C64XX_SPI_MODE_CFG);
+
+	/* Configure Clock */
+	val = readl(regs + S3C64XX_SPI_CLK_CFG);
+	val &= ~S3C64XX_SPI_PSR_MASK;
+	val |= ((clk_get_rate(sci->src_clk) / sdd->cur_speed / 2 - 1)
+			& S3C64XX_SPI_PSR_MASK);
+	writel(val, regs + S3C64XX_SPI_CLK_CFG);
+
+	/* Enable Clock */
+	val = readl(regs + S3C64XX_SPI_CLK_CFG);
+	val |= S3C64XX_SPI_ENCLK_ENABLE;
+	writel(val, regs + S3C64XX_SPI_CLK_CFG);
+}
+
+void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id,
+				int size, enum s3c2410_dma_buffresult res)
+{
+	struct s3c64xx_spi_driver_data *sdd = buf_id;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sdd->lock, flags);
+
+	if (res == S3C2410_RES_OK)
+		sdd->state &= ~RXBUSY;
+	else
+		dev_err(&sdd->pdev->dev, "DmaAbrtRx-%d\n", size);
+
+	/* If the other done */
+	if (!(sdd->state & TXBUSY))
+		complete(&sdd->xfer_completion);
+
+	spin_unlock_irqrestore(&sdd->lock, flags);
+}
+
+void s3c64xx_spi_dma_txcb(struct s3c2410_dma_chan *chan, void *buf_id,
+				int size, enum s3c2410_dma_buffresult res)
+{
+	struct s3c64xx_spi_driver_data *sdd = buf_id;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sdd->lock, flags);
+
+	if (res == S3C2410_RES_OK)
+		sdd->state &= ~TXBUSY;
+	else
+		dev_err(&sdd->pdev->dev, "DmaAbrtTx-%d \n", size);
+
+	/* If the other done */
+	if (!(sdd->state & RXBUSY))
+		complete(&sdd->xfer_completion);
+
+	spin_unlock_irqrestore(&sdd->lock, flags);
+}
+
+#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32)
+
+static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
+						struct spi_message *msg)
+{
+	struct device *dev = &sdd->pdev->dev;
+	struct spi_transfer *xfer;
+
+	if (msg->is_dma_mapped)
+		return 0;
+
+	/* First mark all xfer unmapped */
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+		xfer->rx_dma = XFER_DMAADDR_INVALID;
+		xfer->tx_dma = XFER_DMAADDR_INVALID;
+	}
+
+	/* Map until end or first fail */
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+
+		if (xfer->tx_buf != NULL) {
+			xfer->tx_dma = dma_map_single(dev, xfer->tx_buf,
+						xfer->len, DMA_TO_DEVICE);
+			if (dma_mapping_error(dev, xfer->tx_dma)) {
+				dev_err(dev, "dma_map_single Tx failed\n");
+				xfer->tx_dma = XFER_DMAADDR_INVALID;
+				return -ENOMEM;
+			}
+		}
+
+		if (xfer->rx_buf != NULL) {
+			xfer->rx_dma = dma_map_single(dev, xfer->rx_buf,
+						xfer->len, DMA_FROM_DEVICE);
+			if (dma_mapping_error(dev, xfer->rx_dma)) {
+				dev_err(dev, "dma_map_single Rx failed\n");
+				dma_unmap_single(dev, xfer->tx_dma,
+						xfer->len, DMA_TO_DEVICE);
+				xfer->tx_dma = XFER_DMAADDR_INVALID;
+				xfer->rx_dma = XFER_DMAADDR_INVALID;
+				return -ENOMEM;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
+						struct spi_message *msg)
+{
+	struct device *dev = &sdd->pdev->dev;
+	struct spi_transfer *xfer;
+
+	if (msg->is_dma_mapped)
+		return;
+
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+
+		if (xfer->rx_buf != NULL
+				&& xfer->rx_dma != XFER_DMAADDR_INVALID)
+			dma_unmap_single(dev, xfer->rx_dma,
+						xfer->len, DMA_FROM_DEVICE);
+
+		if (xfer->tx_buf != NULL
+				&& xfer->tx_dma != XFER_DMAADDR_INVALID)
+			dma_unmap_single(dev, xfer->tx_dma,
+						xfer->len, DMA_TO_DEVICE);
+	}
+}
+
+static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
+					struct spi_message *msg)
+{
+	struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+	struct spi_device *spi = msg->spi;
+	struct s3c64xx_spi_csinfo *cs = spi->controller_data;
+	struct spi_transfer *xfer;
+	int status = 0, cs_toggle = 0;
+	u32 speed;
+	u8 bpw;
+
+	/* If Master's(controller) state differs from that needed by Slave */
+	if (sdd->cur_speed != spi->max_speed_hz
+			|| sdd->cur_mode != spi->mode
+			|| sdd->cur_bpw != spi->bits_per_word) {
+		sdd->cur_bpw = spi->bits_per_word;
+		sdd->cur_speed = spi->max_speed_hz;
+		sdd->cur_mode = spi->mode;
+		s3c64xx_spi_config(sdd);
+	}
+
+	/* Map all the transfers if needed */
+	if (s3c64xx_spi_map_mssg(sdd, msg)) {
+		dev_err(&spi->dev,
+			"Xfer: Unable to map message buffers!\n");
+		status = -ENOMEM;
+		goto out;
+	}
+
+	/* Configure feedback delay */
+	writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
+
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+
+		unsigned long flags;
+		int use_dma;
+
+		INIT_COMPLETION(sdd->xfer_completion);
+
+		/* Only BPW and Speed may change across transfers */
+		bpw = xfer->bits_per_word ? : spi->bits_per_word;
+		speed = xfer->speed_hz ? : spi->max_speed_hz;
+
+		if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) {
+			sdd->cur_bpw = bpw;
+			sdd->cur_speed = speed;
+			s3c64xx_spi_config(sdd);
+		}
+
+		/* Polling method for xfers not bigger than FIFO capacity */
+		if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1))
+			use_dma = 0;
+		else
+			use_dma = 1;
+
+		spin_lock_irqsave(&sdd->lock, flags);
+
+		/* Pending only which is to be done */
+		sdd->state &= ~RXBUSY;
+		sdd->state &= ~TXBUSY;
+
+		enable_datapath(sdd, spi, xfer, use_dma);
+
+		/* Slave Select */
+		enable_cs(sdd, spi);
+
+		/* Start the signals */
+		S3C64XX_SPI_ACT(sdd);
+
+		spin_unlock_irqrestore(&sdd->lock, flags);
+
+		status = wait_for_xfer(sdd, xfer, use_dma);
+
+		/* Quiese the signals */
+		S3C64XX_SPI_DEACT(sdd);
+
+		if (status) {
+			dev_err(&spi->dev, "I/O Error: \
+				rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
+				xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0,
+				(sdd->state & RXBUSY) ? 'f' : 'p',
+				(sdd->state & TXBUSY) ? 'f' : 'p',
+				xfer->len);
+
+			if (use_dma) {
+				if (xfer->tx_buf != NULL
+						&& (sdd->state & TXBUSY))
+					s3c2410_dma_ctrl(sdd->tx_dmach,
+							S3C2410_DMAOP_FLUSH);
+				if (xfer->rx_buf != NULL
+						&& (sdd->state & RXBUSY))
+					s3c2410_dma_ctrl(sdd->rx_dmach,
+							S3C2410_DMAOP_FLUSH);
+			}
+
+			goto out;
+		}
+
+		if (xfer->delay_usecs)
+			udelay(xfer->delay_usecs);
+
+		if (xfer->cs_change) {
+			/* Hint that the next mssg is gonna be
+			   for the same device */
+			if (list_is_last(&xfer->transfer_list,
+						&msg->transfers))
+				cs_toggle = 1;
+			else
+				disable_cs(sdd, spi);
+		}
+
+		msg->actual_length += xfer->len;
+
+		flush_fifo(sdd);
+	}
+
+out:
+	if (!cs_toggle || status)
+		disable_cs(sdd, spi);
+	else
+		sdd->tgl_spi = spi;
+
+	s3c64xx_spi_unmap_mssg(sdd, msg);
+
+	msg->status = status;
+
+	if (msg->complete)
+		msg->complete(msg->context);
+}
+
+static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
+{
+	if (s3c2410_dma_request(sdd->rx_dmach,
+					&s3c64xx_spi_dma_client, NULL) < 0) {
+		dev_err(&sdd->pdev->dev, "cannot get RxDMA\n");
+		return 0;
+	}
+	s3c2410_dma_set_buffdone_fn(sdd->rx_dmach, s3c64xx_spi_dma_rxcb);
+	s3c2410_dma_devconfig(sdd->rx_dmach, S3C2410_DMASRC_HW,
+					sdd->sfr_start + S3C64XX_SPI_RX_DATA);
+
+	if (s3c2410_dma_request(sdd->tx_dmach,
+					&s3c64xx_spi_dma_client, NULL) < 0) {
+		dev_err(&sdd->pdev->dev, "cannot get TxDMA\n");
+		s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client);
+		return 0;
+	}
+	s3c2410_dma_set_buffdone_fn(sdd->tx_dmach, s3c64xx_spi_dma_txcb);
+	s3c2410_dma_devconfig(sdd->tx_dmach, S3C2410_DMASRC_MEM,
+					sdd->sfr_start + S3C64XX_SPI_TX_DATA);
+
+	return 1;
+}
+
+static void s3c64xx_spi_work(struct work_struct *work)
+{
+	struct s3c64xx_spi_driver_data *sdd = container_of(work,
+					struct s3c64xx_spi_driver_data, work);
+	unsigned long flags;
+
+	/* Acquire DMA channels */
+	while (!acquire_dma(sdd))
+		msleep(10);
+
+	spin_lock_irqsave(&sdd->lock, flags);
+
+	while (!list_empty(&sdd->queue)
+				&& !(sdd->state & SUSPND)) {
+
+		struct spi_message *msg;
+
+		msg = container_of(sdd->queue.next, struct spi_message, queue);
+
+		list_del_init(&msg->queue);
+
+		/* Set Xfer busy flag */
+		sdd->state |= SPIBUSY;
+
+		spin_unlock_irqrestore(&sdd->lock, flags);
+
+		handle_msg(sdd, msg);
+
+		spin_lock_irqsave(&sdd->lock, flags);
+
+		sdd->state &= ~SPIBUSY;
+	}
+
+	spin_unlock_irqrestore(&sdd->lock, flags);
+
+	/* Free DMA channels */
+	s3c2410_dma_free(sdd->tx_dmach, &s3c64xx_spi_dma_client);
+	s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client);
+}
+
+static int s3c64xx_spi_transfer(struct spi_device *spi,
+						struct spi_message *msg)
+{
+	struct s3c64xx_spi_driver_data *sdd;
+	unsigned long flags;
+
+	sdd = spi_master_get_devdata(spi->master);
+
+	spin_lock_irqsave(&sdd->lock, flags);
+
+	if (sdd->state & SUSPND) {
+		spin_unlock_irqrestore(&sdd->lock, flags);
+		return -ESHUTDOWN;
+	}
+
+	msg->status = -EINPROGRESS;
+	msg->actual_length = 0;
+
+	list_add_tail(&msg->queue, &sdd->queue);
+
+	queue_work(sdd->workqueue, &sdd->work);
+
+	spin_unlock_irqrestore(&sdd->lock, flags);
+
+	return 0;
+}
+
+/*
+ * Here we only check the validity of requested configuration
+ * and save the configuration in a local data-structure.
+ * The controller is actually configured only just before we
+ * get a message to transfer.
+ */
+static int s3c64xx_spi_setup(struct spi_device *spi)
+{
+	struct s3c64xx_spi_csinfo *cs = spi->controller_data;
+	struct s3c64xx_spi_driver_data *sdd;
+	struct s3c64xx_spi_cntrlr_info *sci;
+	struct spi_message *msg;
+	u32 psr, speed;
+	unsigned long flags;
+	int err = 0;
+
+	if (cs == NULL || cs->set_level == NULL) {
+		dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select);
+		return -ENODEV;
+	}
+
+	sdd = spi_master_get_devdata(spi->master);
+	sci = sdd->cntrlr_info;
+
+	spin_lock_irqsave(&sdd->lock, flags);
+
+	list_for_each_entry(msg, &sdd->queue, queue) {
+		/* Is some mssg is already queued for this device */
+		if (msg->spi == spi) {
+			dev_err(&spi->dev,
+				"setup: attempt while mssg in queue!\n");
+			spin_unlock_irqrestore(&sdd->lock, flags);
+			return -EBUSY;
+		}
+	}
+
+	if (sdd->state & SUSPND) {
+		spin_unlock_irqrestore(&sdd->lock, flags);
+		dev_err(&spi->dev,
+			"setup: SPI-%d not active!\n", spi->master->bus_num);
+		return -ESHUTDOWN;
+	}
+
+	spin_unlock_irqrestore(&sdd->lock, flags);
+
+	if (spi->bits_per_word != 8
+			&& spi->bits_per_word != 16
+			&& spi->bits_per_word != 32) {
+		dev_err(&spi->dev, "setup: %dbits/wrd not supported!\n",
+							spi->bits_per_word);
+		err = -EINVAL;
+		goto setup_exit;
+	}
+
+	/* Check if we can provide the requested rate */
+	speed = clk_get_rate(sci->src_clk) / 2 / (0 + 1); /* Max possible */
+
+	if (spi->max_speed_hz > speed)
+		spi->max_speed_hz = speed;
+
+	psr = clk_get_rate(sci->src_clk) / 2 / spi->max_speed_hz - 1;
+	psr &= S3C64XX_SPI_PSR_MASK;
+	if (psr == S3C64XX_SPI_PSR_MASK)
+		psr--;
+
+	speed = clk_get_rate(sci->src_clk) / 2 / (psr + 1);
+	if (spi->max_speed_hz < speed) {
+		if (psr+1 < S3C64XX_SPI_PSR_MASK) {
+			psr++;
+		} else {
+			err = -EINVAL;
+			goto setup_exit;
+		}
+	}
+
+	speed = clk_get_rate(sci->src_clk) / 2 / (psr + 1);
+	if (spi->max_speed_hz >= speed)
+		spi->max_speed_hz = speed;
+	else
+		err = -EINVAL;
+
+setup_exit:
+
+	/* setup() returns with device de-selected */
+	disable_cs(sdd, spi);
+
+	return err;
+}
+
+static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
+{
+	struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+	void __iomem *regs = sdd->regs;
+	unsigned int val;
+
+	sdd->cur_speed = 0;
+
+	S3C64XX_SPI_DEACT(sdd);
+
+	/* Disable Interrupts - we use Polling if not DMA mode */
+	writel(0, regs + S3C64XX_SPI_INT_EN);
+
+	writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,
+				regs + S3C64XX_SPI_CLK_CFG);
+	writel(0, regs + S3C64XX_SPI_MODE_CFG);
+	writel(0, regs + S3C64XX_SPI_PACKET_CNT);
+
+	/* Clear any irq pending bits */
+	writel(readl(regs + S3C64XX_SPI_PENDING_CLR),
+				regs + S3C64XX_SPI_PENDING_CLR);
+
+	writel(0, regs + S3C64XX_SPI_SWAP_CFG);
+
+	val = readl(regs + S3C64XX_SPI_MODE_CFG);
+	val &= ~S3C64XX_SPI_MODE_4BURST;
+	val &= ~(S3C64XX_SPI_MAX_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF);
+	val |= (S3C64XX_SPI_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF);
+	writel(val, regs + S3C64XX_SPI_MODE_CFG);
+
+	flush_fifo(sdd);
+}
+
+static int __init s3c64xx_spi_probe(struct platform_device *pdev)
+{
+	struct resource	*mem_res, *dmatx_res, *dmarx_res;
+	struct s3c64xx_spi_driver_data *sdd;
+	struct s3c64xx_spi_cntrlr_info *sci;
+	struct spi_master *master;
+	int ret;
+
+	if (pdev->id < 0) {
+		dev_err(&pdev->dev,
+				"Invalid platform device id-%d\n", pdev->id);
+		return -ENODEV;
+	}
+
+	if (pdev->dev.platform_data == NULL) {
+		dev_err(&pdev->dev, "platform_data missing!\n");
+		return -ENODEV;
+	}
+
+	/* Check for availability of necessary resource */
+
+	dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (dmatx_res == NULL) {
+		dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n");
+		return -ENXIO;
+	}
+
+	dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (dmarx_res == NULL) {
+		dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n");
+		return -ENXIO;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (mem_res == NULL) {
+		dev_err(&pdev->dev, "Unable to get SPI MEM resource\n");
+		return -ENXIO;
+	}
+
+	master = spi_alloc_master(&pdev->dev,
+				sizeof(struct s3c64xx_spi_driver_data));
+	if (master == NULL) {
+		dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
+		return -ENOMEM;
+	}
+
+	sci = pdev->dev.platform_data;
+
+	platform_set_drvdata(pdev, master);
+
+	sdd = spi_master_get_devdata(master);
+	sdd->master = master;
+	sdd->cntrlr_info = sci;
+	sdd->pdev = pdev;
+	sdd->sfr_start = mem_res->start;
+	sdd->tx_dmach = dmatx_res->start;
+	sdd->rx_dmach = dmarx_res->start;
+
+	sdd->cur_bpw = 8;
+
+	master->bus_num = pdev->id;
+	master->setup = s3c64xx_spi_setup;
+	master->transfer = s3c64xx_spi_transfer;
+	master->num_chipselect = sci->num_cs;
+	master->dma_alignment = 8;
+	/* the spi->mode bits understood by this driver: */
+	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+
+	if (request_mem_region(mem_res->start,
+			resource_size(mem_res), pdev->name) == NULL) {
+		dev_err(&pdev->dev, "Req mem region failed\n");
+		ret = -ENXIO;
+		goto err0;
+	}
+
+	sdd->regs = ioremap(mem_res->start, resource_size(mem_res));
+	if (sdd->regs == NULL) {
+		dev_err(&pdev->dev, "Unable to remap IO\n");
+		ret = -ENXIO;
+		goto err1;
+	}
+
+	if (sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) {
+		dev_err(&pdev->dev, "Unable to config gpio\n");
+		ret = -EBUSY;
+		goto err2;
+	}
+
+	/* Setup clocks */
+	sdd->clk = clk_get(&pdev->dev, "spi");
+	if (IS_ERR(sdd->clk)) {
+		dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
+		ret = PTR_ERR(sdd->clk);
+		goto err3;
+	}
+
+	if (clk_enable(sdd->clk)) {
+		dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
+		ret = -EBUSY;
+		goto err4;
+	}
+
+	if (sci->src_clk_nr == S3C64XX_SPI_SRCCLK_PCLK)
+		sci->src_clk = sdd->clk;
+	else
+		sci->src_clk = clk_get(&pdev->dev, sci->src_clk_name);
+	if (IS_ERR(sci->src_clk)) {
+		dev_err(&pdev->dev,
+			"Unable to acquire clock '%s'\n", sci->src_clk_name);
+		ret = PTR_ERR(sci->src_clk);
+		goto err5;
+	}
+
+	if (sci->src_clk != sdd->clk && clk_enable(sci->src_clk)) {
+		dev_err(&pdev->dev, "Couldn't enable clock '%s'\n",
+							sci->src_clk_name);
+		ret = -EBUSY;
+		goto err6;
+	}
+
+	sdd->workqueue = create_singlethread_workqueue(
+						dev_name(master->dev.parent));
+	if (sdd->workqueue == NULL) {
+		dev_err(&pdev->dev, "Unable to create workqueue\n");
+		ret = -ENOMEM;
+		goto err7;
+	}
+
+	/* Setup Deufult Mode */
+	s3c64xx_spi_hwinit(sdd, pdev->id);
+
+	spin_lock_init(&sdd->lock);
+	init_completion(&sdd->xfer_completion);
+	INIT_WORK(&sdd->work, s3c64xx_spi_work);
+	INIT_LIST_HEAD(&sdd->queue);
+
+	if (spi_register_master(master)) {
+		dev_err(&pdev->dev, "cannot register SPI master\n");
+		ret = -EBUSY;
+		goto err8;
+	}
+
+	dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d \
+					with %d Slaves attached\n",
+					pdev->id, master->num_chipselect);
+	dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\
+					\tDMA=[Rx-%d, Tx-%d]\n",
+					mem_res->end, mem_res->start,
+					sdd->rx_dmach, sdd->tx_dmach);
+
+	return 0;
+
+err8:
+	destroy_workqueue(sdd->workqueue);
+err7:
+	if (sci->src_clk != sdd->clk)
+		clk_disable(sci->src_clk);
+err6:
+	if (sci->src_clk != sdd->clk)
+		clk_put(sci->src_clk);
+err5:
+	clk_disable(sdd->clk);
+err4:
+	clk_put(sdd->clk);
+err3:
+err2:
+	iounmap((void *) sdd->regs);
+err1:
+	release_mem_region(mem_res->start, resource_size(mem_res));
+err0:
+	platform_set_drvdata(pdev, NULL);
+	spi_master_put(master);
+
+	return ret;
+}
+
+static int s3c64xx_spi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+	struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
+	struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+	struct resource	*mem_res;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sdd->lock, flags);
+	sdd->state |= SUSPND;
+	spin_unlock_irqrestore(&sdd->lock, flags);
+
+	while (sdd->state & SPIBUSY)
+		msleep(10);
+
+	spi_unregister_master(master);
+
+	destroy_workqueue(sdd->workqueue);
+
+	if (sci->src_clk != sdd->clk)
+		clk_disable(sci->src_clk);
+
+	if (sci->src_clk != sdd->clk)
+		clk_put(sci->src_clk);
+
+	clk_disable(sdd->clk);
+	clk_put(sdd->clk);
+
+	iounmap((void *) sdd->regs);
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(mem_res->start, resource_size(mem_res));
+
+	platform_set_drvdata(pdev, NULL);
+	spi_master_put(master);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+	struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
+	struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+	struct s3c64xx_spi_csinfo *cs;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sdd->lock, flags);
+	sdd->state |= SUSPND;
+	spin_unlock_irqrestore(&sdd->lock, flags);
+
+	while (sdd->state & SPIBUSY)
+		msleep(10);
+
+	/* Disable the clock */
+	if (sci->src_clk != sdd->clk)
+		clk_disable(sci->src_clk);
+
+	clk_disable(sdd->clk);
+
+	sdd->cur_speed = 0; /* Output Clock is stopped */
+
+	return 0;
+}
+
+static int s3c64xx_spi_resume(struct platform_device *pdev)
+{
+	struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+	struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
+	struct s3c64xx_spi_cntrlr_info *sci = sdd->cntrlr_info;
+	unsigned long flags;
+
+	sci->cfg_gpio(pdev);
+
+	/* Enable the clock */
+	if (sci->src_clk != sdd->clk)
+		clk_enable(sci->src_clk);
+
+	clk_enable(sdd->clk);
+
+	s3c64xx_spi_hwinit(sdd, pdev->id);
+
+	spin_lock_irqsave(&sdd->lock, flags);
+	sdd->state &= ~SUSPND;
+	spin_unlock_irqrestore(&sdd->lock, flags);
+
+	return 0;
+}
+#else
+#define s3c64xx_spi_suspend	NULL
+#define s3c64xx_spi_resume	NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver s3c64xx_spi_driver = {
+	.driver = {
+		.name	= "s3c64xx-spi",
+		.owner = THIS_MODULE,
+	},
+	.remove = s3c64xx_spi_remove,
+	.suspend = s3c64xx_spi_suspend,
+	.resume = s3c64xx_spi_resume,
+};
+MODULE_ALIAS("platform:s3c64xx-spi");
+
+static int __init s3c64xx_spi_init(void)
+{
+	return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
+}
+module_init(s3c64xx_spi_init);
+
+static void __exit s3c64xx_spi_exit(void)
+{
+	platform_driver_unregister(&s3c64xx_spi_driver);
+}
+module_exit(s3c64xx_spi_exit);
+
+MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
+MODULE_DESCRIPTION("S3C64XX SPI Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_sh_sci.c b/drivers/spi/spi_sh_sci.c
index 7d36720..a65c12f 100644
--- a/drivers/spi/spi_sh_sci.c
+++ b/drivers/spi/spi_sh_sci.c
@@ -148,7 +148,7 @@
 		ret = -ENOENT;
 		goto err1;
 	}
-	sp->membase = ioremap(r->start, r->end - r->start + 1);
+	sp->membase = ioremap(r->start, resource_size(r));
 	if (!sp->membase) {
 		ret = -ENXIO;
 		goto err1;
diff --git a/drivers/spi/spi_txx9.c b/drivers/spi/spi_txx9.c
index 19f7562..dfa024b 100644
--- a/drivers/spi/spi_txx9.c
+++ b/drivers/spi/spi_txx9.c
@@ -375,12 +375,10 @@
 	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
 	if (!res)
 		goto exit_busy;
-	if (!devm_request_mem_region(&dev->dev,
-				     res->start, res->end - res->start + 1,
+	if (!devm_request_mem_region(&dev->dev, res->start, resource_size(res),
 				     "spi_txx9"))
 		goto exit_busy;
-	c->membase = devm_ioremap(&dev->dev,
-				  res->start, res->end - res->start + 1);
+	c->membase = devm_ioremap(&dev->dev, res->start, resource_size(res));
 	if (!c->membase)
 		goto exit_busy;
 
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 9c446e6..ea1bec3 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -53,7 +53,7 @@
 #define SPIDEV_MAJOR			153	/* assigned */
 #define N_SPI_MINORS			32	/* ... up to 256 */
 
-static unsigned long	minors[N_SPI_MINORS / BITS_PER_LONG];
+static DECLARE_BITMAP(minors, N_SPI_MINORS);
 
 
 /* Bit masks for spi_device.mode management.  Note that incorrect
@@ -558,7 +558,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-static int spidev_probe(struct spi_device *spi)
+static int __devinit spidev_probe(struct spi_device *spi)
 {
 	struct spidev_data	*spidev;
 	int			status;
@@ -607,7 +607,7 @@
 	return status;
 }
 
-static int spidev_remove(struct spi_device *spi)
+static int __devexit spidev_remove(struct spi_device *spi)
 {
 	struct spidev_data	*spidev = spi_get_drvdata(spi);
 
@@ -629,7 +629,7 @@
 	return 0;
 }
 
-static struct spi_driver spidev_spi = {
+static struct spi_driver spidev_spi_driver = {
 	.driver = {
 		.name =		"spidev",
 		.owner =	THIS_MODULE,
@@ -661,14 +661,14 @@
 
 	spidev_class = class_create(THIS_MODULE, "spidev");
 	if (IS_ERR(spidev_class)) {
-		unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
+		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
 		return PTR_ERR(spidev_class);
 	}
 
-	status = spi_register_driver(&spidev_spi);
+	status = spi_register_driver(&spidev_spi_driver);
 	if (status < 0) {
 		class_destroy(spidev_class);
-		unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
+		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
 	}
 	return status;
 }
@@ -676,9 +676,9 @@
 
 static void __exit spidev_exit(void)
 {
-	spi_unregister_driver(&spidev_spi);
+	spi_unregister_driver(&spidev_spi_driver);
 	class_destroy(spidev_class);
-	unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
+	unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
 }
 module_exit(spidev_exit);
 
diff --git a/drivers/video/backlight/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c
index 4c10ede..86d95c22 100644
--- a/drivers/video/backlight/adp5520_bl.c
+++ b/drivers/video/backlight/adp5520_bl.c
@@ -85,7 +85,7 @@
 	return error ? data->current_brightness : reg_val;
 }
 
-static struct backlight_ops adp5520_bl_ops = {
+static const struct backlight_ops adp5520_bl_ops = {
 	.update_status	= adp5520_bl_update_status,
 	.get_brightness	= adp5520_bl_get_brightness,
 };
diff --git a/drivers/video/backlight/adx_bl.c b/drivers/video/backlight/adx_bl.c
index 2c3bdfc..d769b0b 100644
--- a/drivers/video/backlight/adx_bl.c
+++ b/drivers/video/backlight/adx_bl.c
@@ -61,7 +61,7 @@
 	return 1;
 }
 
-static struct backlight_ops adx_backlight_ops = {
+static const struct backlight_ops adx_backlight_ops = {
 	.options = 0,
 	.update_status = adx_backlight_update_status,
 	.get_brightness = adx_backlight_get_brightness,
diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c
index 2cf7ba5..f625ffc 100644
--- a/drivers/video/backlight/atmel-pwm-bl.c
+++ b/drivers/video/backlight/atmel-pwm-bl.c
@@ -113,7 +113,7 @@
 	return pwm_channel_enable(&pwmbl->pwmc);
 }
 
-static struct backlight_ops atmel_pwm_bl_ops = {
+static const struct backlight_ops atmel_pwm_bl_ops = {
 	.get_brightness = atmel_pwm_bl_get_intensity,
 	.update_status  = atmel_pwm_bl_set_intensity,
 };
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
index 6615ac7..18829cf 100644
--- a/drivers/video/backlight/backlight.c
+++ b/drivers/video/backlight/backlight.c
@@ -269,7 +269,7 @@
  * ERR_PTR() or a pointer to the newly allocated device.
  */
 struct backlight_device *backlight_device_register(const char *name,
-		struct device *parent, void *devdata, struct backlight_ops *ops)
+		struct device *parent, void *devdata, const struct backlight_ops *ops)
 {
 	struct backlight_device *new_bd;
 	int rc;
diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c
index 9677494..b4bcf80 100644
--- a/drivers/video/backlight/corgi_lcd.c
+++ b/drivers/video/backlight/corgi_lcd.c
@@ -451,7 +451,7 @@
 }
 EXPORT_SYMBOL(corgi_lcd_limit_intensity);
 
-static struct backlight_ops corgi_bl_ops = {
+static const struct backlight_ops corgi_bl_ops = {
 	.get_brightness	= corgi_bl_get_intensity,
 	.update_status  = corgi_bl_update_status,
 };
diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c
index b9fe62b..da86db4 100644
--- a/drivers/video/backlight/cr_bllcd.c
+++ b/drivers/video/backlight/cr_bllcd.c
@@ -108,7 +108,7 @@
 	return intensity;
 }
 
-static struct backlight_ops cr_backlight_ops = {
+static const struct backlight_ops cr_backlight_ops = {
 	.get_brightness = cr_backlight_get_intensity,
 	.update_status = cr_backlight_set_intensity,
 };
@@ -201,7 +201,7 @@
 	if (IS_ERR(ldp)) {
 		backlight_device_unregister(bdp);
 		pci_dev_put(lpc_dev);
-		return PTR_ERR(bdp);
+		return PTR_ERR(ldp);
 	}
 
 	pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR,
diff --git a/drivers/video/backlight/da903x_bl.c b/drivers/video/backlight/da903x_bl.c
index f2d76da..74cdc64 100644
--- a/drivers/video/backlight/da903x_bl.c
+++ b/drivers/video/backlight/da903x_bl.c
@@ -95,7 +95,7 @@
 	return data->current_brightness;
 }
 
-static struct backlight_ops da903x_backlight_ops = {
+static const struct backlight_ops da903x_backlight_ops = {
 	.update_status	= da903x_backlight_update_status,
 	.get_brightness	= da903x_backlight_get_brightness,
 };
diff --git a/drivers/video/backlight/generic_bl.c b/drivers/video/backlight/generic_bl.c
index 6d27f62..e6d348e 100644
--- a/drivers/video/backlight/generic_bl.c
+++ b/drivers/video/backlight/generic_bl.c
@@ -70,7 +70,7 @@
 }
 EXPORT_SYMBOL(corgibl_limit_intensity);
 
-static struct backlight_ops genericbl_ops = {
+static const struct backlight_ops genericbl_ops = {
 	.options = BL_CORE_SUSPENDRESUME,
 	.get_brightness = genericbl_get_intensity,
 	.update_status  = genericbl_send_intensity,
diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c
index 7fb4eef..f7cc528 100644
--- a/drivers/video/backlight/hp680_bl.c
+++ b/drivers/video/backlight/hp680_bl.c
@@ -98,7 +98,7 @@
 	return current_intensity;
 }
 
-static struct backlight_ops hp680bl_ops = {
+static const struct backlight_ops hp680bl_ops = {
 	.get_brightness = hp680bl_get_intensity,
 	.update_status  = hp680bl_set_intensity,
 };
diff --git a/drivers/video/backlight/jornada720_bl.c b/drivers/video/backlight/jornada720_bl.c
index 7aed256..db9071f 100644
--- a/drivers/video/backlight/jornada720_bl.c
+++ b/drivers/video/backlight/jornada720_bl.c
@@ -93,7 +93,7 @@
 	return ret;
 }
 
-static struct backlight_ops jornada_bl_ops = {
+static const struct backlight_ops jornada_bl_ops = {
 	.get_brightness = jornada_bl_get_brightness,
 	.update_status = jornada_bl_update_status,
 	.options = BL_CORE_SUSPENDRESUME,
diff --git a/drivers/video/backlight/kb3886_bl.c b/drivers/video/backlight/kb3886_bl.c
index a38fda1..939e7b8 100644
--- a/drivers/video/backlight/kb3886_bl.c
+++ b/drivers/video/backlight/kb3886_bl.c
@@ -134,7 +134,7 @@
 	return kb3886bl_intensity;
 }
 
-static struct backlight_ops kb3886bl_ops = {
+static const struct backlight_ops kb3886bl_ops = {
 	.get_brightness = kb3886bl_get_intensity,
 	.update_status  = kb3886bl_send_intensity,
 };
diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c
index 6b488b8..00a9591 100644
--- a/drivers/video/backlight/locomolcd.c
+++ b/drivers/video/backlight/locomolcd.c
@@ -141,7 +141,7 @@
 	return current_intensity;
 }
 
-static struct backlight_ops locomobl_data = {
+static const struct backlight_ops locomobl_data = {
 	.get_brightness = locomolcd_get_intensity,
 	.update_status  = locomolcd_set_intensity,
 };
diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c
index 9edb8d7..2e78b07 100644
--- a/drivers/video/backlight/mbp_nvidia_bl.c
+++ b/drivers/video/backlight/mbp_nvidia_bl.c
@@ -33,7 +33,7 @@
 	unsigned long iostart;
 	unsigned long iolen;
 	/* Backlight operations structure. */
-	struct backlight_ops backlight_ops;
+	const struct backlight_ops backlight_ops;
 };
 
 /* Module parameters. */
@@ -220,6 +220,24 @@
 	},
 	{
 		.callback	= mbp_dmi_match,
+		.ident		= "MacBookPro 5,3",
+		.matches	= {
+			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,3"),
+		},
+		.driver_data	= (void *)&nvidia_chipset_data,
+	},
+	{
+		.callback	= mbp_dmi_match,
+		.ident		= "MacBookPro 5,4",
+		.matches	= {
+			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,4"),
+		},
+		.driver_data	= (void *)&nvidia_chipset_data,
+	},
+	{
+		.callback	= mbp_dmi_match,
 		.ident		= "MacBookPro 5,5",
 		.matches	= {
 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
diff --git a/drivers/video/backlight/omap1_bl.c b/drivers/video/backlight/omap1_bl.c
index 8693e5f..409ca96 100644
--- a/drivers/video/backlight/omap1_bl.c
+++ b/drivers/video/backlight/omap1_bl.c
@@ -125,7 +125,7 @@
 	return bl->current_intensity;
 }
 
-static struct backlight_ops omapbl_ops = {
+static const struct backlight_ops omapbl_ops = {
 	.get_brightness = omapbl_get_intensity,
 	.update_status  = omapbl_update_status,
 };
diff --git a/drivers/video/backlight/progear_bl.c b/drivers/video/backlight/progear_bl.c
index 9edaf24..075786e 100644
--- a/drivers/video/backlight/progear_bl.c
+++ b/drivers/video/backlight/progear_bl.c
@@ -54,7 +54,7 @@
 	return intensity - HW_LEVEL_MIN;
 }
 
-static struct backlight_ops progearbl_ops = {
+static const struct backlight_ops progearbl_ops = {
 	.get_brightness = progearbl_get_intensity,
 	.update_status = progearbl_set_intensity,
 };
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 8871662..9d2ec2a 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -22,8 +22,10 @@
 
 struct pwm_bl_data {
 	struct pwm_device	*pwm;
+	struct device		*dev;
 	unsigned int		period;
-	int			(*notify)(int brightness);
+	int			(*notify)(struct device *,
+					  int brightness);
 };
 
 static int pwm_backlight_update_status(struct backlight_device *bl)
@@ -39,7 +41,7 @@
 		brightness = 0;
 
 	if (pb->notify)
-		brightness = pb->notify(brightness);
+		brightness = pb->notify(pb->dev, brightness);
 
 	if (brightness == 0) {
 		pwm_config(pb->pwm, 0, pb->period);
@@ -56,7 +58,7 @@
 	return bl->props.brightness;
 }
 
-static struct backlight_ops pwm_backlight_ops = {
+static const struct backlight_ops pwm_backlight_ops = {
 	.update_status	= pwm_backlight_update_status,
 	.get_brightness	= pwm_backlight_get_brightness,
 };
@@ -88,6 +90,7 @@
 
 	pb->period = data->pwm_period_ns;
 	pb->notify = data->notify;
+	pb->dev = &pdev->dev;
 
 	pb->pwm = pwm_request(data->pwm_id, "backlight");
 	if (IS_ERR(pb->pwm)) {
@@ -146,7 +149,7 @@
 	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
 
 	if (pb->notify)
-		pb->notify(0);
+		pb->notify(pb->dev, 0);
 	pwm_config(pb->pwm, 0, pb->period);
 	pwm_disable(pb->pwm);
 	return 0;
diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c
index 43edbad..e14ce4d 100644
--- a/drivers/video/backlight/tosa_bl.c
+++ b/drivers/video/backlight/tosa_bl.c
@@ -72,7 +72,7 @@
 	return props->brightness;
 }
 
-static struct backlight_ops bl_ops = {
+static const struct backlight_ops bl_ops = {
 	.get_brightness		= tosa_bl_get_brightness,
 	.update_status		= tosa_bl_update_status,
 };
diff --git a/drivers/video/backlight/wm831x_bl.c b/drivers/video/backlight/wm831x_bl.c
index 467bdb7..e32add3 100644
--- a/drivers/video/backlight/wm831x_bl.c
+++ b/drivers/video/backlight/wm831x_bl.c
@@ -112,7 +112,7 @@
 	return data->current_brightness;
 }
 
-static struct backlight_ops wm831x_backlight_ops = {
+static const struct backlight_ops wm831x_backlight_ops = {
 	.options = BL_CORE_SUSPENDRESUME,
 	.update_status	= wm831x_backlight_update_status,
 	.get_brightness	= wm831x_backlight_get_brightness,
diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c
index 10d8c4b..d8df17a 100644
--- a/drivers/video/via/viafbdev.c
+++ b/drivers/video/via/viafbdev.c
@@ -680,7 +680,7 @@
 		if (!viafb_gamma_table)
 			return -ENOMEM;
 		if (copy_from_user(viafb_gamma_table, argp,
-				sizeof(viafb_gamma_table))) {
+				256 * sizeof(u32))) {
 			kfree(viafb_gamma_table);
 			return -EFAULT;
 		}
@@ -694,7 +694,7 @@
 			return -ENOMEM;
 		viafb_get_gamma_table(viafb_gamma_table);
 		if (copy_to_user(argp, viafb_gamma_table,
-			sizeof(viafb_gamma_table))) {
+			256 * sizeof(u32))) {
 			kfree(viafb_gamma_table);
 			return -EFAULT;
 		}
diff --git a/fs/Kconfig b/fs/Kconfig
index f8fccaa..64d44ef 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -6,10 +6,6 @@
 
 if BLOCK
 
-config FS_JOURNAL_INFO
-	bool
-	default n
-
 source "fs/ext2/Kconfig"
 source "fs/ext3/Kconfig"
 source "fs/ext4/Kconfig"
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
index b639dcf..346b694 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -32,7 +32,7 @@
 
 static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
 static int load_aout_library(struct file*);
-static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+static int aout_core_dump(struct coredump_params *cprm);
 
 static struct linux_binfmt aout_format = {
 	.module		= THIS_MODULE,
@@ -89,8 +89,9 @@
  * dumping of the process results in another error..
  */
 
-static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
+static int aout_core_dump(struct coredump_params *cprm)
 {
+	struct file *file = cprm->file;
 	mm_segment_t fs;
 	int has_dumped = 0;
 	unsigned long dump_start, dump_size;
@@ -108,16 +109,16 @@
 	current->flags |= PF_DUMPCORE;
        	strncpy(dump.u_comm, current->comm, sizeof(dump.u_comm));
 	dump.u_ar0 = offsetof(struct user, regs);
-	dump.signal = signr;
-	aout_dump_thread(regs, &dump);
+	dump.signal = cprm->signr;
+	aout_dump_thread(cprm->regs, &dump);
 
 /* If the size of the dump file exceeds the rlimit, then see what would happen
    if we wrote the stack, but not the data area.  */
-	if ((dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE > limit)
+	if ((dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE > cprm->limit)
 		dump.u_dsize = 0;
 
 /* Make sure we have enough room to write the stack and data areas. */
-	if ((dump.u_ssize + 1) * PAGE_SIZE > limit)
+	if ((dump.u_ssize + 1) * PAGE_SIZE > cprm->limit)
 		dump.u_ssize = 0;
 
 /* make sure we actually have a data and stack area to dump */
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 97b6e9e..edd90c4 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -45,7 +45,7 @@
  * don't even try.
  */
 #ifdef CONFIG_ELF_CORE
-static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+static int elf_core_dump(struct coredump_params *cprm);
 #else
 #define elf_core_dump	NULL
 #endif
@@ -1272,8 +1272,9 @@
 }
 #undef DUMP_WRITE
 
-#define DUMP_WRITE(addr, nr)	\
-	if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
+#define DUMP_WRITE(addr, nr)				\
+	if ((size += (nr)) > cprm->limit ||		\
+	    !dump_write(cprm->file, (addr), (nr)))	\
 		goto end_coredump;
 
 static void fill_elf_header(struct elfhdr *elf, int segs,
@@ -1901,7 +1902,7 @@
  * and then they are actually written out.  If we run out of core limit
  * we just truncate.
  */
-static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
+static int elf_core_dump(struct coredump_params *cprm)
 {
 	int has_dumped = 0;
 	mm_segment_t fs;
@@ -1947,7 +1948,7 @@
 	 * notes.  This also sets up the file header.
 	 */
 	if (!fill_note_info(elf, segs + 1, /* including notes section */
-			    &info, signr, regs))
+			    &info, cprm->signr, cprm->regs))
 		goto cleanup;
 
 	has_dumped = 1;
@@ -2009,14 +2010,14 @@
 #endif
 
  	/* write out the notes section */
-	if (!write_note_info(&info, file, &foffset))
+	if (!write_note_info(&info, cprm->file, &foffset))
 		goto end_coredump;
 
-	if (elf_coredump_extra_notes_write(file, &foffset))
+	if (elf_coredump_extra_notes_write(cprm->file, &foffset))
 		goto end_coredump;
 
 	/* Align to page */
-	if (!dump_seek(file, dataoff - foffset))
+	if (!dump_seek(cprm->file, dataoff - foffset))
 		goto end_coredump;
 
 	for (vma = first_vma(current, gate_vma); vma != NULL;
@@ -2033,12 +2034,13 @@
 			page = get_dump_page(addr);
 			if (page) {
 				void *kaddr = kmap(page);
-				stop = ((size += PAGE_SIZE) > limit) ||
-					!dump_write(file, kaddr, PAGE_SIZE);
+				stop = ((size += PAGE_SIZE) > cprm->limit) ||
+					!dump_write(cprm->file, kaddr,
+						    PAGE_SIZE);
 				kunmap(page);
 				page_cache_release(page);
 			} else
-				stop = !dump_seek(file, PAGE_SIZE);
+				stop = !dump_seek(cprm->file, PAGE_SIZE);
 			if (stop)
 				goto end_coredump;
 		}
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 7b05538..c25256a 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -76,7 +76,7 @@
 					     struct file *, struct mm_struct *);
 
 #ifdef CONFIG_ELF_CORE
-static int elf_fdpic_core_dump(long, struct pt_regs *, struct file *, unsigned long limit);
+static int elf_fdpic_core_dump(struct coredump_params *cprm);
 #endif
 
 static struct linux_binfmt elf_fdpic_format = {
@@ -1326,8 +1326,9 @@
 #undef DUMP_WRITE
 #undef DUMP_SEEK
 
-#define DUMP_WRITE(addr, nr)	\
-	if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
+#define DUMP_WRITE(addr, nr)				\
+	if ((size += (nr)) > cprm->limit ||		\
+	    !dump_write(cprm->file, (addr), (nr)))	\
 		goto end_coredump;
 
 static inline void fill_elf_fdpic_header(struct elfhdr *elf, int segs)
@@ -1582,8 +1583,7 @@
  * and then they are actually written out.  If we run out of core limit
  * we just truncate.
  */
-static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
-			       struct file *file, unsigned long limit)
+static int elf_fdpic_core_dump(struct coredump_params *cprm)
 {
 #define	NUM_NOTES	6
 	int has_dumped = 0;
@@ -1642,7 +1642,7 @@
 		goto cleanup;
 #endif
 
-	if (signr) {
+	if (cprm->signr) {
 		struct core_thread *ct;
 		struct elf_thread_status *tmp;
 
@@ -1661,14 +1661,14 @@
 			int sz;
 
 			tmp = list_entry(t, struct elf_thread_status, list);
-			sz = elf_dump_thread_status(signr, tmp);
+			sz = elf_dump_thread_status(cprm->signr, tmp);
 			thread_status_size += sz;
 		}
 	}
 
 	/* now collect the dump for the current */
-	fill_prstatus(prstatus, current, signr);
-	elf_core_copy_regs(&prstatus->pr_reg, regs);
+	fill_prstatus(prstatus, current, cprm->signr);
+	elf_core_copy_regs(&prstatus->pr_reg, cprm->regs);
 
 	segs = current->mm->map_count;
 #ifdef ELF_CORE_EXTRA_PHDRS
@@ -1703,7 +1703,7 @@
 
   	/* Try to dump the FPU. */
 	if ((prstatus->pr_fpvalid =
-	     elf_core_copy_task_fpregs(current, regs, fpu)))
+	     elf_core_copy_task_fpregs(current, cprm->regs, fpu)))
 		fill_note(notes + numnote++,
 			  "CORE", NT_PRFPREG, sizeof(*fpu), fpu);
 #ifdef ELF_CORE_COPY_XFPREGS
@@ -1774,7 +1774,7 @@
 
  	/* write out the notes section */
 	for (i = 0; i < numnote; i++)
-		if (!writenote(notes + i, file))
+		if (!writenote(notes + i, cprm->file))
 			goto end_coredump;
 
 	/* write out the thread status notes section */
@@ -1783,14 +1783,15 @@
 				list_entry(t, struct elf_thread_status, list);
 
 		for (i = 0; i < tmp->num_notes; i++)
-			if (!writenote(&tmp->notes[i], file))
+			if (!writenote(&tmp->notes[i], cprm->file))
 				goto end_coredump;
 	}
 
-	if (!dump_seek(file, dataoff))
+	if (!dump_seek(cprm->file, dataoff))
 		goto end_coredump;
 
-	if (elf_fdpic_dump_segments(file, &size, &limit, mm_flags) < 0)
+	if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit,
+				    mm_flags) < 0)
 		goto end_coredump;
 
 #ifdef ELF_CORE_WRITE_EXTRA_DATA
diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c
index a279665..d4a00ea 100644
--- a/fs/binfmt_flat.c
+++ b/fs/binfmt_flat.c
@@ -87,7 +87,7 @@
 #endif
 
 static int load_flat_binary(struct linux_binprm *, struct pt_regs * regs);
-static int flat_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+static int flat_core_dump(struct coredump_params *cprm);
 
 static struct linux_binfmt flat_format = {
 	.module		= THIS_MODULE,
@@ -102,10 +102,10 @@
  * Currently only a stub-function.
  */
 
-static int flat_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
+static int flat_core_dump(struct coredump_params *cprm)
 {
 	printk("Process %s:%d received signr %d and should have core dumped\n",
-			current->comm, current->pid, (int) signr);
+			current->comm, current->pid, (int) cprm->signr);
 	return(1);
 }
 
diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c
index eff74b9..2a9b533 100644
--- a/fs/binfmt_som.c
+++ b/fs/binfmt_som.c
@@ -43,7 +43,7 @@
  * don't even try.
  */
 #if 0
-static int som_core_dump(long signr, struct pt_regs *regs, unsigned long limit);
+static int som_core_dump(struct coredump_params *cprm);
 #else
 #define som_core_dump	NULL
 #endif
diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig
index 402afe0..7bb3c02 100644
--- a/fs/btrfs/Kconfig
+++ b/fs/btrfs/Kconfig
@@ -4,7 +4,6 @@
 	select LIBCRC32C
 	select ZLIB_INFLATE
 	select ZLIB_DEFLATE
-	select FS_JOURNAL_INFO
 	help
 	  Btrfs is a new filesystem with extents, writable snapshotting,
 	  support for multiple devices and many more features.
diff --git a/fs/exec.c b/fs/exec.c
index 623a5cc..632b02e 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -826,7 +826,9 @@
 		attach_pid(tsk, PIDTYPE_PID,  task_pid(leader));
 		transfer_pid(leader, tsk, PIDTYPE_PGID);
 		transfer_pid(leader, tsk, PIDTYPE_SID);
+
 		list_replace_rcu(&leader->tasks, &tsk->tasks);
+		list_replace_init(&leader->sibling, &tsk->sibling);
 
 		tsk->group_leader = tsk;
 		leader->group_leader = tsk;
@@ -1761,17 +1763,20 @@
 	struct mm_struct *mm = current->mm;
 	struct linux_binfmt * binfmt;
 	struct inode * inode;
-	struct file * file;
 	const struct cred *old_cred;
 	struct cred *cred;
 	int retval = 0;
 	int flag = 0;
 	int ispipe = 0;
-	unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
 	char **helper_argv = NULL;
 	int helper_argc = 0;
 	int dump_count = 0;
 	static atomic_t core_dump_count = ATOMIC_INIT(0);
+	struct coredump_params cprm = {
+		.signr = signr,
+		.regs = regs,
+		.limit = current->signal->rlim[RLIMIT_CORE].rlim_cur,
+	};
 
 	audit_core_dumps(signr);
 
@@ -1827,15 +1832,15 @@
 	ispipe = format_corename(corename, signr);
 	unlock_kernel();
 
-	if ((!ispipe) && (core_limit < binfmt->min_coredump))
+	if ((!ispipe) && (cprm.limit < binfmt->min_coredump))
 		goto fail_unlock;
 
  	if (ispipe) {
-		if (core_limit == 0) {
+		if (cprm.limit == 0) {
 			/*
 			 * Normally core limits are irrelevant to pipes, since
 			 * we're not writing to the file system, but we use
-			 * core_limit of 0 here as a speacial value. Any
+			 * cprm.limit of 0 here as a speacial value. Any
 			 * non-zero limit gets set to RLIM_INFINITY below, but
 			 * a limit of 0 skips the dump.  This is a consistent
 			 * way to catch recursive crashes.  We can still crash
@@ -1868,25 +1873,25 @@
 			goto fail_dropcount;
 		}
 
-		core_limit = RLIM_INFINITY;
+		cprm.limit = RLIM_INFINITY;
 
 		/* SIGPIPE can happen, but it's just never processed */
 		if (call_usermodehelper_pipe(helper_argv[0], helper_argv, NULL,
-				&file)) {
+				&cprm.file)) {
  			printk(KERN_INFO "Core dump to %s pipe failed\n",
 			       corename);
 			goto fail_dropcount;
  		}
  	} else
- 		file = filp_open(corename,
+		cprm.file = filp_open(corename,
 				 O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
 				 0600);
-	if (IS_ERR(file))
+	if (IS_ERR(cprm.file))
 		goto fail_dropcount;
-	inode = file->f_path.dentry->d_inode;
+	inode = cprm.file->f_path.dentry->d_inode;
 	if (inode->i_nlink > 1)
 		goto close_fail;	/* multiple links - don't dump */
-	if (!ispipe && d_unhashed(file->f_path.dentry))
+	if (!ispipe && d_unhashed(cprm.file->f_path.dentry))
 		goto close_fail;
 
 	/* AK: actually i see no reason to not allow this for named pipes etc.,
@@ -1899,21 +1904,22 @@
 	 */
 	if (inode->i_uid != current_fsuid())
 		goto close_fail;
-	if (!file->f_op)
+	if (!cprm.file->f_op)
 		goto close_fail;
-	if (!file->f_op->write)
+	if (!cprm.file->f_op->write)
 		goto close_fail;
-	if (!ispipe && do_truncate(file->f_path.dentry, 0, 0, file) != 0)
+	if (!ispipe &&
+	    do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file) != 0)
 		goto close_fail;
 
-	retval = binfmt->core_dump(signr, regs, file, core_limit);
+	retval = binfmt->core_dump(&cprm);
 
 	if (retval)
 		current->signal->group_exit_code |= 0x80;
 close_fail:
 	if (ispipe && core_pipe_limit)
-		wait_for_dump_helpers(file);
-	filp_close(file, NULL);
+		wait_for_dump_helpers(cprm.file);
+	filp_close(cprm.file, NULL);
 fail_dropcount:
 	if (dump_count)
 		atomic_dec(&core_dump_count);
diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index e5f6774..9acf7e8 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -2,7 +2,6 @@
 	tristate "The Extended 4 (ext4) filesystem"
 	select JBD2
 	select CRC16
-	select FS_JOURNAL_INFO
 	help
 	  This is the next generation of the ext3 filesystem.
 
diff --git a/fs/gfs2/Kconfig b/fs/gfs2/Kconfig
index b192c66..4dcddf8 100644
--- a/fs/gfs2/Kconfig
+++ b/fs/gfs2/Kconfig
@@ -10,7 +10,6 @@
 	select SLOW_WORK
 	select QUOTA
 	select QUOTACTL
-	select FS_JOURNAL_INFO
 	help
 	  A cluster filesystem.
 
diff --git a/fs/jbd/Kconfig b/fs/jbd/Kconfig
index a840898..4e28bee 100644
--- a/fs/jbd/Kconfig
+++ b/fs/jbd/Kconfig
@@ -1,6 +1,5 @@
 config JBD
 	tristate
-	select FS_JOURNAL_INFO
 	help
 	  This is a generic journalling layer for block devices.  It is
 	  currently used by the ext3 file system, but it could also be
diff --git a/fs/jbd2/Kconfig b/fs/jbd2/Kconfig
index 0f7d1ce..f32f346 100644
--- a/fs/jbd2/Kconfig
+++ b/fs/jbd2/Kconfig
@@ -1,7 +1,6 @@
 config JBD2
 	tristate
 	select CRC32
-	select FS_JOURNAL_INFO
 	help
 	  This is a generic journaling layer for block devices that support
 	  both 32-bit and 64-bit block numbers.  It is currently used by
diff --git a/fs/namespace.c b/fs/namespace.c
index faab127..7d70d63 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2068,7 +2068,7 @@
  * create_mnt_ns - creates a private namespace and adds a root filesystem
  * @mnt: pointer to the new root filesystem mountpoint
  */
-static struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt)
+struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt)
 {
 	struct mnt_namespace *new_ns;
 
@@ -2080,6 +2080,7 @@
 	}
 	return new_ns;
 }
+EXPORT_SYMBOL(create_mnt_ns);
 
 SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
 		char __user *, type, unsigned long, flags, void __user *, data)
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index d5b112b..ce907ef 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2648,13 +2648,21 @@
 static int nfs_follow_remote_path(struct vfsmount *root_mnt,
 		const char *export_path, struct vfsmount *mnt_target)
 {
+	struct mnt_namespace *ns_private;
 	struct nameidata nd;
 	struct super_block *s;
 	int ret;
 
+	ns_private = create_mnt_ns(root_mnt);
+	ret = PTR_ERR(ns_private);
+	if (IS_ERR(ns_private))
+		goto out_mntput;
+
 	ret = vfs_path_lookup(root_mnt->mnt_root, root_mnt,
 			export_path, LOOKUP_FOLLOW, &nd);
 
+	put_mnt_ns(ns_private);
+
 	if (ret != 0)
 		goto out_err;
 
diff --git a/fs/nilfs2/Kconfig b/fs/nilfs2/Kconfig
index 1225af7..251da07 100644
--- a/fs/nilfs2/Kconfig
+++ b/fs/nilfs2/Kconfig
@@ -2,7 +2,6 @@
 	tristate "NILFS2 file system support (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
 	select CRC32
-	select FS_JOURNAL_INFO
 	help
 	  NILFS2 is a log-structured file system (LFS) supporting continuous
 	  snapshotting.  In addition to versioning capability of the entire
diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c
index 32fae40..2efc571 100644
--- a/fs/ramfs/file-nommu.c
+++ b/fs/ramfs/file-nommu.c
@@ -60,7 +60,7 @@
  */
 int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize)
 {
-	unsigned long npages, xpages, loop, limit;
+	unsigned long npages, xpages, loop;
 	struct page *pages;
 	unsigned order;
 	void *data;
diff --git a/fs/reiserfs/Kconfig b/fs/reiserfs/Kconfig
index ac7cd75..513f431 100644
--- a/fs/reiserfs/Kconfig
+++ b/fs/reiserfs/Kconfig
@@ -1,7 +1,6 @@
 config REISERFS_FS
 	tristate "Reiserfs support"
 	select CRC32
-	select FS_JOURNAL_INFO
 	help
 	  Stores not just filenames but the files themselves in a balanced
 	  tree.  Uses journalling.
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 3a28e77..290ae38f 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -2538,6 +2538,12 @@
 	return reiserfs_write_full_page(page, wbc);
 }
 
+static void reiserfs_truncate_failed_write(struct inode *inode)
+{
+	truncate_inode_pages(inode->i_mapping, inode->i_size);
+	reiserfs_truncate_file(inode, 0);
+}
+
 static int reiserfs_write_begin(struct file *file,
 				struct address_space *mapping,
 				loff_t pos, unsigned len, unsigned flags,
@@ -2604,6 +2610,8 @@
 	if (ret) {
 		unlock_page(page);
 		page_cache_release(page);
+		/* Truncate allocated blocks */
+		reiserfs_truncate_failed_write(inode);
 	}
 	return ret;
 }
@@ -2701,9 +2709,7 @@
 	 ** transaction tracking stuff when the size changes.  So, we have
 	 ** to do the i_size updates here.
 	 */
-	pos += copied;
-
-	if (pos > inode->i_size) {
+	if (pos + copied > inode->i_size) {
 		struct reiserfs_transaction_handle myth;
 		lock_depth = reiserfs_write_lock_once(inode->i_sb);
 		locked = true;
@@ -2721,7 +2727,7 @@
 			goto journal_error;
 
 		reiserfs_update_inode_transaction(inode);
-		inode->i_size = pos;
+		inode->i_size = pos + copied;
 		/*
 		 * this will just nest into our transaction.  It's important
 		 * to use mark_inode_dirty so the inode gets pushed around on the
@@ -2751,6 +2757,10 @@
 		reiserfs_write_unlock_once(inode->i_sb, lock_depth);
 	unlock_page(page);
 	page_cache_release(page);
+
+	if (pos + len > inode->i_size)
+		reiserfs_truncate_failed_write(inode);
+
 	return ret == 0 ? copied : ret;
 
       journal_error:
diff --git a/include/linux/backlight.h b/include/linux/backlight.h
index 0f5f578..8c4f884 100644
--- a/include/linux/backlight.h
+++ b/include/linux/backlight.h
@@ -36,18 +36,18 @@
 struct fb_info;
 
 struct backlight_ops {
-	unsigned int options;
+	const unsigned int options;
 
 #define BL_CORE_SUSPENDRESUME	(1 << 0)
 
 	/* Notify the backlight driver some property has changed */
-	int (*update_status)(struct backlight_device *);
+	int (* const update_status)(struct backlight_device *);
 	/* Return the current backlight brightness (accounting for power,
 	   fb_blank etc.) */
-	int (*get_brightness)(struct backlight_device *);
+	int (* const get_brightness)(struct backlight_device *);
 	/* Check if given framebuffer device is the one bound to this backlight;
 	   return 0 if not, !=0 if it is. If NULL, backlight always matches the fb. */
-	int (*check_fb)(struct fb_info *);
+	int (* const check_fb)(struct fb_info *);
 };
 
 /* This structure defines all the properties of a backlight */
@@ -86,7 +86,7 @@
 	   registered this device has been unloaded, and if class_get_devdata()
 	   points to something in the body of that driver, it is also invalid. */
 	struct mutex ops_lock;
-	struct backlight_ops *ops;
+	const struct backlight_ops *ops;
 
 	/* The framebuffer notifier block */
 	struct notifier_block fb_notif;
@@ -103,7 +103,7 @@
 }
 
 extern struct backlight_device *backlight_device_register(const char *name,
-	struct device *dev, void *devdata, struct backlight_ops *ops);
+	struct device *dev, void *devdata, const struct backlight_ops *ops);
 extern void backlight_device_unregister(struct backlight_device *bd);
 extern void backlight_force_update(struct backlight_device *bd,
 				   enum backlight_update_reason reason);
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index aece486..cd4349b 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -68,6 +68,14 @@
 
 #define BINPRM_MAX_RECURSION 4
 
+/* Function parameter for binfmt->coredump */
+struct coredump_params {
+	long signr;
+	struct pt_regs *regs;
+	struct file *file;
+	unsigned long limit;
+};
+
 /*
  * This structure defines the functions that are used to load the binary formats that
  * linux accepts.
@@ -77,7 +85,7 @@
 	struct module *module;
 	int (*load_binary)(struct linux_binprm *, struct  pt_regs * regs);
 	int (*load_shlib)(struct file *);
-	int (*core_dump)(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+	int (*core_dump)(struct coredump_params *cprm);
 	unsigned long min_coredump;	/* minimal dump size */
 	int hasvdso;
 };
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index 5ed8b9c..abec69b 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -111,12 +111,6 @@
 # define INIT_PERF_EVENTS(tsk)
 #endif
 
-#ifdef CONFIG_FS_JOURNAL_INFO
-#define INIT_JOURNAL_INFO	.journal_info = NULL,
-#else
-#define INIT_JOURNAL_INFO
-#endif
-
 /*
  *  INIT_TASK is used to set up the first task table, touch at
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -168,6 +162,7 @@
 		.signal = {{0}}},					\
 	.blocked	= {{0}},					\
 	.alloc_lock	= __SPIN_LOCK_UNLOCKED(tsk.alloc_lock),		\
+	.journal_info	= NULL,						\
 	.cpu_timers	= INIT_CPU_TIMERS(tsk.cpu_timers),		\
 	.fs_excl	= ATOMIC_INIT(0),				\
 	.pi_lock	= __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock),	\
@@ -178,7 +173,6 @@
 		[PIDTYPE_SID]  = INIT_PID_LINK(PIDTYPE_SID),		\
 	},								\
 	.dirties = INIT_PROP_LOCAL_SINGLE(dirties),			\
-	INIT_JOURNAL_INFO						\
 	INIT_IDS							\
 	INIT_PERF_EVENTS(tsk)						\
 	INIT_TRACE_IRQFLAGS						\
diff --git a/include/linux/kmemleak.h b/include/linux/kmemleak.h
index 3c7497d..99d9a67 100644
--- a/include/linux/kmemleak.h
+++ b/include/linux/kmemleak.h
@@ -32,8 +32,7 @@
 			     size_t size) __ref;
 extern void kmemleak_not_leak(const void *ptr) __ref;
 extern void kmemleak_ignore(const void *ptr) __ref;
-extern void kmemleak_scan_area(const void *ptr, unsigned long offset,
-			       size_t length, gfp_t gfp) __ref;
+extern void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) __ref;
 extern void kmemleak_no_scan(const void *ptr) __ref;
 
 static inline void kmemleak_alloc_recursive(const void *ptr, size_t size,
@@ -84,8 +83,7 @@
 static inline void kmemleak_ignore(const void *ptr)
 {
 }
-static inline void kmemleak_scan_area(const void *ptr, unsigned long offset,
-				      size_t length, gfp_t gfp)
+static inline void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp)
 {
 }
 static inline void kmemleak_erase(void **ptr)
diff --git a/include/linux/leds-lp3944.h b/include/linux/leds-lp3944.h
index afc9f9f..2618aa9 100644
--- a/include/linux/leds-lp3944.h
+++ b/include/linux/leds-lp3944.h
@@ -12,9 +12,6 @@
 #ifndef __LINUX_LEDS_LP3944_H
 #define __LINUX_LEDS_LP3944_H
 
-#include <linux/leds.h>
-#include <linux/workqueue.h>
-
 #define LP3944_LED0 0
 #define LP3944_LED1 1
 #define LP3944_LED2 2
diff --git a/include/linux/leds-pca9532.h b/include/linux/leds-pca9532.h
index 96eea90..f158eb1 100644
--- a/include/linux/leds-pca9532.h
+++ b/include/linux/leds-pca9532.h
@@ -32,7 +32,7 @@
 	struct i2c_client *client;
 	char *name;
 	struct led_classdev ldev;
-       struct work_struct work;
+	struct work_struct work;
 	enum pca9532_type type;
 	enum pca9532_state state;
 };
diff --git a/include/linux/leds-regulator.h b/include/linux/leds-regulator.h
new file mode 100644
index 0000000..5a8eb38
--- /dev/null
+++ b/include/linux/leds-regulator.h
@@ -0,0 +1,46 @@
+/*
+ * leds-regulator.h - platform data structure for regulator driven LEDs.
+ *
+ * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __LINUX_LEDS_REGULATOR_H
+#define __LINUX_LEDS_REGULATOR_H
+
+/*
+ * Use "vled" as supply id when declaring the regulator consumer:
+ *
+ * static struct regulator_consumer_supply pcap_regulator_VVIB_consumers [] = {
+ * 	{ .dev_name = "leds-regulator.0", supply = "vled" },
+ * };
+ *
+ * If you have several regulator driven LEDs, you can append a numerical id to
+ * .dev_name as done above, and use the same id when declaring the platform
+ * device:
+ *
+ * static struct led_regulator_platform_data a780_vibrator_data = {
+ * 	.name   = "a780::vibrator",
+ * };
+ *
+ * static struct platform_device a780_vibrator = {
+ * 	.name = "leds-regulator",
+ * 	.id   = 0,
+ * 	.dev  = {
+ * 		.platform_data = &a780_vibrator_data,
+ * 	},
+ * };
+ */
+
+#include <linux/leds.h>
+
+struct led_regulator_platform_data {
+	char *name;                     /* LED name as expected by LED class */
+	enum led_brightness brightness; /* initial brightness value */
+};
+
+#endif /* __LINUX_LEDS_REGULATOR_H */
diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h
index d9ebf10..d74785c 100644
--- a/include/linux/mnt_namespace.h
+++ b/include/linux/mnt_namespace.h
@@ -23,6 +23,7 @@
 
 struct fs_struct;
 
+extern struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt);
 extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
 		struct fs_struct *);
 extern void put_mnt_ns(struct mnt_namespace *ns);
diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h
index 7a9754c..01b3d75 100644
--- a/include/linux/pwm_backlight.h
+++ b/include/linux/pwm_backlight.h
@@ -10,7 +10,7 @@
 	unsigned int dft_brightness;
 	unsigned int pwm_period_ns;
 	int (*init)(struct device *dev);
-	int (*notify)(int brightness);
+	int (*notify)(struct device *dev, int brightness);
 	void (*exit)(struct device *dev);
 };
 
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 244c287..211ed32 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1446,10 +1446,8 @@
 	gfp_t lockdep_reclaim_gfp;
 #endif
 
-#ifdef CONFIG_FS_JOURNAL_INFO
 /* journalling filesystem info */
 	void *journal_info;
-#endif
 
 /* stacked block device info */
 	struct bio *bio_list, **bio_tail;
diff --git a/include/linux/spi/dw_spi.h b/include/linux/spi/dw_spi.h
new file mode 100644
index 0000000..51b3e77
--- /dev/null
+++ b/include/linux/spi/dw_spi.h
@@ -0,0 +1,212 @@
+#ifndef DW_SPI_HEADER_H
+#define DW_SPI_HEADER_H
+#include <linux/io.h>
+
+/* Bit fields in CTRLR0 */
+#define SPI_DFS_OFFSET			0
+
+#define SPI_FRF_OFFSET			4
+#define SPI_FRF_SPI			0x0
+#define SPI_FRF_SSP			0x1
+#define SPI_FRF_MICROWIRE		0x2
+#define SPI_FRF_RESV			0x3
+
+#define SPI_MODE_OFFSET			6
+#define SPI_SCPH_OFFSET			6
+#define SPI_SCOL_OFFSET			7
+#define SPI_TMOD_OFFSET			8
+#define	SPI_TMOD_TR			0x0		/* xmit & recv */
+#define SPI_TMOD_TO			0x1		/* xmit only */
+#define SPI_TMOD_RO			0x2		/* recv only */
+#define SPI_TMOD_EPROMREAD		0x3		/* eeprom read mode */
+
+#define SPI_SLVOE_OFFSET		10
+#define SPI_SRL_OFFSET			11
+#define SPI_CFS_OFFSET			12
+
+/* Bit fields in SR, 7 bits */
+#define SR_MASK				0x7f		/* cover 7 bits */
+#define SR_BUSY				(1 << 0)
+#define SR_TF_NOT_FULL			(1 << 1)
+#define SR_TF_EMPT			(1 << 2)
+#define SR_RF_NOT_EMPT			(1 << 3)
+#define SR_RF_FULL			(1 << 4)
+#define SR_TX_ERR			(1 << 5)
+#define SR_DCOL				(1 << 6)
+
+/* Bit fields in ISR, IMR, RISR, 7 bits */
+#define SPI_INT_TXEI			(1 << 0)
+#define SPI_INT_TXOI			(1 << 1)
+#define SPI_INT_RXUI			(1 << 2)
+#define SPI_INT_RXOI			(1 << 3)
+#define SPI_INT_RXFI			(1 << 4)
+#define SPI_INT_MSTI			(1 << 5)
+
+/* TX RX interrupt level threshhold, max can be 256 */
+#define SPI_INT_THRESHOLD		32
+
+enum dw_ssi_type {
+	SSI_MOTO_SPI = 0,
+	SSI_TI_SSP,
+	SSI_NS_MICROWIRE,
+};
+
+struct dw_spi_reg {
+	u32	ctrl0;
+	u32	ctrl1;
+	u32	ssienr;
+	u32	mwcr;
+	u32	ser;
+	u32	baudr;
+	u32	txfltr;
+	u32	rxfltr;
+	u32	txflr;
+	u32	rxflr;
+	u32	sr;
+	u32	imr;
+	u32	isr;
+	u32	risr;
+	u32	txoicr;
+	u32	rxoicr;
+	u32	rxuicr;
+	u32	msticr;
+	u32	icr;
+	u32	dmacr;
+	u32	dmatdlr;
+	u32	dmardlr;
+	u32	idr;
+	u32	version;
+	u32	dr;		/* Currently oper as 32 bits,
+				though only low 16 bits matters */
+} __packed;
+
+struct dw_spi {
+	struct spi_master	*master;
+	struct spi_device	*cur_dev;
+	struct device		*parent_dev;
+	enum dw_ssi_type	type;
+
+	void __iomem		*regs;
+	unsigned long		paddr;
+	u32			iolen;
+	int			irq;
+	u32			max_freq;	/* max bus freq supported */
+
+	u16			bus_num;
+	u16			num_cs;		/* supported slave numbers */
+
+	/* Driver message queue */
+	struct workqueue_struct	*workqueue;
+	struct work_struct	pump_messages;
+	spinlock_t		lock;
+	struct list_head	queue;
+	int			busy;
+	int			run;
+
+	/* Message Transfer pump */
+	struct tasklet_struct	pump_transfers;
+
+	/* Current message transfer state info */
+	struct spi_message	*cur_msg;
+	struct spi_transfer	*cur_transfer;
+	struct chip_data	*cur_chip;
+	struct chip_data	*prev_chip;
+	size_t			len;
+	void			*tx;
+	void			*tx_end;
+	void			*rx;
+	void			*rx_end;
+	int			dma_mapped;
+	dma_addr_t		rx_dma;
+	dma_addr_t		tx_dma;
+	size_t			rx_map_len;
+	size_t			tx_map_len;
+	u8			n_bytes;	/* current is a 1/2 bytes op */
+	u8			max_bits_per_word;	/* maxim is 16b */
+	u32			dma_width;
+	int			cs_change;
+	int			(*write)(struct dw_spi *dws);
+	int			(*read)(struct dw_spi *dws);
+	irqreturn_t		(*transfer_handler)(struct dw_spi *dws);
+	void			(*cs_control)(u32 command);
+
+	/* Dma info */
+	int			dma_inited;
+	struct dma_chan		*txchan;
+	struct dma_chan		*rxchan;
+	int			txdma_done;
+	int			rxdma_done;
+	u64			tx_param;
+	u64			rx_param;
+	struct device		*dma_dev;
+	dma_addr_t		dma_addr;
+
+	/* Bus interface info */
+	void			*priv;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs;
+#endif
+};
+
+#define dw_readl(dw, name) \
+	__raw_readl(&(((struct dw_spi_reg *)dw->regs)->name))
+#define dw_writel(dw, name, val) \
+	__raw_writel((val), &(((struct dw_spi_reg *)dw->regs)->name))
+#define dw_readw(dw, name) \
+	__raw_readw(&(((struct dw_spi_reg *)dw->regs)->name))
+#define dw_writew(dw, name, val) \
+	__raw_writew((val), &(((struct dw_spi_reg *)dw->regs)->name))
+
+static inline void spi_enable_chip(struct dw_spi *dws, int enable)
+{
+	dw_writel(dws, ssienr, (enable ? 1 : 0));
+}
+
+static inline void spi_set_clk(struct dw_spi *dws, u16 div)
+{
+	dw_writel(dws, baudr, div);
+}
+
+static inline void spi_chip_sel(struct dw_spi *dws, u16 cs)
+{
+	if (cs > dws->num_cs)
+		return;
+	dw_writel(dws, ser, 1 << cs);
+}
+
+/* Disable IRQ bits */
+static inline void spi_mask_intr(struct dw_spi *dws, u32 mask)
+{
+	u32 new_mask;
+
+	new_mask = dw_readl(dws, imr) & ~mask;
+	dw_writel(dws, imr, new_mask);
+}
+
+/* Enable IRQ bits */
+static inline void spi_umask_intr(struct dw_spi *dws, u32 mask)
+{
+	u32 new_mask;
+
+	new_mask = dw_readl(dws, imr) | mask;
+	dw_writel(dws, imr, new_mask);
+}
+
+/*
+ * Each SPI slave device to work with dw_api controller should
+ * has such a structure claiming its working mode (PIO/DMA etc),
+ * which can be save in the "controller_data" member of the
+ * struct spi_device
+ */
+struct dw_spi_chip {
+	u8 poll_mode;	/* 0 for contoller polling mode */
+	u8 type;	/* SPI/SSP/Micrwire */
+	u8 enable_dma;
+	void (*cs_control)(u32 command);
+};
+
+extern int dw_spi_add_host(struct dw_spi *dws);
+extern void dw_spi_remove_host(struct dw_spi *dws);
+extern int dw_spi_suspend_host(struct dw_spi *dws);
+extern int dw_spi_resume_host(struct dw_spi *dws);
+#endif /* DW_SPI_HEADER_H */
diff --git a/include/linux/vt.h b/include/linux/vt.h
index 3fb9944..d5dd0bc 100644
--- a/include/linux/vt.h
+++ b/include/linux/vt.h
@@ -84,6 +84,8 @@
 
 #define VT_SETACTIVATE	0x560F	/* Activate and set the mode of a console */
 
+#ifdef __KERNEL__
+
 #ifdef CONFIG_VT_CONSOLE
 
 extern int vt_kmsg_redirect(int new);
@@ -97,6 +99,8 @@
 
 #endif
 
+#endif /* __KERNEL__ */
+
 #define vt_get_kmsg_redirect() vt_kmsg_redirect(-1)
 
 #endif /* _LINUX_VT_H */
diff --git a/kernel/exit.c b/kernel/exit.c
index 5962d7c..546774a 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -68,10 +68,10 @@
 		detach_pid(p, PIDTYPE_SID);
 
 		list_del_rcu(&p->tasks);
+		list_del_init(&p->sibling);
 		__get_cpu_var(process_counts)--;
 	}
 	list_del_rcu(&p->thread_group);
-	list_del_init(&p->sibling);
 }
 
 /*
@@ -736,12 +736,9 @@
 /*
 * Any that need to be release_task'd are put on the @dead list.
  */
-static void reparent_thread(struct task_struct *father, struct task_struct *p,
+static void reparent_leader(struct task_struct *father, struct task_struct *p,
 				struct list_head *dead)
 {
-	if (p->pdeath_signal)
-		group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p);
-
 	list_move_tail(&p->sibling, &p->real_parent->children);
 
 	if (task_detached(p))
@@ -780,12 +777,18 @@
 	reaper = find_new_reaper(father);
 
 	list_for_each_entry_safe(p, n, &father->children, sibling) {
-		p->real_parent = reaper;
-		if (p->parent == father) {
-			BUG_ON(task_ptrace(p));
-			p->parent = p->real_parent;
-		}
-		reparent_thread(father, p, &dead_children);
+		struct task_struct *t = p;
+		do {
+			t->real_parent = reaper;
+			if (t->parent == father) {
+				BUG_ON(task_ptrace(t));
+				t->parent = t->real_parent;
+			}
+			if (t->pdeath_signal)
+				group_send_sig_info(t->pdeath_signal,
+						    SEND_SIG_NOINFO, t);
+		} while_each_thread(p, t);
+		reparent_leader(father, p, &dead_children);
 	}
 	write_unlock_irq(&tasklist_lock);
 
@@ -1551,14 +1554,9 @@
 	struct task_struct *p;
 
 	list_for_each_entry(p, &tsk->children, sibling) {
-		/*
-		 * Do not consider detached threads.
-		 */
-		if (!task_detached(p)) {
-			int ret = wait_consider_task(wo, 0, p);
-			if (ret)
-				return ret;
-		}
+		int ret = wait_consider_task(wo, 0, p);
+		if (ret)
+			return ret;
 	}
 
 	return 0;
diff --git a/kernel/fork.c b/kernel/fork.c
index 202a0ba..5b2959b 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1291,7 +1291,6 @@
 	}
 
 	if (likely(p->pid)) {
-		list_add_tail(&p->sibling, &p->real_parent->children);
 		tracehook_finish_clone(p, clone_flags, trace);
 
 		if (thread_group_leader(p)) {
@@ -1303,6 +1302,7 @@
 			p->signal->tty = tty_kref_get(current->signal->tty);
 			attach_pid(p, PIDTYPE_PGID, task_pgrp(current));
 			attach_pid(p, PIDTYPE_SID, task_session(current));
+			list_add_tail(&p->sibling, &p->real_parent->children);
 			list_add_tail_rcu(&p->tasks, &init_task.tasks);
 			__get_cpu_var(process_counts)++;
 		}
diff --git a/kernel/module.c b/kernel/module.c
index a65dc78..e96b8ed 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1910,9 +1910,7 @@
 	unsigned int i;
 
 	/* only scan the sections containing data */
-	kmemleak_scan_area(mod->module_core, (unsigned long)mod -
-			   (unsigned long)mod->module_core,
-			   sizeof(struct module), GFP_KERNEL);
+	kmemleak_scan_area(mod, sizeof(struct module), GFP_KERNEL);
 
 	for (i = 1; i < hdr->e_shnum; i++) {
 		if (!(sechdrs[i].sh_flags & SHF_ALLOC))
@@ -1921,8 +1919,7 @@
 		    && strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) != 0)
 			continue;
 
-		kmemleak_scan_area(mod->module_core, sechdrs[i].sh_addr -
-				   (unsigned long)mod->module_core,
+		kmemleak_scan_area((void *)sechdrs[i].sh_addr,
 				   sechdrs[i].sh_size, GFP_KERNEL);
 	}
 }
@@ -2250,6 +2247,12 @@
 					 "_ftrace_events",
 					 sizeof(*mod->trace_events),
 					 &mod->num_trace_events);
+	/*
+	 * This section contains pointers to allocated objects in the trace
+	 * code and not scanning it leads to false positives.
+	 */
+	kmemleak_scan_area(mod->trace_events, sizeof(*mod->trace_events) *
+			   mod->num_trace_events, GFP_KERNEL);
 #endif
 #ifdef CONFIG_FTRACE_MCOUNT_RECORD
 	/* sechdrs[0].sh_size is always zero */
diff --git a/kernel/printk.c b/kernel/printk.c
index 1ded8e7..17463ca 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -1412,7 +1412,7 @@
 
 /**
  * kmsg_dump_register - register a kernel log dumper.
- * @dump: pointer to the kmsg_dumper structure
+ * @dumper: pointer to the kmsg_dumper structure
  *
  * Adds a kernel log dumper to the system. The dump callback in the
  * structure will be called when the kernel oopses or panics and must be
@@ -1442,7 +1442,7 @@
 
 /**
  * kmsg_dump_unregister - unregister a kmsg dumper.
- * @dump: pointer to the kmsg_dumper structure
+ * @dumper: pointer to the kmsg_dumper structure
  *
  * Removes a dump device from the system. Returns zero on success and
  * %-EINVAL otherwise.
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 45e4bef..6665761 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1131,7 +1131,7 @@
 		.data		= &sysctl_max_map_count,
 		.maxlen		= sizeof(sysctl_max_map_count),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.proc_handler	= proc_dointvec_minmax,
 		.extra1		= &zero,
 	},
 #else
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 8cf9938..25c3ed5 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -360,6 +360,7 @@
 	select DEBUG_FS if SYSFS
 	select STACKTRACE if STACKTRACE_SUPPORT
 	select KALLSYMS
+	select CRC32
 	help
 	  Say Y here if you want to enable the memory leak
 	  detector. The memory allocation/freeing is traced in a way
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 735343f..d4996cf 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1179,7 +1179,18 @@
  * %ps output the name of a text symbol without offset
  * %pF output the name of a function pointer with its offset
  * %pf output the name of a function pointer without its offset
- * %pR output the address range in a struct resource
+ * %pR output the address range in a struct resource with decoded flags
+ * %pr output the address range in a struct resource with raw flags
+ * %pM output a 6-byte MAC address with colons
+ * %pm output a 6-byte MAC address without colons
+ * %pI4 print an IPv4 address without leading zeros
+ * %pi4 print an IPv4 address with leading zeros
+ * %pI6 print an IPv6 address with colons
+ * %pi6 print an IPv6 address without colons
+ * %pI6c print an IPv6 address as specified by
+ *   http://www.ietf.org/id/draft-kawamura-ipv6-text-representation-03.txt
+ * %pU[bBlL] print a UUID/GUID in big or little endian using lower or upper
+ *   case.
  * %n is ignored
  *
  * The return value is the number of characters which would
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 13f33b3..5b069e4 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -93,6 +93,7 @@
 #include <linux/nodemask.h>
 #include <linux/mm.h>
 #include <linux/workqueue.h>
+#include <linux/crc32.h>
 
 #include <asm/sections.h>
 #include <asm/processor.h>
@@ -108,7 +109,6 @@
 #define MSECS_MIN_AGE		5000	/* minimum object age for reporting */
 #define SECS_FIRST_SCAN		60	/* delay before the first scan */
 #define SECS_SCAN_WAIT		600	/* subsequent auto scanning delay */
-#define GRAY_LIST_PASSES	25	/* maximum number of gray list scans */
 #define MAX_SCAN_SIZE		4096	/* maximum size of a scanned block */
 
 #define BYTES_PER_POINTER	sizeof(void *)
@@ -119,8 +119,8 @@
 /* scanning area inside a memory block */
 struct kmemleak_scan_area {
 	struct hlist_node node;
-	unsigned long offset;
-	size_t length;
+	unsigned long start;
+	size_t size;
 };
 
 #define KMEMLEAK_GREY	0
@@ -149,6 +149,8 @@
 	int min_count;
 	/* the total number of pointers found pointing to this object */
 	int count;
+	/* checksum for detecting modified objects */
+	u32 checksum;
 	/* memory ranges to be scanned inside an object (empty for all) */
 	struct hlist_head area_list;
 	unsigned long trace[MAX_TRACE];
@@ -164,8 +166,6 @@
 #define OBJECT_REPORTED		(1 << 1)
 /* flag set to not scan the object */
 #define OBJECT_NO_SCAN		(1 << 2)
-/* flag set on newly allocated objects */
-#define OBJECT_NEW		(1 << 3)
 
 /* number of bytes to print per line; must be 16 or 32 */
 #define HEX_ROW_SIZE		16
@@ -241,8 +241,6 @@
 	const void *ptr;		/* allocated/freed memory block */
 	size_t size;			/* memory block size */
 	int min_count;			/* minimum reference count */
-	unsigned long offset;		/* scan area offset */
-	size_t length;			/* scan area length */
 	unsigned long trace[MAX_TRACE];	/* stack trace */
 	unsigned int trace_len;		/* stack trace length */
 };
@@ -323,11 +321,6 @@
 		object->count >= object->min_count;
 }
 
-static bool color_black(const struct kmemleak_object *object)
-{
-	return object->min_count == KMEMLEAK_BLACK;
-}
-
 /*
  * Objects are considered unreferenced only if their color is white, they have
  * not be deleted and have a minimum age to avoid false positives caused by
@@ -335,7 +328,7 @@
  */
 static bool unreferenced_object(struct kmemleak_object *object)
 {
-	return (object->flags & OBJECT_ALLOCATED) && color_white(object) &&
+	return (color_white(object) && object->flags & OBJECT_ALLOCATED) &&
 		time_before_eq(object->jiffies + jiffies_min_age,
 			       jiffies_last_scan);
 }
@@ -348,11 +341,13 @@
 			       struct kmemleak_object *object)
 {
 	int i;
+	unsigned int msecs_age = jiffies_to_msecs(jiffies - object->jiffies);
 
 	seq_printf(seq, "unreferenced object 0x%08lx (size %zu):\n",
 		   object->pointer, object->size);
-	seq_printf(seq, "  comm \"%s\", pid %d, jiffies %lu\n",
-		   object->comm, object->pid, object->jiffies);
+	seq_printf(seq, "  comm \"%s\", pid %d, jiffies %lu (age %d.%03ds)\n",
+		   object->comm, object->pid, object->jiffies,
+		   msecs_age / 1000, msecs_age % 1000);
 	hex_dump_object(seq, object);
 	seq_printf(seq, "  backtrace:\n");
 
@@ -381,6 +376,7 @@
 	pr_notice("  min_count = %d\n", object->min_count);
 	pr_notice("  count = %d\n", object->count);
 	pr_notice("  flags = 0x%lx\n", object->flags);
+	pr_notice("  checksum = %d\n", object->checksum);
 	pr_notice("  backtrace:\n");
 	print_stack_trace(&trace, 4);
 }
@@ -522,12 +518,13 @@
 	INIT_HLIST_HEAD(&object->area_list);
 	spin_lock_init(&object->lock);
 	atomic_set(&object->use_count, 1);
-	object->flags = OBJECT_ALLOCATED | OBJECT_NEW;
+	object->flags = OBJECT_ALLOCATED;
 	object->pointer = ptr;
 	object->size = size;
 	object->min_count = min_count;
-	object->count = -1;			/* no color initially */
+	object->count = 0;			/* white color initially */
 	object->jiffies = jiffies;
+	object->checksum = 0;
 
 	/* task information */
 	if (in_irq()) {
@@ -720,14 +717,13 @@
  * Add a scanning area to the object. If at least one such area is added,
  * kmemleak will only scan these ranges rather than the whole memory block.
  */
-static void add_scan_area(unsigned long ptr, unsigned long offset,
-			  size_t length, gfp_t gfp)
+static void add_scan_area(unsigned long ptr, size_t size, gfp_t gfp)
 {
 	unsigned long flags;
 	struct kmemleak_object *object;
 	struct kmemleak_scan_area *area;
 
-	object = find_and_get_object(ptr, 0);
+	object = find_and_get_object(ptr, 1);
 	if (!object) {
 		kmemleak_warn("Adding scan area to unknown object at 0x%08lx\n",
 			      ptr);
@@ -741,7 +737,7 @@
 	}
 
 	spin_lock_irqsave(&object->lock, flags);
-	if (offset + length > object->size) {
+	if (ptr + size > object->pointer + object->size) {
 		kmemleak_warn("Scan area larger than object 0x%08lx\n", ptr);
 		dump_object_info(object);
 		kmem_cache_free(scan_area_cache, area);
@@ -749,8 +745,8 @@
 	}
 
 	INIT_HLIST_NODE(&area->node);
-	area->offset = offset;
-	area->length = length;
+	area->start = ptr;
+	area->size = size;
 
 	hlist_add_head(&area->node, &object->area_list);
 out_unlock:
@@ -786,7 +782,7 @@
  * processed later once kmemleak is fully initialized.
  */
 static void __init log_early(int op_type, const void *ptr, size_t size,
-			     int min_count, unsigned long offset, size_t length)
+			     int min_count)
 {
 	unsigned long flags;
 	struct early_log *log;
@@ -808,8 +804,6 @@
 	log->ptr = ptr;
 	log->size = size;
 	log->min_count = min_count;
-	log->offset = offset;
-	log->length = length;
 	if (op_type == KMEMLEAK_ALLOC)
 		log->trace_len = __save_stack_trace(log->trace);
 	crt_early_log++;
@@ -858,7 +852,7 @@
 	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
 		create_object((unsigned long)ptr, size, min_count, gfp);
 	else if (atomic_read(&kmemleak_early_log))
-		log_early(KMEMLEAK_ALLOC, ptr, size, min_count, 0, 0);
+		log_early(KMEMLEAK_ALLOC, ptr, size, min_count);
 }
 EXPORT_SYMBOL_GPL(kmemleak_alloc);
 
@@ -873,7 +867,7 @@
 	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
 		delete_object_full((unsigned long)ptr);
 	else if (atomic_read(&kmemleak_early_log))
-		log_early(KMEMLEAK_FREE, ptr, 0, 0, 0, 0);
+		log_early(KMEMLEAK_FREE, ptr, 0, 0);
 }
 EXPORT_SYMBOL_GPL(kmemleak_free);
 
@@ -888,7 +882,7 @@
 	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
 		delete_object_part((unsigned long)ptr, size);
 	else if (atomic_read(&kmemleak_early_log))
-		log_early(KMEMLEAK_FREE_PART, ptr, size, 0, 0, 0);
+		log_early(KMEMLEAK_FREE_PART, ptr, size, 0);
 }
 EXPORT_SYMBOL_GPL(kmemleak_free_part);
 
@@ -903,7 +897,7 @@
 	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
 		make_gray_object((unsigned long)ptr);
 	else if (atomic_read(&kmemleak_early_log))
-		log_early(KMEMLEAK_NOT_LEAK, ptr, 0, 0, 0, 0);
+		log_early(KMEMLEAK_NOT_LEAK, ptr, 0, 0);
 }
 EXPORT_SYMBOL(kmemleak_not_leak);
 
@@ -919,22 +913,21 @@
 	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
 		make_black_object((unsigned long)ptr);
 	else if (atomic_read(&kmemleak_early_log))
-		log_early(KMEMLEAK_IGNORE, ptr, 0, 0, 0, 0);
+		log_early(KMEMLEAK_IGNORE, ptr, 0, 0);
 }
 EXPORT_SYMBOL(kmemleak_ignore);
 
 /*
  * Limit the range to be scanned in an allocated memory block.
  */
-void __ref kmemleak_scan_area(const void *ptr, unsigned long offset,
-			      size_t length, gfp_t gfp)
+void __ref kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp)
 {
 	pr_debug("%s(0x%p)\n", __func__, ptr);
 
 	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
-		add_scan_area((unsigned long)ptr, offset, length, gfp);
+		add_scan_area((unsigned long)ptr, size, gfp);
 	else if (atomic_read(&kmemleak_early_log))
-		log_early(KMEMLEAK_SCAN_AREA, ptr, 0, 0, offset, length);
+		log_early(KMEMLEAK_SCAN_AREA, ptr, size, 0);
 }
 EXPORT_SYMBOL(kmemleak_scan_area);
 
@@ -948,11 +941,25 @@
 	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
 		object_no_scan((unsigned long)ptr);
 	else if (atomic_read(&kmemleak_early_log))
-		log_early(KMEMLEAK_NO_SCAN, ptr, 0, 0, 0, 0);
+		log_early(KMEMLEAK_NO_SCAN, ptr, 0, 0);
 }
 EXPORT_SYMBOL(kmemleak_no_scan);
 
 /*
+ * Update an object's checksum and return true if it was modified.
+ */
+static bool update_checksum(struct kmemleak_object *object)
+{
+	u32 old_csum = object->checksum;
+
+	if (!kmemcheck_is_obj_initialized(object->pointer, object->size))
+		return false;
+
+	object->checksum = crc32(0, (void *)object->pointer, object->size);
+	return object->checksum != old_csum;
+}
+
+/*
  * Memory scanning is a long process and it needs to be interruptable. This
  * function checks whether such interrupt condition occured.
  */
@@ -1031,11 +1038,14 @@
 		 * added to the gray_list.
 		 */
 		object->count++;
-		if (color_gray(object))
+		if (color_gray(object)) {
 			list_add_tail(&object->gray_list, &gray_list);
-		else
-			put_object(object);
+			spin_unlock_irqrestore(&object->lock, flags);
+			continue;
+		}
+
 		spin_unlock_irqrestore(&object->lock, flags);
+		put_object(object);
 	}
 }
 
@@ -1075,14 +1085,47 @@
 		}
 	} else
 		hlist_for_each_entry(area, elem, &object->area_list, node)
-			scan_block((void *)(object->pointer + area->offset),
-				   (void *)(object->pointer + area->offset
-					    + area->length), object, 0);
+			scan_block((void *)area->start,
+				   (void *)(area->start + area->size),
+				   object, 0);
 out:
 	spin_unlock_irqrestore(&object->lock, flags);
 }
 
 /*
+ * Scan the objects already referenced (gray objects). More objects will be
+ * referenced and, if there are no memory leaks, all the objects are scanned.
+ */
+static void scan_gray_list(void)
+{
+	struct kmemleak_object *object, *tmp;
+
+	/*
+	 * The list traversal is safe for both tail additions and removals
+	 * from inside the loop. The kmemleak objects cannot be freed from
+	 * outside the loop because their use_count was incremented.
+	 */
+	object = list_entry(gray_list.next, typeof(*object), gray_list);
+	while (&object->gray_list != &gray_list) {
+		cond_resched();
+
+		/* may add new objects to the list */
+		if (!scan_should_stop())
+			scan_object(object);
+
+		tmp = list_entry(object->gray_list.next, typeof(*object),
+				 gray_list);
+
+		/* remove the object from the list and release it */
+		list_del(&object->gray_list);
+		put_object(object);
+
+		object = tmp;
+	}
+	WARN_ON(!list_empty(&gray_list));
+}
+
+/*
  * Scan data sections and all the referenced memory blocks allocated via the
  * kernel's standard allocators. This function must be called with the
  * scan_mutex held.
@@ -1090,10 +1133,9 @@
 static void kmemleak_scan(void)
 {
 	unsigned long flags;
-	struct kmemleak_object *object, *tmp;
+	struct kmemleak_object *object;
 	int i;
 	int new_leaks = 0;
-	int gray_list_pass = 0;
 
 	jiffies_last_scan = jiffies;
 
@@ -1114,7 +1156,6 @@
 #endif
 		/* reset the reference count (whiten the object) */
 		object->count = 0;
-		object->flags &= ~OBJECT_NEW;
 		if (color_gray(object) && get_object(object))
 			list_add_tail(&object->gray_list, &gray_list);
 
@@ -1172,62 +1213,36 @@
 
 	/*
 	 * Scan the objects already referenced from the sections scanned
-	 * above. More objects will be referenced and, if there are no memory
-	 * leaks, all the objects will be scanned. The list traversal is safe
-	 * for both tail additions and removals from inside the loop. The
-	 * kmemleak objects cannot be freed from outside the loop because their
-	 * use_count was increased.
+	 * above.
 	 */
-repeat:
-	object = list_entry(gray_list.next, typeof(*object), gray_list);
-	while (&object->gray_list != &gray_list) {
-		cond_resched();
-
-		/* may add new objects to the list */
-		if (!scan_should_stop())
-			scan_object(object);
-
-		tmp = list_entry(object->gray_list.next, typeof(*object),
-				 gray_list);
-
-		/* remove the object from the list and release it */
-		list_del(&object->gray_list);
-		put_object(object);
-
-		object = tmp;
-	}
-
-	if (scan_should_stop() || ++gray_list_pass >= GRAY_LIST_PASSES)
-		goto scan_end;
+	scan_gray_list();
 
 	/*
-	 * Check for new objects allocated during this scanning and add them
-	 * to the gray list.
+	 * Check for new or unreferenced objects modified since the previous
+	 * scan and color them gray until the next scan.
 	 */
 	rcu_read_lock();
 	list_for_each_entry_rcu(object, &object_list, object_list) {
 		spin_lock_irqsave(&object->lock, flags);
-		if ((object->flags & OBJECT_NEW) && !color_black(object) &&
-		    get_object(object)) {
-			object->flags &= ~OBJECT_NEW;
+		if (color_white(object) && (object->flags & OBJECT_ALLOCATED)
+		    && update_checksum(object) && get_object(object)) {
+			/* color it gray temporarily */
+			object->count = object->min_count;
 			list_add_tail(&object->gray_list, &gray_list);
 		}
 		spin_unlock_irqrestore(&object->lock, flags);
 	}
 	rcu_read_unlock();
 
-	if (!list_empty(&gray_list))
-		goto repeat;
-
-scan_end:
-	WARN_ON(!list_empty(&gray_list));
+	/*
+	 * Re-scan the gray list for modified unreferenced objects.
+	 */
+	scan_gray_list();
 
 	/*
-	 * If scanning was stopped or new objects were being allocated at a
-	 * higher rate than gray list scanning, do not report any new
-	 * unreferenced objects.
+	 * If scanning was stopped do not report any new unreferenced objects.
 	 */
-	if (scan_should_stop() || gray_list_pass >= GRAY_LIST_PASSES)
+	if (scan_should_stop())
 		return;
 
 	/*
@@ -1642,8 +1657,7 @@
 			kmemleak_ignore(log->ptr);
 			break;
 		case KMEMLEAK_SCAN_AREA:
-			kmemleak_scan_area(log->ptr, log->offset, log->length,
-					   GFP_KERNEL);
+			kmemleak_scan_area(log->ptr, log->size, GFP_KERNEL);
 			break;
 		case KMEMLEAK_NO_SCAN:
 			kmemleak_no_scan(log->ptr);
diff --git a/mm/readahead.c b/mm/readahead.c
index aa1aa23..033bc13 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -547,5 +547,17 @@
 
 	/* do read-ahead */
 	ondemand_readahead(mapping, ra, filp, true, offset, req_size);
+
+#ifdef CONFIG_BLOCK
+	/*
+	 * Normally the current page is !uptodate and lock_page() will be
+	 * immediately called to implicitly unplug the device. However this
+	 * is not always true for RAID conifgurations, where data arrives
+	 * not strictly in their submission order. In this case we need to
+	 * explicitly kick off the IO.
+	 */
+	if (PageUptodate(page))
+		blk_run_backing_dev(mapping->backing_dev_info, NULL);
+#endif
 }
 EXPORT_SYMBOL_GPL(page_cache_async_readahead);
diff --git a/mm/slab.c b/mm/slab.c
index 3f48229..e17cc2c 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -2275,9 +2275,11 @@
 	/*
 	 * Determine if the slab management is 'on' or 'off' slab.
 	 * (bootstrapping cannot cope with offslab caches so don't do
-	 * it too early on.)
+	 * it too early on. Always use on-slab management when
+	 * SLAB_NOLEAKTRACE to avoid recursive calls into kmemleak)
 	 */
-	if ((size >= (PAGE_SIZE >> 3)) && !slab_early_init)
+	if ((size >= (PAGE_SIZE >> 3)) && !slab_early_init &&
+	    !(flags & SLAB_NOLEAKTRACE))
 		/*
 		 * Size is large, assume best to place the slab management obj
 		 * off-slab (should allow better packing of objs).
@@ -2596,8 +2598,8 @@
 		 * kmemleak does not treat the ->s_mem pointer as a reference
 		 * to the object. Otherwise we will not report the leak.
 		 */
-		kmemleak_scan_area(slabp, offsetof(struct slab, list),
-				   sizeof(struct list_head), local_flags);
+		kmemleak_scan_area(&slabp->list, sizeof(struct list_head),
+				   local_flags);
 		if (!slabp)
 			return NULL;
 	} else {