Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/staging

* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/staging:
  hwmon: (lm93) Add support for LM94
diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms
index 78439b8..7ff9b54 100644
--- a/arch/mips/Kbuild.platforms
+++ b/arch/mips/Kbuild.platforms
@@ -2,6 +2,7 @@
 
 platforms += alchemy
 platforms += ar7
+platforms += ath79
 platforms += bcm47xx
 platforms += bcm63xx
 platforms += cavium-octeon
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index f489ec3..548e6cc 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -21,6 +21,7 @@
 	select HAVE_DMA_API_DEBUG
 	select HAVE_GENERIC_HARDIRQS
 	select GENERIC_IRQ_PROBE
+	select HAVE_ARCH_JUMP_LABEL
 
 menu "Machine selection"
 
@@ -65,6 +66,22 @@
 	  Support for the Texas Instruments AR7 System-on-a-Chip
 	  family: TNETD7100, 7200 and 7300.
 
+config ATH79
+	bool "Atheros AR71XX/AR724X/AR913X based boards"
+	select ARCH_REQUIRE_GPIOLIB
+	select BOOT_RAW
+	select CEVT_R4K
+	select CSRC_R4K
+	select DMA_NONCOHERENT
+	select IRQ_CPU
+	select MIPS_MACHINE
+	select SYS_HAS_CPU_MIPS32_R2
+	select SYS_HAS_EARLY_PRINTK
+	select SYS_SUPPORTS_32BIT_KERNEL
+	select SYS_SUPPORTS_BIG_ENDIAN
+	help
+	  Support for the Atheros AR71XX/AR724X/AR913X SoCs.
+
 config BCM47XX
 	bool "Broadcom BCM47XX based boards"
 	select CEVT_R4K
@@ -717,6 +734,7 @@
 endchoice
 
 source "arch/mips/alchemy/Kconfig"
+source "arch/mips/ath79/Kconfig"
 source "arch/mips/bcm63xx/Kconfig"
 source "arch/mips/jazz/Kconfig"
 source "arch/mips/jz4740/Kconfig"
@@ -883,6 +901,9 @@
 config SYNC_R4K
 	bool
 
+config MIPS_MACHINE
+	def_bool n
+
 config NO_IOPORT
 	def_bool n
 
@@ -2400,4 +2421,20 @@
 
 source "crypto/Kconfig"
 
+menuconfig VIRTUALIZATION
+	bool "Virtualization"
+	default n
+	---help---
+	  Say Y here to get to see options for using your Linux host to run other
+	  operating systems inside virtual machines (guests).
+	  This option alone does not add any kernel code.
+
+	  If you say N, all options in this submenu will be skipped and disabled.
+
+if VIRTUALIZATION
+
+source drivers/virtio/Kconfig
+
+endif # VIRTUALIZATION
+
 source "lib/Kconfig"
diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig
new file mode 100644
index 0000000..b058282
--- /dev/null
+++ b/arch/mips/ath79/Kconfig
@@ -0,0 +1,50 @@
+if ATH79
+
+menu "Atheros AR71XX/AR724X/AR913X machine selection"
+
+config ATH79_MACH_AP81
+	bool "Atheros AP81 reference board"
+	select SOC_AR913X
+	select ATH79_DEV_AR913X_WMAC
+	select ATH79_DEV_GPIO_BUTTONS
+	select ATH79_DEV_LEDS_GPIO
+	select ATH79_DEV_SPI
+	help
+	  Say 'Y' here if you want your kernel to support the
+	  Atheros AP81 reference board.
+
+config ATH79_MACH_PB44
+	bool "Atheros PB44 reference board"
+	select SOC_AR71XX
+	select ATH79_DEV_GPIO_BUTTONS
+	select ATH79_DEV_LEDS_GPIO
+	select ATH79_DEV_SPI
+	help
+	  Say 'Y' here if you want your kernel to support the
+	  Atheros PB44 reference board.
+
+endmenu
+
+config SOC_AR71XX
+	def_bool n
+
+config SOC_AR724X
+	def_bool n
+
+config SOC_AR913X
+	def_bool n
+
+config ATH79_DEV_AR913X_WMAC
+	depends on SOC_AR913X
+	def_bool n
+
+config ATH79_DEV_GPIO_BUTTONS
+	def_bool n
+
+config ATH79_DEV_LEDS_GPIO
+	def_bool n
+
+config ATH79_DEV_SPI
+	def_bool n
+
+endif
diff --git a/arch/mips/ath79/Makefile b/arch/mips/ath79/Makefile
new file mode 100644
index 0000000..c33d465
--- /dev/null
+++ b/arch/mips/ath79/Makefile
@@ -0,0 +1,28 @@
+#
+# Makefile for the Atheros AR71XX/AR724X/AR913X specific parts of the kernel
+#
+# Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+# Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 as published
+# by the Free Software Foundation.
+
+obj-y	:= prom.o setup.o irq.o common.o clock.o gpio.o
+
+obj-$(CONFIG_EARLY_PRINTK)		+= early_printk.o
+
+#
+# Devices
+#
+obj-y					+= dev-common.o
+obj-$(CONFIG_ATH79_DEV_AR913X_WMAC)	+= dev-ar913x-wmac.o
+obj-$(CONFIG_ATH79_DEV_GPIO_BUTTONS)	+= dev-gpio-buttons.o
+obj-$(CONFIG_ATH79_DEV_LEDS_GPIO)	+= dev-leds-gpio.o
+obj-$(CONFIG_ATH79_DEV_SPI)		+= dev-spi.o
+
+#
+# Machines
+#
+obj-$(CONFIG_ATH79_MACH_AP81)		+= mach-ap81.o
+obj-$(CONFIG_ATH79_MACH_PB44)		+= mach-pb44.o
diff --git a/arch/mips/ath79/Platform b/arch/mips/ath79/Platform
new file mode 100644
index 0000000..2bd6636
--- /dev/null
+++ b/arch/mips/ath79/Platform
@@ -0,0 +1,7 @@
+#
+# Atheros AR71xx/AR724x/AR913x
+#
+
+platform-$(CONFIG_ATH79)	+= ath79/
+cflags-$(CONFIG_ATH79)		+= -I$(srctree)/arch/mips/include/asm/mach-ath79
+load-$(CONFIG_ATH79)		= 0xffffffff80060000
diff --git a/arch/mips/ath79/clock.c b/arch/mips/ath79/clock.c
new file mode 100644
index 0000000..680bde9
--- /dev/null
+++ b/arch/mips/ath79/clock.c
@@ -0,0 +1,183 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X common routines
+ *
+ *  Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include "common.h"
+
+#define AR71XX_BASE_FREQ	40000000
+#define AR724X_BASE_FREQ	5000000
+#define AR913X_BASE_FREQ	5000000
+
+struct clk {
+	unsigned long rate;
+};
+
+static struct clk ath79_ref_clk;
+static struct clk ath79_cpu_clk;
+static struct clk ath79_ddr_clk;
+static struct clk ath79_ahb_clk;
+static struct clk ath79_wdt_clk;
+static struct clk ath79_uart_clk;
+
+static void __init ar71xx_clocks_init(void)
+{
+	u32 pll;
+	u32 freq;
+	u32 div;
+
+	ath79_ref_clk.rate = AR71XX_BASE_FREQ;
+
+	pll = ath79_pll_rr(AR71XX_PLL_REG_CPU_CONFIG);
+
+	div = ((pll >> AR71XX_PLL_DIV_SHIFT) & AR71XX_PLL_DIV_MASK) + 1;
+	freq = div * ath79_ref_clk.rate;
+
+	div = ((pll >> AR71XX_CPU_DIV_SHIFT) & AR71XX_CPU_DIV_MASK) + 1;
+	ath79_cpu_clk.rate = freq / div;
+
+	div = ((pll >> AR71XX_DDR_DIV_SHIFT) & AR71XX_DDR_DIV_MASK) + 1;
+	ath79_ddr_clk.rate = freq / div;
+
+	div = (((pll >> AR71XX_AHB_DIV_SHIFT) & AR71XX_AHB_DIV_MASK) + 1) * 2;
+	ath79_ahb_clk.rate = ath79_cpu_clk.rate / div;
+
+	ath79_wdt_clk.rate = ath79_ahb_clk.rate;
+	ath79_uart_clk.rate = ath79_ahb_clk.rate;
+}
+
+static void __init ar724x_clocks_init(void)
+{
+	u32 pll;
+	u32 freq;
+	u32 div;
+
+	ath79_ref_clk.rate = AR724X_BASE_FREQ;
+	pll = ath79_pll_rr(AR724X_PLL_REG_CPU_CONFIG);
+
+	div = ((pll >> AR724X_PLL_DIV_SHIFT) & AR724X_PLL_DIV_MASK);
+	freq = div * ath79_ref_clk.rate;
+
+	div = ((pll >> AR724X_PLL_REF_DIV_SHIFT) & AR724X_PLL_REF_DIV_MASK);
+	freq *= div;
+
+	ath79_cpu_clk.rate = freq;
+
+	div = ((pll >> AR724X_DDR_DIV_SHIFT) & AR724X_DDR_DIV_MASK) + 1;
+	ath79_ddr_clk.rate = freq / div;
+
+	div = (((pll >> AR724X_AHB_DIV_SHIFT) & AR724X_AHB_DIV_MASK) + 1) * 2;
+	ath79_ahb_clk.rate = ath79_cpu_clk.rate / div;
+
+	ath79_wdt_clk.rate = ath79_ahb_clk.rate;
+	ath79_uart_clk.rate = ath79_ahb_clk.rate;
+}
+
+static void __init ar913x_clocks_init(void)
+{
+	u32 pll;
+	u32 freq;
+	u32 div;
+
+	ath79_ref_clk.rate = AR913X_BASE_FREQ;
+	pll = ath79_pll_rr(AR913X_PLL_REG_CPU_CONFIG);
+
+	div = ((pll >> AR913X_PLL_DIV_SHIFT) & AR913X_PLL_DIV_MASK);
+	freq = div * ath79_ref_clk.rate;
+
+	ath79_cpu_clk.rate = freq;
+
+	div = ((pll >> AR913X_DDR_DIV_SHIFT) & AR913X_DDR_DIV_MASK) + 1;
+	ath79_ddr_clk.rate = freq / div;
+
+	div = (((pll >> AR913X_AHB_DIV_SHIFT) & AR913X_AHB_DIV_MASK) + 1) * 2;
+	ath79_ahb_clk.rate = ath79_cpu_clk.rate / div;
+
+	ath79_wdt_clk.rate = ath79_ahb_clk.rate;
+	ath79_uart_clk.rate = ath79_ahb_clk.rate;
+}
+
+void __init ath79_clocks_init(void)
+{
+	if (soc_is_ar71xx())
+		ar71xx_clocks_init();
+	else if (soc_is_ar724x())
+		ar724x_clocks_init();
+	else if (soc_is_ar913x())
+		ar913x_clocks_init();
+	else
+		BUG();
+
+	pr_info("Clocks: CPU:%lu.%03luMHz, DDR:%lu.%03luMHz, AHB:%lu.%03luMHz, "
+		"Ref:%lu.%03luMHz",
+		ath79_cpu_clk.rate / 1000000,
+		(ath79_cpu_clk.rate / 1000) % 1000,
+		ath79_ddr_clk.rate / 1000000,
+		(ath79_ddr_clk.rate / 1000) % 1000,
+		ath79_ahb_clk.rate / 1000000,
+		(ath79_ahb_clk.rate / 1000) % 1000,
+		ath79_ref_clk.rate / 1000000,
+		(ath79_ref_clk.rate / 1000) % 1000);
+}
+
+/*
+ * Linux clock API
+ */
+struct clk *clk_get(struct device *dev, const char *id)
+{
+	if (!strcmp(id, "ref"))
+		return &ath79_ref_clk;
+
+	if (!strcmp(id, "cpu"))
+		return &ath79_cpu_clk;
+
+	if (!strcmp(id, "ddr"))
+		return &ath79_ddr_clk;
+
+	if (!strcmp(id, "ahb"))
+		return &ath79_ahb_clk;
+
+	if (!strcmp(id, "wdt"))
+		return &ath79_wdt_clk;
+
+	if (!strcmp(id, "uart"))
+		return &ath79_uart_clk;
+
+	return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(clk_get);
+
+int clk_enable(struct clk *clk)
+{
+	return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+	return clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+void clk_put(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_put);
diff --git a/arch/mips/ath79/common.c b/arch/mips/ath79/common.c
new file mode 100644
index 0000000..58f60e7
--- /dev/null
+++ b/arch/mips/ath79/common.c
@@ -0,0 +1,97 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X common routines
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include "common.h"
+
+static DEFINE_SPINLOCK(ath79_device_reset_lock);
+
+u32 ath79_cpu_freq;
+EXPORT_SYMBOL_GPL(ath79_cpu_freq);
+
+u32 ath79_ahb_freq;
+EXPORT_SYMBOL_GPL(ath79_ahb_freq);
+
+u32 ath79_ddr_freq;
+EXPORT_SYMBOL_GPL(ath79_ddr_freq);
+
+enum ath79_soc_type ath79_soc;
+
+void __iomem *ath79_pll_base;
+void __iomem *ath79_reset_base;
+EXPORT_SYMBOL_GPL(ath79_reset_base);
+void __iomem *ath79_ddr_base;
+
+void ath79_ddr_wb_flush(u32 reg)
+{
+	void __iomem *flush_reg = ath79_ddr_base + reg;
+
+	/* Flush the DDR write buffer. */
+	__raw_writel(0x1, flush_reg);
+	while (__raw_readl(flush_reg) & 0x1)
+		;
+
+	/* It must be run twice. */
+	__raw_writel(0x1, flush_reg);
+	while (__raw_readl(flush_reg) & 0x1)
+		;
+}
+EXPORT_SYMBOL_GPL(ath79_ddr_wb_flush);
+
+void ath79_device_reset_set(u32 mask)
+{
+	unsigned long flags;
+	u32 reg;
+	u32 t;
+
+	if (soc_is_ar71xx())
+		reg = AR71XX_RESET_REG_RESET_MODULE;
+	else if (soc_is_ar724x())
+		reg = AR724X_RESET_REG_RESET_MODULE;
+	else if (soc_is_ar913x())
+		reg = AR913X_RESET_REG_RESET_MODULE;
+	else
+		BUG();
+
+	spin_lock_irqsave(&ath79_device_reset_lock, flags);
+	t = ath79_reset_rr(reg);
+	ath79_reset_wr(reg, t | mask);
+	spin_unlock_irqrestore(&ath79_device_reset_lock, flags);
+}
+EXPORT_SYMBOL_GPL(ath79_device_reset_set);
+
+void ath79_device_reset_clear(u32 mask)
+{
+	unsigned long flags;
+	u32 reg;
+	u32 t;
+
+	if (soc_is_ar71xx())
+		reg = AR71XX_RESET_REG_RESET_MODULE;
+	else if (soc_is_ar724x())
+		reg = AR724X_RESET_REG_RESET_MODULE;
+	else if (soc_is_ar913x())
+		reg = AR913X_RESET_REG_RESET_MODULE;
+	else
+		BUG();
+
+	spin_lock_irqsave(&ath79_device_reset_lock, flags);
+	t = ath79_reset_rr(reg);
+	ath79_reset_wr(reg, t & ~mask);
+	spin_unlock_irqrestore(&ath79_device_reset_lock, flags);
+}
+EXPORT_SYMBOL_GPL(ath79_device_reset_clear);
diff --git a/arch/mips/ath79/common.h b/arch/mips/ath79/common.h
new file mode 100644
index 0000000..561906c
--- /dev/null
+++ b/arch/mips/ath79/common.h
@@ -0,0 +1,31 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X common definitions
+ *
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *
+ *  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 __ATH79_COMMON_H
+#define __ATH79_COMMON_H
+
+#include <linux/types.h>
+#include <linux/init.h>
+
+#define ATH79_MEM_SIZE_MIN	(2 * 1024 * 1024)
+#define ATH79_MEM_SIZE_MAX	(128 * 1024 * 1024)
+
+void ath79_clocks_init(void);
+void ath79_ddr_wb_flush(unsigned int reg);
+
+void ath79_gpio_function_enable(u32 mask);
+void ath79_gpio_function_disable(u32 mask);
+void ath79_gpio_function_setup(u32 set, u32 clear);
+void ath79_gpio_init(void);
+
+#endif /* __ATH79_COMMON_H */
diff --git a/arch/mips/ath79/dev-ar913x-wmac.c b/arch/mips/ath79/dev-ar913x-wmac.c
new file mode 100644
index 0000000..48f425a
--- /dev/null
+++ b/arch/mips/ath79/dev-ar913x-wmac.c
@@ -0,0 +1,60 @@
+/*
+ *  Atheros AR913X SoC built-in WMAC device support
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/ath9k_platform.h>
+
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include "dev-ar913x-wmac.h"
+
+static struct ath9k_platform_data ar913x_wmac_data;
+
+static struct resource ar913x_wmac_resources[] = {
+	{
+		.start	= AR913X_WMAC_BASE,
+		.end	= AR913X_WMAC_BASE + AR913X_WMAC_SIZE - 1,
+		.flags	= IORESOURCE_MEM,
+	}, {
+		.start	= ATH79_CPU_IRQ_IP2,
+		.end	= ATH79_CPU_IRQ_IP2,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device ar913x_wmac_device = {
+	.name		= "ath9k",
+	.id		= -1,
+	.resource	= ar913x_wmac_resources,
+	.num_resources	= ARRAY_SIZE(ar913x_wmac_resources),
+	.dev = {
+		.platform_data = &ar913x_wmac_data,
+	},
+};
+
+void __init ath79_register_ar913x_wmac(u8 *cal_data)
+{
+	if (cal_data)
+		memcpy(ar913x_wmac_data.eeprom_data, cal_data,
+		       sizeof(ar913x_wmac_data.eeprom_data));
+
+	/* reset the WMAC */
+	ath79_device_reset_set(AR913X_RESET_AMBA2WMAC);
+	mdelay(10);
+
+	ath79_device_reset_clear(AR913X_RESET_AMBA2WMAC);
+	mdelay(10);
+
+	platform_device_register(&ar913x_wmac_device);
+}
diff --git a/arch/mips/ath79/dev-ar913x-wmac.h b/arch/mips/ath79/dev-ar913x-wmac.h
new file mode 100644
index 0000000..579d562
--- /dev/null
+++ b/arch/mips/ath79/dev-ar913x-wmac.h
@@ -0,0 +1,17 @@
+/*
+ *  Atheros AR913X SoC built-in WMAC device support
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#ifndef _ATH79_DEV_AR913X_WMAC_H
+#define _ATH79_DEV_AR913X_WMAC_H
+
+void ath79_register_ar913x_wmac(u8 *cal_data);
+
+#endif /* _ATH79_DEV_AR913X_WMAC_H */
diff --git a/arch/mips/ath79/dev-common.c b/arch/mips/ath79/dev-common.c
new file mode 100644
index 0000000..3b82e32
--- /dev/null
+++ b/arch/mips/ath79/dev-common.c
@@ -0,0 +1,77 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X common devices
+ *
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *
+ *  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/serial_8250.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include "common.h"
+#include "dev-common.h"
+
+static struct resource ath79_uart_resources[] = {
+	{
+		.start	= AR71XX_UART_BASE,
+		.end	= AR71XX_UART_BASE + AR71XX_UART_SIZE - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+#define AR71XX_UART_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP)
+static struct plat_serial8250_port ath79_uart_data[] = {
+	{
+		.mapbase	= AR71XX_UART_BASE,
+		.irq		= ATH79_MISC_IRQ_UART,
+		.flags		= AR71XX_UART_FLAGS,
+		.iotype		= UPIO_MEM32,
+		.regshift	= 2,
+	}, {
+		/* terminating entry */
+	}
+};
+
+static struct platform_device ath79_uart_device = {
+	.name		= "serial8250",
+	.id		= PLAT8250_DEV_PLATFORM,
+	.resource	= ath79_uart_resources,
+	.num_resources	= ARRAY_SIZE(ath79_uart_resources),
+	.dev = {
+		.platform_data	= ath79_uart_data
+	},
+};
+
+void __init ath79_register_uart(void)
+{
+	struct clk *clk;
+
+	clk = clk_get(NULL, "uart");
+	if (IS_ERR(clk))
+		panic("unable to get UART clock, err=%ld", PTR_ERR(clk));
+
+	ath79_uart_data[0].uartclk = clk_get_rate(clk);
+	platform_device_register(&ath79_uart_device);
+}
+
+static struct platform_device ath79_wdt_device = {
+	.name		= "ath79-wdt",
+	.id		= -1,
+};
+
+void __init ath79_register_wdt(void)
+{
+	platform_device_register(&ath79_wdt_device);
+}
diff --git a/arch/mips/ath79/dev-common.h b/arch/mips/ath79/dev-common.h
new file mode 100644
index 0000000..0f514e1
--- /dev/null
+++ b/arch/mips/ath79/dev-common.h
@@ -0,0 +1,18 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X common devices
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#ifndef _ATH79_DEV_COMMON_H
+#define _ATH79_DEV_COMMON_H
+
+void ath79_register_uart(void);
+void ath79_register_wdt(void);
+
+#endif /* _ATH79_DEV_COMMON_H */
diff --git a/arch/mips/ath79/dev-gpio-buttons.c b/arch/mips/ath79/dev-gpio-buttons.c
new file mode 100644
index 0000000..4b0168a
--- /dev/null
+++ b/arch/mips/ath79/dev-gpio-buttons.c
@@ -0,0 +1,58 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X GPIO button support
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include "linux/init.h"
+#include "linux/slab.h"
+#include <linux/platform_device.h>
+
+#include "dev-gpio-buttons.h"
+
+void __init ath79_register_gpio_keys_polled(int id,
+					    unsigned poll_interval,
+					    unsigned nbuttons,
+					    struct gpio_keys_button *buttons)
+{
+	struct platform_device *pdev;
+	struct gpio_keys_platform_data pdata;
+	struct gpio_keys_button *p;
+	int err;
+
+	p = kmalloc(nbuttons * sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return;
+
+	memcpy(p, buttons, nbuttons * sizeof(*p));
+
+	pdev = platform_device_alloc("gpio-keys-polled", id);
+	if (!pdev)
+		goto err_free_buttons;
+
+	memset(&pdata, 0, sizeof(pdata));
+	pdata.poll_interval = poll_interval;
+	pdata.nbuttons = nbuttons;
+	pdata.buttons = p;
+
+	err = platform_device_add_data(pdev, &pdata, sizeof(pdata));
+	if (err)
+		goto err_put_pdev;
+
+	err = platform_device_add(pdev);
+	if (err)
+		goto err_put_pdev;
+
+	return;
+
+err_put_pdev:
+	platform_device_put(pdev);
+
+err_free_buttons:
+	kfree(p);
+}
diff --git a/arch/mips/ath79/dev-gpio-buttons.h b/arch/mips/ath79/dev-gpio-buttons.h
new file mode 100644
index 0000000..481847a
--- /dev/null
+++ b/arch/mips/ath79/dev-gpio-buttons.h
@@ -0,0 +1,23 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X GPIO button support
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#ifndef _ATH79_DEV_GPIO_BUTTONS_H
+#define _ATH79_DEV_GPIO_BUTTONS_H
+
+#include <linux/input.h>
+#include <linux/gpio_keys.h>
+
+void ath79_register_gpio_keys_polled(int id,
+				     unsigned poll_interval,
+				     unsigned nbuttons,
+				     struct gpio_keys_button *buttons);
+
+#endif /* _ATH79_DEV_GPIO_BUTTONS_H */
diff --git a/arch/mips/ath79/dev-leds-gpio.c b/arch/mips/ath79/dev-leds-gpio.c
new file mode 100644
index 0000000..cdade68
--- /dev/null
+++ b/arch/mips/ath79/dev-leds-gpio.c
@@ -0,0 +1,56 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X common GPIO LEDs support
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+#include "dev-leds-gpio.h"
+
+void __init ath79_register_leds_gpio(int id,
+				     unsigned num_leds,
+				     struct gpio_led *leds)
+{
+	struct platform_device *pdev;
+	struct gpio_led_platform_data pdata;
+	struct gpio_led *p;
+	int err;
+
+	p = kmalloc(num_leds * sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return;
+
+	memcpy(p, leds, num_leds * sizeof(*p));
+
+	pdev = platform_device_alloc("leds-gpio", id);
+	if (!pdev)
+		goto err_free_leds;
+
+	memset(&pdata, 0, sizeof(pdata));
+	pdata.num_leds = num_leds;
+	pdata.leds = p;
+
+	err = platform_device_add_data(pdev, &pdata, sizeof(pdata));
+	if (err)
+		goto err_put_pdev;
+
+	err = platform_device_add(pdev);
+	if (err)
+		goto err_put_pdev;
+
+	return;
+
+err_put_pdev:
+	platform_device_put(pdev);
+
+err_free_leds:
+	kfree(p);
+}
diff --git a/arch/mips/ath79/dev-leds-gpio.h b/arch/mips/ath79/dev-leds-gpio.h
new file mode 100644
index 0000000..6e5d885
--- /dev/null
+++ b/arch/mips/ath79/dev-leds-gpio.h
@@ -0,0 +1,21 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X common GPIO LEDs support
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#ifndef _ATH79_DEV_LEDS_GPIO_H
+#define _ATH79_DEV_LEDS_GPIO_H
+
+#include <linux/leds.h>
+
+void ath79_register_leds_gpio(int id,
+			      unsigned num_leds,
+			      struct gpio_led *leds);
+
+#endif /* _ATH79_DEV_LEDS_GPIO_H */
diff --git a/arch/mips/ath79/dev-spi.c b/arch/mips/ath79/dev-spi.c
new file mode 100644
index 0000000..aa30163
--- /dev/null
+++ b/arch/mips/ath79/dev-spi.c
@@ -0,0 +1,38 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X SPI controller device
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include "dev-spi.h"
+
+static struct resource ath79_spi_resources[] = {
+	{
+		.start	= AR71XX_SPI_BASE,
+		.end	= AR71XX_SPI_BASE + AR71XX_SPI_SIZE - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device ath79_spi_device = {
+	.name		= "ath79-spi",
+	.id		= -1,
+	.resource	= ath79_spi_resources,
+	.num_resources	= ARRAY_SIZE(ath79_spi_resources),
+};
+
+void __init ath79_register_spi(struct ath79_spi_platform_data *pdata,
+			       struct spi_board_info const *info,
+			       unsigned n)
+{
+	spi_register_board_info(info, n);
+	ath79_spi_device.dev.platform_data = pdata;
+	platform_device_register(&ath79_spi_device);
+}
diff --git a/arch/mips/ath79/dev-spi.h b/arch/mips/ath79/dev-spi.h
new file mode 100644
index 0000000..d732565
--- /dev/null
+++ b/arch/mips/ath79/dev-spi.h
@@ -0,0 +1,22 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X SPI controller device
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#ifndef _ATH79_DEV_SPI_H
+#define _ATH79_DEV_SPI_H
+
+#include <linux/spi/spi.h>
+#include <asm/mach-ath79/ath79_spi_platform.h>
+
+void ath79_register_spi(struct ath79_spi_platform_data *pdata,
+			 struct spi_board_info const *info,
+			 unsigned n);
+
+#endif /* _ATH79_DEV_SPI_H */
diff --git a/arch/mips/ath79/early_printk.c b/arch/mips/ath79/early_printk.c
new file mode 100644
index 0000000..7499b0e
--- /dev/null
+++ b/arch/mips/ath79/early_printk.c
@@ -0,0 +1,36 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X SoC early printk support
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/serial_reg.h>
+#include <asm/addrspace.h>
+
+#include <asm/mach-ath79/ar71xx_regs.h>
+
+static inline void prom_wait_thre(void __iomem *base)
+{
+	u32 lsr;
+
+	do {
+		lsr = __raw_readl(base + UART_LSR * 4);
+		if (lsr & UART_LSR_THRE)
+			break;
+	} while (1);
+}
+
+void prom_putchar(unsigned char ch)
+{
+	void __iomem *base = (void __iomem *)(KSEG1ADDR(AR71XX_UART_BASE));
+
+	prom_wait_thre(base);
+	__raw_writel(ch, base + UART_TX * 4);
+	prom_wait_thre(base);
+}
diff --git a/arch/mips/ath79/gpio.c b/arch/mips/ath79/gpio.c
new file mode 100644
index 0000000..a0c426b
--- /dev/null
+++ b/arch/mips/ath79/gpio.c
@@ -0,0 +1,197 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X GPIO API support
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/gpio.h>
+
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include <asm/mach-ath79/ath79.h>
+#include "common.h"
+
+static void __iomem *ath79_gpio_base;
+static unsigned long ath79_gpio_count;
+static DEFINE_SPINLOCK(ath79_gpio_lock);
+
+static void __ath79_gpio_set_value(unsigned gpio, int value)
+{
+	void __iomem *base = ath79_gpio_base;
+
+	if (value)
+		__raw_writel(1 << gpio, base + AR71XX_GPIO_REG_SET);
+	else
+		__raw_writel(1 << gpio, base + AR71XX_GPIO_REG_CLEAR);
+}
+
+static int __ath79_gpio_get_value(unsigned gpio)
+{
+	return (__raw_readl(ath79_gpio_base + AR71XX_GPIO_REG_IN) >> gpio) & 1;
+}
+
+static int ath79_gpio_get_value(struct gpio_chip *chip, unsigned offset)
+{
+	return __ath79_gpio_get_value(offset);
+}
+
+static void ath79_gpio_set_value(struct gpio_chip *chip,
+				  unsigned offset, int value)
+{
+	__ath79_gpio_set_value(offset, value);
+}
+
+static int ath79_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned offset)
+{
+	void __iomem *base = ath79_gpio_base;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ath79_gpio_lock, flags);
+
+	__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) & ~(1 << offset),
+		     base + AR71XX_GPIO_REG_OE);
+
+	spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+
+	return 0;
+}
+
+static int ath79_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	void __iomem *base = ath79_gpio_base;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ath79_gpio_lock, flags);
+
+	if (value)
+		__raw_writel(1 << offset, base + AR71XX_GPIO_REG_SET);
+	else
+		__raw_writel(1 << offset, base + AR71XX_GPIO_REG_CLEAR);
+
+	__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) | (1 << offset),
+		     base + AR71XX_GPIO_REG_OE);
+
+	spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+
+	return 0;
+}
+
+static struct gpio_chip ath79_gpio_chip = {
+	.label			= "ath79",
+	.get			= ath79_gpio_get_value,
+	.set			= ath79_gpio_set_value,
+	.direction_input	= ath79_gpio_direction_input,
+	.direction_output	= ath79_gpio_direction_output,
+	.base			= 0,
+};
+
+void ath79_gpio_function_enable(u32 mask)
+{
+	void __iomem *base = ath79_gpio_base;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ath79_gpio_lock, flags);
+
+	__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_FUNC) | mask,
+		     base + AR71XX_GPIO_REG_FUNC);
+	/* flush write */
+	__raw_readl(base + AR71XX_GPIO_REG_FUNC);
+
+	spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+}
+
+void ath79_gpio_function_disable(u32 mask)
+{
+	void __iomem *base = ath79_gpio_base;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ath79_gpio_lock, flags);
+
+	__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_FUNC) & ~mask,
+		     base + AR71XX_GPIO_REG_FUNC);
+	/* flush write */
+	__raw_readl(base + AR71XX_GPIO_REG_FUNC);
+
+	spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+}
+
+void ath79_gpio_function_setup(u32 set, u32 clear)
+{
+	void __iomem *base = ath79_gpio_base;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ath79_gpio_lock, flags);
+
+	__raw_writel((__raw_readl(base + AR71XX_GPIO_REG_FUNC) & ~clear) | set,
+		     base + AR71XX_GPIO_REG_FUNC);
+	/* flush write */
+	__raw_readl(base + AR71XX_GPIO_REG_FUNC);
+
+	spin_unlock_irqrestore(&ath79_gpio_lock, flags);
+}
+
+void __init ath79_gpio_init(void)
+{
+	int err;
+
+	if (soc_is_ar71xx())
+		ath79_gpio_count = AR71XX_GPIO_COUNT;
+	else if (soc_is_ar724x())
+		ath79_gpio_count = AR724X_GPIO_COUNT;
+	else if (soc_is_ar913x())
+		ath79_gpio_count = AR913X_GPIO_COUNT;
+	else
+		BUG();
+
+	ath79_gpio_base = ioremap_nocache(AR71XX_GPIO_BASE, AR71XX_GPIO_SIZE);
+	ath79_gpio_chip.ngpio = ath79_gpio_count;
+
+	err = gpiochip_add(&ath79_gpio_chip);
+	if (err)
+		panic("cannot add AR71xx GPIO chip, error=%d", err);
+}
+
+int gpio_get_value(unsigned gpio)
+{
+	if (gpio < ath79_gpio_count)
+		return __ath79_gpio_get_value(gpio);
+
+	return __gpio_get_value(gpio);
+}
+EXPORT_SYMBOL(gpio_get_value);
+
+void gpio_set_value(unsigned gpio, int value)
+{
+	if (gpio < ath79_gpio_count)
+		__ath79_gpio_set_value(gpio, value);
+	else
+		__gpio_set_value(gpio, value);
+}
+EXPORT_SYMBOL(gpio_set_value);
+
+int gpio_to_irq(unsigned gpio)
+{
+	/* FIXME */
+	return -EINVAL;
+}
+EXPORT_SYMBOL(gpio_to_irq);
+
+int irq_to_gpio(unsigned irq)
+{
+	/* FIXME */
+	return -EINVAL;
+}
+EXPORT_SYMBOL(irq_to_gpio);
diff --git a/arch/mips/ath79/irq.c b/arch/mips/ath79/irq.c
new file mode 100644
index 0000000..1bf7f71
--- /dev/null
+++ b/arch/mips/ath79/irq.c
@@ -0,0 +1,187 @@
+/*
+ *  Atheros AR71xx/AR724x/AR913x specific interrupt handling
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *
+ *  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/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include "common.h"
+
+static unsigned int ath79_ip2_flush_reg;
+static unsigned int ath79_ip3_flush_reg;
+
+static void ath79_misc_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	void __iomem *base = ath79_reset_base;
+	u32 pending;
+
+	pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) &
+		  __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+
+	if (pending & MISC_INT_UART)
+		generic_handle_irq(ATH79_MISC_IRQ_UART);
+
+	else if (pending & MISC_INT_DMA)
+		generic_handle_irq(ATH79_MISC_IRQ_DMA);
+
+	else if (pending & MISC_INT_PERFC)
+		generic_handle_irq(ATH79_MISC_IRQ_PERFC);
+
+	else if (pending & MISC_INT_TIMER)
+		generic_handle_irq(ATH79_MISC_IRQ_TIMER);
+
+	else if (pending & MISC_INT_OHCI)
+		generic_handle_irq(ATH79_MISC_IRQ_OHCI);
+
+	else if (pending & MISC_INT_ERROR)
+		generic_handle_irq(ATH79_MISC_IRQ_ERROR);
+
+	else if (pending & MISC_INT_GPIO)
+		generic_handle_irq(ATH79_MISC_IRQ_GPIO);
+
+	else if (pending & MISC_INT_WDOG)
+		generic_handle_irq(ATH79_MISC_IRQ_WDOG);
+
+	else
+		spurious_interrupt();
+}
+
+static void ar71xx_misc_irq_unmask(unsigned int irq)
+{
+	void __iomem *base = ath79_reset_base;
+	u32 t;
+
+	irq -= ATH79_MISC_IRQ_BASE;
+
+	t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+	__raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+
+	/* flush write */
+	__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+}
+
+static void ar71xx_misc_irq_mask(unsigned int irq)
+{
+	void __iomem *base = ath79_reset_base;
+	u32 t;
+
+	irq -= ATH79_MISC_IRQ_BASE;
+
+	t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+	__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+
+	/* flush write */
+	__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+}
+
+static void ar724x_misc_irq_ack(unsigned int irq)
+{
+	void __iomem *base = ath79_reset_base;
+	u32 t;
+
+	irq -= ATH79_MISC_IRQ_BASE;
+
+	t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
+	__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_STATUS);
+
+	/* flush write */
+	__raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
+}
+
+static struct irq_chip ath79_misc_irq_chip = {
+	.name		= "MISC",
+	.unmask		= ar71xx_misc_irq_unmask,
+	.mask		= ar71xx_misc_irq_mask,
+};
+
+static void __init ath79_misc_irq_init(void)
+{
+	void __iomem *base = ath79_reset_base;
+	int i;
+
+	__raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+	__raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS);
+
+	if (soc_is_ar71xx() || soc_is_ar913x())
+		ath79_misc_irq_chip.mask_ack = ar71xx_misc_irq_mask;
+	else if (soc_is_ar724x())
+		ath79_misc_irq_chip.ack = ar724x_misc_irq_ack;
+	else
+		BUG();
+
+	for (i = ATH79_MISC_IRQ_BASE;
+	     i < ATH79_MISC_IRQ_BASE + ATH79_MISC_IRQ_COUNT; i++) {
+		irq_desc[i].status = IRQ_DISABLED;
+		set_irq_chip_and_handler(i, &ath79_misc_irq_chip,
+					 handle_level_irq);
+	}
+
+	set_irq_chained_handler(ATH79_CPU_IRQ_MISC, ath79_misc_irq_handler);
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+	unsigned long pending;
+
+	pending = read_c0_status() & read_c0_cause() & ST0_IM;
+
+	if (pending & STATUSF_IP7)
+		do_IRQ(ATH79_CPU_IRQ_TIMER);
+
+	else if (pending & STATUSF_IP2) {
+		ath79_ddr_wb_flush(ath79_ip2_flush_reg);
+		do_IRQ(ATH79_CPU_IRQ_IP2);
+	}
+
+	else if (pending & STATUSF_IP4)
+		do_IRQ(ATH79_CPU_IRQ_GE0);
+
+	else if (pending & STATUSF_IP5)
+		do_IRQ(ATH79_CPU_IRQ_GE1);
+
+	else if (pending & STATUSF_IP3) {
+		ath79_ddr_wb_flush(ath79_ip3_flush_reg);
+		do_IRQ(ATH79_CPU_IRQ_USB);
+	}
+
+	else if (pending & STATUSF_IP6)
+		do_IRQ(ATH79_CPU_IRQ_MISC);
+
+	else
+		spurious_interrupt();
+}
+
+void __init arch_init_irq(void)
+{
+	if (soc_is_ar71xx()) {
+		ath79_ip2_flush_reg = AR71XX_DDR_REG_FLUSH_PCI;
+		ath79_ip3_flush_reg = AR71XX_DDR_REG_FLUSH_USB;
+	} else if (soc_is_ar724x()) {
+		ath79_ip2_flush_reg = AR724X_DDR_REG_FLUSH_PCIE;
+		ath79_ip3_flush_reg = AR724X_DDR_REG_FLUSH_USB;
+	} else if (soc_is_ar913x()) {
+		ath79_ip2_flush_reg = AR913X_DDR_REG_FLUSH_WMAC;
+		ath79_ip3_flush_reg = AR913X_DDR_REG_FLUSH_USB;
+	} else
+		BUG();
+
+	cp0_perfcount_irq = ATH79_MISC_IRQ_PERFC;
+	mips_cpu_irq_init();
+	ath79_misc_irq_init();
+}
diff --git a/arch/mips/ath79/mach-ap81.c b/arch/mips/ath79/mach-ap81.c
new file mode 100644
index 0000000..eee4c12
--- /dev/null
+++ b/arch/mips/ath79/mach-ap81.c
@@ -0,0 +1,98 @@
+/*
+ *  Atheros AP81 board support
+ *
+ *  Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2009 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include "machtypes.h"
+#include "dev-ar913x-wmac.h"
+#include "dev-gpio-buttons.h"
+#include "dev-leds-gpio.h"
+#include "dev-spi.h"
+
+#define AP81_GPIO_LED_STATUS	1
+#define AP81_GPIO_LED_AOSS	3
+#define AP81_GPIO_LED_WLAN	6
+#define AP81_GPIO_LED_POWER	14
+
+#define AP81_GPIO_BTN_SW4	12
+#define AP81_GPIO_BTN_SW1	21
+
+#define AP81_KEYS_POLL_INTERVAL		20	/* msecs */
+#define AP81_KEYS_DEBOUNCE_INTERVAL	(3 * AP81_KEYS_POLL_INTERVAL)
+
+#define AP81_CAL_DATA_ADDR	0x1fff1000
+
+static struct gpio_led ap81_leds_gpio[] __initdata = {
+	{
+		.name		= "ap81:green:status",
+		.gpio		= AP81_GPIO_LED_STATUS,
+		.active_low	= 1,
+	}, {
+		.name		= "ap81:amber:aoss",
+		.gpio		= AP81_GPIO_LED_AOSS,
+		.active_low	= 1,
+	}, {
+		.name		= "ap81:green:wlan",
+		.gpio		= AP81_GPIO_LED_WLAN,
+		.active_low	= 1,
+	}, {
+		.name		= "ap81:green:power",
+		.gpio		= AP81_GPIO_LED_POWER,
+		.active_low	= 1,
+	}
+};
+
+static struct gpio_keys_button ap81_gpio_keys[] __initdata = {
+	{
+		.desc		= "sw1",
+		.type		= EV_KEY,
+		.code		= BTN_0,
+		.debounce_interval = AP81_KEYS_DEBOUNCE_INTERVAL,
+		.gpio		= AP81_GPIO_BTN_SW1,
+		.active_low	= 1,
+	} , {
+		.desc		= "sw4",
+		.type		= EV_KEY,
+		.code		= BTN_1,
+		.debounce_interval = AP81_KEYS_DEBOUNCE_INTERVAL,
+		.gpio		= AP81_GPIO_BTN_SW4,
+		.active_low	= 1,
+	}
+};
+
+static struct spi_board_info ap81_spi_info[] = {
+	{
+		.bus_num	= 0,
+		.chip_select	= 0,
+		.max_speed_hz	= 25000000,
+		.modalias	= "m25p64",
+	}
+};
+
+static struct ath79_spi_platform_data ap81_spi_data = {
+	.bus_num	= 0,
+	.num_chipselect	= 1,
+};
+
+static void __init ap81_setup(void)
+{
+	u8 *cal_data = (u8 *) KSEG1ADDR(AP81_CAL_DATA_ADDR);
+
+	ath79_register_leds_gpio(-1, ARRAY_SIZE(ap81_leds_gpio),
+				 ap81_leds_gpio);
+	ath79_register_gpio_keys_polled(-1, AP81_KEYS_POLL_INTERVAL,
+					ARRAY_SIZE(ap81_gpio_keys),
+					ap81_gpio_keys);
+	ath79_register_spi(&ap81_spi_data, ap81_spi_info,
+			   ARRAY_SIZE(ap81_spi_info));
+	ath79_register_ar913x_wmac(cal_data);
+}
+
+MIPS_MACHINE(ATH79_MACH_AP81, "AP81", "Atheros AP81 reference board",
+	     ap81_setup);
diff --git a/arch/mips/ath79/mach-pb44.c b/arch/mips/ath79/mach-pb44.c
new file mode 100644
index 0000000..ec7b7a1
--- /dev/null
+++ b/arch/mips/ath79/mach-pb44.c
@@ -0,0 +1,118 @@
+/*
+ *  Atheros PB44 reference board support
+ *
+ *  Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/i2c-gpio.h>
+#include <linux/i2c/pcf857x.h>
+
+#include "machtypes.h"
+#include "dev-gpio-buttons.h"
+#include "dev-leds-gpio.h"
+#include "dev-spi.h"
+
+#define PB44_GPIO_I2C_SCL	0
+#define PB44_GPIO_I2C_SDA	1
+
+#define PB44_GPIO_EXP_BASE	16
+#define PB44_GPIO_SW_RESET	(PB44_GPIO_EXP_BASE + 6)
+#define PB44_GPIO_SW_JUMP	(PB44_GPIO_EXP_BASE + 8)
+#define PB44_GPIO_LED_JUMP1	(PB44_GPIO_EXP_BASE + 9)
+#define PB44_GPIO_LED_JUMP2	(PB44_GPIO_EXP_BASE + 10)
+
+#define PB44_KEYS_POLL_INTERVAL		20	/* msecs */
+#define PB44_KEYS_DEBOUNCE_INTERVAL	(3 * PB44_KEYS_POLL_INTERVAL)
+
+static struct i2c_gpio_platform_data pb44_i2c_gpio_data = {
+	.sda_pin        = PB44_GPIO_I2C_SDA,
+	.scl_pin        = PB44_GPIO_I2C_SCL,
+};
+
+static struct platform_device pb44_i2c_gpio_device = {
+	.name		= "i2c-gpio",
+	.id		= 0,
+	.dev = {
+		.platform_data	= &pb44_i2c_gpio_data,
+	}
+};
+
+static struct pcf857x_platform_data pb44_pcf857x_data = {
+	.gpio_base	= PB44_GPIO_EXP_BASE,
+};
+
+static struct i2c_board_info pb44_i2c_board_info[] __initdata = {
+	{
+		I2C_BOARD_INFO("pcf8575", 0x20),
+		.platform_data  = &pb44_pcf857x_data,
+	},
+};
+
+static struct gpio_led pb44_leds_gpio[] __initdata = {
+	{
+		.name		= "pb44:amber:jump1",
+		.gpio		= PB44_GPIO_LED_JUMP1,
+		.active_low	= 1,
+	}, {
+		.name		= "pb44:green:jump2",
+		.gpio		= PB44_GPIO_LED_JUMP2,
+		.active_low	= 1,
+	},
+};
+
+static struct gpio_keys_button pb44_gpio_keys[] __initdata = {
+	{
+		.desc		= "soft_reset",
+		.type		= EV_KEY,
+		.code		= KEY_RESTART,
+		.debounce_interval = PB44_KEYS_DEBOUNCE_INTERVAL,
+		.gpio		= PB44_GPIO_SW_RESET,
+		.active_low	= 1,
+	} , {
+		.desc		= "jumpstart",
+		.type		= EV_KEY,
+		.code		= KEY_WPS_BUTTON,
+		.debounce_interval = PB44_KEYS_DEBOUNCE_INTERVAL,
+		.gpio		= PB44_GPIO_SW_JUMP,
+		.active_low	= 1,
+	}
+};
+
+static struct spi_board_info pb44_spi_info[] = {
+	{
+		.bus_num	= 0,
+		.chip_select	= 0,
+		.max_speed_hz	= 25000000,
+		.modalias	= "m25p64",
+	},
+};
+
+static struct ath79_spi_platform_data pb44_spi_data = {
+	.bus_num		= 0,
+	.num_chipselect		= 1,
+};
+
+static void __init pb44_init(void)
+{
+	i2c_register_board_info(0, pb44_i2c_board_info,
+				ARRAY_SIZE(pb44_i2c_board_info));
+	platform_device_register(&pb44_i2c_gpio_device);
+
+	ath79_register_leds_gpio(-1, ARRAY_SIZE(pb44_leds_gpio),
+				 pb44_leds_gpio);
+	ath79_register_gpio_keys_polled(-1, PB44_KEYS_POLL_INTERVAL,
+					ARRAY_SIZE(pb44_gpio_keys),
+					pb44_gpio_keys);
+	ath79_register_spi(&pb44_spi_data, pb44_spi_info,
+			   ARRAY_SIZE(pb44_spi_info));
+}
+
+MIPS_MACHINE(ATH79_MACH_PB44, "PB44", "Atheros PB44 reference board",
+	     pb44_init);
diff --git a/arch/mips/ath79/machtypes.h b/arch/mips/ath79/machtypes.h
new file mode 100644
index 0000000..3940fe4
--- /dev/null
+++ b/arch/mips/ath79/machtypes.h
@@ -0,0 +1,23 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X machine type definitions
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#ifndef _ATH79_MACHTYPE_H
+#define _ATH79_MACHTYPE_H
+
+#include <asm/mips_machine.h>
+
+enum ath79_mach_type {
+	ATH79_MACH_GENERIC = 0,
+	ATH79_MACH_AP81,		/* Atheros AP81 reference board */
+	ATH79_MACH_PB44,		/* Atheros PB44 reference board */
+};
+
+#endif /* _ATH79_MACHTYPE_H */
diff --git a/arch/mips/ath79/prom.c b/arch/mips/ath79/prom.c
new file mode 100644
index 0000000..e9cbd7c
--- /dev/null
+++ b/arch/mips/ath79/prom.c
@@ -0,0 +1,57 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X specific prom routines
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/string.h>
+
+#include <asm/bootinfo.h>
+#include <asm/addrspace.h>
+
+#include "common.h"
+
+static inline int is_valid_ram_addr(void *addr)
+{
+	if (((u32) addr > KSEG0) &&
+	    ((u32) addr < (KSEG0 + ATH79_MEM_SIZE_MAX)))
+		return 1;
+
+	if (((u32) addr > KSEG1) &&
+	    ((u32) addr < (KSEG1 + ATH79_MEM_SIZE_MAX)))
+		return 1;
+
+	return 0;
+}
+
+static __init void ath79_prom_init_cmdline(int argc, char **argv)
+{
+	int i;
+
+	if (!is_valid_ram_addr(argv))
+		return;
+
+	for (i = 0; i < argc; i++)
+		if (is_valid_ram_addr(argv[i])) {
+			strlcat(arcs_cmdline, " ", sizeof(arcs_cmdline));
+			strlcat(arcs_cmdline, argv[i], sizeof(arcs_cmdline));
+		}
+}
+
+void __init prom_init(void)
+{
+	ath79_prom_init_cmdline(fw_arg0, (char **)fw_arg1);
+}
+
+void __init prom_free_prom_memory(void)
+{
+	/* We do not have to prom memory to free */
+}
diff --git a/arch/mips/ath79/setup.c b/arch/mips/ath79/setup.c
new file mode 100644
index 0000000..159b42f
--- /dev/null
+++ b/arch/mips/ath79/setup.c
@@ -0,0 +1,206 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X specific setup
+ *
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *
+ *  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/bootmem.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include <asm/bootinfo.h>
+#include <asm/time.h>		/* for mips_hpt_frequency */
+#include <asm/reboot.h>		/* for _machine_{restart,halt} */
+#include <asm/mips_machine.h>
+
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include "common.h"
+#include "dev-common.h"
+#include "machtypes.h"
+
+#define ATH79_SYS_TYPE_LEN	64
+
+#define AR71XX_BASE_FREQ	40000000
+#define AR724X_BASE_FREQ	5000000
+#define AR913X_BASE_FREQ	5000000
+
+static char ath79_sys_type[ATH79_SYS_TYPE_LEN];
+
+static void ath79_restart(char *command)
+{
+	ath79_device_reset_set(AR71XX_RESET_FULL_CHIP);
+	for (;;)
+		if (cpu_wait)
+			cpu_wait();
+}
+
+static void ath79_halt(void)
+{
+	while (1)
+		cpu_wait();
+}
+
+static void __init ath79_detect_mem_size(void)
+{
+	unsigned long size;
+
+	for (size = ATH79_MEM_SIZE_MIN; size < ATH79_MEM_SIZE_MAX;
+	     size <<= 1) {
+		if (!memcmp(ath79_detect_mem_size,
+			    ath79_detect_mem_size + size, 1024))
+			break;
+	}
+
+	add_memory_region(0, size, BOOT_MEM_RAM);
+}
+
+static void __init ath79_detect_sys_type(void)
+{
+	char *chip = "????";
+	u32 id;
+	u32 major;
+	u32 minor;
+	u32 rev = 0;
+
+	id = ath79_reset_rr(AR71XX_RESET_REG_REV_ID);
+	major = id & REV_ID_MAJOR_MASK;
+
+	switch (major) {
+	case REV_ID_MAJOR_AR71XX:
+		minor = id & AR71XX_REV_ID_MINOR_MASK;
+		rev = id >> AR71XX_REV_ID_REVISION_SHIFT;
+		rev &= AR71XX_REV_ID_REVISION_MASK;
+		switch (minor) {
+		case AR71XX_REV_ID_MINOR_AR7130:
+			ath79_soc = ATH79_SOC_AR7130;
+			chip = "7130";
+			break;
+
+		case AR71XX_REV_ID_MINOR_AR7141:
+			ath79_soc = ATH79_SOC_AR7141;
+			chip = "7141";
+			break;
+
+		case AR71XX_REV_ID_MINOR_AR7161:
+			ath79_soc = ATH79_SOC_AR7161;
+			chip = "7161";
+			break;
+		}
+		break;
+
+	case REV_ID_MAJOR_AR7240:
+		ath79_soc = ATH79_SOC_AR7240;
+		chip = "7240";
+		rev = (id & AR724X_REV_ID_REVISION_MASK);
+		break;
+
+	case REV_ID_MAJOR_AR7241:
+		ath79_soc = ATH79_SOC_AR7241;
+		chip = "7241";
+		rev = (id & AR724X_REV_ID_REVISION_MASK);
+		break;
+
+	case REV_ID_MAJOR_AR7242:
+		ath79_soc = ATH79_SOC_AR7242;
+		chip = "7242";
+		rev = (id & AR724X_REV_ID_REVISION_MASK);
+		break;
+
+	case REV_ID_MAJOR_AR913X:
+		minor = id & AR913X_REV_ID_MINOR_MASK;
+		rev = id >> AR913X_REV_ID_REVISION_SHIFT;
+		rev &= AR913X_REV_ID_REVISION_MASK;
+		switch (minor) {
+		case AR913X_REV_ID_MINOR_AR9130:
+			ath79_soc = ATH79_SOC_AR9130;
+			chip = "9130";
+			break;
+
+		case AR913X_REV_ID_MINOR_AR9132:
+			ath79_soc = ATH79_SOC_AR9132;
+			chip = "9132";
+			break;
+		}
+		break;
+
+	default:
+		panic("ath79: unknown SoC, id:0x%08x\n", id);
+	}
+
+	sprintf(ath79_sys_type, "Atheros AR%s rev %u", chip, rev);
+	pr_info("SoC: %s\n", ath79_sys_type);
+}
+
+const char *get_system_type(void)
+{
+	return ath79_sys_type;
+}
+
+unsigned int __cpuinit get_c0_compare_int(void)
+{
+	return CP0_LEGACY_COMPARE_IRQ;
+}
+
+void __init plat_mem_setup(void)
+{
+	set_io_port_base(KSEG1);
+
+	ath79_reset_base = ioremap_nocache(AR71XX_RESET_BASE,
+					   AR71XX_RESET_SIZE);
+	ath79_pll_base = ioremap_nocache(AR71XX_PLL_BASE,
+					 AR71XX_PLL_SIZE);
+	ath79_ddr_base = ioremap_nocache(AR71XX_DDR_CTRL_BASE,
+					 AR71XX_DDR_CTRL_SIZE);
+
+	ath79_detect_sys_type();
+	ath79_detect_mem_size();
+	ath79_clocks_init();
+
+	_machine_restart = ath79_restart;
+	_machine_halt = ath79_halt;
+	pm_power_off = ath79_halt;
+}
+
+void __init plat_time_init(void)
+{
+	struct clk *clk;
+
+	clk = clk_get(NULL, "cpu");
+	if (IS_ERR(clk))
+		panic("unable to get CPU clock, err=%ld", PTR_ERR(clk));
+
+	mips_hpt_frequency = clk_get_rate(clk) / 2;
+}
+
+static int __init ath79_setup(void)
+{
+	ath79_gpio_init();
+	ath79_register_uart();
+	ath79_register_wdt();
+
+	mips_machine_setup();
+
+	return 0;
+}
+
+arch_initcall(ath79_setup);
+
+static void __init ath79_generic_init(void)
+{
+	/* Nothing to do */
+}
+
+MIPS_MACHINE(ATH79_MACH_GENERIC,
+	     "Generic",
+	     "Generic AR71XX/AR724X/AR913X based board",
+	     ath79_generic_init);
diff --git a/arch/mips/configs/malta_defconfig b/arch/mips/configs/malta_defconfig
index b455d0f..9d03b68 100644
--- a/arch/mips/configs/malta_defconfig
+++ b/arch/mips/configs/malta_defconfig
@@ -369,7 +369,10 @@
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
 # CONFIG_HWMON is not set
+CONFIG_FB=y
+CONFIG_FB_CIRRUS=y
 # CONFIG_VGA_CONSOLE is not set
+CONFIG_FRAMEBUFFER_CONSOLE=y
 CONFIG_HID=m
 CONFIG_LEDS_CLASS=m
 CONFIG_LEDS_TRIGGER_TIMER=m
diff --git a/arch/mips/include/asm/cache.h b/arch/mips/include/asm/cache.h
index 37f175c..650ac9b 100644
--- a/arch/mips/include/asm/cache.h
+++ b/arch/mips/include/asm/cache.h
@@ -17,4 +17,6 @@
 #define SMP_CACHE_SHIFT		L1_CACHE_SHIFT
 #define SMP_CACHE_BYTES		L1_CACHE_BYTES
 
+#define __read_mostly __attribute__((__section__(".data.read_mostly")))
+
 #endif /* _ASM_CACHE_H */
diff --git a/arch/mips/include/asm/cpu-info.h b/arch/mips/include/asm/cpu-info.h
index b39def3..c454550 100644
--- a/arch/mips/include/asm/cpu-info.h
+++ b/arch/mips/include/asm/cpu-info.h
@@ -78,6 +78,7 @@
 	unsigned int		watch_reg_use_cnt; /* Usable by ptrace */
 #define NUM_WATCH_REGS 4
 	u16			watch_reg_masks[NUM_WATCH_REGS];
+	unsigned int		kscratch_mask; /* Usable KScratch mask. */
 } __attribute__((aligned(SMP_CACHE_BYTES)));
 
 extern struct cpuinfo_mips cpu_data[];
diff --git a/arch/mips/include/asm/inst.h b/arch/mips/include/asm/inst.h
index 444ff71..7ebfc39 100644
--- a/arch/mips/include/asm/inst.h
+++ b/arch/mips/include/asm/inst.h
@@ -72,6 +72,7 @@
 enum spec3_op {
 	ext_op, dextm_op, dextu_op, dext_op,
 	ins_op, dinsm_op, dinsu_op, dins_op,
+	lx_op = 0x0a,
 	bshfl_op = 0x20,
 	dbshfl_op = 0x24,
 	rdhwr_op = 0x3b
@@ -179,6 +180,19 @@
 };
 
 /*
+ * func field for special3 lx opcodes (Cavium Octeon).
+ */
+enum lx_func {
+	lwx_op	= 0x00,
+	lhx_op	= 0x04,
+	lbux_op	= 0x06,
+	ldx_op	= 0x08,
+	lwux_op	= 0x10,
+	lhux_op	= 0x14,
+	lbx_op	= 0x16,
+};
+
+/*
  * Damn ...  bitfields depend from byteorder :-(
  */
 #ifdef __MIPSEB__
diff --git a/arch/mips/include/asm/jump_label.h b/arch/mips/include/asm/jump_label.h
new file mode 100644
index 0000000..7622ccf
--- /dev/null
+++ b/arch/mips/include/asm/jump_label.h
@@ -0,0 +1,48 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 2010 Cavium Networks, Inc.
+ */
+#ifndef _ASM_MIPS_JUMP_LABEL_H
+#define _ASM_MIPS_JUMP_LABEL_H
+
+#include <linux/types.h>
+
+#ifdef __KERNEL__
+
+#define JUMP_LABEL_NOP_SIZE 4
+
+#ifdef CONFIG_64BIT
+#define WORD_INSN ".dword"
+#else
+#define WORD_INSN ".word"
+#endif
+
+#define JUMP_LABEL(key, label)						\
+	do {								\
+		asm goto("1:\tnop\n\t"					\
+			"nop\n\t"					\
+			".pushsection __jump_table,  \"a\"\n\t"		\
+			WORD_INSN " 1b, %l[" #label "], %0\n\t"		\
+			".popsection\n\t"				\
+			: :  "i" (key) :  : label);			\
+	} while (0)
+
+
+#endif /* __KERNEL__ */
+
+#ifdef CONFIG_64BIT
+typedef u64 jump_label_t;
+#else
+typedef u32 jump_label_t;
+#endif
+
+struct jump_entry {
+	jump_label_t code;
+	jump_label_t target;
+	jump_label_t key;
+};
+
+#endif /* _ASM_MIPS_JUMP_LABEL_H */
diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h
new file mode 100644
index 0000000..cda1c80
--- /dev/null
+++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h
@@ -0,0 +1,233 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X SoC register definitions
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#ifndef __ASM_MACH_AR71XX_REGS_H
+#define __ASM_MACH_AR71XX_REGS_H
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+
+#define AR71XX_APB_BASE		0x18000000
+#define AR71XX_SPI_BASE		0x1f000000
+#define AR71XX_SPI_SIZE		0x01000000
+
+#define AR71XX_DDR_CTRL_BASE	(AR71XX_APB_BASE + 0x00000000)
+#define AR71XX_DDR_CTRL_SIZE	0x100
+#define AR71XX_UART_BASE	(AR71XX_APB_BASE + 0x00020000)
+#define AR71XX_UART_SIZE	0x100
+#define AR71XX_GPIO_BASE        (AR71XX_APB_BASE + 0x00040000)
+#define AR71XX_GPIO_SIZE        0x100
+#define AR71XX_PLL_BASE		(AR71XX_APB_BASE + 0x00050000)
+#define AR71XX_PLL_SIZE		0x100
+#define AR71XX_RESET_BASE	(AR71XX_APB_BASE + 0x00060000)
+#define AR71XX_RESET_SIZE	0x100
+
+#define AR913X_WMAC_BASE	(AR71XX_APB_BASE + 0x000C0000)
+#define AR913X_WMAC_SIZE	0x30000
+
+/*
+ * DDR_CTRL block
+ */
+#define AR71XX_DDR_REG_PCI_WIN0		0x7c
+#define AR71XX_DDR_REG_PCI_WIN1		0x80
+#define AR71XX_DDR_REG_PCI_WIN2		0x84
+#define AR71XX_DDR_REG_PCI_WIN3		0x88
+#define AR71XX_DDR_REG_PCI_WIN4		0x8c
+#define AR71XX_DDR_REG_PCI_WIN5		0x90
+#define AR71XX_DDR_REG_PCI_WIN6		0x94
+#define AR71XX_DDR_REG_PCI_WIN7		0x98
+#define AR71XX_DDR_REG_FLUSH_GE0	0x9c
+#define AR71XX_DDR_REG_FLUSH_GE1	0xa0
+#define AR71XX_DDR_REG_FLUSH_USB	0xa4
+#define AR71XX_DDR_REG_FLUSH_PCI	0xa8
+
+#define AR724X_DDR_REG_FLUSH_GE0	0x7c
+#define AR724X_DDR_REG_FLUSH_GE1	0x80
+#define AR724X_DDR_REG_FLUSH_USB	0x84
+#define AR724X_DDR_REG_FLUSH_PCIE	0x88
+
+#define AR913X_DDR_REG_FLUSH_GE0	0x7c
+#define AR913X_DDR_REG_FLUSH_GE1	0x80
+#define AR913X_DDR_REG_FLUSH_USB	0x84
+#define AR913X_DDR_REG_FLUSH_WMAC	0x88
+
+/*
+ * PLL block
+ */
+#define AR71XX_PLL_REG_CPU_CONFIG	0x00
+#define AR71XX_PLL_REG_SEC_CONFIG	0x04
+#define AR71XX_PLL_REG_ETH0_INT_CLOCK	0x10
+#define AR71XX_PLL_REG_ETH1_INT_CLOCK	0x14
+
+#define AR71XX_PLL_DIV_SHIFT		3
+#define AR71XX_PLL_DIV_MASK		0x1f
+#define AR71XX_CPU_DIV_SHIFT		16
+#define AR71XX_CPU_DIV_MASK		0x3
+#define AR71XX_DDR_DIV_SHIFT		18
+#define AR71XX_DDR_DIV_MASK		0x3
+#define AR71XX_AHB_DIV_SHIFT		20
+#define AR71XX_AHB_DIV_MASK		0x7
+
+#define AR724X_PLL_REG_CPU_CONFIG	0x00
+#define AR724X_PLL_REG_PCIE_CONFIG	0x18
+
+#define AR724X_PLL_DIV_SHIFT		0
+#define AR724X_PLL_DIV_MASK		0x3ff
+#define AR724X_PLL_REF_DIV_SHIFT	10
+#define AR724X_PLL_REF_DIV_MASK		0xf
+#define AR724X_AHB_DIV_SHIFT		19
+#define AR724X_AHB_DIV_MASK		0x1
+#define AR724X_DDR_DIV_SHIFT		22
+#define AR724X_DDR_DIV_MASK		0x3
+
+#define AR913X_PLL_REG_CPU_CONFIG	0x00
+#define AR913X_PLL_REG_ETH_CONFIG	0x04
+#define AR913X_PLL_REG_ETH0_INT_CLOCK	0x14
+#define AR913X_PLL_REG_ETH1_INT_CLOCK	0x18
+
+#define AR913X_PLL_DIV_SHIFT		0
+#define AR913X_PLL_DIV_MASK		0x3ff
+#define AR913X_DDR_DIV_SHIFT		22
+#define AR913X_DDR_DIV_MASK		0x3
+#define AR913X_AHB_DIV_SHIFT		19
+#define AR913X_AHB_DIV_MASK		0x1
+
+/*
+ * RESET block
+ */
+#define AR71XX_RESET_REG_TIMER			0x00
+#define AR71XX_RESET_REG_TIMER_RELOAD		0x04
+#define AR71XX_RESET_REG_WDOG_CTRL		0x08
+#define AR71XX_RESET_REG_WDOG			0x0c
+#define AR71XX_RESET_REG_MISC_INT_STATUS	0x10
+#define AR71XX_RESET_REG_MISC_INT_ENABLE	0x14
+#define AR71XX_RESET_REG_PCI_INT_STATUS		0x18
+#define AR71XX_RESET_REG_PCI_INT_ENABLE		0x1c
+#define AR71XX_RESET_REG_GLOBAL_INT_STATUS	0x20
+#define AR71XX_RESET_REG_RESET_MODULE		0x24
+#define AR71XX_RESET_REG_PERFC_CTRL		0x2c
+#define AR71XX_RESET_REG_PERFC0			0x30
+#define AR71XX_RESET_REG_PERFC1			0x34
+#define AR71XX_RESET_REG_REV_ID			0x90
+
+#define AR913X_RESET_REG_GLOBAL_INT_STATUS	0x18
+#define AR913X_RESET_REG_RESET_MODULE		0x1c
+#define AR913X_RESET_REG_PERF_CTRL		0x20
+#define AR913X_RESET_REG_PERFC0			0x24
+#define AR913X_RESET_REG_PERFC1			0x28
+
+#define AR724X_RESET_REG_RESET_MODULE		0x1c
+
+#define MISC_INT_DMA			BIT(7)
+#define MISC_INT_OHCI			BIT(6)
+#define MISC_INT_PERFC			BIT(5)
+#define MISC_INT_WDOG			BIT(4)
+#define MISC_INT_UART			BIT(3)
+#define MISC_INT_GPIO			BIT(2)
+#define MISC_INT_ERROR			BIT(1)
+#define MISC_INT_TIMER			BIT(0)
+
+#define AR71XX_RESET_EXTERNAL		BIT(28)
+#define AR71XX_RESET_FULL_CHIP		BIT(24)
+#define AR71XX_RESET_CPU_NMI		BIT(21)
+#define AR71XX_RESET_CPU_COLD		BIT(20)
+#define AR71XX_RESET_DMA		BIT(19)
+#define AR71XX_RESET_SLIC		BIT(18)
+#define AR71XX_RESET_STEREO		BIT(17)
+#define AR71XX_RESET_DDR		BIT(16)
+#define AR71XX_RESET_GE1_MAC		BIT(13)
+#define AR71XX_RESET_GE1_PHY		BIT(12)
+#define AR71XX_RESET_USBSUS_OVERRIDE	BIT(10)
+#define AR71XX_RESET_GE0_MAC		BIT(9)
+#define AR71XX_RESET_GE0_PHY		BIT(8)
+#define AR71XX_RESET_USB_OHCI_DLL	BIT(6)
+#define AR71XX_RESET_USB_HOST		BIT(5)
+#define AR71XX_RESET_USB_PHY		BIT(4)
+#define AR71XX_RESET_PCI_BUS		BIT(1)
+#define AR71XX_RESET_PCI_CORE		BIT(0)
+
+#define AR724X_RESET_GE1_MDIO		BIT(23)
+#define AR724X_RESET_GE0_MDIO		BIT(22)
+#define AR724X_RESET_PCIE_PHY_SERIAL	BIT(10)
+#define AR724X_RESET_PCIE_PHY		BIT(7)
+#define AR724X_RESET_PCIE		BIT(6)
+#define AR724X_RESET_OHCI_DLL		BIT(3)
+
+#define AR913X_RESET_AMBA2WMAC		BIT(22)
+
+#define REV_ID_MAJOR_MASK		0xfff0
+#define REV_ID_MAJOR_AR71XX		0x00a0
+#define REV_ID_MAJOR_AR913X		0x00b0
+#define REV_ID_MAJOR_AR7240		0x00c0
+#define REV_ID_MAJOR_AR7241		0x0100
+#define REV_ID_MAJOR_AR7242		0x1100
+
+#define AR71XX_REV_ID_MINOR_MASK	0x3
+#define AR71XX_REV_ID_MINOR_AR7130	0x0
+#define AR71XX_REV_ID_MINOR_AR7141	0x1
+#define AR71XX_REV_ID_MINOR_AR7161	0x2
+#define AR71XX_REV_ID_REVISION_MASK	0x3
+#define AR71XX_REV_ID_REVISION_SHIFT	2
+
+#define AR913X_REV_ID_MINOR_MASK	0x3
+#define AR913X_REV_ID_MINOR_AR9130	0x0
+#define AR913X_REV_ID_MINOR_AR9132	0x1
+#define AR913X_REV_ID_REVISION_MASK	0x3
+#define AR913X_REV_ID_REVISION_SHIFT	2
+
+#define AR724X_REV_ID_REVISION_MASK	0x3
+
+/*
+ * SPI block
+ */
+#define AR71XX_SPI_REG_FS	0x00	/* Function Select */
+#define AR71XX_SPI_REG_CTRL	0x04	/* SPI Control */
+#define AR71XX_SPI_REG_IOC	0x08	/* SPI I/O Control */
+#define AR71XX_SPI_REG_RDS	0x0c	/* Read Data Shift */
+
+#define AR71XX_SPI_FS_GPIO	BIT(0)	/* Enable GPIO mode */
+
+#define AR71XX_SPI_CTRL_RD	BIT(6)	/* Remap Disable */
+#define AR71XX_SPI_CTRL_DIV_MASK 0x3f
+
+#define AR71XX_SPI_IOC_DO	BIT(0)	/* Data Out pin */
+#define AR71XX_SPI_IOC_CLK	BIT(8)	/* CLK pin */
+#define AR71XX_SPI_IOC_CS(n)	BIT(16 + (n))
+#define AR71XX_SPI_IOC_CS0	AR71XX_SPI_IOC_CS(0)
+#define AR71XX_SPI_IOC_CS1	AR71XX_SPI_IOC_CS(1)
+#define AR71XX_SPI_IOC_CS2	AR71XX_SPI_IOC_CS(2)
+#define AR71XX_SPI_IOC_CS_ALL	(AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1 | \
+				 AR71XX_SPI_IOC_CS2)
+
+/*
+ * GPIO block
+ */
+#define AR71XX_GPIO_REG_OE		0x00
+#define AR71XX_GPIO_REG_IN		0x04
+#define AR71XX_GPIO_REG_OUT		0x08
+#define AR71XX_GPIO_REG_SET		0x0c
+#define AR71XX_GPIO_REG_CLEAR		0x10
+#define AR71XX_GPIO_REG_INT_MODE	0x14
+#define AR71XX_GPIO_REG_INT_TYPE	0x18
+#define AR71XX_GPIO_REG_INT_POLARITY	0x1c
+#define AR71XX_GPIO_REG_INT_PENDING	0x20
+#define AR71XX_GPIO_REG_INT_ENABLE	0x24
+#define AR71XX_GPIO_REG_FUNC		0x28
+
+#define AR71XX_GPIO_COUNT		16
+#define AR724X_GPIO_COUNT		18
+#define AR913X_GPIO_COUNT		22
+
+#endif /* __ASM_MACH_AR71XX_REGS_H */
diff --git a/arch/mips/include/asm/mach-ath79/ath79.h b/arch/mips/include/asm/mach-ath79/ath79.h
new file mode 100644
index 0000000..6a9f168
--- /dev/null
+++ b/arch/mips/include/asm/mach-ath79/ath79.h
@@ -0,0 +1,96 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X common definitions
+ *
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#ifndef __ASM_MACH_ATH79_H
+#define __ASM_MACH_ATH79_H
+
+#include <linux/types.h>
+#include <linux/io.h>
+
+enum ath79_soc_type {
+	ATH79_SOC_UNKNOWN,
+	ATH79_SOC_AR7130,
+	ATH79_SOC_AR7141,
+	ATH79_SOC_AR7161,
+	ATH79_SOC_AR7240,
+	ATH79_SOC_AR7241,
+	ATH79_SOC_AR7242,
+	ATH79_SOC_AR9130,
+	ATH79_SOC_AR9132
+};
+
+extern enum ath79_soc_type ath79_soc;
+
+static inline int soc_is_ar71xx(void)
+{
+	return (ath79_soc == ATH79_SOC_AR7130 ||
+		ath79_soc == ATH79_SOC_AR7141 ||
+		ath79_soc == ATH79_SOC_AR7161);
+}
+
+static inline int soc_is_ar724x(void)
+{
+	return (ath79_soc == ATH79_SOC_AR7240 ||
+		ath79_soc == ATH79_SOC_AR7241 ||
+		ath79_soc == ATH79_SOC_AR7242);
+}
+
+static inline int soc_is_ar7240(void)
+{
+	return (ath79_soc == ATH79_SOC_AR7240);
+}
+
+static inline int soc_is_ar7241(void)
+{
+	return (ath79_soc == ATH79_SOC_AR7241);
+}
+
+static inline int soc_is_ar7242(void)
+{
+	return (ath79_soc == ATH79_SOC_AR7242);
+}
+
+static inline int soc_is_ar913x(void)
+{
+	return (ath79_soc == ATH79_SOC_AR9130 ||
+		ath79_soc == ATH79_SOC_AR9132);
+}
+
+extern void __iomem *ath79_ddr_base;
+extern void __iomem *ath79_pll_base;
+extern void __iomem *ath79_reset_base;
+
+static inline void ath79_pll_wr(unsigned reg, u32 val)
+{
+	__raw_writel(val, ath79_pll_base + reg);
+}
+
+static inline u32 ath79_pll_rr(unsigned reg)
+{
+	return __raw_readl(ath79_pll_base + reg);
+}
+
+static inline void ath79_reset_wr(unsigned reg, u32 val)
+{
+	__raw_writel(val, ath79_reset_base + reg);
+}
+
+static inline u32 ath79_reset_rr(unsigned reg)
+{
+	return __raw_readl(ath79_reset_base + reg);
+}
+
+void ath79_device_reset_set(u32 mask);
+void ath79_device_reset_clear(u32 mask);
+
+#endif /* __ASM_MACH_ATH79_H */
diff --git a/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h b/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
new file mode 100644
index 0000000..aa2283e
--- /dev/null
+++ b/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
@@ -0,0 +1,23 @@
+/*
+ *  Platform data definition for Atheros AR71XX/AR724X/AR913X SPI controller
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#ifndef _ATH79_SPI_PLATFORM_H
+#define _ATH79_SPI_PLATFORM_H
+
+struct ath79_spi_platform_data {
+	unsigned	bus_num;
+	unsigned	num_chipselect;
+};
+
+struct ath79_spi_controller_data {
+	unsigned	gpio;
+};
+
+#endif /* _ATH79_SPI_PLATFORM_H */
diff --git a/arch/mips/include/asm/mach-ath79/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ath79/cpu-feature-overrides.h
new file mode 100644
index 0000000..4476fa0
--- /dev/null
+++ b/arch/mips/include/asm/mach-ath79/cpu-feature-overrides.h
@@ -0,0 +1,56 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X specific CPU feature overrides
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This file was derived from: include/asm-mips/cpu-features.h
+ *	Copyright (C) 2003, 2004 Ralf Baechle
+ *	Copyright (C) 2004 Maciej W. Rozycki
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+#ifndef __ASM_MACH_ATH79_CPU_FEATURE_OVERRIDES_H
+#define __ASM_MACH_ATH79_CPU_FEATURE_OVERRIDES_H
+
+#define cpu_has_tlb		1
+#define cpu_has_4kex		1
+#define cpu_has_3k_cache	0
+#define cpu_has_4k_cache	1
+#define cpu_has_tx39_cache	0
+#define cpu_has_sb1_cache	0
+#define cpu_has_fpu		0
+#define cpu_has_32fpr		0
+#define cpu_has_counter		1
+#define cpu_has_watch		1
+#define cpu_has_divec		1
+
+#define cpu_has_prefetch	1
+#define cpu_has_ejtag		1
+#define cpu_has_llsc		1
+
+#define cpu_has_mips16		1
+#define cpu_has_mdmx		0
+#define cpu_has_mips3d		0
+#define cpu_has_smartmips	0
+
+#define cpu_has_mips32r1	1
+#define cpu_has_mips32r2	1
+#define cpu_has_mips64r1	0
+#define cpu_has_mips64r2	0
+
+#define cpu_has_dsp		0
+#define cpu_has_mipsmt		0
+
+#define cpu_has_64bits		0
+#define cpu_has_64bit_zero_reg	0
+#define cpu_has_64bit_gp_regs	0
+#define cpu_has_64bit_addresses	0
+
+#define cpu_dcache_line_size()	32
+#define cpu_icache_line_size()	32
+
+#endif /* __ASM_MACH_ATH79_CPU_FEATURE_OVERRIDES_H */
diff --git a/arch/mips/include/asm/mach-ath79/gpio.h b/arch/mips/include/asm/mach-ath79/gpio.h
new file mode 100644
index 0000000..60dcb62
--- /dev/null
+++ b/arch/mips/include/asm/mach-ath79/gpio.h
@@ -0,0 +1,26 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X GPIO API definitions
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#ifndef __ASM_MACH_ATH79_GPIO_H
+#define __ASM_MACH_ATH79_GPIO_H
+
+#define ARCH_NR_GPIOS	64
+#include <asm-generic/gpio.h>
+
+int gpio_to_irq(unsigned gpio);
+int irq_to_gpio(unsigned irq);
+int gpio_get_value(unsigned gpio);
+void gpio_set_value(unsigned gpio, int value);
+
+#define gpio_cansleep	__gpio_cansleep
+
+#endif /* __ASM_MACH_ATH79_GPIO_H */
diff --git a/arch/mips/include/asm/mach-ath79/irq.h b/arch/mips/include/asm/mach-ath79/irq.h
new file mode 100644
index 0000000..189bc6e
--- /dev/null
+++ b/arch/mips/include/asm/mach-ath79/irq.h
@@ -0,0 +1,36 @@
+/*
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+#ifndef __ASM_MACH_ATH79_IRQ_H
+#define __ASM_MACH_ATH79_IRQ_H
+
+#define MIPS_CPU_IRQ_BASE	0
+#define NR_IRQS			16
+
+#define ATH79_MISC_IRQ_BASE	8
+#define ATH79_MISC_IRQ_COUNT	8
+
+#define ATH79_CPU_IRQ_IP2	(MIPS_CPU_IRQ_BASE + 2)
+#define ATH79_CPU_IRQ_USB	(MIPS_CPU_IRQ_BASE + 3)
+#define ATH79_CPU_IRQ_GE0	(MIPS_CPU_IRQ_BASE + 4)
+#define ATH79_CPU_IRQ_GE1	(MIPS_CPU_IRQ_BASE + 5)
+#define ATH79_CPU_IRQ_MISC	(MIPS_CPU_IRQ_BASE + 6)
+#define ATH79_CPU_IRQ_TIMER	(MIPS_CPU_IRQ_BASE + 7)
+
+#define ATH79_MISC_IRQ_TIMER	(ATH79_MISC_IRQ_BASE + 0)
+#define ATH79_MISC_IRQ_ERROR	(ATH79_MISC_IRQ_BASE + 1)
+#define ATH79_MISC_IRQ_GPIO	(ATH79_MISC_IRQ_BASE + 2)
+#define ATH79_MISC_IRQ_UART	(ATH79_MISC_IRQ_BASE + 3)
+#define ATH79_MISC_IRQ_WDOG	(ATH79_MISC_IRQ_BASE + 4)
+#define ATH79_MISC_IRQ_PERFC	(ATH79_MISC_IRQ_BASE + 5)
+#define ATH79_MISC_IRQ_OHCI	(ATH79_MISC_IRQ_BASE + 6)
+#define ATH79_MISC_IRQ_DMA	(ATH79_MISC_IRQ_BASE + 7)
+
+#include_next <irq.h>
+
+#endif /* __ASM_MACH_ATH79_IRQ_H */
diff --git a/arch/mips/include/asm/mach-ath79/kernel-entry-init.h b/arch/mips/include/asm/mach-ath79/kernel-entry-init.h
new file mode 100644
index 0000000..d8d046b
--- /dev/null
+++ b/arch/mips/include/asm/mach-ath79/kernel-entry-init.h
@@ -0,0 +1,32 @@
+/*
+ *  Atheros AR71XX/AR724X/AR913X specific kernel entry setup
+ *
+ *  Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+#ifndef __ASM_MACH_ATH79_KERNEL_ENTRY_H
+#define __ASM_MACH_ATH79_KERNEL_ENTRY_H
+
+	/*
+	 * Some bootloaders set the 'Kseg0 coherency algorithm' to
+	 * 'Cacheable, noncoherent, write-through, no write allocate'
+	 * and this cause performance issues. Let's go and change it to
+	 * 'Cacheable, noncoherent, write-back, write allocate'
+	 */
+	.macro	kernel_entry_setup
+	mfc0	t0, CP0_CONFIG
+	li	t1, ~CONF_CM_CMASK
+	and	t0, t1
+	ori	t0, CONF_CM_CACHABLE_NONCOHERENT
+	mtc0	t0, CP0_CONFIG
+	nop
+	.endm
+
+	.macro	smp_slave_setup
+	.endm
+
+#endif /* __ASM_MACH_ATH79_KERNEL_ENTRY_H */
diff --git a/arch/mips/include/asm/mach-ath79/war.h b/arch/mips/include/asm/mach-ath79/war.h
new file mode 100644
index 0000000..323d9f1
--- /dev/null
+++ b/arch/mips/include/asm/mach-ath79/war.h
@@ -0,0 +1,25 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@linux-mips.org>
+ */
+#ifndef __ASM_MACH_ATH79_WAR_H
+#define __ASM_MACH_ATH79_WAR_H
+
+#define R4600_V1_INDEX_ICACHEOP_WAR	0
+#define R4600_V1_HIT_CACHEOP_WAR	0
+#define R4600_V2_HIT_CACHEOP_WAR	0
+#define R5432_CP0_INTERRUPT_WAR		0
+#define BCM1250_M3_WAR			0
+#define SIBYTE_1956_WAR			0
+#define MIPS4K_ICACHE_REFILL_WAR	0
+#define MIPS_CACHE_SYNC_WAR		0
+#define TX49XX_ICACHE_INDEX_INV_WAR	0
+#define RM9000_CDEX_SMP_WAR		0
+#define ICACHE_REFILLS_WORKAROUND_WAR	0
+#define R10000_LLSC_WAR			0
+#define MIPS34K_MISSED_ITLB_WAR		0
+
+#endif /* __ASM_MACH_ATH79_WAR_H */
diff --git a/arch/mips/include/asm/mips_machine.h b/arch/mips/include/asm/mips_machine.h
new file mode 100644
index 0000000..363bb35
--- /dev/null
+++ b/arch/mips/include/asm/mips_machine.h
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#ifndef __ASM_MIPS_MACHINE_H
+#define __ASM_MIPS_MACHINE_H
+
+#include <linux/init.h>
+#include <linux/stddef.h>
+
+#include <asm/bootinfo.h>
+
+struct mips_machine {
+	unsigned long		mach_type;
+	const char		*mach_id;
+	const char		*mach_name;
+	void			(*mach_setup)(void);
+};
+
+#define MIPS_MACHINE(_type, _id, _name, _setup)			\
+static const char machine_name_##_type[] __initconst		\
+			__aligned(1) = _name;			\
+static const char machine_id_##_type[] __initconst		\
+			__aligned(1) = _id;			\
+static struct mips_machine machine_##_type			\
+		__used __section(.mips.machines.init) =		\
+{								\
+	.mach_type	= _type,				\
+	.mach_id	= machine_id_##_type,			\
+	.mach_name	= machine_name_##_type,			\
+	.mach_setup	= _setup,				\
+};
+
+extern long __mips_machines_start;
+extern long __mips_machines_end;
+
+#ifdef CONFIG_MIPS_MACHINE
+int  mips_machtype_setup(char *id) __init;
+void mips_machine_setup(void) __init;
+void mips_set_machine_name(const char *name) __init;
+char *mips_get_machine_name(void);
+#else
+static inline int mips_machtype_setup(char *id) { return 1; }
+static inline void mips_machine_setup(void) { }
+static inline void mips_set_machine_name(const char *name) { }
+static inline char *mips_get_machine_name(void) { return NULL; }
+#endif /* CONFIG_MIPS_MACHINE */
+
+#endif /* __ASM_MIPS_MACHINE_H */
diff --git a/arch/mips/include/asm/mmu_context.h b/arch/mips/include/asm/mmu_context.h
index d959273..73c0d45 100644
--- a/arch/mips/include/asm/mmu_context.h
+++ b/arch/mips/include/asm/mmu_context.h
@@ -29,13 +29,7 @@
 #define TLBMISS_HANDLER_SETUP_PGD(pgd)				\
 	tlbmiss_handler_setup_pgd((unsigned long)(pgd))
 
-static inline void tlbmiss_handler_setup_pgd(unsigned long pgd)
-{
-	/* Check for swapper_pg_dir and convert to physical address. */
-	if ((pgd & CKSEG3) == CKSEG0)
-		pgd = CPHYSADDR(pgd);
-	write_c0_context(pgd << 11);
-}
+extern void tlbmiss_handler_setup_pgd(unsigned long pgd);
 
 #define TLBMISS_HANDLER_SETUP()						\
 	do {								\
diff --git a/arch/mips/include/asm/uasm.h b/arch/mips/include/asm/uasm.h
index 892062d..dcbd4bb 100644
--- a/arch/mips/include/asm/uasm.h
+++ b/arch/mips/include/asm/uasm.h
@@ -115,7 +115,12 @@
 Ip_u3u1u2(_xor);
 Ip_u2u1u3(_xori);
 Ip_u2u1msbu3(_dins);
+Ip_u2u1msbu3(_dinsm);
 Ip_u1(_syscall);
+Ip_u1u2s3(_bbit0);
+Ip_u1u2s3(_bbit1);
+Ip_u3u1u2(_lwx);
+Ip_u3u1u2(_ldx);
 
 /* Handle labels. */
 struct uasm_label {
@@ -153,6 +158,7 @@
 # define UASM_i_SUBU(buf, rs, rt, rd) uasm_i_dsubu(buf, rs, rt, rd)
 # define UASM_i_LL(buf, rs, rt, off) uasm_i_lld(buf, rs, rt, off)
 # define UASM_i_SC(buf, rs, rt, off) uasm_i_scd(buf, rs, rt, off)
+# define UASM_i_LWX(buf, rs, rt, rd) uasm_i_ldx(buf, rs, rt, rd)
 #else
 # define UASM_i_LW(buf, rs, rt, off) uasm_i_lw(buf, rs, rt, off)
 # define UASM_i_SW(buf, rs, rt, off) uasm_i_sw(buf, rs, rt, off)
@@ -167,6 +173,7 @@
 # define UASM_i_SUBU(buf, rs, rt, rd) uasm_i_subu(buf, rs, rt, rd)
 # define UASM_i_LL(buf, rs, rt, off) uasm_i_ll(buf, rs, rt, off)
 # define UASM_i_SC(buf, rs, rt, off) uasm_i_sc(buf, rs, rt, off)
+# define UASM_i_LWX(buf, rs, rt, rd) uasm_i_lwx(buf, rs, rt, rd)
 #endif
 
 #define uasm_i_b(buf, off) uasm_i_beq(buf, 0, 0, off)
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 22b2e0e..cedee2b 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -95,6 +95,7 @@
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o
 obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
 obj-$(CONFIG_SPINLOCK_TEST)	+= spinlock_test.o
+obj-$(CONFIG_MIPS_MACHINE)	+= mips_machine.o
 
 obj-$(CONFIG_OF)		+= prom.o
 
@@ -106,4 +107,6 @@
 
 obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
 
+obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o
+
 CPPFLAGS_vmlinux.lds		:= $(KBUILD_CFLAGS)
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index 68dae7b..f65d4c8 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -739,6 +739,8 @@
 	    && cpu_has_tlb)
 		c->tlbsize += (config4 & MIPS_CONF4_MMUSIZEEXT) * 0x40;
 
+	c->kscratch_mask = (config4 >> 16) & 0xff;
+
 	return config4 & MIPS_CONF_M;
 }
 
diff --git a/arch/mips/kernel/jump_label.c b/arch/mips/kernel/jump_label.c
new file mode 100644
index 0000000..6001610
--- /dev/null
+++ b/arch/mips/kernel/jump_label.c
@@ -0,0 +1,54 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 2010 Cavium Networks, Inc.
+ */
+
+#include <linux/jump_label.h>
+#include <linux/kernel.h>
+#include <linux/memory.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/cpu.h>
+
+#include <asm/cacheflush.h>
+#include <asm/inst.h>
+
+#ifdef HAVE_JUMP_LABEL
+
+#define J_RANGE_MASK ((1ul << 28) - 1)
+
+void arch_jump_label_transform(struct jump_entry *e,
+			       enum jump_label_type type)
+{
+	union mips_instruction insn;
+	union mips_instruction *insn_p =
+		(union mips_instruction *)(unsigned long)e->code;
+
+	/* Jump only works within a 256MB aligned region. */
+	BUG_ON((e->target & ~J_RANGE_MASK) != (e->code & ~J_RANGE_MASK));
+
+	/* Target must have 4 byte alignment. */
+	BUG_ON((e->target & 3) != 0);
+
+	if (type == JUMP_LABEL_ENABLE) {
+		insn.j_format.opcode = j_op;
+		insn.j_format.target = (e->target & J_RANGE_MASK) >> 2;
+	} else {
+		insn.word = 0; /* nop */
+	}
+
+	get_online_cpus();
+	mutex_lock(&text_mutex);
+	*insn_p = insn;
+
+	flush_icache_range((unsigned long)insn_p,
+			   (unsigned long)insn_p + sizeof(*insn_p));
+
+	mutex_unlock(&text_mutex);
+	put_online_cpus();
+}
+
+#endif /* HAVE_JUMP_LABEL */
diff --git a/arch/mips/kernel/mips_machine.c b/arch/mips/kernel/mips_machine.c
new file mode 100644
index 0000000..411a058
--- /dev/null
+++ b/arch/mips/kernel/mips_machine.c
@@ -0,0 +1,86 @@
+/*
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include <asm/mips_machine.h>
+
+static struct mips_machine *mips_machine __initdata;
+static char *mips_machine_name = "Unknown";
+
+#define for_each_machine(mach) \
+	for ((mach) = (struct mips_machine *)&__mips_machines_start; \
+	     (mach) && \
+	     (unsigned long)(mach) < (unsigned long)&__mips_machines_end; \
+	     (mach)++)
+
+__init void mips_set_machine_name(const char *name)
+{
+	char *p;
+
+	if (name == NULL)
+		return;
+
+	p = kstrdup(name, GFP_KERNEL);
+	if (!p)
+		pr_err("MIPS: no memory for machine_name\n");
+
+	mips_machine_name = p;
+}
+
+char *mips_get_machine_name(void)
+{
+	return mips_machine_name;
+}
+
+__init int mips_machtype_setup(char *id)
+{
+	struct mips_machine *mach;
+
+	for_each_machine(mach) {
+		if (mach->mach_id == NULL)
+			continue;
+
+		if (strcmp(mach->mach_id, id) == 0) {
+			mips_machtype = mach->mach_type;
+			return 0;
+		}
+	}
+
+	pr_err("MIPS: no machine found for id '%s', supported machines:\n", id);
+	pr_err("%-24s %s\n", "id", "name");
+	for_each_machine(mach)
+		pr_err("%-24s %s\n", mach->mach_id, mach->mach_name);
+
+	return 1;
+}
+
+__setup("machtype=", mips_machtype_setup);
+
+__init void mips_machine_setup(void)
+{
+	struct mips_machine *mach;
+
+	for_each_machine(mach) {
+		if (mips_machtype == mach->mach_type) {
+			mips_machine = mach;
+			break;
+		}
+	}
+
+	if (!mips_machine)
+		return;
+
+	mips_set_machine_name(mips_machine->mach_name);
+	pr_info("MIPS: machine is %s\n", mips_machine_name);
+
+	if (mips_machine->mach_setup)
+		mips_machine->mach_setup();
+}
diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c
index d87a72e..dd940b7 100644
--- a/arch/mips/kernel/module.c
+++ b/arch/mips/kernel/module.c
@@ -30,6 +30,8 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
+#include <linux/jump_label.h>
+
 #include <asm/pgtable.h>	/* MODULE_START */
 
 struct mips_hi16 {
@@ -382,6 +384,9 @@
 	const Elf_Shdr *s;
 	char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
 
+	/* Make jump label nops. */
+	jump_label_apply_nops(me);
+
 	INIT_LIST_HEAD(&me->arch.dbe_list);
 	for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
 		if (strcmp("__dbe_table", secstrings + s->sh_name) != 0)
diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c
index 26109c4..e309665 100644
--- a/arch/mips/kernel/proc.c
+++ b/arch/mips/kernel/proc.c
@@ -12,6 +12,7 @@
 #include <asm/cpu-features.h>
 #include <asm/mipsregs.h>
 #include <asm/processor.h>
+#include <asm/mips_machine.h>
 
 unsigned int vced_count, vcei_count;
 
@@ -31,8 +32,12 @@
 	/*
 	 * For the first processor also print the system type
 	 */
-	if (n == 0)
+	if (n == 0) {
 		seq_printf(m, "system type\t\t: %s\n", get_system_type());
+		if (mips_get_machine_name())
+			seq_printf(m, "machine\t\t\t: %s\n",
+				   mips_get_machine_name());
+	}
 
 	seq_printf(m, "processor\t\t: %ld\n", n);
 	sprintf(fmt, "cpu model\t\t: %%s V%%d.%%d%s\n",
@@ -69,6 +74,8 @@
 		);
 	seq_printf(m, "shadow register sets\t: %d\n",
 		       cpu_data[n].srsets);
+	seq_printf(m, "kscratch registers\t: %d\n",
+		   hweight8(cpu_data[n].kscratch_mask));
 	seq_printf(m, "core\t\t\t: %d\n", cpu_data[n].core);
 
 	sprintf(fmt, "VCE%%c exceptions\t\t: %s\n",
diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index acd3f2c..8ad1d56 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -70,7 +70,7 @@
  * mips_io_port_base is the begin of the address space to which x86 style
  * I/O ports are mapped.
  */
-const unsigned long mips_io_port_base __read_mostly = -1;
+const unsigned long mips_io_port_base = -1;
 EXPORT_SYMBOL(mips_io_port_base);
 
 static struct resource code_resource = { .name = "Kernel code", };
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index e971043..71350f7 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -1592,7 +1592,6 @@
 #endif /* CONFIG_MIPS_MT_SMTC */
 
 	cpu_data[cpu].asid_cache = ASID_FIRST_VERSION;
-	TLBMISS_HANDLER_SETUP();
 
 	atomic_inc(&init_mm.mm_count);
 	current->active_mm = &init_mm;
@@ -1614,6 +1613,7 @@
 		write_c0_wired(0);
 	}
 #endif /* CONFIG_MIPS_MT_SMTC */
+	TLBMISS_HANDLER_SETUP();
 }
 
 /* Install CPU exception handler */
diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S
index f25df73..570607b 100644
--- a/arch/mips/kernel/vmlinux.lds.S
+++ b/arch/mips/kernel/vmlinux.lds.S
@@ -98,6 +98,13 @@
 	INIT_TEXT_SECTION(PAGE_SIZE)
 	INIT_DATA_SECTION(16)
 
+	. = ALIGN(4);
+	.mips.machines.init : AT(ADDR(.mips.machines.init) - LOAD_OFFSET) {
+		__mips_machines_start = .;
+		*(.mips.machines.init)
+		__mips_machines_end = .;
+	}
+
 	/* .exit.text is discarded at runtime, not link time, to deal with
 	 * references from .rodata
 	 */
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 93816f3..083d341 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -26,8 +26,10 @@
 #include <linux/smp.h>
 #include <linux/string.h>
 #include <linux/init.h>
+#include <linux/cache.h>
 
-#include <asm/mmu_context.h>
+#include <asm/cacheflush.h>
+#include <asm/pgtable.h>
 #include <asm/war.h>
 #include <asm/uasm.h>
 
@@ -63,6 +65,52 @@
 	return R10000_LLSC_WAR;
 }
 
+static int use_bbit_insns(void)
+{
+	switch (current_cpu_type()) {
+	case CPU_CAVIUM_OCTEON:
+	case CPU_CAVIUM_OCTEON_PLUS:
+	case CPU_CAVIUM_OCTEON2:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static int use_lwx_insns(void)
+{
+	switch (current_cpu_type()) {
+	case CPU_CAVIUM_OCTEON2:
+		return 1;
+	default:
+		return 0;
+	}
+}
+#if defined(CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE) && \
+    CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0
+static bool scratchpad_available(void)
+{
+	return true;
+}
+static int scratchpad_offset(int i)
+{
+	/*
+	 * CVMSEG starts at address -32768 and extends for
+	 * CAVIUM_OCTEON_CVMSEG_SIZE 128 byte cache lines.
+	 */
+	i += 1; /* Kernel use starts at the top and works down. */
+	return CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE * 128 - (8 * i) - 32768;
+}
+#else
+static bool scratchpad_available(void)
+{
+	return false;
+}
+static int scratchpad_offset(int i)
+{
+	BUG();
+}
+#endif
 /*
  * Found by experiment: At least some revisions of the 4kc throw under
  * some circumstances a machine check exception, triggered by invalid
@@ -173,11 +221,41 @@
 static int check_for_high_segbits __cpuinitdata;
 #endif
 
+static int check_for_high_segbits __cpuinitdata;
+
+static unsigned int kscratch_used_mask __cpuinitdata;
+
+static int __cpuinit allocate_kscratch(void)
+{
+	int r;
+	unsigned int a = cpu_data[0].kscratch_mask & ~kscratch_used_mask;
+
+	r = ffs(a);
+
+	if (r == 0)
+		return -1;
+
+	r--; /* make it zero based */
+
+	kscratch_used_mask |= (1 << r);
+
+	return r;
+}
+
+static int scratch_reg __cpuinitdata;
+static int pgd_reg __cpuinitdata;
+enum vmalloc64_mode {not_refill, refill_scratch, refill_noscratch};
+
 #ifndef CONFIG_MIPS_PGD_C0_CONTEXT
+
 /*
  * CONFIG_MIPS_PGD_C0_CONTEXT implies 64 bit and lack of pgd_current,
  * we cannot do r3000 under these circumstances.
+ *
+ * Declare pgd_current here instead of including mmu_context.h to avoid type
+ * conflicts for tlbmiss_handler_setup_pgd
  */
+extern unsigned long pgd_current[];
 
 /*
  * The R3000 TLB handler is simple.
@@ -440,21 +518,43 @@
 static __cpuinit void build_restore_pagemask(u32 **p,
 					     struct uasm_reloc **r,
 					     unsigned int tmp,
-					     enum label_id lid)
+					     enum label_id lid,
+					     int restore_scratch)
 {
-	/* Reset default page size */
-	if (PM_DEFAULT_MASK >> 16) {
-		uasm_i_lui(p, tmp, PM_DEFAULT_MASK >> 16);
-		uasm_i_ori(p, tmp, tmp, PM_DEFAULT_MASK & 0xffff);
-		uasm_il_b(p, r, lid);
-		uasm_i_mtc0(p, tmp, C0_PAGEMASK);
-	} else if (PM_DEFAULT_MASK) {
-		uasm_i_ori(p, tmp, 0, PM_DEFAULT_MASK);
-		uasm_il_b(p, r, lid);
-		uasm_i_mtc0(p, tmp, C0_PAGEMASK);
+	if (restore_scratch) {
+		/* Reset default page size */
+		if (PM_DEFAULT_MASK >> 16) {
+			uasm_i_lui(p, tmp, PM_DEFAULT_MASK >> 16);
+			uasm_i_ori(p, tmp, tmp, PM_DEFAULT_MASK & 0xffff);
+			uasm_i_mtc0(p, tmp, C0_PAGEMASK);
+			uasm_il_b(p, r, lid);
+		} else if (PM_DEFAULT_MASK) {
+			uasm_i_ori(p, tmp, 0, PM_DEFAULT_MASK);
+			uasm_i_mtc0(p, tmp, C0_PAGEMASK);
+			uasm_il_b(p, r, lid);
+		} else {
+			uasm_i_mtc0(p, 0, C0_PAGEMASK);
+			uasm_il_b(p, r, lid);
+		}
+		if (scratch_reg > 0)
+			UASM_i_MFC0(p, 1, 31, scratch_reg);
+		else
+			UASM_i_LW(p, 1, scratchpad_offset(0), 0);
 	} else {
-		uasm_il_b(p, r, lid);
-		uasm_i_mtc0(p, 0, C0_PAGEMASK);
+		/* Reset default page size */
+		if (PM_DEFAULT_MASK >> 16) {
+			uasm_i_lui(p, tmp, PM_DEFAULT_MASK >> 16);
+			uasm_i_ori(p, tmp, tmp, PM_DEFAULT_MASK & 0xffff);
+			uasm_il_b(p, r, lid);
+			uasm_i_mtc0(p, tmp, C0_PAGEMASK);
+		} else if (PM_DEFAULT_MASK) {
+			uasm_i_ori(p, tmp, 0, PM_DEFAULT_MASK);
+			uasm_il_b(p, r, lid);
+			uasm_i_mtc0(p, tmp, C0_PAGEMASK);
+		} else {
+			uasm_il_b(p, r, lid);
+			uasm_i_mtc0(p, 0, C0_PAGEMASK);
+		}
 	}
 }
 
@@ -462,7 +562,8 @@
 						 struct uasm_label **l,
 						 struct uasm_reloc **r,
 						 unsigned int tmp,
-						 enum tlb_write_entry wmode)
+						 enum tlb_write_entry wmode,
+						 int restore_scratch)
 {
 	/* Set huge page tlb entry size */
 	uasm_i_lui(p, tmp, PM_HUGE_MASK >> 16);
@@ -471,7 +572,7 @@
 
 	build_tlb_write_entry(p, l, r, wmode);
 
-	build_restore_pagemask(p, r, tmp, label_leave);
+	build_restore_pagemask(p, r, tmp, label_leave, restore_scratch);
 }
 
 /*
@@ -482,8 +583,12 @@
 		unsigned int pmd, int lid)
 {
 	UASM_i_LW(p, tmp, 0, pmd);
-	uasm_i_andi(p, tmp, tmp, _PAGE_HUGE);
-	uasm_il_bnez(p, r, tmp, lid);
+	if (use_bbit_insns()) {
+		uasm_il_bbit1(p, r, tmp, ilog2(_PAGE_HUGE), lid);
+	} else {
+		uasm_i_andi(p, tmp, tmp, _PAGE_HUGE);
+		uasm_il_bnez(p, r, tmp, lid);
+	}
 }
 
 static __cpuinit void build_huge_update_entries(u32 **p,
@@ -532,7 +637,7 @@
 	UASM_i_SW(p, pte, 0, ptr);
 #endif
 	build_huge_update_entries(p, pte, ptr);
-	build_huge_tlb_write_entry(p, l, r, pte, tlb_indexed);
+	build_huge_tlb_write_entry(p, l, r, pte, tlb_indexed, 0);
 }
 #endif /* CONFIG_HUGETLB_PAGE */
 
@@ -573,13 +678,22 @@
 	/* No uasm_i_nop needed here, since the next insn doesn't touch TMP. */
 
 #ifdef CONFIG_MIPS_PGD_C0_CONTEXT
-	/*
-	 * &pgd << 11 stored in CONTEXT [23..63].
-	 */
-	UASM_i_MFC0(p, ptr, C0_CONTEXT);
-	uasm_i_dins(p, ptr, 0, 0, 23); /* Clear lower 23 bits of context. */
-	uasm_i_ori(p, ptr, ptr, 0x540); /* 1 0  1 0 1  << 6  xkphys cached */
-	uasm_i_drotr(p, ptr, ptr, 11);
+	if (pgd_reg != -1) {
+		/* pgd is in pgd_reg */
+		UASM_i_MFC0(p, ptr, 31, pgd_reg);
+	} else {
+		/*
+		 * &pgd << 11 stored in CONTEXT [23..63].
+		 */
+		UASM_i_MFC0(p, ptr, C0_CONTEXT);
+
+		/* Clear lower 23 bits of context. */
+		uasm_i_dins(p, ptr, 0, 0, 23);
+
+		/* 1 0  1 0 1  << 6  xkphys cached */
+		uasm_i_ori(p, ptr, ptr, 0x540);
+		uasm_i_drotr(p, ptr, ptr, 11);
+	}
 #elif defined(CONFIG_SMP)
 # ifdef  CONFIG_MIPS_MT_SMTC
 	/*
@@ -620,7 +734,6 @@
 #endif
 }
 
-enum vmalloc64_mode {not_refill, refill};
 /*
  * BVADDR is the faulting address, PTR is scratch.
  * PTR will hold the pgd for vmalloc.
@@ -638,7 +751,7 @@
 
 	uasm_l_vmalloc(l, *p);
 
-	if (mode == refill && check_for_high_segbits) {
+	if (mode != not_refill && check_for_high_segbits) {
 		if (single_insn_swpd) {
 			uasm_il_bltz(p, r, bvaddr, label_vmalloc_done);
 			uasm_i_lui(p, ptr, uasm_rel_hi(swpd));
@@ -661,7 +774,7 @@
 				uasm_i_daddiu(p, ptr, ptr, uasm_rel_lo(swpd));
 		}
 	}
-	if (mode == refill && check_for_high_segbits) {
+	if (mode != not_refill && check_for_high_segbits) {
 		uasm_l_large_segbits_fault(l, *p);
 		/*
 		 * We get here if we are an xsseg address, or if we are
@@ -677,7 +790,15 @@
 		 */
 		UASM_i_LA(p, ptr, (unsigned long)tlb_do_page_fault_0);
 		uasm_i_jr(p, ptr);
-		uasm_i_nop(p);
+
+		if (mode == refill_scratch) {
+			if (scratch_reg > 0)
+				UASM_i_MFC0(p, 1, 31, scratch_reg);
+			else
+				UASM_i_LW(p, 1, scratchpad_offset(0), 0);
+		} else {
+			uasm_i_nop(p);
+		}
 	}
 }
 
@@ -834,6 +955,185 @@
 #endif
 }
 
+struct mips_huge_tlb_info {
+	int huge_pte;
+	int restore_scratch;
+};
+
+static struct mips_huge_tlb_info __cpuinit
+build_fast_tlb_refill_handler (u32 **p, struct uasm_label **l,
+			       struct uasm_reloc **r, unsigned int tmp,
+			       unsigned int ptr, int c0_scratch)
+{
+	struct mips_huge_tlb_info rv;
+	unsigned int even, odd;
+	int vmalloc_branch_delay_filled = 0;
+	const int scratch = 1; /* Our extra working register */
+
+	rv.huge_pte = scratch;
+	rv.restore_scratch = 0;
+
+	if (check_for_high_segbits) {
+		UASM_i_MFC0(p, tmp, C0_BADVADDR);
+
+		if (pgd_reg != -1)
+			UASM_i_MFC0(p, ptr, 31, pgd_reg);
+		else
+			UASM_i_MFC0(p, ptr, C0_CONTEXT);
+
+		if (c0_scratch >= 0)
+			UASM_i_MTC0(p, scratch, 31, c0_scratch);
+		else
+			UASM_i_SW(p, scratch, scratchpad_offset(0), 0);
+
+		uasm_i_dsrl_safe(p, scratch, tmp,
+				 PGDIR_SHIFT + PGD_ORDER + PAGE_SHIFT - 3);
+		uasm_il_bnez(p, r, scratch, label_vmalloc);
+
+		if (pgd_reg == -1) {
+			vmalloc_branch_delay_filled = 1;
+			/* Clear lower 23 bits of context. */
+			uasm_i_dins(p, ptr, 0, 0, 23);
+		}
+	} else {
+		if (pgd_reg != -1)
+			UASM_i_MFC0(p, ptr, 31, pgd_reg);
+		else
+			UASM_i_MFC0(p, ptr, C0_CONTEXT);
+
+		UASM_i_MFC0(p, tmp, C0_BADVADDR);
+
+		if (c0_scratch >= 0)
+			UASM_i_MTC0(p, scratch, 31, c0_scratch);
+		else
+			UASM_i_SW(p, scratch, scratchpad_offset(0), 0);
+
+		if (pgd_reg == -1)
+			/* Clear lower 23 bits of context. */
+			uasm_i_dins(p, ptr, 0, 0, 23);
+
+		uasm_il_bltz(p, r, tmp, label_vmalloc);
+	}
+
+	if (pgd_reg == -1) {
+		vmalloc_branch_delay_filled = 1;
+		/* 1 0  1 0 1  << 6  xkphys cached */
+		uasm_i_ori(p, ptr, ptr, 0x540);
+		uasm_i_drotr(p, ptr, ptr, 11);
+	}
+
+#ifdef __PAGETABLE_PMD_FOLDED
+#define LOC_PTEP scratch
+#else
+#define LOC_PTEP ptr
+#endif
+
+	if (!vmalloc_branch_delay_filled)
+		/* get pgd offset in bytes */
+		uasm_i_dsrl_safe(p, scratch, tmp, PGDIR_SHIFT - 3);
+
+	uasm_l_vmalloc_done(l, *p);
+
+	/*
+	 *                         tmp          ptr
+	 * fall-through case =   badvaddr  *pgd_current
+	 * vmalloc case      =   badvaddr  swapper_pg_dir
+	 */
+
+	if (vmalloc_branch_delay_filled)
+		/* get pgd offset in bytes */
+		uasm_i_dsrl_safe(p, scratch, tmp, PGDIR_SHIFT - 3);
+
+#ifdef __PAGETABLE_PMD_FOLDED
+	GET_CONTEXT(p, tmp); /* get context reg */
+#endif
+	uasm_i_andi(p, scratch, scratch, (PTRS_PER_PGD - 1) << 3);
+
+	if (use_lwx_insns()) {
+		UASM_i_LWX(p, LOC_PTEP, scratch, ptr);
+	} else {
+		uasm_i_daddu(p, ptr, ptr, scratch); /* add in pgd offset */
+		uasm_i_ld(p, LOC_PTEP, 0, ptr); /* get pmd pointer */
+	}
+
+#ifndef __PAGETABLE_PMD_FOLDED
+	/* get pmd offset in bytes */
+	uasm_i_dsrl_safe(p, scratch, tmp, PMD_SHIFT - 3);
+	uasm_i_andi(p, scratch, scratch, (PTRS_PER_PMD - 1) << 3);
+	GET_CONTEXT(p, tmp); /* get context reg */
+
+	if (use_lwx_insns()) {
+		UASM_i_LWX(p, scratch, scratch, ptr);
+	} else {
+		uasm_i_daddu(p, ptr, ptr, scratch); /* add in pmd offset */
+		UASM_i_LW(p, scratch, 0, ptr);
+	}
+#endif
+	/* Adjust the context during the load latency. */
+	build_adjust_context(p, tmp);
+
+#ifdef CONFIG_HUGETLB_PAGE
+	uasm_il_bbit1(p, r, scratch, ilog2(_PAGE_HUGE), label_tlb_huge_update);
+	/*
+	 * The in the LWX case we don't want to do the load in the
+	 * delay slot.  It cannot issue in the same cycle and may be
+	 * speculative and unneeded.
+	 */
+	if (use_lwx_insns())
+		uasm_i_nop(p);
+#endif /* CONFIG_HUGETLB_PAGE */
+
+
+	/* build_update_entries */
+	if (use_lwx_insns()) {
+		even = ptr;
+		odd = tmp;
+		UASM_i_LWX(p, even, scratch, tmp);
+		UASM_i_ADDIU(p, tmp, tmp, sizeof(pte_t));
+		UASM_i_LWX(p, odd, scratch, tmp);
+	} else {
+		UASM_i_ADDU(p, ptr, scratch, tmp); /* add in offset */
+		even = tmp;
+		odd = ptr;
+		UASM_i_LW(p, even, 0, ptr); /* get even pte */
+		UASM_i_LW(p, odd, sizeof(pte_t), ptr); /* get odd pte */
+	}
+	if (kernel_uses_smartmips_rixi) {
+		uasm_i_dsrl_safe(p, even, even, ilog2(_PAGE_NO_EXEC));
+		uasm_i_dsrl_safe(p, odd, odd, ilog2(_PAGE_NO_EXEC));
+		uasm_i_drotr(p, even, even,
+			     ilog2(_PAGE_GLOBAL) - ilog2(_PAGE_NO_EXEC));
+		UASM_i_MTC0(p, even, C0_ENTRYLO0); /* load it */
+		uasm_i_drotr(p, odd, odd,
+			     ilog2(_PAGE_GLOBAL) - ilog2(_PAGE_NO_EXEC));
+	} else {
+		uasm_i_dsrl_safe(p, even, even, ilog2(_PAGE_GLOBAL));
+		UASM_i_MTC0(p, even, C0_ENTRYLO0); /* load it */
+		uasm_i_dsrl_safe(p, odd, odd, ilog2(_PAGE_GLOBAL));
+	}
+	UASM_i_MTC0(p, odd, C0_ENTRYLO1); /* load it */
+
+	if (c0_scratch >= 0) {
+		UASM_i_MFC0(p, scratch, 31, c0_scratch);
+		build_tlb_write_entry(p, l, r, tlb_random);
+		uasm_l_leave(l, *p);
+		rv.restore_scratch = 1;
+	} else if (PAGE_SHIFT == 14 || PAGE_SHIFT == 13)  {
+		build_tlb_write_entry(p, l, r, tlb_random);
+		uasm_l_leave(l, *p);
+		UASM_i_LW(p, scratch, scratchpad_offset(0), 0);
+	} else {
+		UASM_i_LW(p, scratch, scratchpad_offset(0), 0);
+		build_tlb_write_entry(p, l, r, tlb_random);
+		uasm_l_leave(l, *p);
+		rv.restore_scratch = 1;
+	}
+
+	uasm_i_eret(p); /* return from trap */
+
+	return rv;
+}
+
 /*
  * For a 64-bit kernel, we are using the 64-bit XTLB refill exception
  * because EXL == 0.  If we wrap, we can also use the 32 instruction
@@ -849,54 +1149,67 @@
 	struct uasm_reloc *r = relocs;
 	u32 *f;
 	unsigned int final_len;
+	struct mips_huge_tlb_info htlb_info;
+	enum vmalloc64_mode vmalloc_mode;
 
 	memset(tlb_handler, 0, sizeof(tlb_handler));
 	memset(labels, 0, sizeof(labels));
 	memset(relocs, 0, sizeof(relocs));
 	memset(final_handler, 0, sizeof(final_handler));
 
-	/*
-	 * create the plain linear handler
-	 */
-	if (bcm1250_m3_war()) {
-		unsigned int segbits = 44;
+	if (scratch_reg == 0)
+		scratch_reg = allocate_kscratch();
 
-		uasm_i_dmfc0(&p, K0, C0_BADVADDR);
-		uasm_i_dmfc0(&p, K1, C0_ENTRYHI);
-		uasm_i_xor(&p, K0, K0, K1);
-		uasm_i_dsrl_safe(&p, K1, K0, 62);
-		uasm_i_dsrl_safe(&p, K0, K0, 12 + 1);
-		uasm_i_dsll_safe(&p, K0, K0, 64 + 12 + 1 - segbits);
-		uasm_i_or(&p, K0, K0, K1);
-		uasm_il_bnez(&p, &r, K0, label_leave);
-		/* No need for uasm_i_nop */
-	}
+	if ((scratch_reg > 0 || scratchpad_available()) && use_bbit_insns()) {
+		htlb_info = build_fast_tlb_refill_handler(&p, &l, &r, K0, K1,
+							  scratch_reg);
+		vmalloc_mode = refill_scratch;
+	} else {
+		htlb_info.huge_pte = K0;
+		htlb_info.restore_scratch = 0;
+		vmalloc_mode = refill_noscratch;
+		/*
+		 * create the plain linear handler
+		 */
+		if (bcm1250_m3_war()) {
+			unsigned int segbits = 44;
+
+			uasm_i_dmfc0(&p, K0, C0_BADVADDR);
+			uasm_i_dmfc0(&p, K1, C0_ENTRYHI);
+			uasm_i_xor(&p, K0, K0, K1);
+			uasm_i_dsrl_safe(&p, K1, K0, 62);
+			uasm_i_dsrl_safe(&p, K0, K0, 12 + 1);
+			uasm_i_dsll_safe(&p, K0, K0, 64 + 12 + 1 - segbits);
+			uasm_i_or(&p, K0, K0, K1);
+			uasm_il_bnez(&p, &r, K0, label_leave);
+			/* No need for uasm_i_nop */
+		}
 
 #ifdef CONFIG_64BIT
-	build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */
+		build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */
 #else
-	build_get_pgde32(&p, K0, K1); /* get pgd in K1 */
+		build_get_pgde32(&p, K0, K1); /* get pgd in K1 */
 #endif
 
 #ifdef CONFIG_HUGETLB_PAGE
-	build_is_huge_pte(&p, &r, K0, K1, label_tlb_huge_update);
+		build_is_huge_pte(&p, &r, K0, K1, label_tlb_huge_update);
 #endif
 
-	build_get_ptep(&p, K0, K1);
-	build_update_entries(&p, K0, K1);
-	build_tlb_write_entry(&p, &l, &r, tlb_random);
-	uasm_l_leave(&l, p);
-	uasm_i_eret(&p); /* return from trap */
-
+		build_get_ptep(&p, K0, K1);
+		build_update_entries(&p, K0, K1);
+		build_tlb_write_entry(&p, &l, &r, tlb_random);
+		uasm_l_leave(&l, p);
+		uasm_i_eret(&p); /* return from trap */
+	}
 #ifdef CONFIG_HUGETLB_PAGE
 	uasm_l_tlb_huge_update(&l, p);
-	UASM_i_LW(&p, K0, 0, K1);
-	build_huge_update_entries(&p, K0, K1);
-	build_huge_tlb_write_entry(&p, &l, &r, K0, tlb_random);
+	build_huge_update_entries(&p, htlb_info.huge_pte, K1);
+	build_huge_tlb_write_entry(&p, &l, &r, K0, tlb_random,
+				   htlb_info.restore_scratch);
 #endif
 
 #ifdef CONFIG_64BIT
-	build_get_pgd_vmalloc64(&p, &l, &r, K0, K1, refill);
+	build_get_pgd_vmalloc64(&p, &l, &r, K0, K1, vmalloc_mode);
 #endif
 
 	/*
@@ -1014,6 +1327,55 @@
 u32 handle_tlbl[FASTPATH_SIZE] __cacheline_aligned;
 u32 handle_tlbs[FASTPATH_SIZE] __cacheline_aligned;
 u32 handle_tlbm[FASTPATH_SIZE] __cacheline_aligned;
+#ifdef CONFIG_MIPS_PGD_C0_CONTEXT
+u32 tlbmiss_handler_setup_pgd[16] __cacheline_aligned;
+
+static void __cpuinit build_r4000_setup_pgd(void)
+{
+	const int a0 = 4;
+	const int a1 = 5;
+	u32 *p = tlbmiss_handler_setup_pgd;
+	struct uasm_label *l = labels;
+	struct uasm_reloc *r = relocs;
+
+	memset(tlbmiss_handler_setup_pgd, 0, sizeof(tlbmiss_handler_setup_pgd));
+	memset(labels, 0, sizeof(labels));
+	memset(relocs, 0, sizeof(relocs));
+
+	pgd_reg = allocate_kscratch();
+
+	if (pgd_reg == -1) {
+		/* PGD << 11 in c0_Context */
+		/*
+		 * If it is a ckseg0 address, convert to a physical
+		 * address.  Shifting right by 29 and adding 4 will
+		 * result in zero for these addresses.
+		 *
+		 */
+		UASM_i_SRA(&p, a1, a0, 29);
+		UASM_i_ADDIU(&p, a1, a1, 4);
+		uasm_il_bnez(&p, &r, a1, label_tlbl_goaround1);
+		uasm_i_nop(&p);
+		uasm_i_dinsm(&p, a0, 0, 29, 64 - 29);
+		uasm_l_tlbl_goaround1(&l, p);
+		UASM_i_SLL(&p, a0, a0, 11);
+		uasm_i_jr(&p, 31);
+		UASM_i_MTC0(&p, a0, C0_CONTEXT);
+	} else {
+		/* PGD in c0_KScratch */
+		uasm_i_jr(&p, 31);
+		UASM_i_MTC0(&p, a0, 31, pgd_reg);
+	}
+	if (p - tlbmiss_handler_setup_pgd > ARRAY_SIZE(tlbmiss_handler_setup_pgd))
+		panic("tlbmiss_handler_setup_pgd space exceeded");
+	uasm_resolve_relocs(relocs, labels);
+	pr_debug("Wrote tlbmiss_handler_setup_pgd (%u instructions).\n",
+		 (unsigned int)(p - tlbmiss_handler_setup_pgd));
+
+	dump_handler(tlbmiss_handler_setup_pgd,
+		     ARRAY_SIZE(tlbmiss_handler_setup_pgd));
+}
+#endif
 
 static void __cpuinit
 iPTE_LW(u32 **p, unsigned int pte, unsigned int ptr)
@@ -1100,14 +1462,20 @@
 		  unsigned int pte, unsigned int ptr, enum label_id lid)
 {
 	if (kernel_uses_smartmips_rixi) {
-		uasm_i_andi(p, pte, pte, _PAGE_PRESENT);
-		uasm_il_beqz(p, r, pte, lid);
+		if (use_bbit_insns()) {
+			uasm_il_bbit0(p, r, pte, ilog2(_PAGE_PRESENT), lid);
+			uasm_i_nop(p);
+		} else {
+			uasm_i_andi(p, pte, pte, _PAGE_PRESENT);
+			uasm_il_beqz(p, r, pte, lid);
+			iPTE_LW(p, pte, ptr);
+		}
 	} else {
 		uasm_i_andi(p, pte, pte, _PAGE_PRESENT | _PAGE_READ);
 		uasm_i_xori(p, pte, pte, _PAGE_PRESENT | _PAGE_READ);
 		uasm_il_bnez(p, r, pte, lid);
+		iPTE_LW(p, pte, ptr);
 	}
-	iPTE_LW(p, pte, ptr);
 }
 
 /* Make PTE valid, store result in PTR. */
@@ -1128,10 +1496,17 @@
 build_pte_writable(u32 **p, struct uasm_reloc **r,
 		   unsigned int pte, unsigned int ptr, enum label_id lid)
 {
-	uasm_i_andi(p, pte, pte, _PAGE_PRESENT | _PAGE_WRITE);
-	uasm_i_xori(p, pte, pte, _PAGE_PRESENT | _PAGE_WRITE);
-	uasm_il_bnez(p, r, pte, lid);
-	iPTE_LW(p, pte, ptr);
+	if (use_bbit_insns()) {
+		uasm_il_bbit0(p, r, pte, ilog2(_PAGE_PRESENT), lid);
+		uasm_i_nop(p);
+		uasm_il_bbit0(p, r, pte, ilog2(_PAGE_WRITE), lid);
+		uasm_i_nop(p);
+	} else {
+		uasm_i_andi(p, pte, pte, _PAGE_PRESENT | _PAGE_WRITE);
+		uasm_i_xori(p, pte, pte, _PAGE_PRESENT | _PAGE_WRITE);
+		uasm_il_bnez(p, r, pte, lid);
+		iPTE_LW(p, pte, ptr);
+	}
 }
 
 /* Make PTE writable, update software status bits as well, then store
@@ -1155,12 +1530,19 @@
 build_pte_modifiable(u32 **p, struct uasm_reloc **r,
 		     unsigned int pte, unsigned int ptr, enum label_id lid)
 {
-	uasm_i_andi(p, pte, pte, _PAGE_WRITE);
-	uasm_il_beqz(p, r, pte, lid);
-	iPTE_LW(p, pte, ptr);
+	if (use_bbit_insns()) {
+		uasm_il_bbit0(p, r, pte, ilog2(_PAGE_WRITE), lid);
+		uasm_i_nop(p);
+	} else {
+		uasm_i_andi(p, pte, pte, _PAGE_WRITE);
+		uasm_il_beqz(p, r, pte, lid);
+		iPTE_LW(p, pte, ptr);
+	}
 }
 
 #ifndef CONFIG_MIPS_PGD_C0_CONTEXT
+
+
 /*
  * R3000 style TLB load/store/modify handlers.
  */
@@ -1402,14 +1784,23 @@
 		 * If the page is not _PAGE_VALID, RI or XI could not
 		 * have triggered it.  Skip the expensive test..
 		 */
-		uasm_i_andi(&p, K0, K0, _PAGE_VALID);
-		uasm_il_beqz(&p, &r, K0, label_tlbl_goaround1);
+		if (use_bbit_insns()) {
+			uasm_il_bbit0(&p, &r, K0, ilog2(_PAGE_VALID),
+				      label_tlbl_goaround1);
+		} else {
+			uasm_i_andi(&p, K0, K0, _PAGE_VALID);
+			uasm_il_beqz(&p, &r, K0, label_tlbl_goaround1);
+		}
 		uasm_i_nop(&p);
 
 		uasm_i_tlbr(&p);
 		/* Examine  entrylo 0 or 1 based on ptr. */
-		uasm_i_andi(&p, K0, K1, sizeof(pte_t));
-		uasm_i_beqz(&p, K0, 8);
+		if (use_bbit_insns()) {
+			uasm_i_bbit0(&p, K1, ilog2(sizeof(pte_t)), 8);
+		} else {
+			uasm_i_andi(&p, K0, K1, sizeof(pte_t));
+			uasm_i_beqz(&p, K0, 8);
+		}
 
 		UASM_i_MFC0(&p, K0, C0_ENTRYLO0); /* load it in the delay slot*/
 		UASM_i_MFC0(&p, K0, C0_ENTRYLO1); /* load it if ptr is odd */
@@ -1417,12 +1808,18 @@
 		 * If the entryLo (now in K0) is valid (bit 1), RI or
 		 * XI must have triggered it.
 		 */
-		uasm_i_andi(&p, K0, K0, 2);
-		uasm_il_bnez(&p, &r, K0, label_nopage_tlbl);
-
-		uasm_l_tlbl_goaround1(&l, p);
-		/* Reload the PTE value */
-		iPTE_LW(&p, K0, K1);
+		if (use_bbit_insns()) {
+			uasm_il_bbit1(&p, &r, K0, 1, label_nopage_tlbl);
+			/* Reload the PTE value */
+			iPTE_LW(&p, K0, K1);
+			uasm_l_tlbl_goaround1(&l, p);
+		} else {
+			uasm_i_andi(&p, K0, K0, 2);
+			uasm_il_bnez(&p, &r, K0, label_nopage_tlbl);
+			uasm_l_tlbl_goaround1(&l, p);
+			/* Reload the PTE value */
+			iPTE_LW(&p, K0, K1);
+		}
 	}
 	build_make_valid(&p, &r, K0, K1);
 	build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1);
@@ -1442,23 +1839,35 @@
 		 * If the page is not _PAGE_VALID, RI or XI could not
 		 * have triggered it.  Skip the expensive test..
 		 */
-		uasm_i_andi(&p, K0, K0, _PAGE_VALID);
-		uasm_il_beqz(&p, &r, K0, label_tlbl_goaround2);
+		if (use_bbit_insns()) {
+			uasm_il_bbit0(&p, &r, K0, ilog2(_PAGE_VALID),
+				      label_tlbl_goaround2);
+		} else {
+			uasm_i_andi(&p, K0, K0, _PAGE_VALID);
+			uasm_il_beqz(&p, &r, K0, label_tlbl_goaround2);
+		}
 		uasm_i_nop(&p);
 
 		uasm_i_tlbr(&p);
 		/* Examine  entrylo 0 or 1 based on ptr. */
-		uasm_i_andi(&p, K0, K1, sizeof(pte_t));
-		uasm_i_beqz(&p, K0, 8);
-
+		if (use_bbit_insns()) {
+			uasm_i_bbit0(&p, K1, ilog2(sizeof(pte_t)), 8);
+		} else {
+			uasm_i_andi(&p, K0, K1, sizeof(pte_t));
+			uasm_i_beqz(&p, K0, 8);
+		}
 		UASM_i_MFC0(&p, K0, C0_ENTRYLO0); /* load it in the delay slot*/
 		UASM_i_MFC0(&p, K0, C0_ENTRYLO1); /* load it if ptr is odd */
 		/*
 		 * If the entryLo (now in K0) is valid (bit 1), RI or
 		 * XI must have triggered it.
 		 */
-		uasm_i_andi(&p, K0, K0, 2);
-		uasm_il_beqz(&p, &r, K0, label_tlbl_goaround2);
+		if (use_bbit_insns()) {
+			uasm_il_bbit0(&p, &r, K0, 1, label_tlbl_goaround2);
+		} else {
+			uasm_i_andi(&p, K0, K0, 2);
+			uasm_il_beqz(&p, &r, K0, label_tlbl_goaround2);
+		}
 		/* Reload the PTE value */
 		iPTE_LW(&p, K0, K1);
 
@@ -1466,7 +1875,7 @@
 		 * We clobbered C0_PAGEMASK, restore it.  On the other branch
 		 * it is restored in build_huge_tlb_write_entry.
 		 */
-		build_restore_pagemask(&p, &r, K0, label_nopage_tlbl);
+		build_restore_pagemask(&p, &r, K0, label_nopage_tlbl, 0);
 
 		uasm_l_tlbl_goaround2(&l, p);
 	}
@@ -1623,13 +2032,16 @@
 		break;
 
 	default:
-		build_r4000_tlb_refill_handler();
 		if (!run_once) {
+#ifdef CONFIG_MIPS_PGD_C0_CONTEXT
+			build_r4000_setup_pgd();
+#endif
 			build_r4000_tlb_load_handler();
 			build_r4000_tlb_store_handler();
 			build_r4000_tlb_modify_handler();
 			run_once++;
 		}
+		build_r4000_tlb_refill_handler();
 	}
 }
 
@@ -1641,4 +2053,8 @@
 			   (unsigned long)handle_tlbs + sizeof(handle_tlbs));
 	local_flush_icache_range((unsigned long)handle_tlbm,
 			   (unsigned long)handle_tlbm + sizeof(handle_tlbm));
+#ifdef CONFIG_MIPS_PGD_C0_CONTEXT
+	local_flush_icache_range((unsigned long)tlbmiss_handler_setup_pgd,
+			   (unsigned long)tlbmiss_handler_setup_pgd + sizeof(handle_tlbm));
+#endif
 }
diff --git a/arch/mips/mm/uasm.c b/arch/mips/mm/uasm.c
index 23afdeb..5fa1851 100644
--- a/arch/mips/mm/uasm.c
+++ b/arch/mips/mm/uasm.c
@@ -68,7 +68,8 @@
 	insn_pref, insn_rfe, insn_sc, insn_scd, insn_sd, insn_sll,
 	insn_sra, insn_srl, insn_rotr, insn_subu, insn_sw, insn_tlbp,
 	insn_tlbr, insn_tlbwi, insn_tlbwr, insn_xor, insn_xori,
-	insn_dins, insn_syscall, insn_bbit0, insn_bbit1
+	insn_dins, insn_dinsm, insn_syscall, insn_bbit0, insn_bbit1,
+	insn_lwx, insn_ldx
 };
 
 struct insn {
@@ -142,9 +143,12 @@
 	{ insn_xor,  M(spec_op, 0, 0, 0, 0, xor_op),  RS | RT | RD },
 	{ insn_xori,  M(xori_op, 0, 0, 0, 0, 0),  RS | RT | UIMM },
 	{ insn_dins, M(spec3_op, 0, 0, 0, 0, dins_op), RS | RT | RD | RE },
+	{ insn_dinsm, M(spec3_op, 0, 0, 0, 0, dinsm_op), RS | RT | RD | RE },
 	{ insn_syscall, M(spec_op, 0, 0, 0, 0, syscall_op), SCIMM},
 	{ insn_bbit0, M(lwc2_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
 	{ insn_bbit1, M(swc2_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
+	{ insn_lwx, M(spec3_op, 0, 0, 0, lwx_op, lx_op), RS | RT | RD },
+	{ insn_ldx, M(spec3_op, 0, 0, 0, ldx_op, lx_op), RS | RT | RD },
 	{ insn_invalid, 0, 0 }
 };
 
@@ -152,91 +156,83 @@
 
 static inline __uasminit u32 build_rs(u32 arg)
 {
-	if (arg & ~RS_MASK)
-		printk(KERN_WARNING "Micro-assembler field overflow\n");
+	WARN(arg & ~RS_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 
 	return (arg & RS_MASK) << RS_SH;
 }
 
 static inline __uasminit u32 build_rt(u32 arg)
 {
-	if (arg & ~RT_MASK)
-		printk(KERN_WARNING "Micro-assembler field overflow\n");
+	WARN(arg & ~RT_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 
 	return (arg & RT_MASK) << RT_SH;
 }
 
 static inline __uasminit u32 build_rd(u32 arg)
 {
-	if (arg & ~RD_MASK)
-		printk(KERN_WARNING "Micro-assembler field overflow\n");
+	WARN(arg & ~RD_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 
 	return (arg & RD_MASK) << RD_SH;
 }
 
 static inline __uasminit u32 build_re(u32 arg)
 {
-	if (arg & ~RE_MASK)
-		printk(KERN_WARNING "Micro-assembler field overflow\n");
+	WARN(arg & ~RE_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 
 	return (arg & RE_MASK) << RE_SH;
 }
 
 static inline __uasminit u32 build_simm(s32 arg)
 {
-	if (arg > 0x7fff || arg < -0x8000)
-		printk(KERN_WARNING "Micro-assembler field overflow\n");
+	WARN(arg > 0x7fff || arg < -0x8000,
+	     KERN_WARNING "Micro-assembler field overflow\n");
 
 	return arg & 0xffff;
 }
 
 static inline __uasminit u32 build_uimm(u32 arg)
 {
-	if (arg & ~IMM_MASK)
-		printk(KERN_WARNING "Micro-assembler field overflow\n");
+	WARN(arg & ~IMM_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 
 	return arg & IMM_MASK;
 }
 
 static inline __uasminit u32 build_bimm(s32 arg)
 {
-	if (arg > 0x1ffff || arg < -0x20000)
-		printk(KERN_WARNING "Micro-assembler field overflow\n");
+	WARN(arg > 0x1ffff || arg < -0x20000,
+	     KERN_WARNING "Micro-assembler field overflow\n");
 
-	if (arg & 0x3)
-		printk(KERN_WARNING "Invalid micro-assembler branch target\n");
+	WARN(arg & 0x3, KERN_WARNING "Invalid micro-assembler branch target\n");
 
 	return ((arg < 0) ? (1 << 15) : 0) | ((arg >> 2) & 0x7fff);
 }
 
 static inline __uasminit u32 build_jimm(u32 arg)
 {
-	if (arg & ~((JIMM_MASK) << 2))
-		printk(KERN_WARNING "Micro-assembler field overflow\n");
+	WARN(arg & ~(JIMM_MASK << 2),
+	     KERN_WARNING "Micro-assembler field overflow\n");
 
 	return (arg >> 2) & JIMM_MASK;
 }
 
 static inline __uasminit u32 build_scimm(u32 arg)
 {
-	if (arg & ~SCIMM_MASK)
-		printk(KERN_WARNING "Micro-assembler field overflow\n");
+	WARN(arg & ~SCIMM_MASK,
+	     KERN_WARNING "Micro-assembler field overflow\n");
 
 	return (arg & SCIMM_MASK) << SCIMM_SH;
 }
 
 static inline __uasminit u32 build_func(u32 arg)
 {
-	if (arg & ~FUNC_MASK)
-		printk(KERN_WARNING "Micro-assembler field overflow\n");
+	WARN(arg & ~FUNC_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 
 	return arg & FUNC_MASK;
 }
 
 static inline __uasminit u32 build_set(u32 arg)
 {
-	if (arg & ~SET_MASK)
-		printk(KERN_WARNING "Micro-assembler field overflow\n");
+	WARN(arg & ~SET_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 
 	return arg & SET_MASK;
 }
@@ -340,6 +336,13 @@
 }							\
 UASM_EXPORT_SYMBOL(uasm_i##op);
 
+#define I_u2u1msb32u3(op)				\
+Ip_u2u1msbu3(op)					\
+{							\
+	build_insn(buf, insn##op, b, a, c+d-33, c);	\
+}							\
+UASM_EXPORT_SYMBOL(uasm_i##op);
+
 #define I_u1u2(op)					\
 Ip_u1u2(op)						\
 {							\
@@ -422,9 +425,12 @@
 I_u3u1u2(_xor)
 I_u2u1u3(_xori)
 I_u2u1msbu3(_dins);
+I_u2u1msb32u3(_dinsm);
 I_u1(_syscall);
 I_u1u2s3(_bbit0);
 I_u1u2s3(_bbit1);
+I_u3u1u2(_lwx)
+I_u3u1u2(_ldx)
 
 #ifdef CONFIG_CPU_CAVIUM_OCTEON
 #include <asm/octeon/octeon.h>
diff --git a/arch/mips/sibyte/common/sb_tbprof.c b/arch/mips/sibyte/common/sb_tbprof.c
index 87ccdb4..48853ab 100644
--- a/arch/mips/sibyte/common/sb_tbprof.c
+++ b/arch/mips/sibyte/common/sb_tbprof.c
@@ -410,14 +410,13 @@
 		return -EBUSY;
 
 	memset(&sbp, 0, sizeof(struct sbprof_tb));
-	sbp.sbprof_tbbuf = vmalloc(MAX_TBSAMPLE_BYTES);
+	sbp.sbprof_tbbuf = vzalloc(MAX_TBSAMPLE_BYTES);
 	if (!sbp.sbprof_tbbuf) {
 		sbp.open = SB_CLOSED;
 		wmb();
 		return -ENOMEM;
 	}
 
-	memset(sbp.sbprof_tbbuf, 0, MAX_TBSAMPLE_BYTES);
 	init_waitqueue_head(&sbp.tb_sync);
 	init_waitqueue_head(&sbp.tb_read);
 	mutex_init(&sbp.lock);
diff --git a/arch/mips/txx9/generic/pci.c b/arch/mips/txx9/generic/pci.c
index 96e69a0..85a87de 100644
--- a/arch/mips/txx9/generic/pci.c
+++ b/arch/mips/txx9/generic/pci.c
@@ -213,11 +213,8 @@
 
 	pcic->mem_offset = 0;	/* busaddr == physaddr */
 
-	printk(KERN_INFO "PCI: IO 0x%08llx-0x%08llx MEM 0x%08llx-0x%08llx\n",
-	       (unsigned long long)pcic->mem_resource[1].start,
-	       (unsigned long long)pcic->mem_resource[1].end,
-	       (unsigned long long)pcic->mem_resource[0].start,
-	       (unsigned long long)pcic->mem_resource[0].end);
+	printk(KERN_INFO "PCI: IO %pR MEM %pR\n",
+	       &pcic->mem_resource[1], &pcic->mem_resource[0]);
 
 	/* register_pci_controller() will request MEM resource */
 	release_resource(&pcic->mem_resource[0]);
diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c
index df971fa..4896ed0 100644
--- a/arch/parisc/kernel/firmware.c
+++ b/arch/parisc/kernel/firmware.c
@@ -1126,15 +1126,13 @@
 	unsigned int i;
 	unsigned long flags;
 
-	for (i = 0; i < count && i < 79;) {
+	for (i = 0; i < count;) {
 		switch(str[i]) {
 		case '\n':
 			iodc_dbuf[i+0] = '\r';
 			iodc_dbuf[i+1] = '\n';
 			i += 2;
 			goto print;
-		case '\b':	/* BS */
-			i--; /* overwrite last */
 		default:
 			iodc_dbuf[i] = str[i];
 			i++;
@@ -1142,15 +1140,6 @@
 		}
 	}
 
-	/* if we're at the end of line, and not already inserting a newline,
-	 * insert one anyway. iodc console doesn't claim to support >79 char
-	 * lines. don't account for this in the return value.
-	 */
-	if (i == 79 && iodc_dbuf[i-1] != '\n') {
-		iodc_dbuf[i+0] = '\r';
-		iodc_dbuf[i+1] = '\n';
-	}
-
 print:
         spin_lock_irqsave(&pdc_lock, flags);
         real32_call(PAGE0->mem_cons.iodc_io,
diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c
index 48ff6dc..9974d21 100644
--- a/arch/x86/kernel/irq_32.c
+++ b/arch/x86/kernel/irq_32.c
@@ -129,8 +129,7 @@
 	irqctx = page_address(alloc_pages_node(cpu_to_node(cpu),
 					       THREAD_FLAGS,
 					       THREAD_ORDER));
-	irqctx->tinfo.task		= NULL;
-	irqctx->tinfo.exec_domain	= NULL;
+	memset(&irqctx->tinfo, 0, sizeof(struct thread_info));
 	irqctx->tinfo.cpu		= cpu;
 	irqctx->tinfo.preempt_count	= HARDIRQ_OFFSET;
 	irqctx->tinfo.addr_limit	= MAKE_MM_SEG(0);
@@ -140,10 +139,8 @@
 	irqctx = page_address(alloc_pages_node(cpu_to_node(cpu),
 					       THREAD_FLAGS,
 					       THREAD_ORDER));
-	irqctx->tinfo.task		= NULL;
-	irqctx->tinfo.exec_domain	= NULL;
+	memset(&irqctx->tinfo, 0, sizeof(struct thread_info));
 	irqctx->tinfo.cpu		= cpu;
-	irqctx->tinfo.preempt_count	= 0;
 	irqctx->tinfo.addr_limit	= MAKE_MM_SEG(0);
 
 	per_cpu(softirq_ctx, cpu) = irqctx;
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index bf47007..b34ab80 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -34,11 +34,9 @@
 #ifdef CONFIG_X86_32
 OUTPUT_ARCH(i386)
 ENTRY(phys_startup_32)
-jiffies = jiffies_64;
 #else
 OUTPUT_ARCH(i386:x86-64)
 ENTRY(phys_startup_64)
-jiffies_64 = jiffies;
 #endif
 
 #if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)
@@ -142,6 +140,15 @@
 		CACHELINE_ALIGNED_DATA(L1_CACHE_BYTES)
 
 		DATA_DATA
+		/*
+		 * Workaround a binutils (2.20.51.0.12 to 2.21.51.0.3) bug.
+		 * This makes jiffies relocatable in such binutils
+		 */
+#ifdef CONFIG_X86_32
+		jiffies = jiffies_64;
+#else
+		jiffies_64 = jiffies;
+#endif
 		CONSTRUCTORS
 
 		/* rarely changed data like cpu maps */
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 13bfa9d..bb233a9 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -53,6 +53,14 @@
 
 comment "SPI Master Controller Drivers"
 
+config SPI_ATH79
+	tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver"
+	depends on ATH79 && GENERIC_GPIO
+	select SPI_BITBANG
+	help
+	  This enables support for the SPI controller present on the
+	  Atheros AR71XX/AR724X/AR913X SoCs.
+
 config SPI_ATMEL
 	tristate "Atmel SPI Controller"
 	depends on (ARCH_AT91 || AVR32)
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 3a42463..86d1b5f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -10,6 +10,7 @@
 
 # SPI master controller drivers (bus)
 obj-$(CONFIG_SPI_ATMEL)			+= atmel_spi.o
+obj-$(CONFIG_SPI_ATH79)			+= ath79_spi.o
 obj-$(CONFIG_SPI_BFIN)			+= spi_bfin5xx.o
 obj-$(CONFIG_SPI_BITBANG)		+= spi_bitbang.o
 obj-$(CONFIG_SPI_AU1550)		+= au1550_spi.o
diff --git a/drivers/spi/ath79_spi.c b/drivers/spi/ath79_spi.c
new file mode 100644
index 0000000..fcff810
--- /dev/null
+++ b/drivers/spi/ath79_spi.c
@@ -0,0 +1,292 @@
+/*
+ * SPI controller driver for the Atheros AR71XX/AR724X/AR913X SoCs
+ *
+ * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This driver has been based on the spi-gpio.c:
+ *	Copyright (C) 2006,2008 David Brownell
+ *
+ * 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/delay.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include <asm/mach-ath79/ath79_spi_platform.h>
+
+#define DRV_NAME	"ath79-spi"
+
+struct ath79_spi {
+	struct spi_bitbang	bitbang;
+	u32			ioc_base;
+	u32			reg_ctrl;
+	void __iomem		*base;
+};
+
+static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned reg)
+{
+	return ioread32(sp->base + reg);
+}
+
+static inline void ath79_spi_wr(struct ath79_spi *sp, unsigned reg, u32 val)
+{
+	iowrite32(val, sp->base + reg);
+}
+
+static inline struct ath79_spi *ath79_spidev_to_sp(struct spi_device *spi)
+{
+	return spi_master_get_devdata(spi->master);
+}
+
+static void ath79_spi_chipselect(struct spi_device *spi, int is_active)
+{
+	struct ath79_spi *sp = ath79_spidev_to_sp(spi);
+	int cs_high = (spi->mode & SPI_CS_HIGH) ? is_active : !is_active;
+
+	if (is_active) {
+		/* set initial clock polarity */
+		if (spi->mode & SPI_CPOL)
+			sp->ioc_base |= AR71XX_SPI_IOC_CLK;
+		else
+			sp->ioc_base &= ~AR71XX_SPI_IOC_CLK;
+
+		ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
+	}
+
+	if (spi->chip_select) {
+		struct ath79_spi_controller_data *cdata = spi->controller_data;
+
+		/* SPI is normally active-low */
+		gpio_set_value(cdata->gpio, cs_high);
+	} else {
+		if (cs_high)
+			sp->ioc_base |= AR71XX_SPI_IOC_CS0;
+		else
+			sp->ioc_base &= ~AR71XX_SPI_IOC_CS0;
+
+		ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
+	}
+
+}
+
+static int ath79_spi_setup_cs(struct spi_device *spi)
+{
+	struct ath79_spi *sp = ath79_spidev_to_sp(spi);
+	struct ath79_spi_controller_data *cdata;
+
+	cdata = spi->controller_data;
+	if (spi->chip_select && !cdata)
+		return -EINVAL;
+
+	/* enable GPIO mode */
+	ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
+
+	/* save CTRL register */
+	sp->reg_ctrl = ath79_spi_rr(sp, AR71XX_SPI_REG_CTRL);
+	sp->ioc_base = ath79_spi_rr(sp, AR71XX_SPI_REG_IOC);
+
+	/* TODO: setup speed? */
+	ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, 0x43);
+
+	if (spi->chip_select) {
+		int status = 0;
+
+		status = gpio_request(cdata->gpio, dev_name(&spi->dev));
+		if (status)
+			return status;
+
+		status = gpio_direction_output(cdata->gpio,
+					       spi->mode & SPI_CS_HIGH);
+		if (status) {
+			gpio_free(cdata->gpio);
+			return status;
+		}
+	} else {
+		if (spi->mode & SPI_CS_HIGH)
+			sp->ioc_base |= AR71XX_SPI_IOC_CS0;
+		else
+			sp->ioc_base &= ~AR71XX_SPI_IOC_CS0;
+		ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
+	}
+
+	return 0;
+}
+
+static void ath79_spi_cleanup_cs(struct spi_device *spi)
+{
+	struct ath79_spi *sp = ath79_spidev_to_sp(spi);
+
+	if (spi->chip_select) {
+		struct ath79_spi_controller_data *cdata = spi->controller_data;
+		gpio_free(cdata->gpio);
+	}
+
+	/* restore CTRL register */
+	ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, sp->reg_ctrl);
+	/* disable GPIO mode */
+	ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0);
+}
+
+static int ath79_spi_setup(struct spi_device *spi)
+{
+	int status = 0;
+
+	if (spi->bits_per_word > 32)
+		return -EINVAL;
+
+	if (!spi->controller_state) {
+		status = ath79_spi_setup_cs(spi);
+		if (status)
+			return status;
+	}
+
+	status = spi_bitbang_setup(spi);
+	if (status && !spi->controller_state)
+		ath79_spi_cleanup_cs(spi);
+
+	return status;
+}
+
+static void ath79_spi_cleanup(struct spi_device *spi)
+{
+	ath79_spi_cleanup_cs(spi);
+	spi_bitbang_cleanup(spi);
+}
+
+static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs,
+			       u32 word, u8 bits)
+{
+	struct ath79_spi *sp = ath79_spidev_to_sp(spi);
+	u32 ioc = sp->ioc_base;
+
+	/* clock starts at inactive polarity */
+	for (word <<= (32 - bits); likely(bits); bits--) {
+		u32 out;
+
+		if (word & (1 << 31))
+			out = ioc | AR71XX_SPI_IOC_DO;
+		else
+			out = ioc & ~AR71XX_SPI_IOC_DO;
+
+		/* setup MSB (to slave) on trailing edge */
+		ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out);
+		ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out | AR71XX_SPI_IOC_CLK);
+
+		word <<= 1;
+	}
+
+	return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS);
+}
+
+static __devinit int ath79_spi_probe(struct platform_device *pdev)
+{
+	struct spi_master *master;
+	struct ath79_spi *sp;
+	struct ath79_spi_platform_data *pdata;
+	struct resource	*r;
+	int ret;
+
+	master = spi_alloc_master(&pdev->dev, sizeof(*sp));
+	if (master == NULL) {
+		dev_err(&pdev->dev, "failed to allocate spi master\n");
+		return -ENOMEM;
+	}
+
+	sp = spi_master_get_devdata(master);
+	platform_set_drvdata(pdev, sp);
+
+	pdata = pdev->dev.platform_data;
+
+	master->setup = ath79_spi_setup;
+	master->cleanup = ath79_spi_cleanup;
+	if (pdata) {
+		master->bus_num = pdata->bus_num;
+		master->num_chipselect = pdata->num_chipselect;
+	} else {
+		master->bus_num = -1;
+		master->num_chipselect = 1;
+	}
+
+	sp->bitbang.master = spi_master_get(master);
+	sp->bitbang.chipselect = ath79_spi_chipselect;
+	sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0;
+	sp->bitbang.setup_transfer = spi_bitbang_setup_transfer;
+	sp->bitbang.flags = SPI_CS_HIGH;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		ret = -ENOENT;
+		goto err_put_master;
+	}
+
+	sp->base = ioremap(r->start, r->end - r->start + 1);
+	if (!sp->base) {
+		ret = -ENXIO;
+		goto err_put_master;
+	}
+
+	ret = spi_bitbang_start(&sp->bitbang);
+	if (ret)
+		goto err_unmap;
+
+	return 0;
+
+err_unmap:
+	iounmap(sp->base);
+err_put_master:
+	platform_set_drvdata(pdev, NULL);
+	spi_master_put(sp->bitbang.master);
+
+	return ret;
+}
+
+static __devexit int ath79_spi_remove(struct platform_device *pdev)
+{
+	struct ath79_spi *sp = platform_get_drvdata(pdev);
+
+	spi_bitbang_stop(&sp->bitbang);
+	iounmap(sp->base);
+	platform_set_drvdata(pdev, NULL);
+	spi_master_put(sp->bitbang.master);
+
+	return 0;
+}
+
+static struct platform_driver ath79_spi_driver = {
+	.probe		= ath79_spi_probe,
+	.remove		= __devexit_p(ath79_spi_remove),
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static __init int ath79_spi_init(void)
+{
+	return platform_driver_register(&ath79_spi_driver);
+}
+module_init(ath79_spi_init);
+
+static __exit void ath79_spi_exit(void)
+{
+	platform_driver_unregister(&ath79_spi_driver);
+}
+module_exit(ath79_spi_exit);
+
+MODULE_DESCRIPTION("SPI controller driver for Atheros AR71XX/AR724X/AR913X");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index 05ebe84..84522c7 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -2228,14 +2228,11 @@
 	unsigned long flags;
 	int ctxn, err;
 
-	if (!task && cpu != -1) {
+	if (!task) {
 		/* Must be root to operate on a CPU event: */
 		if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN))
 			return ERR_PTR(-EACCES);
 
-		if (cpu < 0 || cpu >= nr_cpumask_bits)
-			return ERR_PTR(-EINVAL);
-
 		/*
 		 * We could be clever and allow to attach a event to an
 		 * offline CPU and activate it when the CPU comes up, but
@@ -5541,6 +5538,11 @@
 	struct hw_perf_event *hwc;
 	long err;
 
+	if ((unsigned)cpu >= nr_cpu_ids) {
+		if (!task || cpu != -1)
+			return ERR_PTR(-EINVAL);
+	}
+
 	event = kzalloc(sizeof(*event), GFP_KERNEL);
 	if (!event)
 		return ERR_PTR(-ENOMEM);
@@ -5589,7 +5591,7 @@
 
 	if (!overflow_handler && parent_event)
 		overflow_handler = parent_event->overflow_handler;
-	
+
 	event->overflow_handler	= overflow_handler;
 
 	if (attr->disabled)
@@ -6494,7 +6496,6 @@
 
 	raw_spin_lock_irqsave(&parent_ctx->lock, flags);
 	parent_ctx->rotate_disable = 0;
-	raw_spin_unlock_irqrestore(&parent_ctx->lock, flags);
 
 	child_ctx = child->perf_event_ctxp[ctxn];
 
@@ -6502,12 +6503,11 @@
 		/*
 		 * Mark the child context as a clone of the parent
 		 * context, or of whatever the parent is a clone of.
-		 * Note that if the parent is a clone, it could get
-		 * uncloned at any point, but that doesn't matter
-		 * because the list of events and the generation
-		 * count can't have changed since we took the mutex.
+		 *
+		 * Note that if the parent is a clone, the holding of
+		 * parent_ctx->lock avoids it from being uncloned.
 		 */
-		cloned_ctx = rcu_dereference(parent_ctx->parent_ctx);
+		cloned_ctx = parent_ctx->parent_ctx;
 		if (cloned_ctx) {
 			child_ctx->parent_ctx = cloned_ctx;
 			child_ctx->parent_gen = parent_ctx->parent_gen;
@@ -6518,6 +6518,7 @@
 		get_ctx(child_ctx->parent_ctx);
 	}
 
+	raw_spin_unlock_irqrestore(&parent_ctx->lock, flags);
 	mutex_unlock(&parent_ctx->mutex);
 
 	perf_unpin_context(parent_ctx);