]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/upstream-linus
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 18 Jan 2011 22:28:48 +0000 (14:28 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 18 Jan 2011 22:28:48 +0000 (14:28 -0800)
* 'upstream' of git://git.linux-mips.org/pub/scm/upstream-linus: (26 commits)
  MIPS: Malta: enable Cirrus FB console
  MIPS: add CONFIG_VIRTUALIZATION for virtio support
  MIPS: Implement __read_mostly
  MIPS: ath79: add common WMAC device for AR913X based boards
  MIPS: ath79: Add initial support for the Atheros AP81 reference board
  MIPS: ath79: add common SPI controller device
  SPI: Add SPI controller driver for the Atheros AR71XX/AR724X/AR913X SoCs
  MIPS: ath79: add common GPIO buttons device
  MIPS: ath79: add common watchdog device
  MIPS: ath79: add common GPIO LEDs device
  MIPS: ath79: add initial support for the Atheros PB44 reference board
  MIPS: ath79: utilize the MIPS multi-machine support
  MIPS: ath79: add GPIOLIB support
  MIPS: Add initial support for the Atheros AR71XX/AR724X/AR931X SoCs
  MIPS: jump label: Add MIPS support.
  MIPS: Use WARN() in uasm for better diagnostics.
  MIPS: Optimize TLB handlers for Octeon CPUs
  MIPS: Add LDX and LWX instructions to uasm.
  MIPS: Use BBIT instructions in TLB handlers
  MIPS: Declare uasm bbit0 and bbit1 functions.
  ...

58 files changed:
arch/mips/Kbuild.platforms
arch/mips/Kconfig
arch/mips/ath79/Kconfig [new file with mode: 0644]
arch/mips/ath79/Makefile [new file with mode: 0644]
arch/mips/ath79/Platform [new file with mode: 0644]
arch/mips/ath79/clock.c [new file with mode: 0644]
arch/mips/ath79/common.c [new file with mode: 0644]
arch/mips/ath79/common.h [new file with mode: 0644]
arch/mips/ath79/dev-ar913x-wmac.c [new file with mode: 0644]
arch/mips/ath79/dev-ar913x-wmac.h [new file with mode: 0644]
arch/mips/ath79/dev-common.c [new file with mode: 0644]
arch/mips/ath79/dev-common.h [new file with mode: 0644]
arch/mips/ath79/dev-gpio-buttons.c [new file with mode: 0644]
arch/mips/ath79/dev-gpio-buttons.h [new file with mode: 0644]
arch/mips/ath79/dev-leds-gpio.c [new file with mode: 0644]
arch/mips/ath79/dev-leds-gpio.h [new file with mode: 0644]
arch/mips/ath79/dev-spi.c [new file with mode: 0644]
arch/mips/ath79/dev-spi.h [new file with mode: 0644]
arch/mips/ath79/early_printk.c [new file with mode: 0644]
arch/mips/ath79/gpio.c [new file with mode: 0644]
arch/mips/ath79/irq.c [new file with mode: 0644]
arch/mips/ath79/mach-ap81.c [new file with mode: 0644]
arch/mips/ath79/mach-pb44.c [new file with mode: 0644]
arch/mips/ath79/machtypes.h [new file with mode: 0644]
arch/mips/ath79/prom.c [new file with mode: 0644]
arch/mips/ath79/setup.c [new file with mode: 0644]
arch/mips/configs/malta_defconfig
arch/mips/include/asm/cache.h
arch/mips/include/asm/cpu-info.h
arch/mips/include/asm/inst.h
arch/mips/include/asm/jump_label.h [new file with mode: 0644]
arch/mips/include/asm/mach-ath79/ar71xx_regs.h [new file with mode: 0644]
arch/mips/include/asm/mach-ath79/ath79.h [new file with mode: 0644]
arch/mips/include/asm/mach-ath79/ath79_spi_platform.h [new file with mode: 0644]
arch/mips/include/asm/mach-ath79/cpu-feature-overrides.h [new file with mode: 0644]
arch/mips/include/asm/mach-ath79/gpio.h [new file with mode: 0644]
arch/mips/include/asm/mach-ath79/irq.h [new file with mode: 0644]
arch/mips/include/asm/mach-ath79/kernel-entry-init.h [new file with mode: 0644]
arch/mips/include/asm/mach-ath79/war.h [new file with mode: 0644]
arch/mips/include/asm/mips_machine.h [new file with mode: 0644]
arch/mips/include/asm/mmu_context.h
arch/mips/include/asm/uasm.h
arch/mips/kernel/Makefile
arch/mips/kernel/cpu-probe.c
arch/mips/kernel/jump_label.c [new file with mode: 0644]
arch/mips/kernel/mips_machine.c [new file with mode: 0644]
arch/mips/kernel/module.c
arch/mips/kernel/proc.c
arch/mips/kernel/setup.c
arch/mips/kernel/traps.c
arch/mips/kernel/vmlinux.lds.S
arch/mips/mm/tlbex.c
arch/mips/mm/uasm.c
arch/mips/sibyte/common/sb_tbprof.c
arch/mips/txx9/generic/pci.c
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/ath79_spi.c [new file with mode: 0644]

index 78439b8a83c48418ce93e2eb4a0a0e065bca4efe..7ff9b54920419b99cbae1f74bac4c62fc3d1b73c 100644 (file)
@@ -2,6 +2,7 @@
 
 platforms += alchemy
 platforms += ar7
+platforms += ath79
 platforms += bcm47xx
 platforms += bcm63xx
 platforms += cavium-octeon
index f489ec30e071aa1932c43d4149a1130b23ae0349..548e6cc3bc28d59cf8eb594556f5000dfcf49ffe 100644 (file)
@@ -21,6 +21,7 @@ config MIPS
        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 @@ config AR7
          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 @@ config CAVIUM_OCTEON_REFERENCE_BOARD
 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 MIPS_DISABLE_OBSOLETE_IDE
 config SYNC_R4K
        bool
 
+config MIPS_MACHINE
+       def_bool n
+
 config NO_IOPORT
        def_bool n
 
@@ -2400,4 +2421,20 @@ source "security/Kconfig"
 
 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 (file)
index 0000000..b058282
--- /dev/null
@@ -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 (file)
index 0000000..c33d465
--- /dev/null
@@ -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 (file)
index 0000000..2bd6636
--- /dev/null
@@ -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 (file)
index 0000000..680bde9
--- /dev/null
@@ -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 (file)
index 0000000..58f60e7
--- /dev/null
@@ -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 (file)
index 0000000..561906c
--- /dev/null
@@ -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 (file)
index 0000000..48f425a
--- /dev/null
@@ -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 (file)
index 0000000..579d562
--- /dev/null
@@ -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 (file)
index 0000000..3b82e32
--- /dev/null
@@ -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 (file)
index 0000000..0f514e1
--- /dev/null
@@ -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 (file)
index 0000000..4b0168a
--- /dev/null
@@ -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 (file)
index 0000000..481847a
--- /dev/null
@@ -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 (file)
index 0000000..cdade68
--- /dev/null
@@ -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 (file)
index 0000000..6e5d885
--- /dev/null
@@ -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 (file)
index 0000000..aa30163
--- /dev/null
@@ -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 (file)
index 0000000..d732565
--- /dev/null
@@ -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 (file)
index 0000000..7499b0e
--- /dev/null
@@ -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 (file)
index 0000000..a0c426b
--- /dev/null
@@ -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 (file)
index 0000000..1bf7f71
--- /dev/null
@@ -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 (file)
index 0000000..eee4c12
--- /dev/null
@@ -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 (file)
index 0000000..ec7b7a1
--- /dev/null
@@ -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 (file)
index 0000000..3940fe4
--- /dev/null
@@ -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 (file)
index 0000000..e9cbd7c
--- /dev/null
@@ -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 (file)
index 0000000..159b42f
--- /dev/null
@@ -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);
index b455d0f36486a2accaa96d5ccc997d7d99b0b200..9d03b68aece83487912e44d22cab569b62195538 100644 (file)
@@ -369,7 +369,10 @@ CONFIG_VT_HW_CONSOLE_BINDING=y
 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
index 37f175c42bb5530c9c7c76655cb54e94722597fe..650ac9ba734c9bb254baec52f6d076b9ce53583f 100644 (file)
@@ -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 */
index b39def3f6e03bdd950544b05cd4f765fbeb2b2e2..c454550eb0c07195874926969578c1367a6bd19b 100644 (file)
@@ -78,6 +78,7 @@ struct cpuinfo_mips {
        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[];
index 444ff71aa0e8d2759b85fb56c6955806234ef4e0..7ebfc392e58d1d5cb7c8f2a686e25a9041f853b8 100644 (file)
@@ -72,6 +72,7 @@ enum spec2_op {
 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
@@ -178,6 +179,19 @@ enum mad_func {
        nmadd_fp_op     = 0x0c, nmsub_fp_op     = 0x0e
 };
 
+/*
+ * 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 :-(
  */
diff --git a/arch/mips/include/asm/jump_label.h b/arch/mips/include/asm/jump_label.h
new file mode 100644 (file)
index 0000000..7622ccf
--- /dev/null
@@ -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 (file)
index 0000000..cda1c80
--- /dev/null
@@ -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 (file)
index 0000000..6a9f168
--- /dev/null
@@ -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 (file)
index 0000000..aa2283e
--- /dev/null
@@ -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 (file)
index 0000000..4476fa0
--- /dev/null
@@ -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 (file)
index 0000000..60dcb62
--- /dev/null
@@ -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 (file)
index 0000000..189bc6e
--- /dev/null
@@ -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 (file)
index 0000000..d8d046b
--- /dev/null
@@ -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 (file)
index 0000000..323d9f1
--- /dev/null
@@ -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 (file)
index 0000000..363bb35
--- /dev/null
@@ -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 */
index d9592733a7ba4759dbebc01f82efaef4a783e1e5..73c0d45798dec6cf00fc99021fc4bc00664ccaad 100644 (file)
 #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 {                                                            \
index 892062d6d748f5a234a3da2d1ee98211f11c2d4d..dcbd4bb417ec094d931c8188b72daae1799779eb 100644 (file)
@@ -115,7 +115,12 @@ Ip_0(_tlbwr);
 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 @@ static inline void __uasminit uasm_l##lb(struct uasm_label **lab, u32 *addr) \
 # 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 @@ static inline void __uasminit uasm_l##lb(struct uasm_label **lab, u32 *addr) \
 # 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)
index 22b2e0e386170cd1fba74d7fa6bc06bd90d74226..cedee2bcbd182c8eab8ad59aadfc0d02e1fdb79d 100644 (file)
@@ -95,6 +95,7 @@ obj-$(CONFIG_GPIO_TXX9)               += gpio_txx9.o
 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_MIPS_CPUFREQ)  += cpufreq/
 
 obj-$(CONFIG_HW_PERF_EVENTS)   += perf_event.o
 
+obj-$(CONFIG_JUMP_LABEL)       += jump_label.o
+
 CPPFLAGS_vmlinux.lds           := $(KBUILD_CFLAGS)
index 68dae7b6b5db62a0a48a17e6fe339a2d56bd8bd8..f65d4c8c65a6a19ec4be040091861444a8ea78a3 100644 (file)
@@ -739,6 +739,8 @@ static inline unsigned int decode_config4(struct cpuinfo_mips *c)
            && 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 (file)
index 0000000..6001610
--- /dev/null
@@ -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 (file)
index 0000000..411a058
--- /dev/null
@@ -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();
+}
index d87a72e9fac714bd2d1f73b564426f7a77e2dcc5..dd940b70196387c5b9f39a026100e0054444a175 100644 (file)
@@ -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 @@ int module_finalize(const Elf_Ehdr *hdr,
        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)
index 26109c4d51704ae5ca9a1d83cacc4fe30b3c399d..e309665b6c81962a82cb247b76f7b0312b0ba172 100644 (file)
@@ -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 @@ static int show_cpuinfo(struct seq_file *m, void *v)
        /*
         * 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 @@ static int show_cpuinfo(struct seq_file *m, void *v)
                );
        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",
index acd3f2c49c069fa04af112d8c30dc008cf869351..8ad1d5679f1440ebe2393e1d25f1a3ab38c7a017 100644 (file)
@@ -70,7 +70,7 @@ static char __initdata builtin_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE;
  * 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", };
index e97104302541fd4111ae9ceca477e04cf2b5add7..71350f7f2d8857507cc8da104c3dcee9a6a0a072 100644 (file)
@@ -1592,7 +1592,6 @@ void __cpuinit per_cpu_trap_init(void)
 #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 @@ void __cpuinit per_cpu_trap_init(void)
                write_c0_wired(0);
        }
 #endif /* CONFIG_MIPS_MT_SMTC */
+       TLBMISS_HANDLER_SETUP();
 }
 
 /* Install CPU exception handler */
index f25df73db923a792b9debd05ba6184fca0030f41..570607b376b57d16696089773343f8a9f9e5f6e9 100644 (file)
@@ -98,6 +98,13 @@ SECTIONS
        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
         */
index 93816f3bca67f79b1f5b6aeb028487c6d06e67ec..083d3412d0bccc7744ec151cd493de614d0375b8 100644 (file)
 #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 @@ static inline int __maybe_unused r10000_llsc_war(void)
        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 struct uasm_reloc relocs[128] __cpuinitdata;
 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 __maybe_unused void build_convert_pte_to_entrylo(u32 **p,
 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 @@ static __cpuinit void build_huge_tlb_write_entry(u32 **p,
                                                 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 @@ static __cpuinit void build_huge_tlb_write_entry(u32 **p,
 
        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 @@ build_is_huge_pte(u32 **p, struct uasm_reloc **r, unsigned int tmp,
                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 @@ static __cpuinit void build_huge_handler_tail(u32 **p,
        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 @@ build_get_pmde64(u32 **p, struct uasm_label **l, struct uasm_reloc **r,
        /* 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 @@ build_get_pmde64(u32 **p, struct uasm_label **l, struct uasm_reloc **r,
 #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 @@ build_get_pgd_vmalloc64(u32 **p, struct uasm_label **l, struct uasm_reloc **r,
 
        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 @@ build_get_pgd_vmalloc64(u32 **p, struct uasm_label **l, struct uasm_reloc **r,
                                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 @@ build_get_pgd_vmalloc64(u32 **p, struct uasm_label **l, struct uasm_reloc **r,
                 */
                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 @@ static void __cpuinit build_update_entries(u32 **p, unsigned int tmp,
 #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 @@ static void __cpuinit build_r4000_tlb_refill_handler(void)
        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 @@ static void __cpuinit build_r4000_tlb_refill_handler(void)
 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 @@ build_pte_present(u32 **p, struct uasm_reloc **r,
                  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 @@ static void __cpuinit
 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 @@ static void __cpuinit
 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 @@ static void __cpuinit build_r4000_tlb_load_handler(void)
                 * 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 @@ static void __cpuinit build_r4000_tlb_load_handler(void)
                 * 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 @@ static void __cpuinit build_r4000_tlb_load_handler(void)
                 * 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 @@ static void __cpuinit build_r4000_tlb_load_handler(void)
                 * 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 @@ void __cpuinit build_tlb_refill_handler(void)
                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 @@ void __cpuinit flush_tlb_handlers(void)
                           (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
 }
index 23afdebc8e5cf2db134af1d6e9d0df3f0f2dea38..5fa185151fc8c29b121415db015b4bb878a5b7c1 100644 (file)
@@ -68,7 +68,8 @@ enum opcode {
        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 @@ static struct insn insn_table[] __uasminitdata = {
        { 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 struct insn insn_table[] __uasminitdata = {
 
 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 @@ Ip_u2u1msbu3(op)                                   \
 }                                                      \
 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_0(_tlbwr)
 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>
index 87ccdb4b5ac911a0448293bd8ee168c521fc6848..48853ab5bcf02331919b469b8d00ced02d8f06ed 100644 (file)
@@ -410,14 +410,13 @@ static int sbprof_tb_open(struct inode *inode, struct file *filp)
                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);
index 96e69a00ffc84019b740b51eb9f75f795d031dba..85a87de17eb46f1197f92771a76b7812282aa4ff 100644 (file)
@@ -213,11 +213,8 @@ txx9_alloc_pci_controller(struct pci_controller *pcic,
 
        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]);
index 13bfa9d480822243b6254ad6f8e4c8867e595d90..bb233a9cbad2fef09da95aef7225cad4e78c518d 100644 (file)
@@ -53,6 +53,14 @@ if SPI_MASTER
 
 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)
index 3a42463c92a4935c7b56019910c096c1780e587c..86d1b5f9bbd9a9b93c91db63ce7af659b5f6c74c 100644 (file)
@@ -10,6 +10,7 @@ obj-$(CONFIG_SPI_MASTER)              += spi.o
 
 # 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 (file)
index 0000000..fcff810
--- /dev/null
@@ -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);