view sources/patches/linux-bcm47xx.patch @ 577:2d22a34b6105

Add hw-wrt610n hardware target using BASE_ARCH=mipsel.
author Rob Landley <rob@landley.net>
date Fri, 09 Jan 2009 22:03:04 -0600
parents
children
line wrap: on
line source

This patch does three things:

1) Add gigabit support to the broadcom ssb ethernet driver.

2) Add code to read environment variables from the CFE boot loader, and use
   them during boot.

3) Add code to read the CFE flash partitions as mtd partitions.

diff -ruN linux-2.6.25.10/arch/mips/bcm47xx/cfe_env.c linux-new/arch/mips/bcm47xx/cfe_env.c
--- linux-2.6.25.10/arch/mips/bcm47xx/cfe_env.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-new/arch/mips/bcm47xx/cfe_env.c	2009-01-09 21:45:52.000000000 -0600
@@ -0,0 +1,229 @@
+/*
+ * CFE environment variable access
+ *
+ * Copyright 2001-2003, Broadcom Corporation
+ * Copyright 2006, Felix Fietkau <nbd@openwrt.org>
+ * 
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define NVRAM_SIZE       (0x1ff0)
+static char _nvdata[NVRAM_SIZE];
+static char _valuestr[256];
+
+/*
+ * TLV types.  These codes are used in the "type-length-value"
+ * encoding of the items stored in the NVRAM device (flash or EEPROM)
+ *
+ * The layout of the flash/nvram is as follows:
+ *
+ * <type> <length> <data ...> <type> <length> <data ...> <type_end>
+ *
+ * The type code of "ENV_TLV_TYPE_END" marks the end of the list.
+ * The "length" field marks the length of the data section, not
+ * including the type and length fields.
+ *
+ * Environment variables are stored as follows:
+ *
+ * <type_env> <length> <flags> <name> = <value>
+ *
+ * If bit 0 (low bit) is set, the length is an 8-bit value.
+ * If bit 0 (low bit) is clear, the length is a 16-bit value
+ * 
+ * Bit 7 set indicates "user" TLVs.  In this case, bit 0 still
+ * indicates the size of the length field.  
+ *
+ * Flags are from the constants below:
+ *
+ */
+#define ENV_LENGTH_16BITS	0x00	/* for low bit */
+#define ENV_LENGTH_8BITS	0x01
+
+#define ENV_TYPE_USER		0x80
+
+#define ENV_CODE_SYS(n,l) (((n)<<1)|(l))
+#define ENV_CODE_USER(n,l) ((((n)<<1)|(l)) | ENV_TYPE_USER)
+
+/*
+ * The actual TLV types we support
+ */
+
+#define ENV_TLV_TYPE_END	0x00	
+#define ENV_TLV_TYPE_ENV	ENV_CODE_SYS(0,ENV_LENGTH_8BITS)
+
+/*
+ * Environment variable flags 
+ */
+
+#define ENV_FLG_NORMAL		0x00	/* normal read/write */
+#define ENV_FLG_BUILTIN		0x01	/* builtin - not stored in flash */
+#define ENV_FLG_READONLY	0x02	/* read-only - cannot be changed */
+
+#define ENV_FLG_MASK		0xFF	/* mask of attributes we keep */
+#define ENV_FLG_ADMIN		0x100	/* lets us internally override permissions */
+
+
+/*  *********************************************************************
+    *  _nvram_read(buffer,offset,length)
+    *  
+    *  Read data from the NVRAM device
+    *  
+    *  Input parameters: 
+    *  	   buffer - destination buffer
+    *  	   offset - offset of data to read
+    *  	   length - number of bytes to read
+    *  	   
+    *  Return value:
+    *  	   number of bytes read, or <0 if error occured
+    ********************************************************************* */
+static int
+_nvram_read(unsigned char *nv_buf, unsigned char *buffer, int offset, int length)
+{
+    int i;
+    if (offset > NVRAM_SIZE)
+	return -1; 
+
+    for ( i = 0; i < length; i++) {
+	buffer[i] = ((volatile unsigned char*)nv_buf)[offset + i];
+    }
+    return length;
+}
+
+
+static char*
+_strnchr(const char *dest,int c,size_t cnt)
+{
+	while (*dest && (cnt > 0)) {
+	if (*dest == c) return (char *) dest;
+	dest++;
+	cnt--;
+	}
+	return NULL;
+}
+
+
+
+/*
+ * Core support API: Externally visible.
+ */
+
+/*
+ * Get the value of an NVRAM variable
+ * @param	name	name of variable to get
+ * @return	value of variable or NULL if undefined
+ */
+
+char* 
+cfe_env_get(unsigned char *nv_buf, char* name)
+{
+    int size;
+    unsigned char *buffer;
+    unsigned char *ptr;
+    unsigned char *envval;
+    unsigned int reclen;
+    unsigned int rectype;
+    int offset;
+    int flg;
+    
+	if (!strcmp(name, "nvram_type"))
+		return "cfe";
+	
+    size = NVRAM_SIZE;
+    buffer = &_nvdata[0];
+
+    ptr = buffer;
+    offset = 0;
+
+    /* Read the record type and length */
+    if (_nvram_read(nv_buf, ptr,offset,1) != 1) {
+	goto error;
+    }
+    
+    while ((*ptr != ENV_TLV_TYPE_END)  && (size > 1)) {
+
+	/* Adjust pointer for TLV type */
+	rectype = *(ptr);
+	offset++;
+	size--;
+
+	/* 
+	 * Read the length.  It can be either 1 or 2 bytes
+	 * depending on the code 
+	 */
+	if (rectype & ENV_LENGTH_8BITS) {
+	    /* Read the record type and length - 8 bits */
+	    if (_nvram_read(nv_buf, ptr,offset,1) != 1) {
+		goto error;
+	    }
+	    reclen = *(ptr);
+	    size--;
+	    offset++;
+	}
+	else {
+	    /* Read the record type and length - 16 bits, MSB first */
+	    if (_nvram_read(nv_buf, ptr,offset,2) != 2) {
+		goto error;
+	    }
+	    reclen = (((unsigned int) *(ptr)) << 8) + (unsigned int) *(ptr+1);
+	    size -= 2;
+	    offset += 2;
+	}
+
+	if (reclen > size)
+	    break;	/* should not happen, bad NVRAM */
+
+	switch (rectype) {
+	    case ENV_TLV_TYPE_ENV:
+		/* Read the TLV data */
+		if (_nvram_read(nv_buf, ptr,offset,reclen) != reclen)
+		    goto error;
+		flg = *ptr++;
+		envval = (unsigned char *) _strnchr(ptr,'=',(reclen-1));
+		if (envval) {
+		    *envval++ = '\0';
+		    memcpy(_valuestr,envval,(reclen-1)-(envval-ptr));
+		    _valuestr[(reclen-1)-(envval-ptr)] = '\0';
+#if 0			
+		    printk(KERN_INFO "NVRAM:%s=%s\n", ptr, _valuestr);
+#endif
+		    if(!strcmp(ptr, name)){
+			return _valuestr;
+		    }
+		    if((strlen(ptr) > 1) && !strcmp(&ptr[1], name))
+			return _valuestr;
+		}
+		break;
+		
+	    default: 
+		/* Unknown TLV type, skip it. */
+		break;
+	    }
+
+	/*
+	 * Advance to next TLV 
+	 */
+		
+	size -= (int)reclen;
+	offset += reclen;
+
+	/* Read the next record type */
+	ptr = buffer;
+	if (_nvram_read(nv_buf, ptr,offset,1) != 1)
+	    goto error;
+	}
+
+error:
+    return NULL;
+
+}
+
diff -ruN linux-2.6.25.10/arch/mips/bcm47xx/include/nvram.h linux-new/arch/mips/bcm47xx/include/nvram.h
--- linux-2.6.25.10/arch/mips/bcm47xx/include/nvram.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-new/arch/mips/bcm47xx/include/nvram.h	2009-01-09 21:45:52.000000000 -0600
@@ -0,0 +1,37 @@
+/*
+ *  Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#ifndef __NVRAM_H
+#define __NVRAM_H
+
+struct nvram_header {
+	u32 magic;
+	u32 len;
+	u32 crc_ver_init;	/* 0:7 crc, 8:15 ver, 16:31 sdram_init */
+	u32 config_refresh;	/* 0:15 sdram_config, 16:31 sdram_refresh */
+	u32 config_ncdl;	/* ncdl values for memc */
+};
+
+struct nvram_tuple {
+	char *name;
+	char *value;
+	struct nvram_tuple *next;
+};
+
+#define NVRAM_HEADER		0x48534C46	/* 'FLSH' */
+#define NVRAM_VERSION		1
+#define NVRAM_HEADER_SIZE	20
+#define NVRAM_SPACE		0x8000
+
+#define NVRAM_MAX_VALUE_LEN 255
+#define NVRAM_MAX_PARAM_LEN 64
+
+char *nvram_get(const char *name);
+
+#endif
diff -ruN linux-2.6.25.10/arch/mips/bcm47xx/irq.c linux-new/arch/mips/bcm47xx/irq.c
--- linux-2.6.25.10/arch/mips/bcm47xx/irq.c	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/arch/mips/bcm47xx/irq.c	2009-01-09 21:45:52.000000000 -0600
@@ -1,5 +1,6 @@
 /*
  *  Copyright (C) 2004 Florian Schirmer <jolt@tuxbox.org>
+ *  Copyright (C) 2008 Michael Buesch <mb@bu3sch.de>
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  under  the terms of  the GNU General  Public License as published by the
@@ -23,10 +24,19 @@
  */
 
 #include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/pci.h>
+#include <linux/ssb/ssb.h>
+
 #include <asm/irq_cpu.h>
 
+
+extern struct ssb_bus ssb_bcm47xx;
+
+
 void plat_irq_dispatch(void)
 {
 	u32 cause;
@@ -53,3 +63,19 @@
 {
 	mips_cpu_irq_init();
 }
+
+int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+	int res;
+
+	res = ssb_pcibios_map_irq(dev, slot, pin);
+	if (res < 0) {
+		printk(KERN_ALERT "PCI: Failed to map IRQ of device %s\n",
+		       dev->dev.bus_id);
+		return 0;
+	}
+	/* IRQ-0 and IRQ-1 are software interrupts. */
+	WARN_ON((res == 0) || (res == 1));
+
+	return res;
+}
diff -ruN linux-2.6.25.10/arch/mips/bcm47xx/Makefile linux-new/arch/mips/bcm47xx/Makefile
--- linux-2.6.25.10/arch/mips/bcm47xx/Makefile	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/arch/mips/bcm47xx/Makefile	2009-01-09 21:45:52.000000000 -0600
@@ -3,4 +3,4 @@
 # under Linux.
 #
 
-obj-y := gpio.o irq.o prom.o serial.o setup.o time.o wgt634u.o
+obj-y := cfe_env.o gpio.o irq.o nvram.o prom.o serial.o setup.o time.o wgt634u.o
diff -ruN linux-2.6.25.10/arch/mips/bcm47xx/nvram.c linux-new/arch/mips/bcm47xx/nvram.c
--- linux-2.6.25.10/arch/mips/bcm47xx/nvram.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-new/arch/mips/bcm47xx/nvram.c	2009-01-09 21:45:52.000000000 -0600
@@ -0,0 +1,125 @@
+/*
+ * BCM947xx nvram variable access
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, Felix Fietkau <nbd@openwrt.org>
+ * 
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ssb/ssb.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+#include <asm/bootinfo.h>
+#include <asm/addrspace.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "include/nvram.h"
+
+#define MB * 1048576
+extern struct ssb_bus ssb_bcm47xx;
+
+static char nvram_buf[NVRAM_SPACE];
+static int cfe_env;
+extern char *cfe_env_get(char *nv_buf, const char *name);
+		
+/* Probe for NVRAM header */
+static void __init early_nvram_init(void)
+{
+	struct ssb_mipscore *mcore = &ssb_bcm47xx.mipscore;
+	struct nvram_header *header;
+	int i;
+	u32 base, lim, off;
+	u32 *src, *dst;
+	
+	base = mcore->flash_window;
+	lim = mcore->flash_window_size;
+	cfe_env = 0;
+
+	
+	/* XXX: hack for supporting the CFE environment stuff on WGT634U */
+	if (lim >= 8 MB) {
+		src = (u32 *) KSEG1ADDR(base + 8 MB - 0x2000);
+		dst = (u32 *) nvram_buf;
+
+		if ((*src & 0xff00ff) == 0x000001) {
+			printk("early_nvram_init: WGT634U NVRAM found.\n");
+
+			for (i = 0; i < 0x1ff0; i++) {
+				if (*src == 0xFFFFFFFF)
+					break;
+				*dst++ = *src++;
+			}
+			cfe_env = 1;
+			return;
+		}
+	}
+
+	off = 0x20000;
+	while (off <= lim) {
+		/* Windowed flash access */
+		header = (struct nvram_header *) KSEG1ADDR(base + off - NVRAM_SPACE);
+		if (header->magic == NVRAM_HEADER)
+			goto found;
+		off <<= 1;
+	}
+
+	/* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
+	header = (struct nvram_header *) KSEG1ADDR(base + 4096);
+	if (header->magic == NVRAM_HEADER)
+		goto found;
+	
+	header = (struct nvram_header *) KSEG1ADDR(base + 1024);
+	if (header->magic == NVRAM_HEADER)
+		goto found;
+	
+	return;
+
+found:
+	src = (u32 *) header;
+	dst = (u32 *) nvram_buf;
+	for (i = 0; i < sizeof(struct nvram_header); i += 4)
+		*dst++ = *src++;
+	for (; i < header->len && i < NVRAM_SPACE; i += 4)
+		*dst++ = le32_to_cpu(*src++);
+}
+
+char *nvram_get(const char *name)
+{
+	char *var, *value, *end, *eq;
+
+	if (!name)
+		return NULL;
+
+	if (!nvram_buf[0])
+		early_nvram_init();
+
+	if (cfe_env)
+		return cfe_env_get(nvram_buf, name);
+
+	/* Look for name=value and return value */
+	var = &nvram_buf[sizeof(struct nvram_header)];
+	end = nvram_buf + sizeof(nvram_buf) - 2;
+	end[0] = end[1] = '\0';
+	for (; *var; var = value + strlen(value) + 1) {
+		if (!(eq = strchr(var, '=')))
+			break;
+		value = eq + 1;
+		if ((eq - var) == strlen(name) && strncmp(var, name, (eq - var)) == 0)
+			return value;
+	}
+
+	return NULL;
+}
+
+EXPORT_SYMBOL(nvram_get);
diff -ruN linux-2.6.25.10/arch/mips/bcm47xx/setup.c linux-new/arch/mips/bcm47xx/setup.c
--- linux-2.6.25.10/arch/mips/bcm47xx/setup.c	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/arch/mips/bcm47xx/setup.c	2009-01-09 21:45:52.000000000 -0600
@@ -2,7 +2,7 @@
  *  Copyright (C) 2004 Florian Schirmer <jolt@tuxbox.org>
  *  Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org>
  *  Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
- *  Copyright (C) 2006 Michael Buesch <mb@bu3sch.de>
+ *  Copyright (C) 2006-2008 Michael Buesch <mb@bu3sch.de>
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  under  the terms of  the GNU General  Public License as published by the
@@ -25,23 +25,52 @@
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <linux/init.h>
 #include <linux/types.h>
-#include <linux/ssb/ssb.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/serial_8250.h>
 #include <asm/bootinfo.h>
-#include <asm/reboot.h>
 #include <asm/time.h>
-#include <bcm47xx.h>
+#include <asm/reboot.h>
 #include <asm/fw/cfe/cfe_api.h>
+#include <linux/pm.h>
+#include <linux/ssb/ssb.h>
+#include <linux/ssb/ssb_embedded.h>
+
+#include "include/nvram.h"
 
 struct ssb_bus ssb_bcm47xx;
 EXPORT_SYMBOL(ssb_bcm47xx);
 
+extern void bcm47xx_pci_init(void);
+
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+	int err;
+
+	err = ssb_pcibios_plat_dev_init(dev);
+	if (err) {
+		printk(KERN_ALERT "PCI: Failed to init device %s\n",
+		       pci_name(dev));
+	}
+
+	return err;
+}
+
 static void bcm47xx_machine_restart(char *command)
 {
 	printk(KERN_ALERT "Please stand by while rebooting the system...\n");
 	local_irq_disable();
+	/* CFE has a reboot callback, but that does not work.
+	 * Oopses with: Reserved instruction in kernel code.
+	 */
+
 	/* Set the watchdog timer to reset immediately */
-	ssb_chipco_watchdog_timer_set(&ssb_bcm47xx.chipco, 1);
+	if (ssb_watchdog_timer_set(&ssb_bcm47xx, 1))
+		printk(KERN_EMERG "SSB watchdog-triggered reboot failed!\n");
 	while (1)
 		cpu_relax();
 }
@@ -50,12 +79,13 @@
 {
 	/* Disable interrupts and watchdog and spin forever */
 	local_irq_disable();
-	ssb_chipco_watchdog_timer_set(&ssb_bcm47xx.chipco, 0);
+	if (ssb_watchdog_timer_set(&ssb_bcm47xx, 0))
+		printk(KERN_EMERG "Failed to disable SSB watchdog!\n");
 	while (1)
 		cpu_relax();
 }
 
-static void str2eaddr(char *str, char *dest)
+static void e_aton(char *str, char *dest)
 {
 	int i = 0;
 
@@ -72,52 +102,141 @@
 	}
 }
 
-static int bcm47xx_get_invariants(struct ssb_bus *bus,
-				   struct ssb_init_invariants *iv)
+static void bcm47xx_fill_sprom(struct ssb_sprom *sprom)
 {
-	char buf[100];
+	char *s;
 
-	/* Fill boardinfo structure */
-	memset(&(iv->boardinfo), 0 , sizeof(struct ssb_boardinfo));
+	memset(sprom, 0xFF, sizeof(struct ssb_sprom));
 
-	if (cfe_getenv("boardvendor", buf, sizeof(buf)) >= 0)
-		iv->boardinfo.type = (u16)simple_strtoul(buf, NULL, 0);
-	if (cfe_getenv("boardtype", buf, sizeof(buf)) >= 0)
-		iv->boardinfo.type = (u16)simple_strtoul(buf, NULL, 0);
-	if (cfe_getenv("boardrev", buf, sizeof(buf)) >= 0)
-		iv->boardinfo.rev = (u16)simple_strtoul(buf, NULL, 0);
-
-	/* Fill sprom structure */
-	memset(&(iv->sprom), 0, sizeof(struct ssb_sprom));
-	iv->sprom.revision = 3;
-
-	if (cfe_getenv("et0macaddr", buf, sizeof(buf)) >= 0)
-		str2eaddr(buf, iv->sprom.et0mac);
-	if (cfe_getenv("et1macaddr", buf, sizeof(buf)) >= 0)
-		str2eaddr(buf, iv->sprom.et1mac);
-	if (cfe_getenv("et0phyaddr", buf, sizeof(buf)) >= 0)
-		iv->sprom.et0phyaddr = simple_strtoul(buf, NULL, 10);
-	if (cfe_getenv("et1phyaddr", buf, sizeof(buf)) >= 0)
-		iv->sprom.et1phyaddr = simple_strtoul(buf, NULL, 10);
-	if (cfe_getenv("et0mdcport", buf, sizeof(buf)) >= 0)
-		iv->sprom.et0mdcport = simple_strtoul(buf, NULL, 10);
-	if (cfe_getenv("et1mdcport", buf, sizeof(buf)) >= 0)
-		iv->sprom.et1mdcport = simple_strtoul(buf, NULL, 10);
+	sprom->revision = 1;
+	if ((s = nvram_get("il0macaddr")))
+		e_aton(s, sprom->il0mac);
+	if ((s = nvram_get("et0macaddr")))
+		e_aton(s, sprom->et0mac);
+	if ((s = nvram_get("et1macaddr")))
+		e_aton(s, sprom->et1mac);
+	if ((s = nvram_get("et0phyaddr")))
+		sprom->et0phyaddr = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("et1phyaddr")))
+		sprom->et1phyaddr = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("et0mdcport")))
+		sprom->et0mdcport = !!simple_strtoul(s, NULL, 10);
+	if ((s = nvram_get("et1mdcport")))
+		sprom->et1mdcport = !!simple_strtoul(s, NULL, 10);
+	if ((s = nvram_get("pa0b0")))
+		sprom->pa0b0 = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("pa0b1")))
+		sprom->pa0b1 = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("pa0b2")))
+		sprom->pa0b2 = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("pa1b0")))
+		sprom->pa1b0 = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("pa1b1")))
+		sprom->pa1b1 = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("pa1b2")))
+		sprom->pa1b2 = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("wl0gpio0")))
+		sprom->gpio0 = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("wl0gpio1")))
+		sprom->gpio1 = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("wl0gpio2")))
+		sprom->gpio2 = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("wl0gpio3")))
+		sprom->gpio3 = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("pa0maxpwr")))
+		sprom->maxpwr_bg = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("pa1maxpwr")))
+		sprom->maxpwr_a = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("pa0itssit")))
+		sprom->itssi_bg = simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("pa1itssit")))
+		sprom->itssi_a = simple_strtoul(s, NULL, 0);
+	sprom->boardflags_lo = 0;
+	if ((s = nvram_get("boardflags")))
+		sprom->boardflags_lo = simple_strtoul(s, NULL, 0);
+	sprom->boardflags_hi = 0;
+	if ((s = nvram_get("boardflags2")))
+		sprom->boardflags_hi = simple_strtoul(s, NULL, 0);
+}
+
+static int bcm47xx_get_invariants(struct ssb_bus *bus, struct ssb_init_invariants *iv)
+{
+	char *s;
+
+	iv->boardinfo.vendor = SSB_BOARDVENDOR_BCM;
+	if ((s = nvram_get("boardtype")))
+		iv->boardinfo.type = (u16)simple_strtoul(s, NULL, 0);
+	if ((s = nvram_get("boardrev")))
+		iv->boardinfo.rev = (u16)simple_strtoul(s, NULL, 0);
+
+	bcm47xx_fill_sprom(&iv->sprom);
+
+	if ((s = nvram_get("cardbus")))
+		iv->has_cardbus_slot = !!simple_strtoul(s, NULL, 10);
 
 	return 0;
 }
 
 void __init plat_mem_setup(void)
 {
-	int err;
+	int i, err;
+	char *s;
+	struct ssb_mipscore *mcore;
+
+	err = ssb_bus_ssbbus_register(&ssb_bcm47xx, SSB_ENUM_BASE, bcm47xx_get_invariants);
+	if (err) {
+		const char *msg = "Failed to initialize SSB bus (err %d)\n";
+		printk(msg, err); /* Make sure the message gets out of the box. */
+		panic(msg, err);
+	}
+	mcore = &ssb_bcm47xx.mipscore;
+
+	s = nvram_get("kernel_args");
+	if (s && !strncmp(s, "console=ttyS1", 13)) {
+		struct ssb_serial_port port;
+
+		printk("Swapping serial ports!\n");
+		/* swap serial ports */
+		memcpy(&port, &mcore->serial_ports[0], sizeof(port));
+		memcpy(&mcore->serial_ports[0], &mcore->serial_ports[1], sizeof(port));
+		memcpy(&mcore->serial_ports[1], &port, sizeof(port));
+	}
 
-	err = ssb_bus_ssbbus_register(&ssb_bcm47xx, SSB_ENUM_BASE,
-				      bcm47xx_get_invariants);
-	if (err)
-		panic("Failed to initialize SSB bus (err %d)\n", err);
+	for (i = 0; i < mcore->nr_serial_ports; i++) {
+		struct ssb_serial_port *port = &(mcore->serial_ports[i]);
+		struct uart_port s;
+
+		memset(&s, 0, sizeof(s));
+		s.line = i;
+		s.membase = port->regs;
+		s.irq = port->irq + 2;
+		s.uartclk = port->baud_base;
+		s.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+		s.iotype = SERIAL_IO_MEM;
+		s.regshift = port->reg_shift;
+
+		early_serial_setup(&s);
+	}
+	printk("Serial init done.\n");
 
 	_machine_restart = bcm47xx_machine_restart;
 	_machine_halt = bcm47xx_machine_halt;
 	pm_power_off = bcm47xx_machine_halt;
 }
 
+static int __init bcm47xx_register_gpiodev(void)
+{
+	static struct resource res = {
+		.start = 0xFFFFFFFF,
+	};
+	struct platform_device *pdev;
+
+	pdev = platform_device_register_simple("GPIODEV", 0, &res, 1);
+	if (!pdev) {
+		printk(KERN_ERR "bcm47xx: GPIODEV init failed\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+device_initcall(bcm47xx_register_gpiodev);
diff -ruN linux-2.6.25.10/arch/mips/bcm47xx/time.c linux-new/arch/mips/bcm47xx/time.c
--- linux-2.6.25.10/arch/mips/bcm47xx/time.c	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/arch/mips/bcm47xx/time.c	2009-01-09 21:45:52.000000000 -0600
@@ -22,11 +22,17 @@
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-
 #include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/serial_reg.h>
+#include <linux/interrupt.h>
 #include <linux/ssb/ssb.h>
+#include <asm/addrspace.h>
+#include <asm/io.h>
 #include <asm/time.h>
-#include <bcm47xx.h>
+
+extern struct ssb_bus ssb_bcm47xx;
 
 void __init plat_time_init(void)
 {
diff -ruN linux-2.6.25.10/drivers/mtd/maps/bcm47xx-flash.c linux-new/drivers/mtd/maps/bcm47xx-flash.c
--- linux-2.6.25.10/drivers/mtd/maps/bcm47xx-flash.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-new/drivers/mtd/maps/bcm47xx-flash.c	2009-01-09 21:45:52.000000000 -0600
@@ -0,0 +1,439 @@
+/*
+ *  Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
+ *  Copyright (C) 2005 Waldemar Brodkorb <wbx@openwrt.org>
+ *  Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org)
+ *
+ *  original functions for finding root filesystem from Mike Baker 
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the  GNU General Public License along
+ *  with this program; if not, write  to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ * 
+ *  Copyright 2001-2003, Broadcom Corporation
+ *  All Rights Reserved.
+ * 
+ *  THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+ *  KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+ *  SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+ *  FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+ *
+ *  Flash mapping for BCM947XX boards
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+#include <linux/crc32.h>
+#ifdef CONFIG_SSB
+#include <linux/ssb/ssb.h>
+#endif
+#include <asm/io.h>
+
+
+#define TRX_MAGIC	0x30524448	/* "HDR0" */
+#define TRX_VERSION	1
+#define TRX_MAX_LEN	0x3A0000
+#define TRX_NO_HEADER	1		/* Do not write TRX header */	
+#define TRX_GZ_FILES	0x2     /* Contains up to TRX_MAX_OFFSET individual gzip files */
+#define TRX_MAX_OFFSET	3
+
+struct trx_header {
+	u32 magic;		/* "HDR0" */
+	u32 len;		/* Length of file including header */
+	u32 crc32;		/* 32-bit CRC from flag_version to end of file */
+	u32 flag_version;	/* 0:15 flags, 16:31 version */
+	u32 offsets[TRX_MAX_OFFSET];	/* Offsets of partitions from start of header */
+};
+
+#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
+#define NVRAM_SPACE 0x8000
+#define WINDOW_ADDR 0x1fc00000
+#define WINDOW_SIZE 0x400000
+#define BUSWIDTH 2
+
+#ifdef CONFIG_SSB
+extern struct ssb_bus ssb_bcm47xx;
+#endif
+static struct mtd_info *bcm47xx_mtd;
+
+static void bcm47xx_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+	if (len==1) {
+		memcpy_fromio(to, map->virt + from, len);
+	} else {
+		int i;
+		u16 *dest = (u16 *) to;
+		u16 *src  = (u16 *) (map->virt + from);
+		for (i = 0; i < (len / 2); i++) {
+			dest[i] = src[i];
+		}
+		if (len & 1)
+			*((u8 *)dest+len-1) = src[i] & 0xff;
+	}
+}
+
+static struct map_info bcm47xx_map = {
+	name: "Physically mapped flash",
+	size: WINDOW_SIZE,
+	bankwidth: BUSWIDTH,
+	phys: WINDOW_ADDR,
+};
+
+#ifdef CONFIG_MTD_PARTITIONS
+
+static struct mtd_partition bcm47xx_parts[] = {
+	{ name: "cfe",	offset: 0, size: 0, mask_flags: MTD_WRITEABLE, },
+	{ name: "linux", offset: 0, size: 0, },
+	{ name: "rootfs", offset: 0, size: 0, },
+	{ name: "nvram", offset: 0, size: 0, },
+	{ name: NULL, },
+};
+
+static int __init
+find_cfe_size(struct mtd_info *mtd, size_t size)
+{
+	struct trx_header *trx;
+	unsigned char buf[512];
+	int off;
+	size_t len;
+	int blocksize;
+
+	trx = (struct trx_header *) buf;
+
+	blocksize = mtd->erasesize;
+	if (blocksize < 0x10000)
+		blocksize = 0x10000;
+
+	for (off = (128*1024); off < size; off += blocksize) {
+		memset(buf, 0xe5, sizeof(buf));
+
+		/*
+		 * Read into buffer 
+		 */
+		if (mtd->read(mtd, off, sizeof(buf), &len, buf) ||
+		    len != sizeof(buf))
+			continue;
+
+		/* found a TRX header */
+		if (le32_to_cpu(trx->magic) == TRX_MAGIC) {
+			goto found;
+		}
+	}
+
+	printk(KERN_NOTICE
+	       "%s: Couldn't find bootloader size\n",
+	       mtd->name);
+	return -1;
+
+ found:
+	printk(KERN_NOTICE "bootloader size: %d\n", off);
+	return off;
+
+}
+
+/*
+ * Copied from mtdblock.c
+ *
+ * Cache stuff...
+ * 
+ * Since typical flash erasable sectors are much larger than what Linux's
+ * buffer cache can handle, we must implement read-modify-write on flash
+ * sectors for each block write requests.  To avoid over-erasing flash sectors
+ * and to speed things up, we locally cache a whole flash sector while it is
+ * being written to until a different sector is required.
+ */
+
+static void erase_callback(struct erase_info *done)
+{
+	wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
+	wake_up(wait_q);
+}
+
+static int erase_write (struct mtd_info *mtd, unsigned long pos, 
+			int len, const char *buf)
+{
+	struct erase_info erase;
+	DECLARE_WAITQUEUE(wait, current);
+	wait_queue_head_t wait_q;
+	size_t retlen;
+	int ret;
+
+	/*
+	 * First, let's erase the flash block.
+	 */
+
+	init_waitqueue_head(&wait_q);
+	erase.mtd = mtd;
+	erase.callback = erase_callback;
+	erase.addr = pos;
+	erase.len = len;
+	erase.priv = (u_long)&wait_q;
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue(&wait_q, &wait);
+
+	ret = mtd->erase(mtd, &erase);
+	if (ret) {
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&wait_q, &wait);
+		printk (KERN_WARNING "erase of region [0x%lx, 0x%x] "
+				     "on \"%s\" failed\n",
+			pos, len, mtd->name);
+		return ret;
+	}
+
+	schedule();  /* Wait for erase to finish. */
+	remove_wait_queue(&wait_q, &wait);
+
+	/*
+	 * Next, writhe data to flash.
+	 */
+
+	ret = mtd->write (mtd, pos, len, &retlen, buf);
+	if (ret)
+		return ret;
+	if (retlen != len)
+		return -EIO;
+	return 0;
+}
+
+
+
+
+static int __init
+find_root(struct mtd_info *mtd, size_t size, struct mtd_partition *part)
+{
+	struct trx_header trx, *trx2;
+	unsigned char buf[512], *block;
+	int off, blocksize;
+	u32 i, crc = ~0;
+	size_t len;
+	struct squashfs_super_block *sb = (struct squashfs_super_block *) buf;
+
+	blocksize = mtd->erasesize;
+	if (blocksize < 0x10000)
+		blocksize = 0x10000;
+
+	for (off = (128*1024); off < size; off += blocksize) {
+		memset(&trx, 0xe5, sizeof(trx));
+
+		/*
+		 * Read into buffer 
+		 */
+		if (mtd->read(mtd, off, sizeof(trx), &len, (char *) &trx) ||
+		    len != sizeof(trx))
+			continue;
+
+		/* found a TRX header */
+		if (le32_to_cpu(trx.magic) == TRX_MAGIC) {
+			part->offset = le32_to_cpu(trx.offsets[2]) ? : 
+				le32_to_cpu(trx.offsets[1]);
+			part->size = le32_to_cpu(trx.len); 
+
+			part->size -= part->offset;
+			part->offset += off;
+
+			goto found;
+		}
+	}
+
+	printk(KERN_NOTICE
+	       "%s: Couldn't find root filesystem\n",
+	       mtd->name);
+	return -1;
+
+ found:
+	if (part->size == 0)
+		return 0;
+	
+	if (mtd->read(mtd, part->offset, sizeof(buf), &len, buf) || len != sizeof(buf))
+		return 0;
+
+	/* Move the fs outside of the trx */
+	part->size = 0;
+
+	if (trx.len != part->offset + part->size - off) {
+		/* Update the trx offsets and length */
+		trx.len = part->offset + part->size - off;
+	
+		/* Update the trx crc32 */
+		for (i = (u32) &(((struct trx_header *)NULL)->flag_version); i <= trx.len; i += sizeof(buf)) {
+			if (mtd->read(mtd, off + i, sizeof(buf), &len, buf) || len != sizeof(buf))
+				return 0;
+			crc = crc32_le(crc, buf, min(sizeof(buf), trx.len - i));
+		}
+		trx.crc32 = crc;
+
+		/* read first eraseblock from the trx */
+		block = kmalloc(mtd->erasesize, GFP_KERNEL);
+		trx2 = (struct trx_header *) block;
+		if (mtd->read(mtd, off, mtd->erasesize, &len, block) || len != mtd->erasesize) {
+			printk("Error accessing the first trx eraseblock\n");
+			return 0;
+		}
+		
+		printk("Updating TRX offsets and length:\n");
+		printk("old trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n", trx2->offsets[0], trx2->offsets[1], trx2->offsets[2], trx2->len, trx2->crc32);
+		printk("new trx = [0x%08x, 0x%08x, 0x%08x], len=0x%08x crc32=0x%08x\n",   trx.offsets[0],   trx.offsets[1],   trx.offsets[2],   trx.len, trx.crc32);
+
+		/* Write updated trx header to the flash */
+		memcpy(block, &trx, sizeof(trx));
+		if (mtd->unlock)
+			mtd->unlock(mtd, off, mtd->erasesize);
+		erase_write(mtd, off, mtd->erasesize, block);
+		if (mtd->sync)
+			mtd->sync(mtd);
+		kfree(block);
+		printk("Done\n");
+	}
+	
+	return part->size;
+}
+
+struct mtd_partition * __init
+init_mtd_partitions(struct mtd_info *mtd, size_t size)
+{
+	int cfe_size;
+
+	if ((cfe_size = find_cfe_size(mtd,size)) < 0)
+		return NULL;
+
+	/* boot loader */
+	bcm47xx_parts[0].offset = 0;
+	bcm47xx_parts[0].size   = cfe_size;
+
+	/* nvram */
+	if (cfe_size != 384 * 1024) {
+		bcm47xx_parts[3].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize);
+		bcm47xx_parts[3].size   = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
+	} else {
+		/* nvram (old 128kb config partition on netgear wgt634u) */
+		bcm47xx_parts[3].offset = bcm47xx_parts[0].size;
+		bcm47xx_parts[3].size   = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
+	}
+
+	/* linux (kernel and rootfs) */
+	if (cfe_size != 384 * 1024) {
+		bcm47xx_parts[1].offset = bcm47xx_parts[0].size;
+		bcm47xx_parts[1].size   = bcm47xx_parts[3].offset - 
+			bcm47xx_parts[1].offset;
+	} else {
+		/* do not count the elf loader, which is on one block */
+		bcm47xx_parts[1].offset = bcm47xx_parts[0].size + 
+			bcm47xx_parts[3].size + mtd->erasesize;
+		bcm47xx_parts[1].size   = size - 
+			bcm47xx_parts[0].size - 
+			(2*bcm47xx_parts[3].size) - 
+			mtd->erasesize;
+	}
+
+	/* find and size rootfs */
+	find_root(mtd,size,&bcm47xx_parts[2]);
+	bcm47xx_parts[2].size = size - bcm47xx_parts[2].offset - bcm47xx_parts[3].size;
+
+	return bcm47xx_parts;
+}
+#endif
+
+int __init init_bcm47xx_map(void)
+{
+#ifdef CONFIG_SSB
+	struct ssb_mipscore *mcore = &ssb_bcm47xx.mipscore;
+#endif
+	size_t size;
+	int ret = 0;
+#ifdef CONFIG_MTD_PARTITIONS
+	struct mtd_partition *parts;
+	int i;
+#endif
+
+#ifdef CONFIG_SSB
+	u32 window = mcore->flash_window;
+	u32 window_size = mcore->flash_window_size;
+
+	printk("flash init: 0x%08x 0x%08x\n", window, window_size);
+	bcm47xx_map.phys = window;
+	bcm47xx_map.size = window_size;
+	bcm47xx_map.virt = ioremap_nocache(window, window_size);
+#else
+	printk("flash init: 0x%08x 0x%08x\n", WINDOW_ADDR, WINDOW_SIZE);
+	bcm47xx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
+#endif
+
+	if (!bcm47xx_map.virt) {
+		printk("Failed to ioremap\n");
+		return -EIO;
+	}
+
+	simple_map_init(&bcm47xx_map);
+	
+	if (!(bcm47xx_mtd = do_map_probe("cfi_probe", &bcm47xx_map))) {
+		printk("Failed to do_map_probe\n");
+		iounmap((void *)bcm47xx_map.virt);
+		return -ENXIO;
+	}
+
+	/* override copy_from routine */
+ 	/* bcm47xx_map.copy_from = bcm47xx_map_copy_from; */
+
+	bcm47xx_mtd->owner = THIS_MODULE;
+
+	size = bcm47xx_mtd->size;
+
+	printk(KERN_NOTICE "Flash device: 0x%x at 0x%x\n", size, WINDOW_ADDR);
+
+#ifdef CONFIG_MTD_PARTITIONS
+	parts = init_mtd_partitions(bcm47xx_mtd, size);
+	for (i = 0; parts[i].name; i++);
+	ret = add_mtd_partitions(bcm47xx_mtd, parts, i);
+	if (ret) {
+		printk(KERN_ERR "Flash: add_mtd_partitions failed\n");
+		goto fail;
+	}
+#endif
+	return 0;
+
+ fail:
+	if (bcm47xx_mtd)
+		map_destroy(bcm47xx_mtd);
+	if (bcm47xx_map.virt)
+		iounmap((void *)bcm47xx_map.virt);
+	bcm47xx_map.virt = 0;
+	return ret;
+}
+
+void __exit cleanup_bcm47xx_map(void)
+{
+#ifdef CONFIG_MTD_PARTITIONS
+	del_mtd_partitions(bcm47xx_mtd);
+#endif
+	map_destroy(bcm47xx_mtd);
+	iounmap((void *)bcm47xx_map.virt);
+}
+
+module_init(init_bcm47xx_map);
+module_exit(cleanup_bcm47xx_map);
diff -ruN linux-2.6.25.10/drivers/mtd/maps/Kconfig linux-new/drivers/mtd/maps/Kconfig
--- linux-2.6.25.10/drivers/mtd/maps/Kconfig	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/drivers/mtd/maps/Kconfig	2009-01-09 21:45:52.000000000 -0600
@@ -337,6 +337,12 @@
 	  Mapping for the Flaga digital module. If you don't have one, ignore
 	  this setting.
 
+config MTD_BCM47XX
+	tristate "BCM947xx flash device"
+	depends on MIPS && MTD_CFI && BCM47XX
+	help
+	  Support for the flash chips on the BCM947xx board.
+
 config MTD_WALNUT
 	tristate "Flash device mapped on IBM 405GP Walnut"
 	depends on MTD_JEDECPROBE && WALNUT && !PPC_MERGE
diff -ruN linux-2.6.25.10/drivers/mtd/maps/Makefile linux-new/drivers/mtd/maps/Makefile
--- linux-2.6.25.10/drivers/mtd/maps/Makefile	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/drivers/mtd/maps/Makefile	2009-01-09 21:45:52.000000000 -0600
@@ -31,6 +31,7 @@
 obj-$(CONFIG_MTD_PCMCIA)	+= pcmciamtd.o
 obj-$(CONFIG_MTD_RPXLITE)	+= rpxlite.o
 obj-$(CONFIG_MTD_TQM8XXL)	+= tqm8xxl.o
+obj-$(CONFIG_MTD_BCM47XX)	+= bcm47xx-flash.o
 obj-$(CONFIG_MTD_SA1100)	+= sa1100-flash.o
 obj-$(CONFIG_MTD_IPAQ)		+= ipaq-flash.o
 obj-$(CONFIG_MTD_SBC_GXX)	+= sbc_gxx.o
diff -ruN linux-2.6.25.10/drivers/net/tg3.c linux-new/drivers/net/tg3.c
--- linux-2.6.25.10/drivers/net/tg3.c	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/drivers/net/tg3.c	2009-01-09 21:45:52.000000000 -0600
@@ -38,6 +38,7 @@
 #include <linux/workqueue.h>
 #include <linux/prefetch.h>
 #include <linux/dma-mapping.h>
+#include <linux/ssb/ssb_driver_gige.h>
 
 #include <net/checksum.h>
 #include <net/ip.h>
@@ -425,8 +426,9 @@
 static inline void tw32_mailbox_flush(struct tg3 *tp, u32 off, u32 val)
 {
 	tp->write32_mbox(tp, off, val);
-	if (!(tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) &&
-	    !(tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND))
+	if ((tp->tg3_flags3 & TG3_FLG3_FLUSH_POSTED_WRITES) ||
+	    (!(tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) &&
+	     !(tp->tg3_flags2 & TG3_FLG2_ICH_WORKAROUND)))
 		tp->read32_mbox(tp, off);
 }
 
@@ -706,7 +708,7 @@
 
 #define PHY_BUSY_LOOPS	5000
 
-static int tg3_readphy(struct tg3 *tp, int reg, u32 *val)
+static int __tg3_readphy(struct tg3 *tp, unsigned int phy_addr, int reg, u32 *val)
 {
 	u32 frame_val;
 	unsigned int loops;
@@ -720,7 +722,7 @@
 
 	*val = 0x0;
 
-	frame_val  = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) &
+	frame_val  = ((phy_addr << MI_COM_PHY_ADDR_SHIFT) &
 		      MI_COM_PHY_ADDR_MASK);
 	frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
 		      MI_COM_REG_ADDR_MASK);
@@ -755,7 +757,12 @@
 	return ret;
 }
 
-static int tg3_writephy(struct tg3 *tp, int reg, u32 val)
+static int tg3_readphy(struct tg3 *tp, int reg, u32 *val)
+{
+	return __tg3_readphy(tp, PHY_ADDR, reg, val);
+}
+
+static int __tg3_writephy(struct tg3 *tp, unsigned int phy_addr, int reg, u32 val)
 {
 	u32 frame_val;
 	unsigned int loops;
@@ -771,7 +778,7 @@
 		udelay(80);
 	}
 
-	frame_val  = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) &
+	frame_val  = ((phy_addr << MI_COM_PHY_ADDR_SHIFT) &
 		      MI_COM_PHY_ADDR_MASK);
 	frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
 		      MI_COM_REG_ADDR_MASK);
@@ -804,6 +811,11 @@
 	return ret;
 }
 
+static int tg3_writephy(struct tg3 *tp, int reg, u32 val)
+{
+	return __tg3_writephy(tp, PHY_ADDR, reg, val);
+}
+
 static void tg3_phydsp_write(struct tg3 *tp, u32 reg, u32 val)
 {
 	tg3_writephy(tp, MII_TG3_DSP_ADDRESS, reg);
@@ -2250,6 +2262,14 @@
 			}
 		}
 
+		if (tp->tg3_flags & TG3_FLG3_ROBOSWITCH) {
+			current_link_up = 1;
+			current_speed = SPEED_1000; //FIXME
+			current_duplex = DUPLEX_FULL;
+			tp->link_config.active_speed = current_speed;
+			tp->link_config.active_duplex = current_duplex;
+		}
+
 		if (current_link_up == 1 &&
 		    tp->link_config.active_duplex == DUPLEX_FULL)
 			tg3_setup_flow_control(tp, lcl_adv, rmt_adv);
@@ -5197,6 +5217,11 @@
 	int i;
 	u32 val;
 
+	if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) {
+		/* We don't use firmware. */
+		return 0;
+	}
+
 	if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
 		/* Wait up to 20ms for init done. */
 		for (i = 0; i < 200; i++) {
@@ -5435,6 +5460,14 @@
 		tw32(0x5000, 0x400);
 	}
 
+	if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) {
+		/* BCM4785: In order to avoid repercussions from using potentially
+		 * defective internal ROM, stop the Rx RISC CPU, which is not
+		 * required. */
+		tg3_stop_fw(tp);
+		tg3_halt_cpu(tp, RX_CPU_BASE);
+	}
+
 	tw32(GRC_MODE, tp->grc_mode);
 
 	if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A0) {
@@ -5704,9 +5737,12 @@
 		return -ENODEV;
 	}
 
-	/* Clear firmware's nvram arbitration. */
-	if (tp->tg3_flags & TG3_FLAG_NVRAM)
-		tw32(NVRAM_SWARB, SWARB_REQ_CLR0);
+	if (!(tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE)) {
+		/* Clear firmware's nvram arbitration. */
+		if (tp->tg3_flags & TG3_FLAG_NVRAM)
+			tw32(NVRAM_SWARB, SWARB_REQ_CLR0);
+	}
+
 	return 0;
 }
 
@@ -5787,6 +5823,11 @@
 	struct fw_info info;
 	int err, i;
 
+	if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) {
+		/* We don't use firmware. */
+		return 0;
+	}
+
 	info.text_base = TG3_FW_TEXT_ADDR;
 	info.text_len = TG3_FW_TEXT_LEN;
 	info.text_data = &tg3FwText[0];
@@ -6345,6 +6386,11 @@
 	unsigned long cpu_base, cpu_scratch_base, cpu_scratch_size;
 	int err, i;
 
+	if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) {
+		/* We don't use firmware. */
+		return 0;
+	}
+
 	if (tp->tg3_flags2 & TG3_FLG2_HW_TSO)
 		return 0;
 
@@ -7306,6 +7352,11 @@
 
 	spin_lock(&tp->lock);
 
+	if (tp->tg3_flags3 & TG3_FLG3_FLUSH_POSTED_WRITES) {
+		/* BCM4785: Flush posted writes from GbE to host memory. */
+		tr32(HOSTCC_MODE);
+	}
+
 	if (!(tp->tg3_flags & TG3_FLAG_TAGGED_STATUS)) {
 		/* All of this garbage is because when using non-tagged
 		 * IRQ status the mailbox/status_block protocol the chip
@@ -8906,6 +8957,11 @@
 	__le32 *buf;
 	int i, j, k, err = 0, size;
 
+	if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) {
+		/* We don't have NVRAM. */
+		return 0;
+	}
+
 	if (tg3_nvram_read_swab(tp, 0, &magic) != 0)
 		return -EIO;
 
@@ -9689,7 +9745,7 @@
 			return -EAGAIN;
 
 		spin_lock_bh(&tp->lock);
-		err = tg3_readphy(tp, data->reg_num & 0x1f, &mii_regval);
+		err = __tg3_readphy(tp, data->phy_id & 0x1f, data->reg_num & 0x1f, &mii_regval);
 		spin_unlock_bh(&tp->lock);
 
 		data->val_out = mii_regval;
@@ -9708,7 +9764,7 @@
 			return -EAGAIN;
 
 		spin_lock_bh(&tp->lock);
-		err = tg3_writephy(tp, data->reg_num & 0x1f, data->val_in);
+		err = __tg3_writephy(tp, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
 		spin_unlock_bh(&tp->lock);
 
 		return err;
@@ -10177,6 +10233,12 @@
 /* Chips other than 5700/5701 use the NVRAM for fetching info. */
 static void __devinit tg3_nvram_init(struct tg3 *tp)
 {
+	if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE) {
+		/* No NVRAM and EEPROM on the SSB Broadcom GigE core. */
+		tp->tg3_flags &= ~(TG3_FLAG_NVRAM | TG3_FLAG_NVRAM_BUFFERED);
+		return;
+	}
+
 	tw32_f(GRC_EEPROM_ADDR,
 	     (EEPROM_ADDR_FSM_RESET |
 	      (EEPROM_DEFAULT_CLOCK_PERIOD <<
@@ -10317,6 +10379,9 @@
 {
 	int ret;
 
+	if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE)
+		return -ENODEV;
+
 	if (!(tp->tg3_flags & TG3_FLAG_NVRAM))
 		return tg3_nvram_read_using_eeprom(tp, offset, val);
 
@@ -10563,6 +10628,9 @@
 {
 	int ret;
 
+	if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE)
+		return -ENODEV;
+
 	if (tp->tg3_flags & TG3_FLAG_EEPROM_WRITE_PROT) {
 		tw32_f(GRC_LOCAL_CTRL, tp->grc_local_ctrl &
 		       ~GRC_LCLCTRL_GPIO_OUTPUT1);
@@ -11610,7 +11678,6 @@
 		tp->write32 = tg3_write_flush_reg32;
 	}
 
-
 	if ((tp->tg3_flags & TG3_FLAG_TXD_MBOX_HWBUG) ||
 	    (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER)) {
 		tp->write32_tx_mbox = tg3_write32_tx_mbox;
@@ -11646,6 +11713,11 @@
 	      GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)))
 		tp->tg3_flags |= TG3_FLAG_SRAM_USE_CONFIG;
 
+	if (tp->tg3_flags3 & TG3_FLG3_FLUSH_POSTED_WRITES) {
+		tp->write32_tx_mbox = tg3_write_flush_reg32;
+		tp->write32_rx_mbox = tg3_write_flush_reg32;
+	}
+
 	/* Get eeprom hw config before calling tg3_set_power_state().
 	 * In particular, the TG3_FLG2_IS_NIC flag must be
 	 * determined before calling tg3_set_power_state() so that
@@ -12017,6 +12089,10 @@
 	}
 
 	if (!is_valid_ether_addr(&dev->dev_addr[0])) {
+		if (tp->tg3_flags3 & TG3_FLG3_IS_SSB_CORE)
+			ssb_gige_get_macaddr(tp->pdev, &dev->dev_addr[0]);
+	}
+	if (!is_valid_ether_addr(&dev->dev_addr[0])) {
 #ifdef CONFIG_SPARC
 		if (!tg3_get_default_macaddr_sparc(tp))
 			return 0;
@@ -12508,6 +12584,7 @@
 	case PHY_ID_BCM5704:	return "5704";
 	case PHY_ID_BCM5705:	return "5705";
 	case PHY_ID_BCM5750:	return "5750";
+	case PHY_ID_BCM5750_2:	return "5750-2";
 	case PHY_ID_BCM5752:	return "5752";
 	case PHY_ID_BCM5714:	return "5714";
 	case PHY_ID_BCM5780:	return "5780";
@@ -12695,6 +12772,13 @@
 		tp->msg_enable = tg3_debug;
 	else
 		tp->msg_enable = TG3_DEF_MSG_ENABLE;
+	if (pdev_is_ssb_gige_core(pdev)) {
+		tp->tg3_flags3 |= TG3_FLG3_IS_SSB_CORE;
+		if (ssb_gige_must_flush_posted_writes(pdev))
+			tp->tg3_flags3 |= TG3_FLG3_FLUSH_POSTED_WRITES;
+		if (ssb_gige_have_roboswitch(pdev))
+			tp->tg3_flags3 |= TG3_FLG3_ROBOSWITCH;
+	}
 
 	/* The word/byte swap controls here control register access byte
 	 * swapping.  DMA data byte swapping is controlled in the GRC_MODE
diff -ruN linux-2.6.25.10/drivers/net/tg3.h linux-new/drivers/net/tg3.h
--- linux-2.6.25.10/drivers/net/tg3.h	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/drivers/net/tg3.h	2009-01-09 21:45:52.000000000 -0600
@@ -2477,6 +2477,9 @@
 #define TG3_FLG3_ENABLE_APE		0x00000002
 #define TG3_FLG3_5761_5784_AX_FIXES	0x00000004
 #define TG3_FLG3_5701_DMA_BUG		0x00000008
+#define TG3_FLG3_IS_SSB_CORE		0x00000010
+#define TG3_FLG3_FLUSH_POSTED_WRITES	0x00000020
+#define TG3_FLG3_ROBOSWITCH		0x00000040
 
 	struct timer_list		timer;
 	u16				timer_counter;
@@ -2532,6 +2535,7 @@
 #define PHY_ID_BCM5714			0x60008340
 #define PHY_ID_BCM5780			0x60008350
 #define PHY_ID_BCM5755			0xbc050cc0
+#define PHY_ID_BCM5750_2		0xbc050cd0
 #define PHY_ID_BCM5787			0xbc050ce0
 #define PHY_ID_BCM5756			0xbc050ed0
 #define PHY_ID_BCM5784			0xbc050fa0
@@ -2568,7 +2572,7 @@
 	 (X) == PHY_ID_BCM5780 || (X) == PHY_ID_BCM5787 || \
 	 (X) == PHY_ID_BCM5755 || (X) == PHY_ID_BCM5756 || \
 	 (X) == PHY_ID_BCM5906 || (X) == PHY_ID_BCM5761 || \
-	 (X) == PHY_ID_BCM8002)
+	 (X) == PHY_ID_BCM8002 || (X) == PHY_ID_BCM5750_2)
 
 	struct tg3_hw_stats		*hw_stats;
 	dma_addr_t			stats_mapping;
diff -ruN linux-2.6.25.10/drivers/ssb/driver_gige.c linux-new/drivers/ssb/driver_gige.c
--- linux-2.6.25.10/drivers/ssb/driver_gige.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-new/drivers/ssb/driver_gige.c	2009-01-09 21:45:52.000000000 -0600
@@ -0,0 +1,294 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom Gigabit Ethernet core driver
+ *
+ * Copyright 2008, Broadcom Corporation
+ * Copyright 2008, Michael Buesch <mb@bu3sch.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/ssb/ssb_driver_gige.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+
+
+/*
+MODULE_DESCRIPTION("SSB Broadcom Gigabit Ethernet driver");
+MODULE_AUTHOR("Michael Buesch");
+MODULE_LICENSE("GPL");
+*/
+
+static const struct ssb_device_id ssb_gige_tbl[] = {
+	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET_GBIT, SSB_ANY_REV),
+	SSB_DEVTABLE_END
+};
+/* MODULE_DEVICE_TABLE(ssb, ssb_gige_tbl); */
+
+
+static inline u8 gige_read8(struct ssb_gige *dev, u16 offset)
+{
+	return ssb_read8(dev->dev, offset);
+}
+
+static inline u16 gige_read16(struct ssb_gige *dev, u16 offset)
+{
+	return ssb_read16(dev->dev, offset);
+}
+
+static inline u32 gige_read32(struct ssb_gige *dev, u16 offset)
+{
+	return ssb_read32(dev->dev, offset);
+}
+
+static inline void gige_write8(struct ssb_gige *dev,
+			       u16 offset, u8 value)
+{
+	ssb_write8(dev->dev, offset, value);
+}
+
+static inline void gige_write16(struct ssb_gige *dev,
+				u16 offset, u16 value)
+{
+	ssb_write16(dev->dev, offset, value);
+}
+
+static inline void gige_write32(struct ssb_gige *dev,
+				u16 offset, u32 value)
+{
+	ssb_write32(dev->dev, offset, value);
+}
+
+static inline
+u8 gige_pcicfg_read8(struct ssb_gige *dev, unsigned int offset)
+{
+	BUG_ON(offset >= 256);
+	return gige_read8(dev, SSB_GIGE_PCICFG + offset);
+}
+
+static inline
+u16 gige_pcicfg_read16(struct ssb_gige *dev, unsigned int offset)
+{
+	BUG_ON(offset >= 256);
+	return gige_read16(dev, SSB_GIGE_PCICFG + offset);
+}
+
+static inline
+u32 gige_pcicfg_read32(struct ssb_gige *dev, unsigned int offset)
+{
+	BUG_ON(offset >= 256);
+	return gige_read32(dev, SSB_GIGE_PCICFG + offset);
+}
+
+static inline
+void gige_pcicfg_write8(struct ssb_gige *dev,
+			unsigned int offset, u8 value)
+{
+	BUG_ON(offset >= 256);
+	gige_write8(dev, SSB_GIGE_PCICFG + offset, value);
+}
+
+static inline
+void gige_pcicfg_write16(struct ssb_gige *dev,
+			 unsigned int offset, u16 value)
+{
+	BUG_ON(offset >= 256);
+	gige_write16(dev, SSB_GIGE_PCICFG + offset, value);
+}
+
+static inline
+void gige_pcicfg_write32(struct ssb_gige *dev,
+			 unsigned int offset, u32 value)
+{
+	BUG_ON(offset >= 256);
+	gige_write32(dev, SSB_GIGE_PCICFG + offset, value);
+}
+
+static int ssb_gige_pci_read_config(struct pci_bus *bus, unsigned int devfn,
+				    int reg, int size, u32 *val)
+{
+	struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops);
+	unsigned long flags;
+
+	if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if (reg >= 256)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	switch (size) {
+	case 1:
+		*val = gige_pcicfg_read8(dev, reg);
+		break;
+	case 2:
+		*val = gige_pcicfg_read16(dev, reg);
+		break;
+	case 4:
+		*val = gige_pcicfg_read32(dev, reg);
+		break;
+	default:
+		WARN_ON(1);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int ssb_gige_pci_write_config(struct pci_bus *bus, unsigned int devfn,
+				     int reg, int size, u32 val)
+{
+	struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops);
+	unsigned long flags;
+
+	if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0))
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	if (reg >= 256)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	switch (size) {
+	case 1:
+		gige_pcicfg_write8(dev, reg, val);
+		break;
+	case 2:
+		gige_pcicfg_write16(dev, reg, val);
+		break;
+	case 4:
+		gige_pcicfg_write32(dev, reg, val);
+		break;
+	default:
+		WARN_ON(1);
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int ssb_gige_probe(struct ssb_device *sdev, const struct ssb_device_id *id)
+{
+	struct ssb_gige *dev;
+	u32 base, tmslow, tmshigh;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	dev->dev = sdev;
+
+	spin_lock_init(&dev->lock);
+	dev->pci_controller.pci_ops = &dev->pci_ops;
+	dev->pci_controller.io_resource = &dev->io_resource;
+	dev->pci_controller.mem_resource = &dev->mem_resource;
+	dev->pci_controller.io_map_base = 0x800;
+	dev->pci_ops.read = ssb_gige_pci_read_config;
+	dev->pci_ops.write = ssb_gige_pci_write_config;
+
+	dev->io_resource.name = SSB_GIGE_IO_RES_NAME;
+	dev->io_resource.start = 0x800;
+	dev->io_resource.end = 0x8FF;
+	dev->io_resource.flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED;
+
+	if (!ssb_device_is_enabled(sdev))
+		ssb_device_enable(sdev, 0);
+
+	/* Setup BAR0. This is a 64k MMIO region. */
+	base = ssb_admatch_base(ssb_read32(sdev, SSB_ADMATCH1));
+	gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_0, base);
+	gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_1, 0);
+
+	dev->mem_resource.name = SSB_GIGE_MEM_RES_NAME;
+	dev->mem_resource.start = base;
+	dev->mem_resource.end = base + 0x10000 - 1;
+	dev->mem_resource.flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED;
+
+	/* Enable the memory region. */
+	gige_pcicfg_write16(dev, PCI_COMMAND,
+			    gige_pcicfg_read16(dev, PCI_COMMAND)
+			    | PCI_COMMAND_MEMORY);
+
+	/* Write flushing is controlled by the Flush Status Control register.
+	 * We want to flush every register write with a timeout and we want
+	 * to disable the IRQ mask while flushing to avoid concurrency.
+	 * Note that automatic write flushing does _not_ work from
+	 * an IRQ handler. The driver must flush manually by reading a register.
+	 */
+	gige_write32(dev, SSB_GIGE_SHIM_FLUSHSTAT, 0x00000068);
+
+	/* Check if we have an RGMII or GMII PHY-bus.
+	 * On RGMII do not bypass the DLLs */
+	tmslow = ssb_read32(sdev, SSB_TMSLOW);
+	tmshigh = ssb_read32(sdev, SSB_TMSHIGH);
+	if (tmshigh & SSB_GIGE_TMSHIGH_RGMII) {
+		tmslow &= ~SSB_GIGE_TMSLOW_TXBYPASS;
+		tmslow &= ~SSB_GIGE_TMSLOW_RXBYPASS;
+		dev->has_rgmii = 1;
+	} else {
+		tmslow |= SSB_GIGE_TMSLOW_TXBYPASS;
+		tmslow |= SSB_GIGE_TMSLOW_RXBYPASS;
+		dev->has_rgmii = 0;
+	}
+	tmslow |= SSB_GIGE_TMSLOW_DLLEN;
+	ssb_write32(sdev, SSB_TMSLOW, tmslow);
+
+	ssb_set_drvdata(sdev, dev);
+	register_pci_controller(&dev->pci_controller);
+
+	return 0;
+}
+
+bool pdev_is_ssb_gige_core(struct pci_dev *pdev)
+{
+	if (!pdev->resource[0].name)
+		return 0;
+	return (strcmp(pdev->resource[0].name, SSB_GIGE_MEM_RES_NAME) == 0);
+}
+EXPORT_SYMBOL(pdev_is_ssb_gige_core);
+
+int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev,
+				   struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = ssb_get_drvdata(sdev);
+	struct resource *res;
+
+	if (pdev->bus->ops != &dev->pci_ops) {
+		/* The PCI device is not on this SSB GigE bridge device. */
+		return -ENODEV;
+	}
+
+	/* Fixup the PCI resources. */
+	res = &(pdev->resource[0]);
+	res->flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED;
+	res->name = dev->mem_resource.name;
+	res->start = dev->mem_resource.start;
+	res->end = dev->mem_resource.end;
+
+	/* Fixup interrupt lines. */
+	pdev->irq = ssb_mips_irq(sdev) + 2;
+	pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, pdev->irq);
+
+	return 0;
+}
+
+int ssb_gige_map_irq(struct ssb_device *sdev,
+		     const struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = ssb_get_drvdata(sdev);
+
+	if (pdev->bus->ops != &dev->pci_ops) {
+		/* The PCI device is not on this SSB GigE bridge device. */
+		return -ENODEV;
+	}
+
+	return ssb_mips_irq(sdev) + 2;
+}
+
+static struct ssb_driver ssb_gige_driver = {
+	.name		= "BCM-GigE",
+	.id_table	= ssb_gige_tbl,
+	.probe		= ssb_gige_probe,
+};
+
+int ssb_gige_init(void)
+{
+	return ssb_driver_register(&ssb_gige_driver);
+}
diff -ruN linux-2.6.25.10/drivers/ssb/driver_mipscore.c linux-new/drivers/ssb/driver_mipscore.c
--- linux-2.6.25.10/drivers/ssb/driver_mipscore.c	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/drivers/ssb/driver_mipscore.c	2009-01-09 21:45:52.000000000 -0600
@@ -210,6 +210,7 @@
 			/* fallthrough */
 		case SSB_DEV_PCI:
 		case SSB_DEV_ETHERNET:
+		case SSB_DEV_ETHERNET_GBIT:
 		case SSB_DEV_80211:
 		case SSB_DEV_USB20_HOST:
 			/* These devices get their own IRQ line if available, the rest goes on IRQ0 */
diff -ruN linux-2.6.25.10/drivers/ssb/driver_pcicore.c linux-new/drivers/ssb/driver_pcicore.c
--- linux-2.6.25.10/drivers/ssb/driver_pcicore.c	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/drivers/ssb/driver_pcicore.c	2009-01-09 21:45:52.000000000 -0600
@@ -60,78 +60,6 @@
 /* Core to access the external PCI config space. Can only have one. */
 static struct ssb_pcicore *extpci_core;
 
-static u32 ssb_pcicore_pcibus_iobase = 0x100;
-static u32 ssb_pcicore_pcibus_membase = SSB_PCI_DMA;
-
-int pcibios_plat_dev_init(struct pci_dev *d)
-{
-	struct resource *res;
-	int pos, size;
-	u32 *base;
-
-	ssb_printk(KERN_INFO "PCI: Fixing up device %s\n",
-		   pci_name(d));
-
-	/* Fix up resource bases */
-	for (pos = 0; pos < 6; pos++) {
-		res = &d->resource[pos];
-		if (res->flags & IORESOURCE_IO)
-			base = &ssb_pcicore_pcibus_iobase;
-		else
-			base = &ssb_pcicore_pcibus_membase;
-		res->flags |= IORESOURCE_PCI_FIXED;
-		if (res->end) {
-			size = res->end - res->start + 1;
-			if (*base & (size - 1))
-				*base = (*base + size) & ~(size - 1);
-			res->start = *base;
-			res->end = res->start + size - 1;
-			*base += size;
-			pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start);
-		}
-		/* Fix up PCI bridge BAR0 only */
-		if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0)
-			break;
-	}
-	/* Fix up interrupt lines */
-	d->irq = ssb_mips_irq(extpci_core->dev) + 2;
-	pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq);
-
-	return 0;
-}
-
-static void __init ssb_fixup_pcibridge(struct pci_dev *dev)
-{
-	u8 lat;
-
-	if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)
-		return;
-
-	ssb_printk(KERN_INFO "PCI: Fixing up bridge %s\n", pci_name(dev));
-
-	/* Enable PCI bridge bus mastering and memory space */
-	pci_set_master(dev);
-	if (pcibios_enable_device(dev, ~0) < 0) {
-		ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n");
-		return;
-	}
-
-	/* Enable PCI bridge BAR1 prefetch and burst */
-	pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3);
-
-	/* Make sure our latency is high enough to handle the devices behind us */
-	lat = 168;
-	ssb_printk(KERN_INFO "PCI: Fixing latency timer of device %s to %u\n",
-		   pci_name(dev), lat);
-	pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
-}
-DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_fixup_pcibridge);
-
-int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
-{
-	return ssb_mips_irq(extpci_core->dev) + 2;
-}
-
 static u32 get_cfgspace_addr(struct ssb_pcicore *pc,
 			     unsigned int bus, unsigned int dev,
 			     unsigned int func, unsigned int off)
@@ -320,6 +248,95 @@
 	.mem_offset	= 0x24000000,
 };
 
+static u32 ssb_pcicore_pcibus_iobase = 0x100;
+static u32 ssb_pcicore_pcibus_membase = SSB_PCI_DMA;
+
+/* This function is called when doing a pci_enable_device().
+ * We must first check if the device is a device on the PCI-core bridge. */
+int ssb_pcicore_plat_dev_init(struct pci_dev *d)
+{
+	struct resource *res;
+	int pos, size;
+	u32 *base;
+
+	if (d->bus->ops != &ssb_pcicore_pciops) {
+		/* This is not a device on the PCI-core bridge. */
+		return -ENODEV;
+	}
+
+	ssb_printk(KERN_INFO "PCI: Fixing up device %s\n",
+		   pci_name(d));
+
+	/* Fix up resource bases */
+	for (pos = 0; pos < 6; pos++) {
+		res = &d->resource[pos];
+		if (res->flags & IORESOURCE_IO)
+			base = &ssb_pcicore_pcibus_iobase;
+		else
+			base = &ssb_pcicore_pcibus_membase;
+		res->flags |= IORESOURCE_PCI_FIXED;
+		if (res->end) {
+			size = res->end - res->start + 1;
+			if (*base & (size - 1))
+				*base = (*base + size) & ~(size - 1);
+			res->start = *base;
+			res->end = res->start + size - 1;
+			*base += size;
+			pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start);
+		}
+		/* Fix up PCI bridge BAR0 only */
+		if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0)
+			break;
+	}
+	/* Fix up interrupt lines */
+	d->irq = ssb_mips_irq(extpci_core->dev) + 2;
+	pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq);
+
+	return 0;
+}
+
+/* Early PCI fixup for a device on the PCI-core bridge. */
+static void ssb_pcicore_fixup_pcibridge(struct pci_dev *dev)
+{
+	u8 lat;
+
+	if (dev->bus->ops != &ssb_pcicore_pciops) {
+		/* This is not a device on the PCI-core bridge. */
+		return;
+	}
+	if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)
+		return;
+
+	ssb_printk(KERN_INFO "PCI: Fixing up bridge %s\n", pci_name(dev));
+
+	/* Enable PCI bridge bus mastering and memory space */
+	pci_set_master(dev);
+	if (pcibios_enable_device(dev, ~0) < 0) {
+		ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n");
+		return;
+	}
+
+	/* Enable PCI bridge BAR1 prefetch and burst */
+	pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3);
+
+	/* Make sure our latency is high enough to handle the devices behind us */
+	lat = 168;
+	ssb_printk(KERN_INFO "PCI: Fixing latency timer of device %s to %u\n",
+		   pci_name(dev), lat);
+	pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_pcicore_fixup_pcibridge);
+
+/* PCI device IRQ mapping. */
+int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+	if (dev->bus->ops != &ssb_pcicore_pciops) {
+		/* This is not a device on the PCI-core bridge. */
+		return -ENODEV;
+	}
+	return ssb_mips_irq(extpci_core->dev) + 2;
+}
+
 static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
 {
 	u32 val;
diff -ruN linux-2.6.25.10/drivers/ssb/embedded.c linux-new/drivers/ssb/embedded.c
--- linux-2.6.25.10/drivers/ssb/embedded.c	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/drivers/ssb/embedded.c	2009-01-09 21:45:52.000000000 -0600
@@ -10,6 +10,9 @@
 
 #include <linux/ssb/ssb.h>
 #include <linux/ssb/ssb_embedded.h>
+#include <linux/ssb/ssb_driver_pci.h>
+#include <linux/ssb/ssb_driver_gige.h>
+#include <linux/pci.h>
 
 #include "ssb_private.h"
 
@@ -130,3 +133,90 @@
 	return res;
 }
 EXPORT_SYMBOL(ssb_gpio_polarity);
+
+#ifdef CONFIG_SSB_DRIVER_GIGE
+static int gige_pci_init_callback(struct ssb_bus *bus, unsigned long data)
+{
+	struct pci_dev *pdev = (struct pci_dev *)data;
+	struct ssb_device *dev;
+	unsigned int i;
+	int res;
+
+	for (i = 0; i < bus->nr_devices; i++) {
+		dev = &(bus->devices[i]);
+		if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT)
+			continue;
+		if (!dev->dev ||
+		    !dev->dev->driver ||
+		    !device_is_registered(dev->dev))
+			continue;
+		res = ssb_gige_pcibios_plat_dev_init(dev, pdev);
+		if (res >= 0)
+			return res;
+	}
+
+	return -ENODEV;
+}
+#endif /* CONFIG_SSB_DRIVER_GIGE */
+
+int ssb_pcibios_plat_dev_init(struct pci_dev *dev)
+{
+	int err;
+
+	err = ssb_pcicore_plat_dev_init(dev);
+	if (!err)
+		return 0;
+#ifdef CONFIG_SSB_DRIVER_GIGE
+	err = ssb_for_each_bus_call((unsigned long)dev, gige_pci_init_callback);
+	if (err >= 0)
+		return err;
+#endif
+	/* This is not a PCI device on any SSB device. */
+
+	return -ENODEV;
+}
+
+#ifdef CONFIG_SSB_DRIVER_GIGE
+static int gige_map_irq_callback(struct ssb_bus *bus, unsigned long data)
+{
+	const struct pci_dev *pdev = (const struct pci_dev *)data;
+	struct ssb_device *dev;
+	unsigned int i;
+	int res;
+
+	for (i = 0; i < bus->nr_devices; i++) {
+		dev = &(bus->devices[i]);
+		if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT)
+			continue;
+		if (!dev->dev ||
+		    !dev->dev->driver ||
+		    !device_is_registered(dev->dev))
+			continue;
+		res = ssb_gige_map_irq(dev, pdev);
+		if (res >= 0)
+			return res;
+	}
+
+	return -ENODEV;
+}
+#endif /* CONFIG_SSB_DRIVER_GIGE */
+
+int ssb_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+	int res;
+
+	/* Check if this PCI device is a device on a SSB bus or device
+	 * and return the IRQ number for it. */
+
+	res = ssb_pcicore_pcibios_map_irq(dev, slot, pin);
+	if (res >= 0)
+		return res;
+#ifdef CONFIG_SSB_DRIVER_GIGE
+	res = ssb_for_each_bus_call((unsigned long)dev, gige_map_irq_callback);
+	if (res >= 0)
+		return res;
+#endif
+	/* This is not a PCI device on any SSB device. */
+
+	return -ENODEV;
+}
diff -ruN linux-2.6.25.10/drivers/ssb/Kconfig linux-new/drivers/ssb/Kconfig
--- linux-2.6.25.10/drivers/ssb/Kconfig	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/drivers/ssb/Kconfig	2009-01-09 21:45:52.000000000 -0600
@@ -125,4 +125,13 @@
 
 	  If unsure, say N
 
+config SSB_DRIVER_GIGE
+	bool "SSB Broadcom Gigabit Ethernet driver"
+	depends on SSB_PCIHOST_POSSIBLE && SSB_EMBEDDED && MIPS
+	help
+	  Driver for the Sonics Silicon Backplane attached
+	  Broadcom Gigabit Ethernet.
+
+	  If unsure, say N
+
 endmenu
diff -ruN linux-2.6.25.10/drivers/ssb/main.c linux-new/drivers/ssb/main.c
--- linux-2.6.25.10/drivers/ssb/main.c	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/drivers/ssb/main.c	2009-01-09 21:45:52.000000000 -0600
@@ -14,6 +14,7 @@
 #include <linux/io.h>
 #include <linux/ssb/ssb.h>
 #include <linux/ssb/ssb_regs.h>
+#include <linux/ssb/ssb_driver_gige.h>
 #include <linux/dma-mapping.h>
 #include <linux/pci.h>
 
@@ -68,6 +69,25 @@
 }
 #endif /* CONFIG_SSB_PCIHOST */
 
+int ssb_for_each_bus_call(unsigned long data,
+			  int (*func)(struct ssb_bus *bus, unsigned long data))
+{
+	struct ssb_bus *bus;
+	int res;
+
+	ssb_buses_lock();
+	list_for_each_entry(bus, &buses, list) {
+		res = func(bus, data);
+		if (res >= 0) {
+			ssb_buses_unlock();
+			return res;
+		}
+	}
+	ssb_buses_unlock();
+
+	return -ENODEV;
+}
+
 static struct ssb_device *ssb_device_get(struct ssb_device *dev)
 {
 	if (dev)
@@ -1161,7 +1181,14 @@
 	err = b43_pci_ssb_bridge_init();
 	if (err) {
 		ssb_printk(KERN_ERR "Broadcom 43xx PCI-SSB-bridge "
-			   "initialization failed");
+			   "initialization failed\n");
+		/* don't fail SSB init because of this */
+		err = 0;
+	}
+	err = ssb_gige_init();
+	if (err) {
+		ssb_printk(KERN_ERR "SSB Broadcom Gigabit Ethernet "
+			   "driver initialization failed\n");
 		/* don't fail SSB init because of this */
 		err = 0;
 	}
@@ -1175,6 +1202,7 @@
 
 static void __exit ssb_modexit(void)
 {
+	ssb_gige_exit();
 	b43_pci_ssb_bridge_exit();
 	bus_unregister(&ssb_bustype);
 }
diff -ruN linux-2.6.25.10/drivers/ssb/Makefile linux-new/drivers/ssb/Makefile
--- linux-2.6.25.10/drivers/ssb/Makefile	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/drivers/ssb/Makefile	2009-01-09 21:45:52.000000000 -0600
@@ -11,6 +11,7 @@
 ssb-$(CONFIG_SSB_DRIVER_MIPS)		+= driver_mipscore.o
 ssb-$(CONFIG_SSB_DRIVER_EXTIF)		+= driver_extif.o
 ssb-$(CONFIG_SSB_DRIVER_PCICORE)	+= driver_pcicore.o
+ssb-$(CONFIG_SSB_DRIVER_GIGE)		+= driver_gige.o
 
 # b43 pci-ssb-bridge driver
 # Not strictly a part of SSB, but kept here for convenience
diff -ruN linux-2.6.25.10/drivers/ssb/ssb_private.h linux-new/drivers/ssb/ssb_private.h
--- linux-2.6.25.10/drivers/ssb/ssb_private.h	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/drivers/ssb/ssb_private.h	2009-01-09 21:45:52.000000000 -0600
@@ -118,6 +118,8 @@
 extern int ssb_devices_freeze(struct ssb_bus *bus);
 extern int ssb_devices_thaw(struct ssb_bus *bus);
 extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev);
+int ssb_for_each_bus_call(unsigned long data,
+			  int (*func)(struct ssb_bus *bus, unsigned long data));
 
 /* b43_pci_bridge.c */
 #ifdef CONFIG_SSB_B43_PCI_BRIDGE
diff -ruN linux-2.6.25.10/include/linux/ssb/ssb_driver_gige.h linux-new/include/linux/ssb/ssb_driver_gige.h
--- linux-2.6.25.10/include/linux/ssb/ssb_driver_gige.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-new/include/linux/ssb/ssb_driver_gige.h	2009-01-09 21:45:52.000000000 -0600
@@ -0,0 +1,174 @@
+#ifndef LINUX_SSB_DRIVER_GIGE_H_
+#define LINUX_SSB_DRIVER_GIGE_H_
+
+#include <linux/ssb/ssb.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+
+#ifdef CONFIG_SSB_DRIVER_GIGE
+
+
+#define SSB_GIGE_PCIIO			0x0000 /* PCI I/O Registers (1024 bytes) */
+#define SSB_GIGE_RESERVED		0x0400 /* Reserved (1024 bytes) */
+#define SSB_GIGE_PCICFG			0x0800 /* PCI config space (256 bytes) */
+#define SSB_GIGE_SHIM_FLUSHSTAT		0x0C00 /* PCI to OCP: Flush status control (32bit) */
+#define SSB_GIGE_SHIM_FLUSHRDA		0x0C04 /* PCI to OCP: Flush read address (32bit) */
+#define SSB_GIGE_SHIM_FLUSHTO		0x0C08 /* PCI to OCP: Flush timeout counter (32bit) */
+#define SSB_GIGE_SHIM_BARRIER		0x0C0C /* PCI to OCP: Barrier register (32bit) */
+#define SSB_GIGE_SHIM_MAOCPSI		0x0C10 /* PCI to OCP: MaocpSI Control (32bit) */
+#define SSB_GIGE_SHIM_SIOCPMA		0x0C14 /* PCI to OCP: SiocpMa Control (32bit) */
+
+/* TM Status High flags */
+#define SSB_GIGE_TMSHIGH_RGMII		0x00010000 /* Have an RGMII PHY-bus */
+/* TM Status Low flags */
+#define SSB_GIGE_TMSLOW_TXBYPASS	0x00080000 /* TX bypass (no delay) */
+#define SSB_GIGE_TMSLOW_RXBYPASS	0x00100000 /* RX bypass (no delay) */
+#define SSB_GIGE_TMSLOW_DLLEN		0x01000000 /* Enable DLL controls */
+
+/* Boardflags (low) */
+#define SSB_GIGE_BFL_ROBOSWITCH		0x0010
+
+
+#define SSB_GIGE_MEM_RES_NAME		"SSB Broadcom 47xx GigE memory"
+#define SSB_GIGE_IO_RES_NAME		"SSB Broadcom 47xx GigE I/O"
+
+struct ssb_gige {
+	struct ssb_device *dev;
+
+	spinlock_t lock;
+
+	/* True, if the device has an RGMII bus.
+	 * False, if the device has a GMII bus. */
+	bool has_rgmii;
+
+	/* The PCI controller device. */
+	struct pci_controller pci_controller;
+	struct pci_ops pci_ops;
+	struct resource mem_resource;
+	struct resource io_resource;
+};
+
+/* Check whether a PCI device is a SSB Gigabit Ethernet core. */
+extern bool pdev_is_ssb_gige_core(struct pci_dev *pdev);
+
+/* Convert a pci_dev pointer to a ssb_gige pointer. */
+static inline struct ssb_gige * pdev_to_ssb_gige(struct pci_dev *pdev)
+{
+	if (!pdev_is_ssb_gige_core(pdev))
+		return NULL;
+	return container_of(pdev->bus->ops, struct ssb_gige, pci_ops);
+}
+
+/* Returns whether the PHY is connected by an RGMII bus. */
+static inline bool ssb_gige_is_rgmii(struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
+	return (dev ? dev->has_rgmii : 0);
+}
+
+/* Returns whether we have a Roboswitch. */
+static inline bool ssb_gige_have_roboswitch(struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
+	if (dev)
+		return !!(dev->dev->bus->sprom.boardflags_lo &
+			  SSB_GIGE_BFL_ROBOSWITCH);
+	return 0;
+}
+
+/* Returns whether we can only do one DMA at once. */
+static inline bool ssb_gige_one_dma_at_once(struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
+	if (dev)
+		return ((dev->dev->bus->chip_id == 0x4785) &&
+			(dev->dev->bus->chip_rev < 2));
+	return 0;
+}
+
+/* Returns whether we must flush posted writes. */
+static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev)
+{
+	struct ssb_gige *dev = pdev_to_ssb_gige(pdev);
+	if (dev)
+		return (dev->dev->bus->chip_id == 0x4785);
+	return 0;
+}
+
+extern char * nvram_get(const char *name);
+/* Get the device MAC address */
+static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr)
+{
+#ifdef CONFIG_BCM947XX
+	char *res = nvram_get("et0macaddr");
+	if (res)
+		memcpy(macaddr, res, 6);
+#endif
+}
+
+extern int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev,
+					  struct pci_dev *pdev);
+extern int ssb_gige_map_irq(struct ssb_device *sdev,
+			    const struct pci_dev *pdev);
+
+/* The GigE driver is not a standalone module, because we don't have support
+ * for unregistering the driver. So we could not unload the module anyway. */
+extern int ssb_gige_init(void);
+static inline void ssb_gige_exit(void)
+{
+	/* Currently we can not unregister the GigE driver,
+	 * because we can not unregister the PCI bridge. */
+	BUG();
+}
+
+
+#else /* CONFIG_SSB_DRIVER_GIGE */
+/* Gigabit Ethernet driver disabled */
+
+
+static inline int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev,
+						 struct pci_dev *pdev)
+{
+	return -ENOSYS;
+}
+static inline int ssb_gige_map_irq(struct ssb_device *sdev,
+				   const struct pci_dev *pdev)
+{
+	return -ENOSYS;
+}
+static inline int ssb_gige_init(void)
+{
+	return 0;
+}
+static inline void ssb_gige_exit(void)
+{
+}
+
+static inline bool pdev_is_ssb_gige_core(struct pci_dev *pdev)
+{
+	return 0;
+}
+static inline struct ssb_gige * pdev_to_ssb_gige(struct pci_dev *pdev)
+{
+	return NULL;
+}
+static inline bool ssb_gige_is_rgmii(struct pci_dev *pdev)
+{
+	return 0;
+}
+static inline bool ssb_gige_have_roboswitch(struct pci_dev *pdev)
+{
+	return 0;
+}
+static inline bool ssb_gige_one_dma_at_once(struct pci_dev *pdev)
+{
+	return 0;
+}
+static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev)
+{
+	return 0;
+}
+
+#endif /* CONFIG_SSB_DRIVER_GIGE */
+#endif /* LINUX_SSB_DRIVER_GIGE_H_ */
diff -ruN linux-2.6.25.10/include/linux/ssb/ssb_driver_pci.h linux-new/include/linux/ssb/ssb_driver_pci.h
--- linux-2.6.25.10/include/linux/ssb/ssb_driver_pci.h	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/include/linux/ssb/ssb_driver_pci.h	2009-01-09 21:45:52.000000000 -0600
@@ -1,6 +1,11 @@
 #ifndef LINUX_SSB_PCICORE_H_
 #define LINUX_SSB_PCICORE_H_
 
+#include <linux/types.h>
+
+struct pci_dev;
+
+
 #ifdef CONFIG_SSB_DRIVER_PCICORE
 
 /* PCI core registers. */
@@ -88,6 +93,9 @@
 extern int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
 					  struct ssb_device *dev);
 
+int ssb_pcicore_plat_dev_init(struct pci_dev *d);
+int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
+
 
 #else /* CONFIG_SSB_DRIVER_PCICORE */
 
@@ -107,5 +115,16 @@
 	return 0;
 }
 
+static inline
+int ssb_pcicore_plat_dev_init(struct pci_dev *d)
+{
+	return -ENODEV;
+}
+static inline
+int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+	return -ENODEV;
+}
+
 #endif /* CONFIG_SSB_DRIVER_PCICORE */
 #endif /* LINUX_SSB_PCICORE_H_ */
diff -ruN linux-2.6.25.10/include/linux/ssb/ssb.h linux-new/include/linux/ssb/ssb.h
--- linux-2.6.25.10/include/linux/ssb/ssb.h	2008-07-02 22:46:47.000000000 -0500
+++ linux-new/include/linux/ssb/ssb.h	2009-01-09 21:45:52.000000000 -0600
@@ -416,5 +416,12 @@
 extern u32 ssb_admatch_base(u32 adm);
 extern u32 ssb_admatch_size(u32 adm);
 
+/* PCI device mapping and fixup routines.
+ * Called from the architecture pcibios init code.
+ * These are only available on SSB_EMBEDDED configurations. */
+#ifdef CONFIG_SSB_EMBEDDED
+int ssb_pcibios_plat_dev_init(struct pci_dev *dev);
+int ssb_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
+#endif /* CONFIG_SSB_EMBEDDED */
 
 #endif /* LINUX_SSB_H_ */