MIPS: BCM63xx: Add support for the Broadcom BCM63xx family of SOCs.

Signed-off-by: Maxime Bizon <mbizon@freebox.fr>
Signed-off-by: Florian Fainelli <florian@openwrt.org>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/bcm63xx/Kconfig b/arch/mips/bcm63xx/Kconfig
new file mode 100644
index 0000000..fb177d6
--- /dev/null
+++ b/arch/mips/bcm63xx/Kconfig
@@ -0,0 +1,25 @@
+menu "CPU support"
+	depends on BCM63XX
+
+config BCM63XX_CPU_6338
+	bool "support 6338 CPU"
+	select HW_HAS_PCI
+	select USB_ARCH_HAS_OHCI
+	select USB_OHCI_BIG_ENDIAN_DESC
+	select USB_OHCI_BIG_ENDIAN_MMIO
+
+config BCM63XX_CPU_6345
+	bool "support 6345 CPU"
+	select USB_OHCI_BIG_ENDIAN_DESC
+	select USB_OHCI_BIG_ENDIAN_MMIO
+
+config BCM63XX_CPU_6348
+	bool "support 6348 CPU"
+	select HW_HAS_PCI
+
+config BCM63XX_CPU_6358
+	bool "support 6358 CPU"
+	select HW_HAS_PCI
+endmenu
+
+source "arch/mips/bcm63xx/boards/Kconfig"
diff --git a/arch/mips/bcm63xx/Makefile b/arch/mips/bcm63xx/Makefile
new file mode 100644
index 0000000..99bbc87
--- /dev/null
+++ b/arch/mips/bcm63xx/Makefile
@@ -0,0 +1,7 @@
+obj-y		+= clk.o cpu.o cs.o gpio.o irq.o prom.o setup.o timer.o \
+		   dev-dsp.o
+obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
+
+obj-y		+= boards/
+
+EXTRA_CFLAGS += -Werror
diff --git a/arch/mips/bcm63xx/boards/Kconfig b/arch/mips/bcm63xx/boards/Kconfig
new file mode 100644
index 0000000..c6aed33
--- /dev/null
+++ b/arch/mips/bcm63xx/boards/Kconfig
@@ -0,0 +1,11 @@
+choice
+	prompt "Board support"
+	depends on BCM63XX
+	default BOARD_BCM963XX
+
+config BOARD_BCM963XX
+       bool "Generic Broadcom 963xx boards"
+	select SSB
+       help
+
+endchoice
diff --git a/arch/mips/bcm63xx/boards/Makefile b/arch/mips/bcm63xx/boards/Makefile
new file mode 100644
index 0000000..e5cc86d
--- /dev/null
+++ b/arch/mips/bcm63xx/boards/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_BOARD_BCM963XX)		+= board_bcm963xx.o
+
+EXTRA_CFLAGS += -Werror
diff --git a/arch/mips/bcm63xx/boards/board_bcm963xx.c b/arch/mips/bcm63xx/boards/board_bcm963xx.c
new file mode 100644
index 0000000..fd77f54
--- /dev/null
+++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c
@@ -0,0 +1,837 @@
+/*
+ * 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) 2008 Maxime Bizon <mbizon@freebox.fr>
+ * Copyright (C) 2008 Florian Fainelli <florian@openwrt.org>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+#include <linux/ssb/ssb.h>
+#include <asm/addrspace.h>
+#include <bcm63xx_board.h>
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_board.h>
+#include <bcm63xx_dev_pci.h>
+#include <bcm63xx_dev_enet.h>
+#include <bcm63xx_dev_dsp.h>
+#include <board_bcm963xx.h>
+
+#define PFX	"board_bcm963xx: "
+
+static struct bcm963xx_nvram nvram;
+static unsigned int mac_addr_used;
+static struct board_info board;
+
+/*
+ * known 6338 boards
+ */
+#ifdef CONFIG_BCM63XX_CPU_6338
+static struct board_info __initdata board_96338gw = {
+	.name				= "96338GW",
+	.expected_cpu_id		= 0x6338,
+
+	.has_enet0			= 1,
+	.enet0 = {
+		.force_speed_100	= 1,
+		.force_duplex_full	= 1,
+	},
+
+	.has_ohci0			= 1,
+
+	.leds = {
+		{
+			.name		= "adsl",
+			.gpio		= 3,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ses",
+			.gpio		= 5,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp-fail",
+			.gpio		= 4,
+			.active_low	= 1,
+		},
+		{
+			.name		= "power",
+			.gpio		= 0,
+			.active_low	= 1,
+			.default_trigger = "default-on",
+		},
+		{
+			.name		= "stop",
+			.gpio		= 1,
+			.active_low	= 1,
+		}
+	},
+};
+
+static struct board_info __initdata board_96338w = {
+	.name				= "96338W",
+	.expected_cpu_id		= 0x6338,
+
+	.has_enet0			= 1,
+	.enet0 = {
+		.force_speed_100	= 1,
+		.force_duplex_full	= 1,
+	},
+
+	.leds = {
+		{
+			.name		= "adsl",
+			.gpio		= 3,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ses",
+			.gpio		= 5,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp-fail",
+			.gpio		= 4,
+			.active_low	= 1,
+		},
+		{
+			.name		= "power",
+			.gpio		= 0,
+			.active_low	= 1,
+			.default_trigger = "default-on",
+		},
+		{
+			.name		= "stop",
+			.gpio		= 1,
+			.active_low	= 1,
+		},
+	},
+};
+#endif
+
+/*
+ * known 6345 boards
+ */
+#ifdef CONFIG_BCM63XX_CPU_6345
+static struct board_info __initdata board_96345gw2 = {
+	.name				= "96345GW2",
+	.expected_cpu_id		= 0x6345,
+};
+#endif
+
+/*
+ * known 6348 boards
+ */
+#ifdef CONFIG_BCM63XX_CPU_6348
+static struct board_info __initdata board_96348r = {
+	.name				= "96348R",
+	.expected_cpu_id		= 0x6348,
+
+	.has_enet0			= 1,
+	.has_pci			= 1,
+
+	.enet0 = {
+		.has_phy		= 1,
+		.use_internal_phy	= 1,
+	},
+
+	.leds = {
+		{
+			.name		= "adsl-fail",
+			.gpio		= 2,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp",
+			.gpio		= 3,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp-fail",
+			.gpio		= 4,
+			.active_low	= 1,
+		},
+		{
+			.name		= "power",
+			.gpio		= 0,
+			.active_low	= 1,
+			.default_trigger = "default-on",
+
+		},
+		{
+			.name		= "stop",
+			.gpio		= 1,
+			.active_low	= 1,
+		},
+	},
+};
+
+static struct board_info __initdata board_96348gw_10 = {
+	.name				= "96348GW-10",
+	.expected_cpu_id		= 0x6348,
+
+	.has_enet0			= 1,
+	.has_enet1			= 1,
+	.has_pci			= 1,
+
+	.enet0 = {
+		.has_phy		= 1,
+		.use_internal_phy	= 1,
+	},
+	.enet1 = {
+		.force_speed_100	= 1,
+		.force_duplex_full	= 1,
+	},
+
+	.has_ohci0			= 1,
+	.has_pccard			= 1,
+	.has_ehci0			= 1,
+
+	.has_dsp			= 1,
+	.dsp = {
+		.gpio_rst		= 6,
+		.gpio_int		= 34,
+		.cs			= 2,
+		.ext_irq		= 2,
+	},
+
+	.leds = {
+		{
+			.name		= "adsl-fail",
+			.gpio		= 2,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp",
+			.gpio		= 3,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp-fail",
+			.gpio		= 4,
+			.active_low	= 1,
+		},
+		{
+			.name		= "power",
+			.gpio		= 0,
+			.active_low	= 1,
+			.default_trigger = "default-on",
+		},
+		{
+			.name		= "stop",
+			.gpio		= 1,
+			.active_low	= 1,
+		},
+	},
+};
+
+static struct board_info __initdata board_96348gw_11 = {
+	.name				= "96348GW-11",
+	.expected_cpu_id		= 0x6348,
+
+	.has_enet0			= 1,
+	.has_enet1			= 1,
+	.has_pci			= 1,
+
+	.enet0 = {
+		.has_phy		= 1,
+		.use_internal_phy	= 1,
+	},
+
+	.enet1 = {
+		.force_speed_100	= 1,
+		.force_duplex_full	= 1,
+	},
+
+
+	.has_ohci0 = 1,
+	.has_pccard = 1,
+	.has_ehci0 = 1,
+
+	.leds = {
+		{
+			.name		= "adsl-fail",
+			.gpio		= 2,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp",
+			.gpio		= 3,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp-fail",
+			.gpio		= 4,
+			.active_low	= 1,
+		},
+		{
+			.name		= "power",
+			.gpio		= 0,
+			.active_low	= 1,
+			.default_trigger = "default-on",
+		},
+		{
+			.name		= "stop",
+			.gpio		= 1,
+			.active_low	= 1,
+		},
+	},
+};
+
+static struct board_info __initdata board_96348gw = {
+	.name				= "96348GW",
+	.expected_cpu_id		= 0x6348,
+
+	.has_enet0			= 1,
+	.has_enet1			= 1,
+	.has_pci			= 1,
+
+	.enet0 = {
+		.has_phy		= 1,
+		.use_internal_phy	= 1,
+	},
+	.enet1 = {
+		.force_speed_100	= 1,
+		.force_duplex_full	= 1,
+	},
+
+	.has_ohci0 = 1,
+
+	.has_dsp			= 1,
+	.dsp = {
+		.gpio_rst		= 6,
+		.gpio_int		= 34,
+		.ext_irq		= 2,
+		.cs			= 2,
+	},
+
+	.leds = {
+		{
+			.name		= "adsl-fail",
+			.gpio		= 2,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp",
+			.gpio		= 3,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp-fail",
+			.gpio		= 4,
+			.active_low	= 1,
+		},
+		{
+			.name		= "power",
+			.gpio		= 0,
+			.active_low	= 1,
+			.default_trigger = "default-on",
+		},
+		{
+			.name		= "stop",
+			.gpio		= 1,
+			.active_low	= 1,
+		},
+	},
+};
+
+static struct board_info __initdata board_FAST2404 = {
+        .name                           = "F@ST2404",
+        .expected_cpu_id                = 0x6348,
+
+        .has_enet0                      = 1,
+        .has_enet1                      = 1,
+        .has_pci                        = 1,
+
+        .enet0 = {
+                .has_phy                = 1,
+                .use_internal_phy       = 1,
+        },
+
+        .enet1 = {
+                .force_speed_100        = 1,
+                .force_duplex_full      = 1,
+        },
+
+
+        .has_ohci0 = 1,
+        .has_pccard = 1,
+        .has_ehci0 = 1,
+};
+
+static struct board_info __initdata board_DV201AMR = {
+	.name				= "DV201AMR",
+	.expected_cpu_id		= 0x6348,
+
+	.has_pci			= 1,
+	.has_ohci0			= 1,
+
+	.has_enet0			= 1,
+	.has_enet1			= 1,
+	.enet0 = {
+		.has_phy		= 1,
+		.use_internal_phy	= 1,
+	},
+	.enet1 = {
+		.force_speed_100	= 1,
+		.force_duplex_full	= 1,
+	},
+};
+
+static struct board_info __initdata board_96348gw_a = {
+	.name				= "96348GW-A",
+	.expected_cpu_id		= 0x6348,
+
+	.has_enet0			= 1,
+	.has_enet1			= 1,
+	.has_pci			= 1,
+
+	.enet0 = {
+		.has_phy		= 1,
+		.use_internal_phy	= 1,
+	},
+	.enet1 = {
+		.force_speed_100	= 1,
+		.force_duplex_full	= 1,
+	},
+
+	.has_ohci0 = 1,
+};
+#endif
+
+/*
+ * known 6358 boards
+ */
+#ifdef CONFIG_BCM63XX_CPU_6358
+static struct board_info __initdata board_96358vw = {
+	.name				= "96358VW",
+	.expected_cpu_id		= 0x6358,
+
+	.has_enet0			= 1,
+	.has_enet1			= 1,
+	.has_pci			= 1,
+
+	.enet0 = {
+		.has_phy		= 1,
+		.use_internal_phy	= 1,
+	},
+
+	.enet1 = {
+		.force_speed_100	= 1,
+		.force_duplex_full	= 1,
+	},
+
+
+	.has_ohci0 = 1,
+	.has_pccard = 1,
+	.has_ehci0 = 1,
+
+	.leds = {
+		{
+			.name		= "adsl-fail",
+			.gpio		= 15,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp",
+			.gpio		= 22,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp-fail",
+			.gpio		= 23,
+			.active_low	= 1,
+		},
+		{
+			.name		= "power",
+			.gpio		= 4,
+			.default_trigger = "default-on",
+		},
+		{
+			.name		= "stop",
+			.gpio		= 5,
+		},
+	},
+};
+
+static struct board_info __initdata board_96358vw2 = {
+	.name				= "96358VW2",
+	.expected_cpu_id		= 0x6358,
+
+	.has_enet0			= 1,
+	.has_enet1			= 1,
+	.has_pci			= 1,
+
+	.enet0 = {
+		.has_phy		= 1,
+		.use_internal_phy	= 1,
+	},
+
+	.enet1 = {
+		.force_speed_100	= 1,
+		.force_duplex_full	= 1,
+	},
+
+
+	.has_ohci0 = 1,
+	.has_pccard = 1,
+	.has_ehci0 = 1,
+
+	.leds = {
+		{
+			.name		= "adsl",
+			.gpio		= 22,
+			.active_low	= 1,
+		},
+		{
+			.name		= "ppp-fail",
+			.gpio		= 23,
+		},
+		{
+			.name		= "power",
+			.gpio		= 5,
+			.active_low	= 1,
+			.default_trigger = "default-on",
+		},
+		{
+			.name		= "stop",
+			.gpio		= 4,
+			.active_low	= 1,
+		},
+	},
+};
+
+static struct board_info __initdata board_AGPFS0 = {
+	.name                           = "AGPF-S0",
+	.expected_cpu_id                = 0x6358,
+
+	.has_enet0                      = 1,
+	.has_enet1                      = 1,
+	.has_pci                        = 1,
+
+	.enet0 = {
+		.has_phy                = 1,
+		.use_internal_phy       = 1,
+	},
+
+	.enet1 = {
+		.force_speed_100        = 1,
+		.force_duplex_full      = 1,
+	},
+
+	.has_ohci0 = 1,
+	.has_ehci0 = 1,
+};
+#endif
+
+/*
+ * all boards
+ */
+static const struct board_info __initdata *bcm963xx_boards[] = {
+#ifdef CONFIG_BCM63XX_CPU_6338
+	&board_96338gw,
+	&board_96338w,
+#endif
+#ifdef CONFIG_BCM63XX_CPU_6345
+	&board_96345gw2,
+#endif
+#ifdef CONFIG_BCM63XX_CPU_6348
+	&board_96348r,
+	&board_96348gw,
+	&board_96348gw_10,
+	&board_96348gw_11,
+	&board_FAST2404,
+	&board_DV201AMR,
+	&board_96348gw_a,
+#endif
+
+#ifdef CONFIG_BCM63XX_CPU_6358
+	&board_96358vw,
+	&board_96358vw2,
+	&board_AGPFS0,
+#endif
+};
+
+/*
+ * early init callback, read nvram data from flash and checksum it
+ */
+void __init board_prom_init(void)
+{
+	unsigned int check_len, i;
+	u8 *boot_addr, *cfe, *p;
+	char cfe_version[32];
+	u32 val;
+
+	/* read base address of boot chip select (0)
+	 * 6345 does not have MPI but boots from standard
+	 * MIPS Flash address */
+	if (BCMCPU_IS_6345())
+		val = 0x1fc00000;
+	else {
+		val = bcm_mpi_readl(MPI_CSBASE_REG(0));
+		val &= MPI_CSBASE_BASE_MASK;
+	}
+	boot_addr = (u8 *)KSEG1ADDR(val);
+
+	/* dump cfe version */
+	cfe = boot_addr + BCM963XX_CFE_VERSION_OFFSET;
+	if (!memcmp(cfe, "cfe-v", 5))
+		snprintf(cfe_version, sizeof(cfe_version), "%u.%u.%u-%u.%u",
+			 cfe[5], cfe[6], cfe[7], cfe[8], cfe[9]);
+	else
+		strcpy(cfe_version, "unknown");
+	printk(KERN_INFO PFX "CFE version: %s\n", cfe_version);
+
+	/* extract nvram data */
+	memcpy(&nvram, boot_addr + BCM963XX_NVRAM_OFFSET, sizeof(nvram));
+
+	/* check checksum before using data */
+	if (nvram.version <= 4)
+		check_len = offsetof(struct bcm963xx_nvram, checksum_old);
+	else
+		check_len = sizeof(nvram);
+	val = 0;
+	p = (u8 *)&nvram;
+	while (check_len--)
+		val += *p;
+	if (val) {
+		printk(KERN_ERR PFX "invalid nvram checksum\n");
+		return;
+	}
+
+	/* find board by name */
+	for (i = 0; i < ARRAY_SIZE(bcm963xx_boards); i++) {
+		if (strncmp(nvram.name, bcm963xx_boards[i]->name,
+			    sizeof(nvram.name)))
+			continue;
+		/* copy, board desc array is marked initdata */
+		memcpy(&board, bcm963xx_boards[i], sizeof(board));
+		break;
+	}
+
+	/* bail out if board is not found, will complain later */
+	if (!board.name[0]) {
+		char name[17];
+		memcpy(name, nvram.name, 16);
+		name[16] = 0;
+		printk(KERN_ERR PFX "unknown bcm963xx board: %s\n",
+		       name);
+		return;
+	}
+
+	/* setup pin multiplexing depending on board enabled device,
+	 * this has to be done this early since PCI init is done
+	 * inside arch_initcall */
+	val = 0;
+
+#ifdef CONFIG_PCI
+	if (board.has_pci) {
+		bcm63xx_pci_enabled = 1;
+		if (BCMCPU_IS_6348())
+			val |= GPIO_MODE_6348_G2_PCI;
+	}
+#endif
+
+	if (board.has_pccard) {
+		if (BCMCPU_IS_6348())
+			val |= GPIO_MODE_6348_G1_MII_PCCARD;
+	}
+
+	if (board.has_enet0 && !board.enet0.use_internal_phy) {
+		if (BCMCPU_IS_6348())
+			val |= GPIO_MODE_6348_G3_EXT_MII |
+				GPIO_MODE_6348_G0_EXT_MII;
+	}
+
+	if (board.has_enet1 && !board.enet1.use_internal_phy) {
+		if (BCMCPU_IS_6348())
+			val |= GPIO_MODE_6348_G3_EXT_MII |
+				GPIO_MODE_6348_G0_EXT_MII;
+	}
+
+	bcm_gpio_writel(val, GPIO_MODE_REG);
+}
+
+/*
+ * second stage init callback, good time to panic if we couldn't
+ * identify on which board we're running since early printk is working
+ */
+void __init board_setup(void)
+{
+	if (!board.name[0])
+		panic("unable to detect bcm963xx board");
+	printk(KERN_INFO PFX "board name: %s\n", board.name);
+
+	/* make sure we're running on expected cpu */
+	if (bcm63xx_get_cpu_id() != board.expected_cpu_id)
+		panic("unexpected CPU for bcm963xx board");
+}
+
+/*
+ * return board name for /proc/cpuinfo
+ */
+const char *board_get_name(void)
+{
+	return board.name;
+}
+
+/*
+ * register & return a new board mac address
+ */
+static int board_get_mac_address(u8 *mac)
+{
+	u8 *p;
+	int count;
+
+	if (mac_addr_used >= nvram.mac_addr_count) {
+		printk(KERN_ERR PFX "not enough mac address\n");
+		return -ENODEV;
+	}
+
+	memcpy(mac, nvram.mac_addr_base, ETH_ALEN);
+	p = mac + ETH_ALEN - 1;
+	count = mac_addr_used;
+
+	while (count--) {
+		do {
+			(*p)++;
+			if (*p != 0)
+				break;
+			p--;
+		} while (p != mac);
+	}
+
+	if (p == mac) {
+		printk(KERN_ERR PFX "unable to fetch mac address\n");
+		return -ENODEV;
+	}
+
+	mac_addr_used++;
+	return 0;
+}
+
+static struct mtd_partition mtd_partitions[] = {
+	{
+		.name		= "cfe",
+		.offset		= 0x0,
+		.size		= 0x40000,
+	}
+};
+
+static struct physmap_flash_data flash_data = {
+	.width			= 2,
+	.nr_parts		= ARRAY_SIZE(mtd_partitions),
+	.parts			= mtd_partitions,
+};
+
+static struct resource mtd_resources[] = {
+	{
+		.start		= 0,	/* filled at runtime */
+		.end		= 0,	/* filled at runtime */
+		.flags		= IORESOURCE_MEM,
+	}
+};
+
+static struct platform_device mtd_dev = {
+	.name			= "physmap-flash",
+	.resource		= mtd_resources,
+	.num_resources		= ARRAY_SIZE(mtd_resources),
+	.dev			= {
+		.platform_data	= &flash_data,
+	},
+};
+
+/*
+ * Register a sane SPROMv2 to make the on-board
+ * bcm4318 WLAN work
+ */
+#ifdef CONFIG_SSB_PCIHOST
+static struct ssb_sprom bcm63xx_sprom = {
+	.revision		= 0x02,
+	.board_rev		= 0x17,
+	.country_code		= 0x0,
+	.ant_available_bg 	= 0x3,
+	.pa0b0			= 0x15ae,
+	.pa0b1			= 0xfa85,
+	.pa0b2			= 0xfe8d,
+	.pa1b0			= 0xffff,
+	.pa1b1			= 0xffff,
+	.pa1b2			= 0xffff,
+	.gpio0			= 0xff,
+	.gpio1			= 0xff,
+	.gpio2			= 0xff,
+	.gpio3			= 0xff,
+	.maxpwr_bg		= 0x004c,
+	.itssi_bg		= 0x00,
+	.boardflags_lo		= 0x2848,
+	.boardflags_hi		= 0x0000,
+};
+#endif
+
+static struct gpio_led_platform_data bcm63xx_led_data;
+
+static struct platform_device bcm63xx_gpio_leds = {
+	.name			= "leds-gpio",
+	.id			= 0,
+	.dev.platform_data	= &bcm63xx_led_data,
+};
+
+/*
+ * third stage init callback, register all board devices.
+ */
+int __init board_register_devices(void)
+{
+	u32 val;
+
+	if (board.has_enet0 &&
+	    !board_get_mac_address(board.enet0.mac_addr))
+		bcm63xx_enet_register(0, &board.enet0);
+
+	if (board.has_enet1 &&
+	    !board_get_mac_address(board.enet1.mac_addr))
+		bcm63xx_enet_register(1, &board.enet1);
+
+	if (board.has_dsp)
+		bcm63xx_dsp_register(&board.dsp);
+
+	/* Generate MAC address for WLAN and
+	 * register our SPROM */
+#ifdef CONFIG_SSB_PCIHOST
+	if (!board_get_mac_address(bcm63xx_sprom.il0mac)) {
+		memcpy(bcm63xx_sprom.et0mac, bcm63xx_sprom.il0mac, ETH_ALEN);
+		memcpy(bcm63xx_sprom.et1mac, bcm63xx_sprom.il0mac, ETH_ALEN);
+		if (ssb_arch_set_fallback_sprom(&bcm63xx_sprom) < 0)
+			printk(KERN_ERR "failed to register fallback SPROM\n");
+	}
+#endif
+
+	/* read base address of boot chip select (0) */
+	if (BCMCPU_IS_6345())
+		val = 0x1fc00000;
+	else {
+		val = bcm_mpi_readl(MPI_CSBASE_REG(0));
+		val &= MPI_CSBASE_BASE_MASK;
+	}
+	mtd_resources[0].start = val;
+	mtd_resources[0].end = 0x1FFFFFFF;
+
+	platform_device_register(&mtd_dev);
+
+	bcm63xx_led_data.num_leds = ARRAY_SIZE(board.leds);
+	bcm63xx_led_data.leds = board.leds;
+
+	platform_device_register(&bcm63xx_gpio_leds);
+
+	return 0;
+}
+
diff --git a/arch/mips/bcm63xx/clk.c b/arch/mips/bcm63xx/clk.c
new file mode 100644
index 0000000..2c68ee9
--- /dev/null
+++ b/arch/mips/bcm63xx/clk.c
@@ -0,0 +1,226 @@
+/*
+ * 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) 2008 Maxime Bizon <mbizon@freebox.fr>
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_clk.h>
+
+static DEFINE_MUTEX(clocks_mutex);
+
+
+static void clk_enable_unlocked(struct clk *clk)
+{
+	if (clk->set && (clk->usage++) == 0)
+		clk->set(clk, 1);
+}
+
+static void clk_disable_unlocked(struct clk *clk)
+{
+	if (clk->set && (--clk->usage) == 0)
+		clk->set(clk, 0);
+}
+
+static void bcm_hwclock_set(u32 mask, int enable)
+{
+	u32 reg;
+
+	reg = bcm_perf_readl(PERF_CKCTL_REG);
+	if (enable)
+		reg |= mask;
+	else
+		reg &= ~mask;
+	bcm_perf_writel(reg, PERF_CKCTL_REG);
+}
+
+/*
+ * Ethernet MAC "misc" clock: dma clocks and main clock on 6348
+ */
+static void enet_misc_set(struct clk *clk, int enable)
+{
+	u32 mask;
+
+	if (BCMCPU_IS_6338())
+		mask = CKCTL_6338_ENET_EN;
+	else if (BCMCPU_IS_6345())
+		mask = CKCTL_6345_ENET_EN;
+	else if (BCMCPU_IS_6348())
+		mask = CKCTL_6348_ENET_EN;
+	else
+		/* BCMCPU_IS_6358 */
+		mask = CKCTL_6358_EMUSB_EN;
+	bcm_hwclock_set(mask, enable);
+}
+
+static struct clk clk_enet_misc = {
+	.set	= enet_misc_set,
+};
+
+/*
+ * Ethernet MAC clocks: only revelant on 6358, silently enable misc
+ * clocks
+ */
+static void enetx_set(struct clk *clk, int enable)
+{
+	if (enable)
+		clk_enable_unlocked(&clk_enet_misc);
+	else
+		clk_disable_unlocked(&clk_enet_misc);
+
+	if (BCMCPU_IS_6358()) {
+		u32 mask;
+
+		if (clk->id == 0)
+			mask = CKCTL_6358_ENET0_EN;
+		else
+			mask = CKCTL_6358_ENET1_EN;
+		bcm_hwclock_set(mask, enable);
+	}
+}
+
+static struct clk clk_enet0 = {
+	.id	= 0,
+	.set	= enetx_set,
+};
+
+static struct clk clk_enet1 = {
+	.id	= 1,
+	.set	= enetx_set,
+};
+
+/*
+ * Ethernet PHY clock
+ */
+static void ephy_set(struct clk *clk, int enable)
+{
+	if (!BCMCPU_IS_6358())
+		return;
+	bcm_hwclock_set(CKCTL_6358_EPHY_EN, enable);
+}
+
+
+static struct clk clk_ephy = {
+	.set	= ephy_set,
+};
+
+/*
+ * PCM clock
+ */
+static void pcm_set(struct clk *clk, int enable)
+{
+	if (!BCMCPU_IS_6358())
+		return;
+	bcm_hwclock_set(CKCTL_6358_PCM_EN, enable);
+}
+
+static struct clk clk_pcm = {
+	.set	= pcm_set,
+};
+
+/*
+ * USB host clock
+ */
+static void usbh_set(struct clk *clk, int enable)
+{
+	if (!BCMCPU_IS_6348())
+		return;
+	bcm_hwclock_set(CKCTL_6348_USBH_EN, enable);
+}
+
+static struct clk clk_usbh = {
+	.set	= usbh_set,
+};
+
+/*
+ * SPI clock
+ */
+static void spi_set(struct clk *clk, int enable)
+{
+	u32 mask;
+
+	if (BCMCPU_IS_6338())
+		mask = CKCTL_6338_SPI_EN;
+	else if (BCMCPU_IS_6348())
+		mask = CKCTL_6348_SPI_EN;
+	else
+		/* BCMCPU_IS_6358 */
+		mask = CKCTL_6358_SPI_EN;
+	bcm_hwclock_set(mask, enable);
+}
+
+static struct clk clk_spi = {
+	.set	= spi_set,
+};
+
+/*
+ * Internal peripheral clock
+ */
+static struct clk clk_periph = {
+	.rate	= (50 * 1000 * 1000),
+};
+
+
+/*
+ * Linux clock API implementation
+ */
+int clk_enable(struct clk *clk)
+{
+	mutex_lock(&clocks_mutex);
+	clk_enable_unlocked(clk);
+	mutex_unlock(&clocks_mutex);
+	return 0;
+}
+
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+	mutex_lock(&clocks_mutex);
+	clk_disable_unlocked(clk);
+	mutex_unlock(&clocks_mutex);
+}
+
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+	return clk->rate;
+}
+
+EXPORT_SYMBOL(clk_get_rate);
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+	if (!strcmp(id, "enet0"))
+		return &clk_enet0;
+	if (!strcmp(id, "enet1"))
+		return &clk_enet1;
+	if (!strcmp(id, "ephy"))
+		return &clk_ephy;
+	if (!strcmp(id, "usbh"))
+		return &clk_usbh;
+	if (!strcmp(id, "spi"))
+		return &clk_spi;
+	if (!strcmp(id, "periph"))
+		return &clk_periph;
+	if (BCMCPU_IS_6358() && !strcmp(id, "pcm"))
+		return &clk_pcm;
+	return ERR_PTR(-ENOENT);
+}
+
+EXPORT_SYMBOL(clk_get);
+
+void clk_put(struct clk *clk)
+{
+}
+
+EXPORT_SYMBOL(clk_put);
diff --git a/arch/mips/bcm63xx/cpu.c b/arch/mips/bcm63xx/cpu.c
new file mode 100644
index 0000000..6dc43f0
--- /dev/null
+++ b/arch/mips/bcm63xx/cpu.c
@@ -0,0 +1,345 @@
+/*
+ * 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) 2008 Maxime Bizon <mbizon@freebox.fr>
+ * Copyright (C) 2009 Florian Fainelli <florian@openwrt.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_irq.h>
+
+const unsigned long *bcm63xx_regs_base;
+EXPORT_SYMBOL(bcm63xx_regs_base);
+
+const int *bcm63xx_irqs;
+EXPORT_SYMBOL(bcm63xx_irqs);
+
+static u16 bcm63xx_cpu_id;
+static u16 bcm63xx_cpu_rev;
+static unsigned int bcm63xx_cpu_freq;
+static unsigned int bcm63xx_memory_size;
+
+/*
+ * 6338 register sets and irqs
+ */
+static const unsigned long bcm96338_regs_base[] = {
+	[RSET_DSL_LMEM]		= BCM_6338_DSL_LMEM_BASE,
+	[RSET_PERF]		= BCM_6338_PERF_BASE,
+	[RSET_TIMER]		= BCM_6338_TIMER_BASE,
+	[RSET_WDT]		= BCM_6338_WDT_BASE,
+	[RSET_UART0]		= BCM_6338_UART0_BASE,
+	[RSET_GPIO]		= BCM_6338_GPIO_BASE,
+	[RSET_SPI]		= BCM_6338_SPI_BASE,
+	[RSET_OHCI0]		= BCM_6338_OHCI0_BASE,
+	[RSET_OHCI_PRIV]	= BCM_6338_OHCI_PRIV_BASE,
+	[RSET_USBH_PRIV]	= BCM_6338_USBH_PRIV_BASE,
+	[RSET_UDC0]		= BCM_6338_UDC0_BASE,
+	[RSET_MPI]		= BCM_6338_MPI_BASE,
+	[RSET_PCMCIA]		= BCM_6338_PCMCIA_BASE,
+	[RSET_SDRAM]		= BCM_6338_SDRAM_BASE,
+	[RSET_DSL]		= BCM_6338_DSL_BASE,
+	[RSET_ENET0]		= BCM_6338_ENET0_BASE,
+	[RSET_ENET1]		= BCM_6338_ENET1_BASE,
+	[RSET_ENETDMA]		= BCM_6338_ENETDMA_BASE,
+	[RSET_MEMC]		= BCM_6338_MEMC_BASE,
+	[RSET_DDR]		= BCM_6338_DDR_BASE,
+};
+
+static const int bcm96338_irqs[] = {
+	[IRQ_TIMER]		= BCM_6338_TIMER_IRQ,
+	[IRQ_UART0]		= BCM_6338_UART0_IRQ,
+	[IRQ_DSL]		= BCM_6338_DSL_IRQ,
+	[IRQ_ENET0]		= BCM_6338_ENET0_IRQ,
+	[IRQ_ENET_PHY]		= BCM_6338_ENET_PHY_IRQ,
+	[IRQ_ENET0_RXDMA]	= BCM_6338_ENET0_RXDMA_IRQ,
+	[IRQ_ENET0_TXDMA]	= BCM_6338_ENET0_TXDMA_IRQ,
+};
+
+/*
+ * 6345 register sets and irqs
+ */
+static const unsigned long bcm96345_regs_base[] = {
+	[RSET_DSL_LMEM]		= BCM_6345_DSL_LMEM_BASE,
+	[RSET_PERF]		= BCM_6345_PERF_BASE,
+	[RSET_TIMER]		= BCM_6345_TIMER_BASE,
+	[RSET_WDT]		= BCM_6345_WDT_BASE,
+	[RSET_UART0]		= BCM_6345_UART0_BASE,
+	[RSET_GPIO]		= BCM_6345_GPIO_BASE,
+	[RSET_SPI]		= BCM_6345_SPI_BASE,
+	[RSET_UDC0]		= BCM_6345_UDC0_BASE,
+	[RSET_OHCI0]		= BCM_6345_OHCI0_BASE,
+	[RSET_OHCI_PRIV]	= BCM_6345_OHCI_PRIV_BASE,
+	[RSET_USBH_PRIV]	= BCM_6345_USBH_PRIV_BASE,
+	[RSET_MPI]		= BCM_6345_MPI_BASE,
+	[RSET_PCMCIA]		= BCM_6345_PCMCIA_BASE,
+	[RSET_DSL]		= BCM_6345_DSL_BASE,
+	[RSET_ENET0]		= BCM_6345_ENET0_BASE,
+	[RSET_ENET1]		= BCM_6345_ENET1_BASE,
+	[RSET_ENETDMA]		= BCM_6345_ENETDMA_BASE,
+	[RSET_EHCI0]		= BCM_6345_EHCI0_BASE,
+	[RSET_SDRAM]		= BCM_6345_SDRAM_BASE,
+	[RSET_MEMC]		= BCM_6345_MEMC_BASE,
+	[RSET_DDR]		= BCM_6345_DDR_BASE,
+};
+
+static const int bcm96345_irqs[] = {
+	[IRQ_TIMER]		= BCM_6345_TIMER_IRQ,
+	[IRQ_UART0]		= BCM_6345_UART0_IRQ,
+	[IRQ_DSL]		= BCM_6345_DSL_IRQ,
+	[IRQ_ENET0]		= BCM_6345_ENET0_IRQ,
+	[IRQ_ENET_PHY]		= BCM_6345_ENET_PHY_IRQ,
+	[IRQ_ENET0_RXDMA]	= BCM_6345_ENET0_RXDMA_IRQ,
+	[IRQ_ENET0_TXDMA]	= BCM_6345_ENET0_TXDMA_IRQ,
+};
+
+/*
+ * 6348 register sets and irqs
+ */
+static const unsigned long bcm96348_regs_base[] = {
+	[RSET_DSL_LMEM]		= BCM_6348_DSL_LMEM_BASE,
+	[RSET_PERF]		= BCM_6348_PERF_BASE,
+	[RSET_TIMER]		= BCM_6348_TIMER_BASE,
+	[RSET_WDT]		= BCM_6348_WDT_BASE,
+	[RSET_UART0]		= BCM_6348_UART0_BASE,
+	[RSET_GPIO]		= BCM_6348_GPIO_BASE,
+	[RSET_SPI]		= BCM_6348_SPI_BASE,
+	[RSET_OHCI0]		= BCM_6348_OHCI0_BASE,
+	[RSET_OHCI_PRIV]	= BCM_6348_OHCI_PRIV_BASE,
+	[RSET_USBH_PRIV]	= BCM_6348_USBH_PRIV_BASE,
+	[RSET_MPI]		= BCM_6348_MPI_BASE,
+	[RSET_PCMCIA]		= BCM_6348_PCMCIA_BASE,
+	[RSET_SDRAM]		= BCM_6348_SDRAM_BASE,
+	[RSET_DSL]		= BCM_6348_DSL_BASE,
+	[RSET_ENET0]		= BCM_6348_ENET0_BASE,
+	[RSET_ENET1]		= BCM_6348_ENET1_BASE,
+	[RSET_ENETDMA]		= BCM_6348_ENETDMA_BASE,
+	[RSET_MEMC]		= BCM_6348_MEMC_BASE,
+	[RSET_DDR]		= BCM_6348_DDR_BASE,
+};
+
+static const int bcm96348_irqs[] = {
+	[IRQ_TIMER]		= BCM_6348_TIMER_IRQ,
+	[IRQ_UART0]		= BCM_6348_UART0_IRQ,
+	[IRQ_DSL]		= BCM_6348_DSL_IRQ,
+	[IRQ_ENET0]		= BCM_6348_ENET0_IRQ,
+	[IRQ_ENET1]		= BCM_6348_ENET1_IRQ,
+	[IRQ_ENET_PHY]		= BCM_6348_ENET_PHY_IRQ,
+	[IRQ_OHCI0]		= BCM_6348_OHCI0_IRQ,
+	[IRQ_PCMCIA]		= BCM_6348_PCMCIA_IRQ,
+	[IRQ_ENET0_RXDMA]	= BCM_6348_ENET0_RXDMA_IRQ,
+	[IRQ_ENET0_TXDMA]	= BCM_6348_ENET0_TXDMA_IRQ,
+	[IRQ_ENET1_RXDMA]	= BCM_6348_ENET1_RXDMA_IRQ,
+	[IRQ_ENET1_TXDMA]	= BCM_6348_ENET1_TXDMA_IRQ,
+	[IRQ_PCI]		= BCM_6348_PCI_IRQ,
+};
+
+/*
+ * 6358 register sets and irqs
+ */
+static const unsigned long bcm96358_regs_base[] = {
+	[RSET_DSL_LMEM]		= BCM_6358_DSL_LMEM_BASE,
+	[RSET_PERF]		= BCM_6358_PERF_BASE,
+	[RSET_TIMER]		= BCM_6358_TIMER_BASE,
+	[RSET_WDT]		= BCM_6358_WDT_BASE,
+	[RSET_UART0]		= BCM_6358_UART0_BASE,
+	[RSET_GPIO]		= BCM_6358_GPIO_BASE,
+	[RSET_SPI]		= BCM_6358_SPI_BASE,
+	[RSET_OHCI0]		= BCM_6358_OHCI0_BASE,
+	[RSET_EHCI0]		= BCM_6358_EHCI0_BASE,
+	[RSET_OHCI_PRIV]	= BCM_6358_OHCI_PRIV_BASE,
+	[RSET_USBH_PRIV]	= BCM_6358_USBH_PRIV_BASE,
+	[RSET_MPI]		= BCM_6358_MPI_BASE,
+	[RSET_PCMCIA]		= BCM_6358_PCMCIA_BASE,
+	[RSET_SDRAM]		= BCM_6358_SDRAM_BASE,
+	[RSET_DSL]		= BCM_6358_DSL_BASE,
+	[RSET_ENET0]		= BCM_6358_ENET0_BASE,
+	[RSET_ENET1]		= BCM_6358_ENET1_BASE,
+	[RSET_ENETDMA]		= BCM_6358_ENETDMA_BASE,
+	[RSET_MEMC]		= BCM_6358_MEMC_BASE,
+	[RSET_DDR]		= BCM_6358_DDR_BASE,
+};
+
+static const int bcm96358_irqs[] = {
+	[IRQ_TIMER]		= BCM_6358_TIMER_IRQ,
+	[IRQ_UART0]		= BCM_6358_UART0_IRQ,
+	[IRQ_DSL]		= BCM_6358_DSL_IRQ,
+	[IRQ_ENET0]		= BCM_6358_ENET0_IRQ,
+	[IRQ_ENET1]		= BCM_6358_ENET1_IRQ,
+	[IRQ_ENET_PHY]		= BCM_6358_ENET_PHY_IRQ,
+	[IRQ_OHCI0]		= BCM_6358_OHCI0_IRQ,
+	[IRQ_EHCI0]		= BCM_6358_EHCI0_IRQ,
+	[IRQ_PCMCIA]		= BCM_6358_PCMCIA_IRQ,
+	[IRQ_ENET0_RXDMA]	= BCM_6358_ENET0_RXDMA_IRQ,
+	[IRQ_ENET0_TXDMA]	= BCM_6358_ENET0_TXDMA_IRQ,
+	[IRQ_ENET1_RXDMA]	= BCM_6358_ENET1_RXDMA_IRQ,
+	[IRQ_ENET1_TXDMA]	= BCM_6358_ENET1_TXDMA_IRQ,
+	[IRQ_PCI]		= BCM_6358_PCI_IRQ,
+};
+
+u16 __bcm63xx_get_cpu_id(void)
+{
+	return bcm63xx_cpu_id;
+}
+
+EXPORT_SYMBOL(__bcm63xx_get_cpu_id);
+
+u16 bcm63xx_get_cpu_rev(void)
+{
+	return bcm63xx_cpu_rev;
+}
+
+EXPORT_SYMBOL(bcm63xx_get_cpu_rev);
+
+unsigned int bcm63xx_get_cpu_freq(void)
+{
+	return bcm63xx_cpu_freq;
+}
+
+unsigned int bcm63xx_get_memory_size(void)
+{
+	return bcm63xx_memory_size;
+}
+
+static unsigned int detect_cpu_clock(void)
+{
+	unsigned int tmp, n1 = 0, n2 = 0, m1 = 0;
+
+	/* BCM6338 has a fixed 240 Mhz frequency */
+	if (BCMCPU_IS_6338())
+		return 240000000;
+
+	/* BCM6345 has a fixed 140Mhz frequency */
+	if (BCMCPU_IS_6345())
+		return 140000000;
+
+	/*
+	 * frequency depends on PLL configuration:
+	 */
+	if (BCMCPU_IS_6348()) {
+		/* 16MHz * (N1 + 1) * (N2 + 2) / (M1_CPU + 1) */
+		tmp = bcm_perf_readl(PERF_MIPSPLLCTL_REG);
+		n1 = (tmp & MIPSPLLCTL_N1_MASK) >> MIPSPLLCTL_N1_SHIFT;
+		n2 = (tmp & MIPSPLLCTL_N2_MASK) >> MIPSPLLCTL_N2_SHIFT;
+		m1 = (tmp & MIPSPLLCTL_M1CPU_MASK) >> MIPSPLLCTL_M1CPU_SHIFT;
+		n1 += 1;
+		n2 += 2;
+		m1 += 1;
+	}
+
+	if (BCMCPU_IS_6358()) {
+		/* 16MHz * N1 * N2 / M1_CPU */
+		tmp = bcm_ddr_readl(DDR_DMIPSPLLCFG_REG);
+		n1 = (tmp & DMIPSPLLCFG_N1_MASK) >> DMIPSPLLCFG_N1_SHIFT;
+		n2 = (tmp & DMIPSPLLCFG_N2_MASK) >> DMIPSPLLCFG_N2_SHIFT;
+		m1 = (tmp & DMIPSPLLCFG_M1_MASK) >> DMIPSPLLCFG_M1_SHIFT;
+	}
+
+	return (16 * 1000000 * n1 * n2) / m1;
+}
+
+/*
+ * attempt to detect the amount of memory installed
+ */
+static unsigned int detect_memory_size(void)
+{
+	unsigned int cols = 0, rows = 0, is_32bits = 0, banks = 0;
+	u32 val;
+
+	if (BCMCPU_IS_6345())
+		return (8 * 1024 * 1024);
+
+	if (BCMCPU_IS_6338() || BCMCPU_IS_6348()) {
+		val = bcm_sdram_readl(SDRAM_CFG_REG);
+		rows = (val & SDRAM_CFG_ROW_MASK) >> SDRAM_CFG_ROW_SHIFT;
+		cols = (val & SDRAM_CFG_COL_MASK) >> SDRAM_CFG_COL_SHIFT;
+		is_32bits = (val & SDRAM_CFG_32B_MASK) ? 1 : 0;
+		banks = (val & SDRAM_CFG_BANK_MASK) ? 2 : 1;
+	}
+
+	if (BCMCPU_IS_6358()) {
+		val = bcm_memc_readl(MEMC_CFG_REG);
+		rows = (val & MEMC_CFG_ROW_MASK) >> MEMC_CFG_ROW_SHIFT;
+		cols = (val & MEMC_CFG_COL_MASK) >> MEMC_CFG_COL_SHIFT;
+		is_32bits = (val & MEMC_CFG_32B_MASK) ? 0 : 1;
+		banks = 2;
+	}
+
+	/* 0 => 11 address bits ... 2 => 13 address bits */
+	rows += 11;
+
+	/* 0 => 8 address bits ... 2 => 10 address bits */
+	cols += 8;
+
+	return 1 << (cols + rows + (is_32bits + 1) + banks);
+}
+
+void __init bcm63xx_cpu_init(void)
+{
+	unsigned int tmp, expected_cpu_id;
+	struct cpuinfo_mips *c = &current_cpu_data;
+
+	/* soc registers location depends on cpu type */
+	expected_cpu_id = 0;
+
+	switch (c->cputype) {
+	/*
+	 * BCM6338 as the same PrId as BCM3302 see arch/mips/kernel/cpu-probe.c
+	 */
+	case CPU_BCM3302:
+		expected_cpu_id = BCM6338_CPU_ID;
+		bcm63xx_regs_base = bcm96338_regs_base;
+		bcm63xx_irqs = bcm96338_irqs;
+		break;
+	case CPU_BCM6345:
+		expected_cpu_id = BCM6345_CPU_ID;
+		bcm63xx_regs_base = bcm96345_regs_base;
+		bcm63xx_irqs = bcm96345_irqs;
+		break;
+	case CPU_BCM6348:
+		expected_cpu_id = BCM6348_CPU_ID;
+		bcm63xx_regs_base = bcm96348_regs_base;
+		bcm63xx_irqs = bcm96348_irqs;
+		break;
+	case CPU_BCM6358:
+		expected_cpu_id = BCM6358_CPU_ID;
+		bcm63xx_regs_base = bcm96358_regs_base;
+		bcm63xx_irqs = bcm96358_irqs;
+		break;
+	}
+
+	/*
+	 * really early to panic, but delaying panic would not help since we
+	 * will never get any working console
+	 */
+	if (!expected_cpu_id)
+		panic("unsupported Broadcom CPU");
+
+	/*
+	 * bcm63xx_regs_base is set, we can access soc registers
+	 */
+
+	/* double check CPU type */
+	tmp = bcm_perf_readl(PERF_REV_REG);
+	bcm63xx_cpu_id = (tmp & REV_CHIPID_MASK) >> REV_CHIPID_SHIFT;
+	bcm63xx_cpu_rev = (tmp & REV_REVID_MASK) >> REV_REVID_SHIFT;
+
+	if (bcm63xx_cpu_id != expected_cpu_id)
+		panic("bcm63xx CPU id mismatch");
+
+	bcm63xx_cpu_freq = detect_cpu_clock();
+	bcm63xx_memory_size = detect_memory_size();
+
+	printk(KERN_INFO "Detected Broadcom 0x%04x CPU revision %02x\n",
+	       bcm63xx_cpu_id, bcm63xx_cpu_rev);
+	printk(KERN_INFO "CPU frequency is %u MHz\n",
+	       bcm63xx_cpu_freq / 1000000);
+	printk(KERN_INFO "%uMB of RAM installed\n",
+	       bcm63xx_memory_size >> 20);
+}
diff --git a/arch/mips/bcm63xx/cs.c b/arch/mips/bcm63xx/cs.c
new file mode 100644
index 0000000..50d8190
--- /dev/null
+++ b/arch/mips/bcm63xx/cs.c
@@ -0,0 +1,144 @@
+/*
+ * 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) 2008 Maxime Bizon <mbizon@freebox.fr>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/log2.h>
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_cs.h>
+
+static DEFINE_SPINLOCK(bcm63xx_cs_lock);
+
+/*
+ * check if given chip select exists
+ */
+static int is_valid_cs(unsigned int cs)
+{
+	if (cs > 6)
+		return 0;
+	return 1;
+}
+
+/*
+ * Configure chipselect base address and size (bytes).
+ * Size must be a power of two between 8k and 256M.
+ */
+int bcm63xx_set_cs_base(unsigned int cs, u32 base, unsigned int size)
+{
+	unsigned long flags;
+	u32 val;
+
+	if (!is_valid_cs(cs))
+		return -EINVAL;
+
+	/* sanity check on size */
+	if (size != roundup_pow_of_two(size))
+		return -EINVAL;
+
+	if (size < 8 * 1024 || size > 256 * 1024 * 1024)
+		return -EINVAL;
+
+	val = (base & MPI_CSBASE_BASE_MASK);
+	/* 8k => 0 - 256M => 15 */
+	val |= (ilog2(size) - ilog2(8 * 1024)) << MPI_CSBASE_SIZE_SHIFT;
+
+	spin_lock_irqsave(&bcm63xx_cs_lock, flags);
+	bcm_mpi_writel(val, MPI_CSBASE_REG(cs));
+	spin_unlock_irqrestore(&bcm63xx_cs_lock, flags);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(bcm63xx_set_cs_base);
+
+/*
+ * configure chipselect timing (ns)
+ */
+int bcm63xx_set_cs_timing(unsigned int cs, unsigned int wait,
+			   unsigned int setup, unsigned int hold)
+{
+	unsigned long flags;
+	u32 val;
+
+	if (!is_valid_cs(cs))
+		return -EINVAL;
+
+	spin_lock_irqsave(&bcm63xx_cs_lock, flags);
+	val = bcm_mpi_readl(MPI_CSCTL_REG(cs));
+	val &= ~(MPI_CSCTL_WAIT_MASK);
+	val &= ~(MPI_CSCTL_SETUP_MASK);
+	val &= ~(MPI_CSCTL_HOLD_MASK);
+	val |= wait << MPI_CSCTL_WAIT_SHIFT;
+	val |= setup << MPI_CSCTL_SETUP_SHIFT;
+	val |= hold << MPI_CSCTL_HOLD_SHIFT;
+	bcm_mpi_writel(val, MPI_CSCTL_REG(cs));
+	spin_unlock_irqrestore(&bcm63xx_cs_lock, flags);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(bcm63xx_set_cs_timing);
+
+/*
+ * configure other chipselect parameter (data bus size, ...)
+ */
+int bcm63xx_set_cs_param(unsigned int cs, u32 params)
+{
+	unsigned long flags;
+	u32 val;
+
+	if (!is_valid_cs(cs))
+		return -EINVAL;
+
+	/* none of this fields apply to pcmcia */
+	if (cs == MPI_CS_PCMCIA_COMMON ||
+	    cs == MPI_CS_PCMCIA_ATTR ||
+	    cs == MPI_CS_PCMCIA_IO)
+		return -EINVAL;
+
+	spin_lock_irqsave(&bcm63xx_cs_lock, flags);
+	val = bcm_mpi_readl(MPI_CSCTL_REG(cs));
+	val &= ~(MPI_CSCTL_DATA16_MASK);
+	val &= ~(MPI_CSCTL_SYNCMODE_MASK);
+	val &= ~(MPI_CSCTL_TSIZE_MASK);
+	val &= ~(MPI_CSCTL_ENDIANSWAP_MASK);
+	val |= params;
+	bcm_mpi_writel(val, MPI_CSCTL_REG(cs));
+	spin_unlock_irqrestore(&bcm63xx_cs_lock, flags);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(bcm63xx_set_cs_param);
+
+/*
+ * set cs status (enable/disable)
+ */
+int bcm63xx_set_cs_status(unsigned int cs, int enable)
+{
+	unsigned long flags;
+	u32 val;
+
+	if (!is_valid_cs(cs))
+		return -EINVAL;
+
+	spin_lock_irqsave(&bcm63xx_cs_lock, flags);
+	val = bcm_mpi_readl(MPI_CSCTL_REG(cs));
+	if (enable)
+		val |= MPI_CSCTL_ENABLE_MASK;
+	else
+		val &= ~MPI_CSCTL_ENABLE_MASK;
+	bcm_mpi_writel(val, MPI_CSCTL_REG(cs));
+	spin_unlock_irqrestore(&bcm63xx_cs_lock, flags);
+	return 0;
+}
+
+EXPORT_SYMBOL(bcm63xx_set_cs_status);
diff --git a/arch/mips/bcm63xx/dev-dsp.c b/arch/mips/bcm63xx/dev-dsp.c
new file mode 100644
index 0000000..da46d1d
--- /dev/null
+++ b/arch/mips/bcm63xx/dev-dsp.c
@@ -0,0 +1,56 @@
+/*
+ * Broadcom BCM63xx VoIP DSP registration
+ *
+ * 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) 2009 Florian Fainelli <florian@openwrt.org>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_dev_dsp.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_io.h>
+
+static struct resource voip_dsp_resources[] = {
+	{
+		.start		= -1, /* filled at runtime */
+		.end		= -1, /* filled at runtime */
+		.flags		= IORESOURCE_MEM,
+	},
+	{
+		.start		= -1, /* filled at runtime */
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device bcm63xx_voip_dsp_device = {
+	.name		= "bcm63xx-voip-dsp",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(voip_dsp_resources),
+	.resource	= voip_dsp_resources,
+};
+
+int __init bcm63xx_dsp_register(const struct bcm63xx_dsp_platform_data *pd)
+{
+	struct bcm63xx_dsp_platform_data *dpd;
+	u32 val;
+
+	/* Get the memory window */
+	val = bcm_mpi_readl(MPI_CSBASE_REG(pd->cs - 1));
+	val &= MPI_CSBASE_BASE_MASK;
+	voip_dsp_resources[0].start = val;
+	voip_dsp_resources[0].end = val + 0xFFFFFFF;
+	voip_dsp_resources[1].start = pd->ext_irq;
+
+	/* copy given platform data */
+	dpd = bcm63xx_voip_dsp_device.dev.platform_data;
+	memcpy(dpd, pd, sizeof (*pd));
+
+	return platform_device_register(&bcm63xx_voip_dsp_device);
+}
diff --git a/arch/mips/bcm63xx/early_printk.c b/arch/mips/bcm63xx/early_printk.c
new file mode 100644
index 0000000..bf353c9
--- /dev/null
+++ b/arch/mips/bcm63xx/early_printk.c
@@ -0,0 +1,30 @@
+/*
+ * 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) 2008 Maxime Bizon <mbizon@freebox.fr>
+ */
+
+#include <linux/init.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+
+static void __init wait_xfered(void)
+{
+	unsigned int val;
+
+	/* wait for any previous char to be transmitted */
+	do {
+		val = bcm_uart0_readl(UART_IR_REG);
+		if (val & UART_IR_STAT(UART_IR_TXEMPTY))
+			break;
+	} while (1);
+}
+
+void __init prom_putchar(char c)
+{
+	wait_xfered();
+	bcm_uart0_writel(c, UART_FIFO_REG);
+	wait_xfered();
+}
diff --git a/arch/mips/bcm63xx/gpio.c b/arch/mips/bcm63xx/gpio.c
new file mode 100644
index 0000000..87ca390
--- /dev/null
+++ b/arch/mips/bcm63xx/gpio.c
@@ -0,0 +1,134 @@
+/*
+ * 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) 2008 Maxime Bizon <mbizon@freebox.fr>
+ * Copyright (C) 2008 Florian Fainelli <florian@openwrt.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_gpio.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+
+static DEFINE_SPINLOCK(bcm63xx_gpio_lock);
+static u32 gpio_out_low, gpio_out_high;
+
+static void bcm63xx_gpio_set(struct gpio_chip *chip,
+			     unsigned gpio, int val)
+{
+	u32 reg;
+	u32 mask;
+	u32 *v;
+	unsigned long flags;
+
+	if (gpio >= chip->ngpio)
+		BUG();
+
+	if (gpio < 32) {
+		reg = GPIO_DATA_LO_REG;
+		mask = 1 << gpio;
+		v = &gpio_out_low;
+	} else {
+		reg = GPIO_DATA_HI_REG;
+		mask = 1 << (gpio - 32);
+		v = &gpio_out_high;
+	}
+
+	spin_lock_irqsave(&bcm63xx_gpio_lock, flags);
+	if (val)
+		*v |= mask;
+	else
+		*v &= ~mask;
+	bcm_gpio_writel(*v, reg);
+	spin_unlock_irqrestore(&bcm63xx_gpio_lock, flags);
+}
+
+static int bcm63xx_gpio_get(struct gpio_chip *chip, unsigned gpio)
+{
+	u32 reg;
+	u32 mask;
+
+	if (gpio >= chip->ngpio)
+		BUG();
+
+	if (gpio < 32) {
+		reg = GPIO_DATA_LO_REG;
+		mask = 1 << gpio;
+	} else {
+		reg = GPIO_DATA_HI_REG;
+		mask = 1 << (gpio - 32);
+	}
+
+	return !!(bcm_gpio_readl(reg) & mask);
+}
+
+static int bcm63xx_gpio_set_direction(struct gpio_chip *chip,
+				      unsigned gpio, int dir)
+{
+	u32 reg;
+	u32 mask;
+	u32 tmp;
+	unsigned long flags;
+
+	if (gpio >= chip->ngpio)
+		BUG();
+
+	if (gpio < 32) {
+		reg = GPIO_CTL_LO_REG;
+		mask = 1 << gpio;
+	} else {
+		reg = GPIO_CTL_HI_REG;
+		mask = 1 << (gpio - 32);
+	}
+
+	spin_lock_irqsave(&bcm63xx_gpio_lock, flags);
+	tmp = bcm_gpio_readl(reg);
+	if (dir == GPIO_DIR_IN)
+		tmp &= ~mask;
+	else
+		tmp |= mask;
+	bcm_gpio_writel(tmp, reg);
+	spin_unlock_irqrestore(&bcm63xx_gpio_lock, flags);
+
+	return 0;
+}
+
+static int bcm63xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+	return bcm63xx_gpio_set_direction(chip, gpio, GPIO_DIR_IN);
+}
+
+static int bcm63xx_gpio_direction_output(struct gpio_chip *chip,
+					 unsigned gpio, int value)
+{
+	bcm63xx_gpio_set(chip, gpio, value);
+	return bcm63xx_gpio_set_direction(chip, gpio, GPIO_DIR_OUT);
+}
+
+
+static struct gpio_chip bcm63xx_gpio_chip = {
+	.label			= "bcm63xx-gpio",
+	.direction_input	= bcm63xx_gpio_direction_input,
+	.direction_output	= bcm63xx_gpio_direction_output,
+	.get			= bcm63xx_gpio_get,
+	.set			= bcm63xx_gpio_set,
+	.base			= 0,
+};
+
+int __init bcm63xx_gpio_init(void)
+{
+	bcm63xx_gpio_chip.ngpio = bcm63xx_gpio_count();
+	pr_info("registering %d GPIOs\n", bcm63xx_gpio_chip.ngpio);
+
+	return gpiochip_add(&bcm63xx_gpio_chip);
+}
+
+arch_initcall(bcm63xx_gpio_init);
diff --git a/arch/mips/bcm63xx/irq.c b/arch/mips/bcm63xx/irq.c
new file mode 100644
index 0000000..a0c5cd1
--- /dev/null
+++ b/arch/mips/bcm63xx/irq.c
@@ -0,0 +1,253 @@
+/*
+ * 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) 2008 Maxime Bizon <mbizon@freebox.fr>
+ * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_irq.h>
+
+/*
+ * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
+ * prioritize any interrupt relatively to another. the static counter
+ * will resume the loop where it ended the last time we left this
+ * function.
+ */
+static void bcm63xx_irq_dispatch_internal(void)
+{
+	u32 pending;
+	static int i;
+
+	pending = bcm_perf_readl(PERF_IRQMASK_REG) &
+		bcm_perf_readl(PERF_IRQSTAT_REG);
+
+	if (!pending)
+		return ;
+
+	while (1) {
+		int to_call = i;
+
+		i = (i + 1) & 0x1f;
+		if (pending & (1 << to_call)) {
+			do_IRQ(to_call + IRQ_INTERNAL_BASE);
+			break;
+		}
+	}
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+	u32 cause;
+
+	do {
+		cause = read_c0_cause() & read_c0_status() & ST0_IM;
+
+		if (!cause)
+			break;
+
+		if (cause & CAUSEF_IP7)
+			do_IRQ(7);
+		if (cause & CAUSEF_IP2)
+			bcm63xx_irq_dispatch_internal();
+		if (cause & CAUSEF_IP3)
+			do_IRQ(IRQ_EXT_0);
+		if (cause & CAUSEF_IP4)
+			do_IRQ(IRQ_EXT_1);
+		if (cause & CAUSEF_IP5)
+			do_IRQ(IRQ_EXT_2);
+		if (cause & CAUSEF_IP6)
+			do_IRQ(IRQ_EXT_3);
+	} while (1);
+}
+
+/*
+ * internal IRQs operations: only mask/unmask on PERF irq mask
+ * register.
+ */
+static inline void bcm63xx_internal_irq_mask(unsigned int irq)
+{
+	u32 mask;
+
+	irq -= IRQ_INTERNAL_BASE;
+	mask = bcm_perf_readl(PERF_IRQMASK_REG);
+	mask &= ~(1 << irq);
+	bcm_perf_writel(mask, PERF_IRQMASK_REG);
+}
+
+static void bcm63xx_internal_irq_unmask(unsigned int irq)
+{
+	u32 mask;
+
+	irq -= IRQ_INTERNAL_BASE;
+	mask = bcm_perf_readl(PERF_IRQMASK_REG);
+	mask |= (1 << irq);
+	bcm_perf_writel(mask, PERF_IRQMASK_REG);
+}
+
+static unsigned int bcm63xx_internal_irq_startup(unsigned int irq)
+{
+	bcm63xx_internal_irq_unmask(irq);
+	return 0;
+}
+
+/*
+ * external IRQs operations: mask/unmask and clear on PERF external
+ * irq control register.
+ */
+static void bcm63xx_external_irq_mask(unsigned int irq)
+{
+	u32 reg;
+
+	irq -= IRQ_EXT_BASE;
+	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
+	reg &= ~EXTIRQ_CFG_MASK(irq);
+	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
+}
+
+static void bcm63xx_external_irq_unmask(unsigned int irq)
+{
+	u32 reg;
+
+	irq -= IRQ_EXT_BASE;
+	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
+	reg |= EXTIRQ_CFG_MASK(irq);
+	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
+}
+
+static void bcm63xx_external_irq_clear(unsigned int irq)
+{
+	u32 reg;
+
+	irq -= IRQ_EXT_BASE;
+	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
+	reg |= EXTIRQ_CFG_CLEAR(irq);
+	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
+}
+
+static unsigned int bcm63xx_external_irq_startup(unsigned int irq)
+{
+	set_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
+	irq_enable_hazard();
+	bcm63xx_external_irq_unmask(irq);
+	return 0;
+}
+
+static void bcm63xx_external_irq_shutdown(unsigned int irq)
+{
+	bcm63xx_external_irq_mask(irq);
+	clear_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
+	irq_disable_hazard();
+}
+
+static int bcm63xx_external_irq_set_type(unsigned int irq,
+					 unsigned int flow_type)
+{
+	u32 reg;
+	struct irq_desc *desc = irq_desc + irq;
+
+	irq -= IRQ_EXT_BASE;
+
+	flow_type &= IRQ_TYPE_SENSE_MASK;
+
+	if (flow_type == IRQ_TYPE_NONE)
+		flow_type = IRQ_TYPE_LEVEL_LOW;
+
+	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
+	switch (flow_type) {
+	case IRQ_TYPE_EDGE_BOTH:
+		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
+		reg |= EXTIRQ_CFG_BOTHEDGE(irq);
+		break;
+
+	case IRQ_TYPE_EDGE_RISING:
+		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
+		reg |= EXTIRQ_CFG_SENSE(irq);
+		reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
+		break;
+
+	case IRQ_TYPE_EDGE_FALLING:
+		reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
+		reg &= ~EXTIRQ_CFG_SENSE(irq);
+		reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
+		break;
+
+	case IRQ_TYPE_LEVEL_HIGH:
+		reg |= EXTIRQ_CFG_LEVELSENSE(irq);
+		reg |= EXTIRQ_CFG_SENSE(irq);
+		break;
+
+	case IRQ_TYPE_LEVEL_LOW:
+		reg |= EXTIRQ_CFG_LEVELSENSE(irq);
+		reg &= ~EXTIRQ_CFG_SENSE(irq);
+		break;
+
+	default:
+		printk(KERN_ERR "bogus flow type combination given !\n");
+		return -EINVAL;
+	}
+	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
+
+	if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))  {
+		desc->status |= IRQ_LEVEL;
+		desc->handle_irq = handle_level_irq;
+	} else {
+		desc->handle_irq = handle_edge_irq;
+	}
+
+	return 0;
+}
+
+static struct irq_chip bcm63xx_internal_irq_chip = {
+	.name		= "bcm63xx_ipic",
+	.startup	= bcm63xx_internal_irq_startup,
+	.shutdown	= bcm63xx_internal_irq_mask,
+
+	.mask		= bcm63xx_internal_irq_mask,
+	.mask_ack	= bcm63xx_internal_irq_mask,
+	.unmask		= bcm63xx_internal_irq_unmask,
+};
+
+static struct irq_chip bcm63xx_external_irq_chip = {
+	.name		= "bcm63xx_epic",
+	.startup	= bcm63xx_external_irq_startup,
+	.shutdown	= bcm63xx_external_irq_shutdown,
+
+	.ack		= bcm63xx_external_irq_clear,
+
+	.mask		= bcm63xx_external_irq_mask,
+	.unmask		= bcm63xx_external_irq_unmask,
+
+	.set_type	= bcm63xx_external_irq_set_type,
+};
+
+static struct irqaction cpu_ip2_cascade_action = {
+	.handler	= no_action,
+	.name		= "cascade_ip2",
+};
+
+void __init arch_init_irq(void)
+{
+	int i;
+
+	mips_cpu_irq_init();
+	for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
+		set_irq_chip_and_handler(i, &bcm63xx_internal_irq_chip,
+					 handle_level_irq);
+
+	for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
+		set_irq_chip_and_handler(i, &bcm63xx_external_irq_chip,
+					 handle_edge_irq);
+
+	setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
+}
diff --git a/arch/mips/bcm63xx/prom.c b/arch/mips/bcm63xx/prom.c
new file mode 100644
index 0000000..fb284fb
--- /dev/null
+++ b/arch/mips/bcm63xx/prom.c
@@ -0,0 +1,55 @@
+/*
+ * 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) 2008 Maxime Bizon <mbizon@freebox.fr>
+ */
+
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <asm/bootinfo.h>
+#include <bcm63xx_board.h>
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_gpio.h>
+
+void __init prom_init(void)
+{
+	u32 reg, mask;
+
+	bcm63xx_cpu_init();
+
+	/* stop any running watchdog */
+	bcm_wdt_writel(WDT_STOP_1, WDT_CTL_REG);
+	bcm_wdt_writel(WDT_STOP_2, WDT_CTL_REG);
+
+	/* disable all hardware blocks clock for now */
+	if (BCMCPU_IS_6338())
+		mask = CKCTL_6338_ALL_SAFE_EN;
+	else if (BCMCPU_IS_6345())
+		mask = CKCTL_6345_ALL_SAFE_EN;
+	else if (BCMCPU_IS_6348())
+		mask = CKCTL_6348_ALL_SAFE_EN;
+	else
+		/* BCMCPU_IS_6358() */
+		mask = CKCTL_6358_ALL_SAFE_EN;
+
+	reg = bcm_perf_readl(PERF_CKCTL_REG);
+	reg &= ~mask;
+	bcm_perf_writel(reg, PERF_CKCTL_REG);
+
+	/* assign command line from kernel config */
+	strcpy(arcs_cmdline, CONFIG_CMDLINE);
+
+	/* register gpiochip */
+	bcm63xx_gpio_init();
+
+	/* do low level board init */
+	board_prom_init();
+}
+
+void __init prom_free_prom_memory(void)
+{
+}
diff --git a/arch/mips/bcm63xx/setup.c b/arch/mips/bcm63xx/setup.c
new file mode 100644
index 0000000..b18a0ca
--- /dev/null
+++ b/arch/mips/bcm63xx/setup.c
@@ -0,0 +1,125 @@
+/*
+ * 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) 2008 Maxime Bizon <mbizon@freebox.fr>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/bootmem.h>
+#include <linux/ioport.h>
+#include <linux/pm.h>
+#include <asm/bootinfo.h>
+#include <asm/time.h>
+#include <asm/reboot.h>
+#include <asm/cacheflush.h>
+#include <bcm63xx_board.h>
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_io.h>
+
+void bcm63xx_machine_halt(void)
+{
+	printk(KERN_INFO "System halted\n");
+	while (1)
+		;
+}
+
+static void bcm6348_a1_reboot(void)
+{
+	u32 reg;
+
+	/* soft reset all blocks */
+	printk(KERN_INFO "soft-reseting all blocks ...\n");
+	reg = bcm_perf_readl(PERF_SOFTRESET_REG);
+	reg &= ~SOFTRESET_6348_ALL;
+	bcm_perf_writel(reg, PERF_SOFTRESET_REG);
+	mdelay(10);
+
+	reg = bcm_perf_readl(PERF_SOFTRESET_REG);
+	reg |= SOFTRESET_6348_ALL;
+	bcm_perf_writel(reg, PERF_SOFTRESET_REG);
+	mdelay(10);
+
+	/* Jump to the power on address. */
+	printk(KERN_INFO "jumping to reset vector.\n");
+	/* set high vectors (base at 0xbfc00000 */
+	set_c0_status(ST0_BEV | ST0_ERL);
+	/* run uncached in kseg0 */
+	change_c0_config(CONF_CM_CMASK, CONF_CM_UNCACHED);
+	__flush_cache_all();
+	/* remove all wired TLB entries */
+	write_c0_wired(0);
+	__asm__ __volatile__(
+		"jr\t%0"
+		:
+		: "r" (0xbfc00000));
+	while (1)
+		;
+}
+
+void bcm63xx_machine_reboot(void)
+{
+	u32 reg;
+
+	/* mask and clear all external irq */
+	reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
+	reg &= ~EXTIRQ_CFG_MASK_ALL;
+	reg |= EXTIRQ_CFG_CLEAR_ALL;
+	bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
+
+	if (BCMCPU_IS_6348() && (bcm63xx_get_cpu_rev() == 0xa1))
+		bcm6348_a1_reboot();
+
+	printk(KERN_INFO "triggering watchdog soft-reset...\n");
+	bcm_perf_writel(SYS_PLL_SOFT_RESET, PERF_SYS_PLL_CTL_REG);
+	while (1)
+		;
+}
+
+static void __bcm63xx_machine_reboot(char *p)
+{
+	bcm63xx_machine_reboot();
+}
+
+/*
+ * return system type in /proc/cpuinfo
+ */
+const char *get_system_type(void)
+{
+	static char buf[128];
+	snprintf(buf, sizeof(buf), "bcm63xx/%s (0x%04x/0x%04X)",
+		 board_get_name(),
+		 bcm63xx_get_cpu_id(), bcm63xx_get_cpu_rev());
+	return buf;
+}
+
+void __init plat_time_init(void)
+{
+	mips_hpt_frequency = bcm63xx_get_cpu_freq() / 2;
+}
+
+void __init plat_mem_setup(void)
+{
+	add_memory_region(0, bcm63xx_get_memory_size(), BOOT_MEM_RAM);
+
+	_machine_halt = bcm63xx_machine_halt;
+	_machine_restart = __bcm63xx_machine_reboot;
+	pm_power_off = bcm63xx_machine_halt;
+
+	set_io_port_base(0);
+	ioport_resource.start = 0;
+	ioport_resource.end = ~0;
+
+	board_setup();
+}
+
+int __init bcm63xx_register_devices(void)
+{
+	return board_register_devices();
+}
+
+arch_initcall(bcm63xx_register_devices);
diff --git a/arch/mips/bcm63xx/timer.c b/arch/mips/bcm63xx/timer.c
new file mode 100644
index 0000000..ba522bd
--- /dev/null
+++ b/arch/mips/bcm63xx/timer.c
@@ -0,0 +1,205 @@
+/*
+ * 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) 2008 Maxime Bizon <mbizon@freebox.fr>
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_timer.h>
+#include <bcm63xx_regs.h>
+
+static DEFINE_SPINLOCK(timer_reg_lock);
+static DEFINE_SPINLOCK(timer_data_lock);
+static struct clk *periph_clk;
+
+static struct timer_data {
+	void	(*cb)(void *);
+	void	*data;
+} timer_data[BCM63XX_TIMER_COUNT];
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+	u32 stat;
+	int i;
+
+	spin_lock(&timer_reg_lock);
+	stat = bcm_timer_readl(TIMER_IRQSTAT_REG);
+	bcm_timer_writel(stat, TIMER_IRQSTAT_REG);
+	spin_unlock(&timer_reg_lock);
+
+	for (i = 0; i < BCM63XX_TIMER_COUNT; i++) {
+		if (!(stat & TIMER_IRQSTAT_TIMER_CAUSE(i)))
+			continue;
+
+		spin_lock(&timer_data_lock);
+		if (!timer_data[i].cb) {
+			spin_unlock(&timer_data_lock);
+			continue;
+		}
+
+		timer_data[i].cb(timer_data[i].data);
+		spin_unlock(&timer_data_lock);
+	}
+
+	return IRQ_HANDLED;
+}
+
+int bcm63xx_timer_enable(int id)
+{
+	u32 reg;
+	unsigned long flags;
+
+	if (id >= BCM63XX_TIMER_COUNT)
+		return -EINVAL;
+
+	spin_lock_irqsave(&timer_reg_lock, flags);
+
+	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
+	reg |= TIMER_CTL_ENABLE_MASK;
+	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
+
+	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
+	reg |= TIMER_IRQSTAT_TIMER_IR_EN(id);
+	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
+
+	spin_unlock_irqrestore(&timer_reg_lock, flags);
+	return 0;
+}
+
+EXPORT_SYMBOL(bcm63xx_timer_enable);
+
+int bcm63xx_timer_disable(int id)
+{
+	u32 reg;
+	unsigned long flags;
+
+	if (id >= BCM63XX_TIMER_COUNT)
+		return -EINVAL;
+
+	spin_lock_irqsave(&timer_reg_lock, flags);
+
+	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
+	reg &= ~TIMER_CTL_ENABLE_MASK;
+	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
+
+	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
+	reg &= ~TIMER_IRQSTAT_TIMER_IR_EN(id);
+	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
+
+	spin_unlock_irqrestore(&timer_reg_lock, flags);
+	return 0;
+}
+
+EXPORT_SYMBOL(bcm63xx_timer_disable);
+
+int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data)
+{
+	unsigned long flags;
+	int ret;
+
+	if (id >= BCM63XX_TIMER_COUNT || !callback)
+		return -EINVAL;
+
+	ret = 0;
+	spin_lock_irqsave(&timer_data_lock, flags);
+	if (timer_data[id].cb) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	timer_data[id].cb = callback;
+	timer_data[id].data = data;
+
+out:
+	spin_unlock_irqrestore(&timer_data_lock, flags);
+	return ret;
+}
+
+EXPORT_SYMBOL(bcm63xx_timer_register);
+
+void bcm63xx_timer_unregister(int id)
+{
+	unsigned long flags;
+
+	if (id >= BCM63XX_TIMER_COUNT)
+		return;
+
+	spin_lock_irqsave(&timer_data_lock, flags);
+	timer_data[id].cb = NULL;
+	spin_unlock_irqrestore(&timer_data_lock, flags);
+}
+
+EXPORT_SYMBOL(bcm63xx_timer_unregister);
+
+unsigned int bcm63xx_timer_countdown(unsigned int countdown_us)
+{
+	return (clk_get_rate(periph_clk) / (1000 * 1000)) * countdown_us;
+}
+
+EXPORT_SYMBOL(bcm63xx_timer_countdown);
+
+int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us)
+{
+	u32 reg, countdown;
+	unsigned long flags;
+
+	if (id >= BCM63XX_TIMER_COUNT)
+		return -EINVAL;
+
+	countdown = bcm63xx_timer_countdown(countdown_us);
+	if (countdown & ~TIMER_CTL_COUNTDOWN_MASK)
+		return -EINVAL;
+
+	spin_lock_irqsave(&timer_reg_lock, flags);
+	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
+
+	if (monotonic)
+		reg &= ~TIMER_CTL_MONOTONIC_MASK;
+	else
+		reg |= TIMER_CTL_MONOTONIC_MASK;
+
+	reg &= ~TIMER_CTL_COUNTDOWN_MASK;
+	reg |= countdown;
+	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
+
+	spin_unlock_irqrestore(&timer_reg_lock, flags);
+	return 0;
+}
+
+EXPORT_SYMBOL(bcm63xx_timer_set);
+
+int bcm63xx_timer_init(void)
+{
+	int ret, irq;
+	u32 reg;
+
+	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
+	reg &= ~TIMER_IRQSTAT_TIMER0_IR_EN;
+	reg &= ~TIMER_IRQSTAT_TIMER1_IR_EN;
+	reg &= ~TIMER_IRQSTAT_TIMER2_IR_EN;
+	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
+
+	periph_clk = clk_get(NULL, "periph");
+	if (IS_ERR(periph_clk))
+		return -ENODEV;
+
+	irq = bcm63xx_get_irq_number(IRQ_TIMER);
+	ret = request_irq(irq, timer_interrupt, 0, "bcm63xx_timer", NULL);
+	if (ret) {
+		printk(KERN_ERR "bcm63xx_timer: failed to register irq\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+arch_initcall(bcm63xx_timer_init);