changeset 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 036dbe218036
children 715c94bd059e
files sources/patches/linux-bcm47xx.patch sources/targets/hw-wrt610n/details sources/targets/hw-wrt610n/miniconfig-linux
diffstat 3 files changed, 2575 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/patches/linux-bcm47xx.patch	Fri Jan 09 22:03:04 2009 -0600
@@ -0,0 +1,2468 @@
+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_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/targets/hw-wrt610n/details	Fri Jan 09 22:03:04 2009 -0600
@@ -0,0 +1,1 @@
+BASE_ARCH=mipsel
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/targets/hw-wrt610n/miniconfig-linux	Fri Jan 09 22:03:04 2009 -0600
@@ -0,0 +1,106 @@
+CONFIG_BCM47XX=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_HZ_100=y
+CONFIG_SECCOMP=y
+CONFIG_EXPERIMENTAL=y
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_BLOCK=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_PCI=y
+CONFIG_PCI_LEGACY=y
+CONFIG_BINFMT_ELF=y
+CONFIG_PM=y
+CONFIG_SUSPEND=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+CONFIG_INET_DIAG=y
+CONFIG_IPV6=y
+CONFIG_INET6_XFRM_MODE_TRANSPORT=y
+CONFIG_INET6_XFRM_MODE_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_BEET=y
+CONFIG_IPV6_SIT=y
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_BCM47XX=y
+CONFIG_BLK_DEV=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_MISC_DEVICES=y
+CONFIG_NETDEVICES=y
+CONFIG_NET_ETHERNET=y
+CONFIG_AX88796=y
+CONFIG_NET_PCI=y
+CONFIG_PCNET32=y
+CONFIG_NE2K_PCI=y
+CONFIG_8139CP=y
+CONFIG_NETDEV_1000=y
+CONFIG_NETDEV_10000=y
+CONFIG_SERIO=y
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_PCI=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_HW_RANDOM=y
+CONFIG_RTC=y
+CONFIG_HWMON=y
+CONFIG_SSB_PCIHOST=y
+CONFIG_SSB_DRIVER_PCICORE=y
+CONFIG_VGA_CONSOLE=y
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+CONFIG_USB_SUPPORT=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_CRYPTO_HW=y