Netdev List
 help / color / mirror / Atom feed
* Re: TCP SACK issue, hung connection, tcpdump included
From: Willy Tarreau @ 2007-07-29 16:07 UTC (permalink / raw)
  To: Ilpo Järvinen; +Cc: Darryl L. Miles, linux-kernel, Netdev
In-Reply-To: <Pine.LNX.4.64.0707291208230.10340@kivilampi-30.cs.helsinki.fi>

On Sun, Jul 29, 2007 at 12:28:04PM +0300, Ilpo Järvinen wrote:
(...)
> > > Limitation for 48 byte segments? You have to be kidding... :-) But yes,
> > > it seems that one of the directions is dropping packets for some reason 
> > > though I would not assume MTU limitation... Or did you mean some other 
> > > segment?
> > 
> > No, I was talking about the 1448 bytes segments. But in fact I don't
> > believe it much because the SACKs are always retransmitted just afterwards.
> 
> Ah, but it's ACKed correctly right below it...:
> 
> [...snip...]
> > > > > 09:21:39.490740 IP SERVER.ssh > CLIENT.50727: P 18200:18464(264) ack 2991 
> > > > > win 2728 <nop,nop,timestamp 7692910 800001727>
> > > > > 09:21:39.490775 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > > > > <nop,nop,timestamp 800001755 7692910>
> > > > > 09:21:39.860245 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > > > > win 2728 <nop,nop,timestamp 7693293 800001749>
> 
> ...segment below snd_una arrived => snd_una remains 18464, receiver 
> generates a duplicate ACK:
>  
> > > > > 09:21:39.860302 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > > > > <nop,nop,timestamp 800001847 7692910,nop,nop,sack sack 1 {12408:13856} >
> 
> The cumulative ACK field of it covers _everything_ below 18464 (i.e., it 
> ACKs them), including the 1448 bytes in 12408:13856... In addition, the 
> SACK block is DSACK information [RFC2883] telling explicitly the address 
> of the received duplicate block. However, if this ACK doesn't reach the 
> SERVER TCP, RTO is triggered and the first not yet cumulatively ACKed 
> segment is retransmitted (I guess cumulative ACKs up to 12408 arrived 
> without problems to the SERVER):

Oh yes, you're damn right. I did not notice that the ACK was higher than
the SACK, I'm more used to read traces with absolute rather than relative
seq/acks.

So I agree, it is this ACK which is lost between client and server,
reinforcing the supposition about the location of the capture (client side).

> [...snip...]
> 
> > BTW, some information are missing. It would have been better if the trace
> > had been read with tcpdump -Svv. We would have got seq numbers and ttl.
> > Also, we do not know if there's a firewall between both sides. Sometimes,
> > some IDS identify attacks in crypted traffic and kill connections. It
> > might have been the case here, with the connection closed one way on an
> > intermediate firewall.
> 
> Yeah, firewall or some other issue, I'd say it's quite unlikely a bug in 
> TCP because behavior to both directions indicate client -> sender 
> blackhole independently of each other...

It would also be possible that something stupid between both ends simply
drops packets with the SACK option set. Also something which sometimes
happen is that some firewalls automatically translate sequence numbers
but not necessarily SACK values, which could pretty well lead to this
packet being received but ignored on the server side.

I'm pretty sure that the same trace taken on the server side will reveal
the reason for the problem.

Willy


^ permalink raw reply

* Win Prizes at Dentocafe
From: webmaster @ 2007-07-28 15:40 UTC (permalink / raw)
  To: netdev

Dear Friends,

Yes, it's true. We are giving away more than Rs. 1,000,000 to every

Dentocafe member!

 For more information please visit

http://www.dentocafe.com/showthread.php?t=2833

If you have questions or need additional assistance, please do not

hesitate to contact us by replying to this email. If you have forgot
your

username as well as password also, than mail us at
webmaster@dentocafe.com

OR

 dentocafe@yahoo.com

with your email address from which you registered on Dentocafe.

Best Regards,

ADMIN

--
If you do not want to receive any more newsletters, 
http://e-hyd.com/mail/?p=unsubscribe&uid=2e0422689271d6451d4b2c62cd90ebca

To update your preferences and to unsubscribe visit
http://e-hyd.com/mail/?p=preferences&uid=2e0422689271d6451d4b2c62cd90ebca

Forward this Message to your Friends
http://e-hyd.com/mail/?p=forward&uid=2e0422689271d6451d4b2c62cd90ebca&mid=7


--
Powered by PHPlist, www.phplist.com --



^ permalink raw reply

* [2.6.24 patch] the planned eepro100 removal
From: Adrian Bunk @ 2007-07-29 15:02 UTC (permalink / raw)
  To: jgarzik; +Cc: netdev, linux-kernel, saw, Kok, Auke

This patch contains the planned removal of the eepro100 driver.

Signed-off-by: Adrian Bunk <bunk@stusta.de>

---

 Documentation/feature-removal-schedule.txt |    8 
 MAINTAINERS                                |    5 
 drivers/net/Kconfig                        |   14 
 drivers/net/Makefile                       |    1 
 drivers/net/eepro100.c                     | 2411 ---------------------
 5 files changed, 2439 deletions(-)

--- linux-2.6.20-rc2-mm1/MAINTAINERS.old	2007-01-02 21:29:08.000000000 +0100
+++ linux-2.6.20-rc2-mm1/MAINTAINERS	2007-01-02 21:29:14.000000000 +0100
@@ -1077,11 +1077,6 @@
 W:	bluesmoke.sourceforge.net
 S:	Maintained
 
-EEPRO100 NETWORK DRIVER
-P:	Andrey V. Savochkin
-M:	saw@saw.sw.com.sg
-S:	Maintained
-
 EFS FILESYSTEM
 W:	http://aeschi.ch.eu.org/efs/
 S:	Orphan
--- linux-2.6.20-rc2-mm1/drivers/net/Kconfig.old	2007-01-02 21:29:22.000000000 +0100
+++ linux-2.6.20-rc2-mm1/drivers/net/Kconfig	2007-01-02 21:29:38.000000000 +0100
@@ -1471,20 +1471,6 @@
 	  <file:Documentation/networking/net-modules.txt>.  The module
 	  will be called dgrs.
 
-config EEPRO100
-	tristate "EtherExpressPro/100 support (eepro100, original Becker driver)"
-	depends on NET_PCI && PCI
-	select MII
-	help
-	  If you have an Intel EtherExpress PRO/100 PCI network (Ethernet)
-	  card, say Y and read the Ethernet-HOWTO, available from
-	  <http://www.tldp.org/docs.html#howto>.
-
-	  To compile this driver as a module, choose M here and read
-	  <file:Documentation/networking/net-modules.txt>.  The module
-	  will be called eepro100.
-
-
 config E100
 	tristate "Intel(R) PRO/100+ support"
 	depends on NET_PCI && PCI
--- linux-2.6.20-rc2-mm1/drivers/net/Makefile.old	2007-01-02 21:29:44.000000000 +0100
+++ linux-2.6.20-rc2-mm1/drivers/net/Makefile	2007-01-02 21:29:51.000000000 +0100
@@ -44,7 +44,6 @@
 obj-$(CONFIG_TYPHOON) += typhoon.o
 obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
 obj-$(CONFIG_PCNET32) += pcnet32.o
-obj-$(CONFIG_EEPRO100) += eepro100.o
 obj-$(CONFIG_E100) += e100.o
 obj-$(CONFIG_TLAN) += tlan.o
 obj-$(CONFIG_EPIC100) += epic100.o

--- linux-2.6.22-rc6-mm1/Documentation/feature-removal-schedule.txt.old	2007-06-30 00:10:08.000000000 +0200
+++ linux-2.6.22-rc6-mm1/Documentation/feature-removal-schedule.txt	2007-06-30 00:10:38.000000000 +0200
@@ -97,13 +97,6 @@
 
 ---------------------------
 
-What:   eepro100 network driver
-When:   January 2007
-Why:    replaced by the e100 driver
-Who:    Adrian Bunk <bunk@stusta.de>
-
----------------------------
-
 What:	Unused EXPORT_SYMBOL/EXPORT_SYMBOL_GPL exports
 	(temporary transition config option provided until then)
 	The transition config option will also be removed at the same time.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
--- linux-2.6/drivers/net/eepro100.c	2007-07-18 07:54:37.000000000 +0200
+++ /dev/null	2006-09-19 00:45:31.000000000 +0200
@@ -1,2411 +0,0 @@
-/* drivers/net/eepro100.c: An Intel i82557-559 Ethernet driver for Linux. */
-/*
-	Written 1996-1999 by Donald Becker.
-
-	The driver also contains updates by different kernel developers
-	(see incomplete list below).
-	Current maintainer is Andrey V. Savochkin <saw@saw.sw.com.sg>.
-	Please use this email address and linux-kernel mailing list for bug reports.
-
-	This software may be used and distributed according to the terms
-	of the GNU General Public License, incorporated herein by reference.
-
-	This driver is for the Intel EtherExpress Pro100 (Speedo3) design.
-	It should work with all i82557/558/559 boards.
-
-	Version history:
-	1998 Apr - 2000 Feb  Andrey V. Savochkin <saw@saw.sw.com.sg>
-		Serious fixes for multicast filter list setting, TX timeout routine;
-		RX ring refilling logic;  other stuff
-	2000 Feb  Jeff Garzik <jgarzik@pobox.com>
-		Convert to new PCI driver interface
-	2000 Mar 24  Dragan Stancevic <visitor@valinux.com>
-		Disabled FC and ER, to avoid lockups when when we get FCP interrupts.
-	2000 Jul 17 Goutham Rao <goutham.rao@intel.com>
-		PCI DMA API fixes, adding pci_dma_sync_single calls where neccesary
-	2000 Aug 31 David Mosberger <davidm@hpl.hp.com>
-		rx_align support: enables rx DMA without causing unaligned accesses.
-*/
-
-static const char * const version =
-"eepro100.c:v1.09j-t 9/29/99 Donald Becker\n"
-"eepro100.c: $Revision: 1.36 $ 2000/11/17 Modified by Andrey V. Savochkin <saw@saw.sw.com.sg> and others\n";
-
-/* A few user-configurable values that apply to all boards.
-   First set is undocumented and spelled per Intel recommendations. */
-
-static int congenb /* = 0 */; /* Enable congestion control in the DP83840. */
-static int txfifo = 8;		/* Tx FIFO threshold in 4 byte units, 0-15 */
-static int rxfifo = 8;		/* Rx FIFO threshold, default 32 bytes. */
-/* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */
-static int txdmacount = 128;
-static int rxdmacount /* = 0 */;
-
-#if defined(__ia64__) || defined(__alpha__) || defined(__sparc__) || defined(__mips__) || \
-	defined(__arm__)
-  /* align rx buffers to 2 bytes so that IP header is aligned */
-# define rx_align(skb)		skb_reserve((skb), 2)
-# define RxFD_ALIGNMENT		__attribute__ ((aligned (2), packed))
-#else
-# define rx_align(skb)
-# define RxFD_ALIGNMENT
-#endif
-
-/* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
-   Lower values use more memory, but are faster. */
-static int rx_copybreak = 200;
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
-
-/* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */
-static int multicast_filter_limit = 64;
-
-/* 'options' is used to pass a transceiver override or full-duplex flag
-   e.g. "options=16" for FD, "options=32" for 100mbps-only. */
-static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-
-/* A few values that may be tweaked. */
-/* The ring sizes should be a power of two for efficiency. */
-#define TX_RING_SIZE	64
-#define RX_RING_SIZE	64
-/* How much slots multicast filter setup may take.
-   Do not descrease without changing set_rx_mode() implementaion. */
-#define TX_MULTICAST_SIZE   2
-#define TX_MULTICAST_RESERV (TX_MULTICAST_SIZE*2)
-/* Actual number of TX packets queued, must be
-   <= TX_RING_SIZE-TX_MULTICAST_RESERV. */
-#define TX_QUEUE_LIMIT  (TX_RING_SIZE-TX_MULTICAST_RESERV)
-/* Hysteresis marking queue as no longer full. */
-#define TX_QUEUE_UNFULL (TX_QUEUE_LIMIT-4)
-
-/* Operational parameters that usually are not changed. */
-
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT		(2*HZ)
-/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
-#define PKT_BUF_SZ		1536
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/pci.h>
-#include <linux/spinlock.h>
-#include <linux/init.h>
-#include <linux/mii.h>
-#include <linux/delay.h>
-#include <linux/bitops.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/irq.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/rtnetlink.h>
-#include <linux/skbuff.h>
-#include <linux/ethtool.h>
-
-static int use_io;
-static int debug = -1;
-#define DEBUG_DEFAULT		(NETIF_MSG_DRV		| \
-				 NETIF_MSG_HW		| \
-				 NETIF_MSG_RX_ERR	| \
-				 NETIF_MSG_TX_ERR)
-#define DEBUG			((debug >= 0) ? (1<<debug)-1 : DEBUG_DEFAULT)
-
-
-MODULE_AUTHOR("Maintainer: Andrey V. Savochkin <saw@saw.sw.com.sg>");
-MODULE_DESCRIPTION("Intel i82557/i82558/i82559 PCI EtherExpressPro driver");
-MODULE_LICENSE("GPL");
-module_param(use_io, int, 0);
-module_param(debug, int, 0);
-module_param_array(options, int, NULL, 0);
-module_param_array(full_duplex, int, NULL, 0);
-module_param(congenb, int, 0);
-module_param(txfifo, int, 0);
-module_param(rxfifo, int, 0);
-module_param(txdmacount, int, 0);
-module_param(rxdmacount, int, 0);
-module_param(rx_copybreak, int, 0);
-module_param(max_interrupt_work, int, 0);
-module_param(multicast_filter_limit, int, 0);
-MODULE_PARM_DESC(debug, "debug level (0-6)");
-MODULE_PARM_DESC(options, "Bits 0-3: transceiver type, bit 4: full duplex, bit 5: 100Mbps");
-MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)");
-MODULE_PARM_DESC(congenb, "Enable congestion control (1)");
-MODULE_PARM_DESC(txfifo, "Tx FIFO threshold in 4 byte units, (0-15)");
-MODULE_PARM_DESC(rxfifo, "Rx FIFO threshold in 4 byte units, (0-15)");
-MODULE_PARM_DESC(txdmacount, "Tx DMA burst length; 128 - disable (0-128)");
-MODULE_PARM_DESC(rxdmacount, "Rx DMA burst length; 128 - disable (0-128)");
-MODULE_PARM_DESC(rx_copybreak, "copy breakpoint for copy-only-tiny-frames");
-MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
-MODULE_PARM_DESC(multicast_filter_limit, "maximum number of filtered multicast addresses");
-
-#define RUN_AT(x) (jiffies + (x))
-
-#define netdevice_start(dev)
-#define netdevice_stop(dev)
-#define netif_set_tx_timeout(dev, tf, tm) \
-								do { \
-									(dev)->tx_timeout = (tf); \
-									(dev)->watchdog_timeo = (tm); \
-								} while(0)
-
-
-
-/*
-				Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's
-single-chip fast Ethernet controller for PCI, as used on the Intel
-EtherExpress Pro 100 adapter.
-
-II. Board-specific settings
-
-PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board.  The system BIOS should be set to assign the
-PCI INTA signal to an otherwise unused system IRQ line.  While it's
-possible to share PCI interrupt lines, it negatively impacts performance and
-only recent kernels support it.
-
-III. Driver operation
-
-IIIA. General
-The Speedo3 is very similar to other Intel network chips, that is to say
-"apparently designed on a different planet".  This chips retains the complex
-Rx and Tx descriptors and multiple buffers pointers as previous chips, but
-also has simplified Tx and Rx buffer modes.  This driver uses the "flexible"
-Tx mode, but in a simplified lower-overhead manner: it associates only a
-single buffer descriptor with each frame descriptor.
-
-Despite the extra space overhead in each receive skbuff, the driver must use
-the simplified Rx buffer mode to assure that only a single data buffer is
-associated with each RxFD. The driver implements this by reserving space
-for the Rx descriptor at the head of each Rx skbuff.
-
-The Speedo-3 has receive and command unit base addresses that are added to
-almost all descriptor pointers.  The driver sets these to zero, so that all
-pointer fields are absolute addresses.
-
-The System Control Block (SCB) of some previous Intel chips exists on the
-chip in both PCI I/O and memory space.  This driver uses the I/O space
-registers, but might switch to memory mapped mode to better support non-x86
-processors.
-
-IIIB. Transmit structure
-
-The driver must use the complex Tx command+descriptor mode in order to
-have a indirect pointer to the skbuff data section.  Each Tx command block
-(TxCB) is associated with two immediately appended Tx Buffer Descriptor
-(TxBD).  A fixed ring of these TxCB+TxBD pairs are kept as part of the
-speedo_private data structure for each adapter instance.
-
-The newer i82558 explicitly supports this structure, and can read the two
-TxBDs in the same PCI burst as the TxCB.
-
-This ring structure is used for all normal transmit packets, but the
-transmit packet descriptors aren't long enough for most non-Tx commands such
-as CmdConfigure.  This is complicated by the possibility that the chip has
-already loaded the link address in the previous descriptor.  So for these
-commands we convert the next free descriptor on the ring to a NoOp, and point
-that descriptor's link to the complex command.
-
-An additional complexity of these non-transmit commands are that they may be
-added asynchronous to the normal transmit queue, so we disable interrupts
-whenever the Tx descriptor ring is manipulated.
-
-A notable aspect of these special configure commands is that they do
-work with the normal Tx ring entry scavenge method.  The Tx ring scavenge
-is done at interrupt time using the 'dirty_tx' index, and checking for the
-command-complete bit.  While the setup frames may have the NoOp command on the
-Tx ring marked as complete, but not have completed the setup command, this
-is not a problem.  The tx_ring entry can be still safely reused, as the
-tx_skbuff[] entry is always empty for config_cmd and mc_setup frames.
-
-Commands may have bits set e.g. CmdSuspend in the command word to either
-suspend or stop the transmit/command unit.  This driver always flags the last
-command with CmdSuspend, erases the CmdSuspend in the previous command, and
-then issues a CU_RESUME.
-Note: Watch out for the potential race condition here: imagine
-	erasing the previous suspend
-		the chip processes the previous command
-		the chip processes the final command, and suspends
-	doing the CU_RESUME
-		the chip processes the next-yet-valid post-final-command.
-So blindly sending a CU_RESUME is only safe if we do it immediately after
-after erasing the previous CmdSuspend, without the possibility of an
-intervening delay.  Thus the resume command is always within the
-interrupts-disabled region.  This is a timing dependence, but handling this
-condition in a timing-independent way would considerably complicate the code.
-
-Note: In previous generation Intel chips, restarting the command unit was a
-notoriously slow process.  This is presumably no longer true.
-
-IIIC. Receive structure
-
-Because of the bus-master support on the Speedo3 this driver uses the new
-SKBUFF_RX_COPYBREAK scheme, rather than a fixed intermediate receive buffer.
-This scheme allocates full-sized skbuffs as receive buffers.  The value
-SKBUFF_RX_COPYBREAK is used as the copying breakpoint: it is chosen to
-trade-off the memory wasted by passing the full-sized skbuff to the queue
-layer for all frames vs. the copying cost of copying a frame to a
-correctly-sized skbuff.
-
-For small frames the copying cost is negligible (esp. considering that we
-are pre-loading the cache with immediately useful header information), so we
-allocate a new, minimally-sized skbuff.  For large frames the copying cost
-is non-trivial, and the larger copy might flush the cache of useful data, so
-we pass up the skbuff the packet was received into.
-
-IV. Notes
-
-Thanks to Steve Williams of Intel for arranging the non-disclosure agreement
-that stated that I could disclose the information.  But I still resent
-having to sign an Intel NDA when I'm helping Intel sell their own product!
-
-*/
-
-static int speedo_found1(struct pci_dev *pdev, void __iomem *ioaddr, int fnd_cnt, int acpi_idle_state);
-
-/* Offsets to the various registers.
-   All accesses need not be longword aligned. */
-enum speedo_offsets {
-	SCBStatus = 0, SCBCmd = 2,	/* Rx/Command Unit command and status. */
-	SCBIntmask = 3,
-	SCBPointer = 4,				/* General purpose pointer. */
-	SCBPort = 8,				/* Misc. commands and operands.  */
-	SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
-	SCBCtrlMDI = 16,			/* MDI interface control. */
-	SCBEarlyRx = 20,			/* Early receive byte count. */
-};
-/* Commands that can be put in a command list entry. */
-enum commands {
-	CmdNOp = 0, CmdIASetup = 0x10000, CmdConfigure = 0x20000,
-	CmdMulticastList = 0x30000, CmdTx = 0x40000, CmdTDR = 0x50000,
-	CmdDump = 0x60000, CmdDiagnose = 0x70000,
-	CmdSuspend = 0x40000000,	/* Suspend after completion. */
-	CmdIntr = 0x20000000,		/* Interrupt after completion. */
-	CmdTxFlex = 0x00080000,		/* Use "Flexible mode" for CmdTx command. */
-};
-/* Clear CmdSuspend (1<<30) avoiding interference with the card access to the
-   status bits.  Previous driver versions used separate 16 bit fields for
-   commands and statuses.  --SAW
- */
-#if defined(__alpha__)
-# define clear_suspend(cmd)  clear_bit(30, &(cmd)->cmd_status);
-#else
-# if defined(__LITTLE_ENDIAN)
-#  define clear_suspend(cmd)  ((__u16 *)&(cmd)->cmd_status)[1] &= ~0x4000
-# elif defined(__BIG_ENDIAN)
-#  define clear_suspend(cmd)  ((__u16 *)&(cmd)->cmd_status)[1] &= ~0x0040
-# else
-#  error Unsupported byteorder
-# endif
-#endif
-
-enum SCBCmdBits {
-	SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000,
-	SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400,
-	SCBTriggerIntr=0x0200, SCBMaskAll=0x0100,
-	/* The rest are Rx and Tx commands. */
-	CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050,
-	CUCmdBase=0x0060,	/* CU Base address (set to zero) . */
-	CUDumpStats=0x0070, /* Dump then reset stats counters. */
-	RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006,
-	RxResumeNoResources=0x0007,
-};
-
-enum SCBPort_cmds {
-	PortReset=0, PortSelfTest=1, PortPartialReset=2, PortDump=3,
-};
-
-/* The Speedo3 Rx and Tx frame/buffer descriptors. */
-struct descriptor {			    /* A generic descriptor. */
-	volatile s32 cmd_status;	/* All command and status fields. */
-	u32 link;				    /* struct descriptor *  */
-	unsigned char params[0];
-};
-
-/* The Speedo3 Rx and Tx buffer descriptors. */
-struct RxFD {					/* Receive frame descriptor. */
-	volatile s32 status;
-	u32 link;					/* struct RxFD * */
-	u32 rx_buf_addr;			/* void * */
-	u32 count;
-} RxFD_ALIGNMENT;
-
-/* Selected elements of the Tx/RxFD.status word. */
-enum RxFD_bits {
-	RxComplete=0x8000, RxOK=0x2000,
-	RxErrCRC=0x0800, RxErrAlign=0x0400, RxErrTooBig=0x0200, RxErrSymbol=0x0010,
-	RxEth2Type=0x0020, RxNoMatch=0x0004, RxNoIAMatch=0x0002,
-	TxUnderrun=0x1000,  StatusComplete=0x8000,
-};
-
-#define CONFIG_DATA_SIZE 22
-struct TxFD {					/* Transmit frame descriptor set. */
-	s32 status;
-	u32 link;					/* void * */
-	u32 tx_desc_addr;			/* Always points to the tx_buf_addr element. */
-	s32 count;					/* # of TBD (=1), Tx start thresh., etc. */
-	/* This constitutes two "TBD" entries -- we only use one. */
-#define TX_DESCR_BUF_OFFSET 16
-	u32 tx_buf_addr0;			/* void *, frame to be transmitted.  */
-	s32 tx_buf_size0;			/* Length of Tx frame. */
-	u32 tx_buf_addr1;			/* void *, frame to be transmitted.  */
-	s32 tx_buf_size1;			/* Length of Tx frame. */
-	/* the structure must have space for at least CONFIG_DATA_SIZE starting
-	 * from tx_desc_addr field */
-};
-
-/* Multicast filter setting block.  --SAW */
-struct speedo_mc_block {
-	struct speedo_mc_block *next;
-	unsigned int tx;
-	dma_addr_t frame_dma;
-	unsigned int len;
-	struct descriptor frame __attribute__ ((__aligned__(16)));
-};
-
-/* Elements of the dump_statistics block. This block must be lword aligned. */
-struct speedo_stats {
-	u32 tx_good_frames;
-	u32 tx_coll16_errs;
-	u32 tx_late_colls;
-	u32 tx_underruns;
-	u32 tx_lost_carrier;
-	u32 tx_deferred;
-	u32 tx_one_colls;
-	u32 tx_multi_colls;
-	u32 tx_total_colls;
-	u32 rx_good_frames;
-	u32 rx_crc_errs;
-	u32 rx_align_errs;
-	u32 rx_resource_errs;
-	u32 rx_overrun_errs;
-	u32 rx_colls_errs;
-	u32 rx_runt_errs;
-	u32 done_marker;
-};
-
-enum Rx_ring_state_bits {
-	RrNoMem=1, RrPostponed=2, RrNoResources=4, RrOOMReported=8,
-};
-
-/* Do not change the position (alignment) of the first few elements!
-   The later elements are grouped for cache locality.
-
-   Unfortunately, all the positions have been shifted since there.
-   A new re-alignment is required.  2000/03/06  SAW */
-struct speedo_private {
-    void __iomem *regs;
-	struct TxFD	*tx_ring;		/* Commands (usually CmdTxPacket). */
-	struct RxFD *rx_ringp[RX_RING_SIZE];	/* Rx descriptor, used as ring. */
-	/* The addresses of a Tx/Rx-in-place packets/buffers. */
-	struct sk_buff *tx_skbuff[TX_RING_SIZE];
-	struct sk_buff *rx_skbuff[RX_RING_SIZE];
-	/* Mapped addresses of the rings. */
-	dma_addr_t tx_ring_dma;
-#define TX_RING_ELEM_DMA(sp, n) ((sp)->tx_ring_dma + (n)*sizeof(struct TxFD))
-	dma_addr_t rx_ring_dma[RX_RING_SIZE];
-	struct descriptor *last_cmd;		/* Last command sent. */
-	unsigned int cur_tx, dirty_tx;		/* The ring entries to be free()ed. */
-	spinlock_t lock;			/* Group with Tx control cache line. */
-	u32 tx_threshold;			/* The value for txdesc.count. */
-	struct RxFD *last_rxf;			/* Last filled RX buffer. */
-	dma_addr_t last_rxf_dma;
-	unsigned int cur_rx, dirty_rx;		/* The next free ring entry */
-	long last_rx_time;			/* Last Rx, in jiffies, to handle Rx hang. */
-	struct net_device_stats stats;
-	struct speedo_stats *lstats;
-	dma_addr_t lstats_dma;
-	int chip_id;
-	struct pci_dev *pdev;
-	struct timer_list timer;		/* Media selection timer. */
-	struct speedo_mc_block *mc_setup_head;	/* Multicast setup frame list head. */
-	struct speedo_mc_block *mc_setup_tail;	/* Multicast setup frame list tail. */
-	long in_interrupt;			/* Word-aligned dev->interrupt */
-	unsigned char acpi_pwr;
-	signed char rx_mode;			/* Current PROMISC/ALLMULTI setting. */
-	unsigned int tx_full:1;			/* The Tx queue is full. */
-	unsigned int flow_ctrl:1;		/* Use 802.3x flow control. */
-	unsigned int rx_bug:1;			/* Work around receiver hang errata. */
-	unsigned char default_port:8;		/* Last dev->if_port value. */
-	unsigned char rx_ring_state;		/* RX ring status flags. */
-	unsigned short phy[2];			/* PHY media interfaces available. */
-	unsigned short partner;			/* Link partner caps. */
-	struct mii_if_info mii_if;		/* MII API hooks, info */
-	u32 msg_enable;				/* debug message level */
-};
-
-/* The parameters for a CmdConfigure operation.
-   There are so many options that it would be difficult to document each bit.
-   We mostly use the default or recommended settings. */
-static const char i82557_config_cmd[CONFIG_DATA_SIZE] = {
-	22, 0x08, 0, 0,  0, 0, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */
-	0, 0x2E, 0,  0x60, 0,
-	0xf2, 0x48,   0, 0x40, 0xf2, 0x80, 		/* 0x40=Force full-duplex */
-	0x3f, 0x05, };
-static const char i82558_config_cmd[CONFIG_DATA_SIZE] = {
-	22, 0x08, 0, 1,  0, 0, 0x22, 0x03,  1, /* 1=Use MII  0=Use AUI */
-	0, 0x2E, 0,  0x60, 0x08, 0x88,
-	0x68, 0, 0x40, 0xf2, 0x84,		/* Disable FC */
-	0x31, 0x05, };
-
-/* PHY media interface chips. */
-static const char * const phys[] = {
-	"None", "i82553-A/B", "i82553-C", "i82503",
-	"DP83840", "80c240", "80c24", "i82555",
-	"unknown-8", "unknown-9", "DP83840A", "unknown-11",
-	"unknown-12", "unknown-13", "unknown-14", "unknown-15", };
-enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
-					 S80C24, I82555, DP83840A=10, };
-static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
-#define EE_READ_CMD		(6)
-
-static int eepro100_init_one(struct pci_dev *pdev,
-		const struct pci_device_id *ent);
-
-static int do_eeprom_cmd(void __iomem *ioaddr, int cmd, int cmd_len);
-static int mdio_read(struct net_device *dev, int phy_id, int location);
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
-static int speedo_open(struct net_device *dev);
-static void speedo_resume(struct net_device *dev);
-static void speedo_timer(unsigned long data);
-static void speedo_init_rx_ring(struct net_device *dev);
-static void speedo_tx_timeout(struct net_device *dev);
-static int speedo_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static void speedo_refill_rx_buffers(struct net_device *dev, int force);
-static int speedo_rx(struct net_device *dev);
-static void speedo_tx_buffer_gc(struct net_device *dev);
-static irqreturn_t speedo_interrupt(int irq, void *dev_instance);
-static int speedo_close(struct net_device *dev);
-static struct net_device_stats *speedo_get_stats(struct net_device *dev);
-static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void set_rx_mode(struct net_device *dev);
-static void speedo_show_state(struct net_device *dev);
-static const struct ethtool_ops ethtool_ops;
-
-
-
-#ifdef honor_default_port
-/* Optional driver feature to allow forcing the transceiver setting.
-   Not recommended. */
-static int mii_ctrl[8] = { 0x3300, 0x3100, 0x0000, 0x0100,
-						   0x2000, 0x2100, 0x0400, 0x3100};
-#endif
-
-/* How to wait for the command unit to accept a command.
-   Typically this takes 0 ticks. */
-static inline unsigned char wait_for_cmd_done(struct net_device *dev,
-											  	struct speedo_private *sp)
-{
-	int wait = 1000;
-	void __iomem *cmd_ioaddr = sp->regs + SCBCmd;
-	unsigned char r;
-
-	do  {
-		udelay(1);
-		r = ioread8(cmd_ioaddr);
-	} while(r && --wait >= 0);
-
-	if (wait < 0)
-		printk(KERN_ALERT "%s: wait_for_cmd_done timeout!\n", dev->name);
-	return r;
-}
-
-static int __devinit eepro100_init_one (struct pci_dev *pdev,
-		const struct pci_device_id *ent)
-{
-	void __iomem *ioaddr;
-	int irq, pci_bar;
-	int acpi_idle_state = 0, pm;
-	static int cards_found /* = 0 */;
-	unsigned long pci_base;
-
-#ifndef MODULE
-	/* when built-in, we only print version if device is found */
-	static int did_version;
-	if (did_version++ == 0)
-		printk(version);
-#endif
-
-	/* save power state before pci_enable_device overwrites it */
-	pm = pci_find_capability(pdev, PCI_CAP_ID_PM);
-	if (pm) {
-		u16 pwr_command;
-		pci_read_config_word(pdev, pm + PCI_PM_CTRL, &pwr_command);
-		acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK;
-	}
-
-	if (pci_enable_device(pdev))
-		goto err_out_free_mmio_region;
-
-	pci_set_master(pdev);
-
-	if (!request_region(pci_resource_start(pdev, 1),
-			pci_resource_len(pdev, 1), "eepro100")) {
-		dev_err(&pdev->dev, "eepro100: cannot reserve I/O ports\n");
-		goto err_out_none;
-	}
-	if (!request_mem_region(pci_resource_start(pdev, 0),
-			pci_resource_len(pdev, 0), "eepro100")) {
-		dev_err(&pdev->dev, "eepro100: cannot reserve MMIO region\n");
-		goto err_out_free_pio_region;
-	}
-
-	irq = pdev->irq;
-	pci_bar = use_io ? 1 : 0;
-	pci_base = pci_resource_start(pdev, pci_bar);
-	if (DEBUG & NETIF_MSG_PROBE)
-		printk("Found Intel i82557 PCI Speedo at %#lx, IRQ %d.\n",
-		       pci_base, irq);
-
-	ioaddr = pci_iomap(pdev, pci_bar, 0);
-	if (!ioaddr) {
-		dev_err(&pdev->dev, "eepro100: cannot remap IO\n");
-		goto err_out_free_mmio_region;
-	}
-
-	if (speedo_found1(pdev, ioaddr, cards_found, acpi_idle_state) == 0)
-		cards_found++;
-	else
-		goto err_out_iounmap;
-
-	return 0;
-
-err_out_iounmap: ;
-	pci_iounmap(pdev, ioaddr);
-err_out_free_mmio_region:
-	release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
-err_out_free_pio_region:
-	release_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
-err_out_none:
-	return -ENODEV;
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling 'interrupt' - used by things like netconsole to send skbs
- * without having to re-enable interrupts. It's not called while
- * the interrupt routine is executing.
- */
-
-static void poll_speedo (struct net_device *dev)
-{
-	/* disable_irq is not very nice, but with the funny lockless design
-	   we have no other choice. */
-	disable_irq(dev->irq);
-	speedo_interrupt (dev->irq, dev);
-	enable_irq(dev->irq);
-}
-#endif
-
-static int __devinit speedo_found1(struct pci_dev *pdev,
-		void __iomem *ioaddr, int card_idx, int acpi_idle_state)
-{
-	struct net_device *dev;
-	struct speedo_private *sp;
-	const char *product;
-	int i, option;
-	u16 eeprom[0x100];
-	int size;
-	void *tx_ring_space;
-	dma_addr_t tx_ring_dma;
-
-	size = TX_RING_SIZE * sizeof(struct TxFD) + sizeof(struct speedo_stats);
-	tx_ring_space = pci_alloc_consistent(pdev, size, &tx_ring_dma);
-	if (tx_ring_space == NULL)
-		return -1;
-
-	dev = alloc_etherdev(sizeof(struct speedo_private));
-	if (dev == NULL) {
-		printk(KERN_ERR "eepro100: Could not allocate ethernet device.\n");
-		pci_free_consistent(pdev, size, tx_ring_space, tx_ring_dma);
-		return -1;
-	}
-
-	SET_MODULE_OWNER(dev);
-	SET_NETDEV_DEV(dev, &pdev->dev);
-
-	if (dev->mem_start > 0)
-		option = dev->mem_start;
-	else if (card_idx >= 0  &&  options[card_idx] >= 0)
-		option = options[card_idx];
-	else
-		option = 0;
-
-	rtnl_lock();
-	if (dev_alloc_name(dev, dev->name) < 0)
-		goto err_free_unlock;
-
-	/* Read the station address EEPROM before doing the reset.
-	   Nominally his should even be done before accepting the device, but
-	   then we wouldn't have a device name with which to report the error.
-	   The size test is for 6 bit vs. 8 bit address serial EEPROMs.
-	*/
-	{
-		void __iomem *iobase;
-		int read_cmd, ee_size;
-		u16 sum;
-		int j;
-
-		/* Use IO only to avoid postponed writes and satisfy EEPROM timing
-		   requirements. */
-		iobase = pci_iomap(pdev, 1, pci_resource_len(pdev, 1));
-		if (!iobase)
-			goto err_free_unlock;
-		if ((do_eeprom_cmd(iobase, EE_READ_CMD << 24, 27) & 0xffe0000)
-			== 0xffe0000) {
-			ee_size = 0x100;
-			read_cmd = EE_READ_CMD << 24;
-		} else {
-			ee_size = 0x40;
-			read_cmd = EE_READ_CMD << 22;
-		}
-
-		for (j = 0, i = 0, sum = 0; i < ee_size; i++) {
-			u16 value = do_eeprom_cmd(iobase, read_cmd | (i << 16), 27);
-			eeprom[i] = value;
-			sum += value;
-			if (i < 3) {
-				dev->dev_addr[j++] = value;
-				dev->dev_addr[j++] = value >> 8;
-			}
-		}
-		if (sum != 0xBABA)
-			printk(KERN_WARNING "%s: Invalid EEPROM checksum %#4.4x, "
-				   "check settings before activating this device!\n",
-				   dev->name, sum);
-		/* Don't  unregister_netdev(dev);  as the EEPro may actually be
-		   usable, especially if the MAC address is set later.
-		   On the other hand, it may be unusable if MDI data is corrupted. */
-
-		pci_iounmap(pdev, iobase);
-	}
-
-	/* Reset the chip: stop Tx and Rx processes and clear counters.
-	   This takes less than 10usec and will easily finish before the next
-	   action. */
-	iowrite32(PortReset, ioaddr + SCBPort);
-	ioread32(ioaddr + SCBPort);
-	udelay(10);
-
-	if (eeprom[3] & 0x0100)
-		product = "OEM i82557/i82558 10/100 Ethernet";
-	else
-		product = pci_name(pdev);
-
-	printk(KERN_INFO "%s: %s, ", dev->name, product);
-
-	for (i = 0; i < 5; i++)
-		printk("%2.2X:", dev->dev_addr[i]);
-	printk("%2.2X, ", dev->dev_addr[i]);
-	printk("IRQ %d.\n", pdev->irq);
-
-	sp = netdev_priv(dev);
-
-	/* we must initialize this early, for mdio_{read,write} */
-	sp->regs = ioaddr;
-
-#if 1 || defined(kernel_bloat)
-	/* OK, this is pure kernel bloat.  I don't like it when other drivers
-	   waste non-pageable kernel space to emit similar messages, but I need
-	   them for bug reports. */
-	{
-		const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
-		/* The self-test results must be paragraph aligned. */
-		volatile s32 *self_test_results;
-		int boguscnt = 16000;	/* Timeout for set-test. */
-		if ((eeprom[3] & 0x03) != 0x03)
-			printk(KERN_INFO "  Receiver lock-up bug exists -- enabling"
-				   " work-around.\n");
-		printk(KERN_INFO "  Board assembly %4.4x%2.2x-%3.3d, Physical"
-			   " connectors present:",
-			   eeprom[8], eeprom[9]>>8, eeprom[9] & 0xff);
-		for (i = 0; i < 4; i++)
-			if (eeprom[5] & (1<<i))
-				printk(connectors[i]);
-		printk("\n"KERN_INFO"  Primary interface chip %s PHY #%d.\n",
-			   phys[(eeprom[6]>>8)&15], eeprom[6] & 0x1f);
-		if (eeprom[7] & 0x0700)
-			printk(KERN_INFO "    Secondary interface chip %s.\n",
-				   phys[(eeprom[7]>>8)&7]);
-		if (((eeprom[6]>>8) & 0x3f) == DP83840
-			||  ((eeprom[6]>>8) & 0x3f) == DP83840A) {
-			int mdi_reg23 = mdio_read(dev, eeprom[6] & 0x1f, 23) | 0x0422;
-			if (congenb)
-			  mdi_reg23 |= 0x0100;
-			printk(KERN_INFO"  DP83840 specific setup, setting register 23 to %4.4x.\n",
-				   mdi_reg23);
-			mdio_write(dev, eeprom[6] & 0x1f, 23, mdi_reg23);
-		}
-		if ((option >= 0) && (option & 0x70)) {
-			printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n",
-				   (option & 0x20 ? 100 : 10),
-				   (option & 0x10 ? "full" : "half"));
-			mdio_write(dev, eeprom[6] & 0x1f, MII_BMCR,
-					   ((option & 0x20) ? 0x2000 : 0) | 	/* 100mbps? */
-					   ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
-		}
-
-		/* Perform a system self-test. */
-		self_test_results = (s32*) ((((long) tx_ring_space) + 15) & ~0xf);
-		self_test_results[0] = 0;
-		self_test_results[1] = -1;
-		iowrite32(tx_ring_dma | PortSelfTest, ioaddr + SCBPort);
-		do {
-			udelay(10);
-		} while (self_test_results[1] == -1  &&  --boguscnt >= 0);
-
-		if (boguscnt < 0) {		/* Test optimized out. */
-			printk(KERN_ERR "Self test failed, status %8.8x:\n"
-				   KERN_ERR " Failure to initialize the i82557.\n"
-				   KERN_ERR " Verify that the card is a bus-master"
-				   " capable slot.\n",
-				   self_test_results[1]);
-		} else
-			printk(KERN_INFO "  General self-test: %s.\n"
-				   KERN_INFO "  Serial sub-system self-test: %s.\n"
-				   KERN_INFO "  Internal registers self-test: %s.\n"
-				   KERN_INFO "  ROM checksum self-test: %s (%#8.8x).\n",
-				   self_test_results[1] & 0x1000 ? "failed" : "passed",
-				   self_test_results[1] & 0x0020 ? "failed" : "passed",
-				   self_test_results[1] & 0x0008 ? "failed" : "passed",
-				   self_test_results[1] & 0x0004 ? "failed" : "passed",
-				   self_test_results[0]);
-	}
-#endif  /* kernel_bloat */
-
-	iowrite32(PortReset, ioaddr + SCBPort);
-	ioread32(ioaddr + SCBPort);
-	udelay(10);
-
-	/* Return the chip to its original power state. */
-	pci_set_power_state(pdev, acpi_idle_state);
-
-	pci_set_drvdata (pdev, dev);
-	SET_NETDEV_DEV(dev, &pdev->dev);
-
-	dev->irq = pdev->irq;
-
-	sp->pdev = pdev;
-	sp->msg_enable = DEBUG;
-	sp->acpi_pwr = acpi_idle_state;
-	sp->tx_ring = tx_ring_space;
-	sp->tx_ring_dma = tx_ring_dma;
-	sp->lstats = (struct speedo_stats *)(sp->tx_ring + TX_RING_SIZE);
-	sp->lstats_dma = TX_RING_ELEM_DMA(sp, TX_RING_SIZE);
-	init_timer(&sp->timer); /* used in ioctl() */
-	spin_lock_init(&sp->lock);
-
-	sp->mii_if.full_duplex = option >= 0 && (option & 0x10) ? 1 : 0;
-	if (card_idx >= 0) {
-		if (full_duplex[card_idx] >= 0)
-			sp->mii_if.full_duplex = full_duplex[card_idx];
-	}
-	sp->default_port = option >= 0 ? (option & 0x0f) : 0;
-
-	sp->phy[0] = eeprom[6];
-	sp->phy[1] = eeprom[7];
-
-	sp->mii_if.phy_id = eeprom[6] & 0x1f;
-	sp->mii_if.phy_id_mask = 0x1f;
-	sp->mii_if.reg_num_mask = 0x1f;
-	sp->mii_if.dev = dev;
-	sp->mii_if.mdio_read = mdio_read;
-	sp->mii_if.mdio_write = mdio_write;
-
-	sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
-	if (((pdev->device > 0x1030 && (pdev->device < 0x103F)))
-	    || (pdev->device == 0x2449) || (pdev->device == 0x2459)
-            || (pdev->device == 0x245D)) {
-	    	sp->chip_id = 1;
-	}
-
-	if (sp->rx_bug)
-		printk(KERN_INFO "  Receiver lock-up workaround activated.\n");
-
-	/* The Speedo-specific entries in the device structure. */
-	dev->open = &speedo_open;
-	dev->hard_start_xmit = &speedo_start_xmit;
-	netif_set_tx_timeout(dev, &speedo_tx_timeout, TX_TIMEOUT);
-	dev->stop = &speedo_close;
-	dev->get_stats = &speedo_get_stats;
-	dev->set_multicast_list = &set_rx_mode;
-	dev->do_ioctl = &speedo_ioctl;
-	SET_ETHTOOL_OPS(dev, &ethtool_ops);
-#ifdef CONFIG_NET_POLL_CONTROLLER
-	dev->poll_controller = &poll_speedo;
-#endif
-
-	if (register_netdevice(dev))
-		goto err_free_unlock;
-	rtnl_unlock();
-
-	return 0;
-
- err_free_unlock:
-	rtnl_unlock();
-	free_netdev(dev);
-	return -1;
-}
-
-static void do_slow_command(struct net_device *dev, struct speedo_private *sp, int cmd)
-{
-	void __iomem *cmd_ioaddr = sp->regs + SCBCmd;
-	int wait = 0;
-	do
-		if (ioread8(cmd_ioaddr) == 0) break;
-	while(++wait <= 200);
-	if (wait > 100)
-		printk(KERN_ERR "Command %4.4x never accepted (%d polls)!\n",
-		       ioread8(cmd_ioaddr), wait);
-
-	iowrite8(cmd, cmd_ioaddr);
-
-	for (wait = 0; wait <= 100; wait++)
-		if (ioread8(cmd_ioaddr) == 0) return;
-	for (; wait <= 20000; wait++)
-		if (ioread8(cmd_ioaddr) == 0) return;
-		else udelay(1);
-	printk(KERN_ERR "Command %4.4x was not accepted after %d polls!"
-	       "  Current status %8.8x.\n",
-	       cmd, wait, ioread32(sp->regs + SCBStatus));
-}
-
-/* Serial EEPROM section.
-   A "bit" grungy, but we work our way through bit-by-bit :->. */
-/*  EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK	0x01	/* EEPROM shift clock. */
-#define EE_CS			0x02	/* EEPROM chip select. */
-#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
-#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
-#define EE_ENB			(0x4800 | EE_CS)
-#define EE_WRITE_0		0x4802
-#define EE_WRITE_1		0x4806
-#define EE_OFFSET		SCBeeprom
-
-/* The fixes for the code were kindly provided by Dragan Stancevic
-   <visitor@valinux.com> to strictly follow Intel specifications of EEPROM
-   access timing.
-   The publicly available sheet 64486302 (sec. 3.1) specifies 1us access
-   interval for serial EEPROM.  However, it looks like that there is an
-   additional requirement dictating larger udelay's in the code below.
-   2000/05/24  SAW */
-static int __devinit do_eeprom_cmd(void __iomem *ioaddr, int cmd, int cmd_len)
-{
-	unsigned retval = 0;
-	void __iomem *ee_addr = ioaddr + SCBeeprom;
-
-	iowrite16(EE_ENB, ee_addr); udelay(2);
-	iowrite16(EE_ENB | EE_SHIFT_CLK, ee_addr); udelay(2);
-
-	/* Shift the command bits out. */
-	do {
-		short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
-		iowrite16(dataval, ee_addr); udelay(2);
-		iowrite16(dataval | EE_SHIFT_CLK, ee_addr); udelay(2);
-		retval = (retval << 1) | ((ioread16(ee_addr) & EE_DATA_READ) ? 1 : 0);
-	} while (--cmd_len >= 0);
-	iowrite16(EE_ENB, ee_addr); udelay(2);
-
-	/* Terminate the EEPROM access. */
-	iowrite16(EE_ENB & ~EE_CS, ee_addr);
-	return retval;
-}
-
-static int mdio_read(struct net_device *dev, int phy_id, int location)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-	int val, boguscnt = 64*10;		/* <64 usec. to complete, typ 27 ticks */
-	iowrite32(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
-	do {
-		val = ioread32(ioaddr + SCBCtrlMDI);
-		if (--boguscnt < 0) {
-			printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val);
-			break;
-		}
-	} while (! (val & 0x10000000));
-	return val & 0xffff;
-}
-
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-	int val, boguscnt = 64*10;		/* <64 usec. to complete, typ 27 ticks */
-	iowrite32(0x04000000 | (location<<16) | (phy_id<<21) | value,
-		 ioaddr + SCBCtrlMDI);
-	do {
-		val = ioread32(ioaddr + SCBCtrlMDI);
-		if (--boguscnt < 0) {
-			printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val);
-			break;
-		}
-	} while (! (val & 0x10000000));
-}
-
-static int
-speedo_open(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-	int retval;
-
-	if (netif_msg_ifup(sp))
-		printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
-
-	pci_set_power_state(sp->pdev, PCI_D0);
-
-	/* Set up the Tx queue early.. */
-	sp->cur_tx = 0;
-	sp->dirty_tx = 0;
-	sp->last_cmd = NULL;
-	sp->tx_full = 0;
-	sp->in_interrupt = 0;
-
-	/* .. we can safely take handler calls during init. */
-	retval = request_irq(dev->irq, &speedo_interrupt, IRQF_SHARED, dev->name, dev);
-	if (retval) {
-		return retval;
-	}
-
-	dev->if_port = sp->default_port;
-
-#ifdef oh_no_you_dont_unless_you_honour_the_options_passed_in_to_us
-	/* Retrigger negotiation to reset previous errors. */
-	if ((sp->phy[0] & 0x8000) == 0) {
-		int phy_addr = sp->phy[0] & 0x1f ;
-		/* Use 0x3300 for restarting NWay, other values to force xcvr:
-		   0x0000 10-HD
-		   0x0100 10-FD
-		   0x2000 100-HD
-		   0x2100 100-FD
-		*/
-#ifdef honor_default_port
-		mdio_write(dev, phy_addr, MII_BMCR, mii_ctrl[dev->default_port & 7]);
-#else
-		mdio_write(dev, phy_addr, MII_BMCR, 0x3300);
-#endif
-	}
-#endif
-
-	speedo_init_rx_ring(dev);
-
-	/* Fire up the hardware. */
-	iowrite16(SCBMaskAll, ioaddr + SCBCmd);
-	speedo_resume(dev);
-
-	netdevice_start(dev);
-	netif_start_queue(dev);
-
-	/* Setup the chip and configure the multicast list. */
-	sp->mc_setup_head = NULL;
-	sp->mc_setup_tail = NULL;
-	sp->flow_ctrl = sp->partner = 0;
-	sp->rx_mode = -1;			/* Invalid -> always reset the mode. */
-	set_rx_mode(dev);
-	if ((sp->phy[0] & 0x8000) == 0)
-		sp->mii_if.advertising = mdio_read(dev, sp->phy[0] & 0x1f, MII_ADVERTISE);
-
-	mii_check_link(&sp->mii_if);
-
-	if (netif_msg_ifup(sp)) {
-		printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n",
-			   dev->name, ioread16(ioaddr + SCBStatus));
-	}
-
-	/* Set the timer.  The timer serves a dual purpose:
-	   1) to monitor the media interface (e.g. link beat) and perhaps switch
-	   to an alternate media type
-	   2) to monitor Rx activity, and restart the Rx process if the receiver
-	   hangs. */
-	sp->timer.expires = RUN_AT((24*HZ)/10); 			/* 2.4 sec. */
-	sp->timer.data = (unsigned long)dev;
-	sp->timer.function = &speedo_timer;					/* timer handler */
-	add_timer(&sp->timer);
-
-	/* No need to wait for the command unit to accept here. */
-	if ((sp->phy[0] & 0x8000) == 0)
-		mdio_read(dev, sp->phy[0] & 0x1f, MII_BMCR);
-
-	return 0;
-}
-
-/* Start the chip hardware after a full reset. */
-static void speedo_resume(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-
-	/* Start with a Tx threshold of 256 (0x..20.... 8 byte units). */
-	sp->tx_threshold = 0x01208000;
-
-	/* Set the segment registers to '0'. */
-	if (wait_for_cmd_done(dev, sp) != 0) {
-		iowrite32(PortPartialReset, ioaddr + SCBPort);
-		udelay(10);
-	}
-
-        iowrite32(0, ioaddr + SCBPointer);
-        ioread32(ioaddr + SCBPointer);			/* Flush to PCI. */
-        udelay(10);			/* Bogus, but it avoids the bug. */
-
-        /* Note: these next two operations can take a while. */
-        do_slow_command(dev, sp, RxAddrLoad);
-        do_slow_command(dev, sp, CUCmdBase);
-
-	/* Load the statistics block and rx ring addresses. */
-	iowrite32(sp->lstats_dma, ioaddr + SCBPointer);
-	ioread32(ioaddr + SCBPointer);			/* Flush to PCI */
-
-	iowrite8(CUStatsAddr, ioaddr + SCBCmd);
-	sp->lstats->done_marker = 0;
-	wait_for_cmd_done(dev, sp);
-
-	if (sp->rx_ringp[sp->cur_rx % RX_RING_SIZE] == NULL) {
-		if (netif_msg_rx_err(sp))
-			printk(KERN_DEBUG "%s: NULL cur_rx in speedo_resume().\n",
-					dev->name);
-	} else {
-		iowrite32(sp->rx_ring_dma[sp->cur_rx % RX_RING_SIZE],
-			 ioaddr + SCBPointer);
-		ioread32(ioaddr + SCBPointer);		/* Flush to PCI */
-	}
-
-	/* Note: RxStart should complete instantly. */
-	do_slow_command(dev, sp, RxStart);
-	do_slow_command(dev, sp, CUDumpStats);
-
-	/* Fill the first command with our physical address. */
-	{
-		struct descriptor *ias_cmd;
-
-		ias_cmd =
-			(struct descriptor *)&sp->tx_ring[sp->cur_tx++ % TX_RING_SIZE];
-		/* Avoid a bug(?!) here by marking the command already completed. */
-		ias_cmd->cmd_status = cpu_to_le32((CmdSuspend | CmdIASetup) | 0xa000);
-		ias_cmd->link =
-			cpu_to_le32(TX_RING_ELEM_DMA(sp, sp->cur_tx % TX_RING_SIZE));
-		memcpy(ias_cmd->params, dev->dev_addr, 6);
-		if (sp->last_cmd)
-			clear_suspend(sp->last_cmd);
-		sp->last_cmd = ias_cmd;
-	}
-
-	/* Start the chip's Tx process and unmask interrupts. */
-	iowrite32(TX_RING_ELEM_DMA(sp, sp->dirty_tx % TX_RING_SIZE),
-		 ioaddr + SCBPointer);
-	/* We are not ACK-ing FCP and ER in the interrupt handler yet so they should
-	   remain masked --Dragan */
-	iowrite16(CUStart | SCBMaskEarlyRx | SCBMaskFlowCtl, ioaddr + SCBCmd);
-}
-
-/*
- * Sometimes the receiver stops making progress.  This routine knows how to
- * get it going again, without losing packets or being otherwise nasty like
- * a chip reset would be.  Previously the driver had a whole sequence
- * of if RxSuspended, if it's no buffers do one thing, if it's no resources,
- * do another, etc.  But those things don't really matter.  Separate logic
- * in the ISR provides for allocating buffers--the other half of operation
- * is just making sure the receiver is active.  speedo_rx_soft_reset does that.
- * This problem with the old, more involved algorithm is shown up under
- * ping floods on the order of 60K packets/second on a 100Mbps fdx network.
- */
-static void
-speedo_rx_soft_reset(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	struct RxFD *rfd;
-	void __iomem *ioaddr;
-
-	ioaddr = sp->regs;
-	if (wait_for_cmd_done(dev, sp) != 0) {
-		printk("%s: previous command stalled\n", dev->name);
-		return;
-	}
-	/*
-	* Put the hardware into a known state.
-	*/
-	iowrite8(RxAbort, ioaddr + SCBCmd);
-
-	rfd = sp->rx_ringp[sp->cur_rx % RX_RING_SIZE];
-
-	rfd->rx_buf_addr = 0xffffffff;
-
-	if (wait_for_cmd_done(dev, sp) != 0) {
-		printk("%s: RxAbort command stalled\n", dev->name);
-		return;
-	}
-	iowrite32(sp->rx_ring_dma[sp->cur_rx % RX_RING_SIZE],
-		ioaddr + SCBPointer);
-	iowrite8(RxStart, ioaddr + SCBCmd);
-}
-
-
-/* Media monitoring and control. */
-static void speedo_timer(unsigned long data)
-{
-	struct net_device *dev = (struct net_device *)data;
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-	int phy_num = sp->phy[0] & 0x1f;
-
-	/* We have MII and lost link beat. */
-	if ((sp->phy[0] & 0x8000) == 0) {
-		int partner = mdio_read(dev, phy_num, MII_LPA);
-		if (partner != sp->partner) {
-			int flow_ctrl = sp->mii_if.advertising & partner & 0x0400 ? 1 : 0;
-			if (netif_msg_link(sp)) {
-				printk(KERN_DEBUG "%s: Link status change.\n", dev->name);
-				printk(KERN_DEBUG "%s: Old partner %x, new %x, adv %x.\n",
-					   dev->name, sp->partner, partner, sp->mii_if.advertising);
-			}
-			sp->partner = partner;
-			if (flow_ctrl != sp->flow_ctrl) {
-				sp->flow_ctrl = flow_ctrl;
-				sp->rx_mode = -1;	/* Trigger a reload. */
-			}
-		}
-	}
-	mii_check_link(&sp->mii_if);
-	if (netif_msg_timer(sp)) {
-		printk(KERN_DEBUG "%s: Media control tick, status %4.4x.\n",
-			   dev->name, ioread16(ioaddr + SCBStatus));
-	}
-	if (sp->rx_mode < 0  ||
-		(sp->rx_bug  && jiffies - sp->last_rx_time > 2*HZ)) {
-		/* We haven't received a packet in a Long Time.  We might have been
-		   bitten by the receiver hang bug.  This can be cleared by sending
-		   a set multicast list command. */
-		if (netif_msg_timer(sp))
-			printk(KERN_DEBUG "%s: Sending a multicast list set command"
-				   " from a timer routine,"
-				   " m=%d, j=%ld, l=%ld.\n",
-				   dev->name, sp->rx_mode, jiffies, sp->last_rx_time);
-		set_rx_mode(dev);
-	}
-	/* We must continue to monitor the media. */
-	sp->timer.expires = RUN_AT(2*HZ); 			/* 2.0 sec. */
-	add_timer(&sp->timer);
-}
-
-static void speedo_show_state(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	int i;
-
-	if (netif_msg_pktdata(sp)) {
-		printk(KERN_DEBUG "%s: Tx ring dump,  Tx queue %u / %u:\n",
-		    dev->name, sp->cur_tx, sp->dirty_tx);
-		for (i = 0; i < TX_RING_SIZE; i++)
-			printk(KERN_DEBUG "%s:  %c%c%2d %8.8x.\n", dev->name,
-			    i == sp->dirty_tx % TX_RING_SIZE ? '*' : ' ',
-			    i == sp->cur_tx % TX_RING_SIZE ? '=' : ' ',
-			    i, sp->tx_ring[i].status);
-
-		printk(KERN_DEBUG "%s: Printing Rx ring"
-		    " (next to receive into %u, dirty index %u).\n",
-		    dev->name, sp->cur_rx, sp->dirty_rx);
-		for (i = 0; i < RX_RING_SIZE; i++)
-			printk(KERN_DEBUG "%s: %c%c%c%2d %8.8x.\n", dev->name,
-			    sp->rx_ringp[i] == sp->last_rxf ? 'l' : ' ',
-			    i == sp->dirty_rx % RX_RING_SIZE ? '*' : ' ',
-			    i == sp->cur_rx % RX_RING_SIZE ? '=' : ' ',
-			    i, (sp->rx_ringp[i] != NULL) ?
-			    (unsigned)sp->rx_ringp[i]->status : 0);
-	}
-
-#if 0
-	{
-		void __iomem *ioaddr = sp->regs;
-		int phy_num = sp->phy[0] & 0x1f;
-		for (i = 0; i < 16; i++) {
-			/* FIXME: what does it mean?  --SAW */
-			if (i == 6) i = 21;
-			printk(KERN_DEBUG "%s:  PHY index %d register %d is %4.4x.\n",
-				   dev->name, phy_num, i, mdio_read(dev, phy_num, i));
-		}
-	}
-#endif
-
-}
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void
-speedo_init_rx_ring(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	struct RxFD *rxf, *last_rxf = NULL;
-	dma_addr_t last_rxf_dma = 0 /* to shut up the compiler */;
-	int i;
-
-	sp->cur_rx = 0;
-
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		struct sk_buff *skb;
-		skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
-		if (skb)
-			rx_align(skb);        /* Align IP on 16 byte boundary */
-		sp->rx_skbuff[i] = skb;
-		if (skb == NULL)
-			break;			/* OK.  Just initially short of Rx bufs. */
-		skb->dev = dev;			/* Mark as being used by this device. */
-		rxf = (struct RxFD *)skb->data;
-		sp->rx_ringp[i] = rxf;
-		sp->rx_ring_dma[i] =
-			pci_map_single(sp->pdev, rxf,
-					PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_BIDIRECTIONAL);
-		skb_reserve(skb, sizeof(struct RxFD));
-		if (last_rxf) {
-			last_rxf->link = cpu_to_le32(sp->rx_ring_dma[i]);
-			pci_dma_sync_single_for_device(sp->pdev, last_rxf_dma,
-										   sizeof(struct RxFD), PCI_DMA_TODEVICE);
-		}
-		last_rxf = rxf;
-		last_rxf_dma = sp->rx_ring_dma[i];
-		rxf->status = cpu_to_le32(0x00000001);	/* '1' is flag value only. */
-		rxf->link = 0;						/* None yet. */
-		/* This field unused by i82557. */
-		rxf->rx_buf_addr = 0xffffffff;
-		rxf->count = cpu_to_le32(PKT_BUF_SZ << 16);
-		pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[i],
-									   sizeof(struct RxFD), PCI_DMA_TODEVICE);
-	}
-	sp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
-	/* Mark the last entry as end-of-list. */
-	last_rxf->status = cpu_to_le32(0xC0000002);	/* '2' is flag value only. */
-	pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[RX_RING_SIZE-1],
-								   sizeof(struct RxFD), PCI_DMA_TODEVICE);
-	sp->last_rxf = last_rxf;
-	sp->last_rxf_dma = last_rxf_dma;
-}
-
-static void speedo_purge_tx(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	int entry;
-
-	while ((int)(sp->cur_tx - sp->dirty_tx) > 0) {
-		entry = sp->dirty_tx % TX_RING_SIZE;
-		if (sp->tx_skbuff[entry]) {
-			sp->stats.tx_errors++;
-			pci_unmap_single(sp->pdev,
-					le32_to_cpu(sp->tx_ring[entry].tx_buf_addr0),
-					sp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
-			dev_kfree_skb_irq(sp->tx_skbuff[entry]);
-			sp->tx_skbuff[entry] = NULL;
-		}
-		sp->dirty_tx++;
-	}
-	while (sp->mc_setup_head != NULL) {
-		struct speedo_mc_block *t;
-		if (netif_msg_tx_err(sp))
-			printk(KERN_DEBUG "%s: freeing mc frame.\n", dev->name);
-		pci_unmap_single(sp->pdev, sp->mc_setup_head->frame_dma,
-				sp->mc_setup_head->len, PCI_DMA_TODEVICE);
-		t = sp->mc_setup_head->next;
-		kfree(sp->mc_setup_head);
-		sp->mc_setup_head = t;
-	}
-	sp->mc_setup_tail = NULL;
-	sp->tx_full = 0;
-	netif_wake_queue(dev);
-}
-
-static void reset_mii(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-
-	/* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */
-	if ((sp->phy[0] & 0x8000) == 0) {
-		int phy_addr = sp->phy[0] & 0x1f;
-		int advertising = mdio_read(dev, phy_addr, MII_ADVERTISE);
-		int mii_bmcr = mdio_read(dev, phy_addr, MII_BMCR);
-		mdio_write(dev, phy_addr, MII_BMCR, 0x0400);
-		mdio_write(dev, phy_addr, MII_BMSR, 0x0000);
-		mdio_write(dev, phy_addr, MII_ADVERTISE, 0x0000);
-		mdio_write(dev, phy_addr, MII_BMCR, 0x8000);
-#ifdef honor_default_port
-		mdio_write(dev, phy_addr, MII_BMCR, mii_ctrl[dev->default_port & 7]);
-#else
-		mdio_read(dev, phy_addr, MII_BMCR);
-		mdio_write(dev, phy_addr, MII_BMCR, mii_bmcr);
-		mdio_write(dev, phy_addr, MII_ADVERTISE, advertising);
-#endif
-	}
-}
-
-static void speedo_tx_timeout(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-	int status = ioread16(ioaddr + SCBStatus);
-	unsigned long flags;
-
-	if (netif_msg_tx_err(sp)) {
-		printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
-		   " %4.4x at %d/%d command %8.8x.\n",
-		   dev->name, status, ioread16(ioaddr + SCBCmd),
-		   sp->dirty_tx, sp->cur_tx,
-		   sp->tx_ring[sp->dirty_tx % TX_RING_SIZE].status);
-
-	}
-	speedo_show_state(dev);
-#if 0
-	if ((status & 0x00C0) != 0x0080
-		&&  (status & 0x003C) == 0x0010) {
-		/* Only the command unit has stopped. */
-		printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
-			   dev->name);
-		iowrite32(TX_RING_ELEM_DMA(sp, dirty_tx % TX_RING_SIZE]),
-			 ioaddr + SCBPointer);
-		iowrite16(CUStart, ioaddr + SCBCmd);
-		reset_mii(dev);
-	} else {
-#else
-	{
-#endif
-		del_timer_sync(&sp->timer);
-		/* Reset the Tx and Rx units. */
-		iowrite32(PortReset, ioaddr + SCBPort);
-		/* We may get spurious interrupts here.  But I don't think that they
-		   may do much harm.  1999/12/09 SAW */
-		udelay(10);
-		/* Disable interrupts. */
-		iowrite16(SCBMaskAll, ioaddr + SCBCmd);
-		synchronize_irq(dev->irq);
-		speedo_tx_buffer_gc(dev);
-		/* Free as much as possible.
-		   It helps to recover from a hang because of out-of-memory.
-		   It also simplifies speedo_resume() in case TX ring is full or
-		   close-to-be full. */
-		speedo_purge_tx(dev);
-		speedo_refill_rx_buffers(dev, 1);
-		spin_lock_irqsave(&sp->lock, flags);
-		speedo_resume(dev);
-		sp->rx_mode = -1;
-		dev->trans_start = jiffies;
-		spin_unlock_irqrestore(&sp->lock, flags);
-		set_rx_mode(dev); /* it takes the spinlock itself --SAW */
-		/* Reset MII transceiver.  Do it before starting the timer to serialize
-		   mdio_xxx operations.  Yes, it's a paranoya :-)  2000/05/09 SAW */
-		reset_mii(dev);
-		sp->timer.expires = RUN_AT(2*HZ);
-		add_timer(&sp->timer);
-	}
-	return;
-}
-
-static int
-speedo_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-	int entry;
-
-	/* Prevent interrupts from changing the Tx ring from underneath us. */
-	unsigned long flags;
-
-	spin_lock_irqsave(&sp->lock, flags);
-
-	/* Check if there are enough space. */
-	if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
-		printk(KERN_ERR "%s: incorrect tbusy state, fixed.\n", dev->name);
-		netif_stop_queue(dev);
-		sp->tx_full = 1;
-		spin_unlock_irqrestore(&sp->lock, flags);
-		return 1;
-	}
-
-	/* Calculate the Tx descriptor entry. */
-	entry = sp->cur_tx++ % TX_RING_SIZE;
-
-	sp->tx_skbuff[entry] = skb;
-	sp->tx_ring[entry].status =
-		cpu_to_le32(CmdSuspend | CmdTx | CmdTxFlex);
-	if (!(entry & ((TX_RING_SIZE>>2)-1)))
-		sp->tx_ring[entry].status |= cpu_to_le32(CmdIntr);
-	sp->tx_ring[entry].link =
-		cpu_to_le32(TX_RING_ELEM_DMA(sp, sp->cur_tx % TX_RING_SIZE));
-	sp->tx_ring[entry].tx_desc_addr =
-		cpu_to_le32(TX_RING_ELEM_DMA(sp, entry) + TX_DESCR_BUF_OFFSET);
-	/* The data region is always in one buffer descriptor. */
-	sp->tx_ring[entry].count = cpu_to_le32(sp->tx_threshold);
-	sp->tx_ring[entry].tx_buf_addr0 =
-		cpu_to_le32(pci_map_single(sp->pdev, skb->data,
-					   skb->len, PCI_DMA_TODEVICE));
-	sp->tx_ring[entry].tx_buf_size0 = cpu_to_le32(skb->len);
-
-	/* workaround for hardware bug on 10 mbit half duplex */
-
-	if ((sp->partner == 0) && (sp->chip_id == 1)) {
-		wait_for_cmd_done(dev, sp);
-		iowrite8(0 , ioaddr + SCBCmd);
-		udelay(1);
-	}
-
-	/* Trigger the command unit resume. */
-	wait_for_cmd_done(dev, sp);
-	clear_suspend(sp->last_cmd);
-	/* We want the time window between clearing suspend flag on the previous
-	   command and resuming CU to be as small as possible.
-	   Interrupts in between are very undesired.  --SAW */
-	iowrite8(CUResume, ioaddr + SCBCmd);
-	sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
-
-	/* Leave room for set_rx_mode(). If there is no more space than reserved
-	   for multicast filter mark the ring as full. */
-	if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
-		netif_stop_queue(dev);
-		sp->tx_full = 1;
-	}
-
-	spin_unlock_irqrestore(&sp->lock, flags);
-
-	dev->trans_start = jiffies;
-
-	return 0;
-}
-
-static void speedo_tx_buffer_gc(struct net_device *dev)
-{
-	unsigned int dirty_tx;
-	struct speedo_private *sp = netdev_priv(dev);
-
-	dirty_tx = sp->dirty_tx;
-	while ((int)(sp->cur_tx - dirty_tx) > 0) {
-		int entry = dirty_tx % TX_RING_SIZE;
-		int status = le32_to_cpu(sp->tx_ring[entry].status);
-
-		if (netif_msg_tx_done(sp))
-			printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n",
-				   entry, status);
-		if ((status & StatusComplete) == 0)
-			break;			/* It still hasn't been processed. */
-		if (status & TxUnderrun)
-			if (sp->tx_threshold < 0x01e08000) {
-				if (netif_msg_tx_err(sp))
-					printk(KERN_DEBUG "%s: TX underrun, threshold adjusted.\n",
-						   dev->name);
-				sp->tx_threshold += 0x00040000;
-			}
-		/* Free the original skb. */
-		if (sp->tx_skbuff[entry]) {
-			sp->stats.tx_packets++;	/* Count only user packets. */
-			sp->stats.tx_bytes += sp->tx_skbuff[entry]->len;
-			pci_unmap_single(sp->pdev,
-					le32_to_cpu(sp->tx_ring[entry].tx_buf_addr0),
-					sp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
-			dev_kfree_skb_irq(sp->tx_skbuff[entry]);
-			sp->tx_skbuff[entry] = NULL;
-		}
-		dirty_tx++;
-	}
-
-	if (netif_msg_tx_err(sp) && (int)(sp->cur_tx - dirty_tx) > TX_RING_SIZE) {
-		printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d,"
-			   " full=%d.\n",
-			   dirty_tx, sp->cur_tx, sp->tx_full);
-		dirty_tx += TX_RING_SIZE;
-	}
-
-	while (sp->mc_setup_head != NULL
-		   && (int)(dirty_tx - sp->mc_setup_head->tx - 1) > 0) {
-		struct speedo_mc_block *t;
-		if (netif_msg_tx_err(sp))
-			printk(KERN_DEBUG "%s: freeing mc frame.\n", dev->name);
-		pci_unmap_single(sp->pdev, sp->mc_setup_head->frame_dma,
-				sp->mc_setup_head->len, PCI_DMA_TODEVICE);
-		t = sp->mc_setup_head->next;
-		kfree(sp->mc_setup_head);
-		sp->mc_setup_head = t;
-	}
-	if (sp->mc_setup_head == NULL)
-		sp->mc_setup_tail = NULL;
-
-	sp->dirty_tx = dirty_tx;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
-   after the Tx thread. */
-static irqreturn_t speedo_interrupt(int irq, void *dev_instance)
-{
-	struct net_device *dev = (struct net_device *)dev_instance;
-	struct speedo_private *sp;
-	void __iomem *ioaddr;
-	long boguscnt = max_interrupt_work;
-	unsigned short status;
-	unsigned int handled = 0;
-
-	sp = netdev_priv(dev);
-	ioaddr = sp->regs;
-
-#ifndef final_version
-	/* A lock to prevent simultaneous entry on SMP machines. */
-	if (test_and_set_bit(0, (void*)&sp->in_interrupt)) {
-		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
-			   dev->name);
-		sp->in_interrupt = 0;	/* Avoid halting machine. */
-		return IRQ_NONE;
-	}
-#endif
-
-	do {
-		status = ioread16(ioaddr + SCBStatus);
-		/* Acknowledge all of the current interrupt sources ASAP. */
-		/* Will change from 0xfc00 to 0xff00 when we start handling
-		   FCP and ER interrupts --Dragan */
-		iowrite16(status & 0xfc00, ioaddr + SCBStatus);
-
-		if (netif_msg_intr(sp))
-			printk(KERN_DEBUG "%s: interrupt  status=%#4.4x.\n",
-				   dev->name, status);
-
-		if ((status & 0xfc00) == 0)
-			break;
-		handled = 1;
-
-
-		if ((status & 0x5000) ||	/* Packet received, or Rx error. */
-			(sp->rx_ring_state&(RrNoMem|RrPostponed)) == RrPostponed)
-									/* Need to gather the postponed packet. */
-			speedo_rx(dev);
-
-		/* Always check if all rx buffers are allocated.  --SAW */
-		speedo_refill_rx_buffers(dev, 0);
-
-		spin_lock(&sp->lock);
-		/*
-		 * The chip may have suspended reception for various reasons.
-		 * Check for that, and re-prime it should this be the case.
-		 */
-		switch ((status >> 2) & 0xf) {
-		case 0: /* Idle */
-			break;
-		case 1:	/* Suspended */
-		case 2:	/* No resources (RxFDs) */
-		case 9:	/* Suspended with no more RBDs */
-		case 10: /* No resources due to no RBDs */
-		case 12: /* Ready with no RBDs */
-			speedo_rx_soft_reset(dev);
-			break;
-		case 3:  case 5:  case 6:  case 7:  case 8:
-		case 11:  case 13:  case 14:  case 15:
-			/* these are all reserved values */
-			break;
-		}
-
-
-		/* User interrupt, Command/Tx unit interrupt or CU not active. */
-		if (status & 0xA400) {
-			speedo_tx_buffer_gc(dev);
-			if (sp->tx_full
-				&& (int)(sp->cur_tx - sp->dirty_tx) < TX_QUEUE_UNFULL) {
-				/* The ring is no longer full. */
-				sp->tx_full = 0;
-				netif_wake_queue(dev); /* Attention: under a spinlock.  --SAW */
-			}
-		}
-
-		spin_unlock(&sp->lock);
-
-		if (--boguscnt < 0) {
-			printk(KERN_ERR "%s: Too much work at interrupt, status=0x%4.4x.\n",
-				   dev->name, status);
-			/* Clear all interrupt sources. */
-			/* Will change from 0xfc00 to 0xff00 when we start handling
-			   FCP and ER interrupts --Dragan */
-			iowrite16(0xfc00, ioaddr + SCBStatus);
-			break;
-		}
-	} while (1);
-
-	if (netif_msg_intr(sp))
-		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
-			   dev->name, ioread16(ioaddr + SCBStatus));
-
-	clear_bit(0, (void*)&sp->in_interrupt);
-	return IRQ_RETVAL(handled);
-}
-
-static inline struct RxFD *speedo_rx_alloc(struct net_device *dev, int entry)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	struct RxFD *rxf;
-	struct sk_buff *skb;
-	/* Get a fresh skbuff to replace the consumed one. */
-	skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
-	if (skb)
-		rx_align(skb);		/* Align IP on 16 byte boundary */
-	sp->rx_skbuff[entry] = skb;
-	if (skb == NULL) {
-		sp->rx_ringp[entry] = NULL;
-		return NULL;
-	}
-	rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->data;
-	sp->rx_ring_dma[entry] =
-		pci_map_single(sp->pdev, rxf,
-					   PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
-	skb->dev = dev;
-	skb_reserve(skb, sizeof(struct RxFD));
-	rxf->rx_buf_addr = 0xffffffff;
-	pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[entry],
-								   sizeof(struct RxFD), PCI_DMA_TODEVICE);
-	return rxf;
-}
-
-static inline void speedo_rx_link(struct net_device *dev, int entry,
-								  struct RxFD *rxf, dma_addr_t rxf_dma)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	rxf->status = cpu_to_le32(0xC0000001); 	/* '1' for driver use only. */
-	rxf->link = 0;			/* None yet. */
-	rxf->count = cpu_to_le32(PKT_BUF_SZ << 16);
-	sp->last_rxf->link = cpu_to_le32(rxf_dma);
-	sp->last_rxf->status &= cpu_to_le32(~0xC0000000);
-	pci_dma_sync_single_for_device(sp->pdev, sp->last_rxf_dma,
-								   sizeof(struct RxFD), PCI_DMA_TODEVICE);
-	sp->last_rxf = rxf;
-	sp->last_rxf_dma = rxf_dma;
-}
-
-static int speedo_refill_rx_buf(struct net_device *dev, int force)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	int entry;
-	struct RxFD *rxf;
-
-	entry = sp->dirty_rx % RX_RING_SIZE;
-	if (sp->rx_skbuff[entry] == NULL) {
-		rxf = speedo_rx_alloc(dev, entry);
-		if (rxf == NULL) {
-			unsigned int forw;
-			int forw_entry;
-			if (netif_msg_rx_err(sp) || !(sp->rx_ring_state & RrOOMReported)) {
-				printk(KERN_WARNING "%s: can't fill rx buffer (force %d)!\n",
-						dev->name, force);
-				sp->rx_ring_state |= RrOOMReported;
-			}
-			speedo_show_state(dev);
-			if (!force)
-				return -1;	/* Better luck next time!  */
-			/* Borrow an skb from one of next entries. */
-			for (forw = sp->dirty_rx + 1; forw != sp->cur_rx; forw++)
-				if (sp->rx_skbuff[forw % RX_RING_SIZE] != NULL)
-					break;
-			if (forw == sp->cur_rx)
-				return -1;
-			forw_entry = forw % RX_RING_SIZE;
-			sp->rx_skbuff[entry] = sp->rx_skbuff[forw_entry];
-			sp->rx_skbuff[forw_entry] = NULL;
-			rxf = sp->rx_ringp[forw_entry];
-			sp->rx_ringp[forw_entry] = NULL;
-			sp->rx_ringp[entry] = rxf;
-		}
-	} else {
-		rxf = sp->rx_ringp[entry];
-	}
-	speedo_rx_link(dev, entry, rxf, sp->rx_ring_dma[entry]);
-	sp->dirty_rx++;
-	sp->rx_ring_state &= ~(RrNoMem|RrOOMReported); /* Mark the progress. */
-	return 0;
-}
-
-static void speedo_refill_rx_buffers(struct net_device *dev, int force)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-
-	/* Refill the RX ring. */
-	while ((int)(sp->cur_rx - sp->dirty_rx) > 0 &&
-			speedo_refill_rx_buf(dev, force) != -1);
-}
-
-static int
-speedo_rx(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	int entry = sp->cur_rx % RX_RING_SIZE;
-	int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx;
-	int alloc_ok = 1;
-	int npkts = 0;
-
-	if (netif_msg_intr(sp))
-		printk(KERN_DEBUG " In speedo_rx().\n");
-	/* If we own the next entry, it's a new packet. Send it up. */
-	while (sp->rx_ringp[entry] != NULL) {
-		int status;
-		int pkt_len;
-
-		pci_dma_sync_single_for_cpu(sp->pdev, sp->rx_ring_dma[entry],
-									sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
-		status = le32_to_cpu(sp->rx_ringp[entry]->status);
-		pkt_len = le32_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff;
-
-		if (!(status & RxComplete))
-			break;
-
-		if (--rx_work_limit < 0)
-			break;
-
-		/* Check for a rare out-of-memory case: the current buffer is
-		   the last buffer allocated in the RX ring.  --SAW */
-		if (sp->last_rxf == sp->rx_ringp[entry]) {
-			/* Postpone the packet.  It'll be reaped at an interrupt when this
-			   packet is no longer the last packet in the ring. */
-			if (netif_msg_rx_err(sp))
-				printk(KERN_DEBUG "%s: RX packet postponed!\n",
-					   dev->name);
-			sp->rx_ring_state |= RrPostponed;
-			break;
-		}
-
-		if (netif_msg_rx_status(sp))
-			printk(KERN_DEBUG "  speedo_rx() status %8.8x len %d.\n", status,
-				   pkt_len);
-		if ((status & (RxErrTooBig|RxOK|0x0f90)) != RxOK) {
-			if (status & RxErrTooBig)
-				printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, "
-					   "status %8.8x!\n", dev->name, status);
-			else if (! (status & RxOK)) {
-				/* There was a fatal error.  This *should* be impossible. */
-				sp->stats.rx_errors++;
-				printk(KERN_ERR "%s: Anomalous event in speedo_rx(), "
-					   "status %8.8x.\n",
-					   dev->name, status);
-			}
-		} else {
-			struct sk_buff *skb;
-
-			/* Check if the packet is long enough to just accept without
-			   copying to a properly sized skbuff. */
-			if (pkt_len < rx_copybreak
-				&& (skb = dev_alloc_skb(pkt_len + 2)) != 0) {
-				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-				/* 'skb_put()' points to the start of sk_buff data area. */
-				pci_dma_sync_single_for_cpu(sp->pdev, sp->rx_ring_dma[entry],
-											sizeof(struct RxFD) + pkt_len,
-											PCI_DMA_FROMDEVICE);
-
-#if 1 || USE_IP_CSUM
-				/* Packet is in one chunk -- we can copy + cksum. */
-				skb_copy_to_linear_data(skb, sp->rx_skbuff[entry]->data, pkt_len);
-				skb_put(skb, pkt_len);
-#else
-				skb_copy_from_linear_data(sp->rx_skbuff[entry],
-							  skb_put(skb, pkt_len),
-							  pkt_len);
-#endif
-				pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[entry],
-											   sizeof(struct RxFD) + pkt_len,
-											   PCI_DMA_FROMDEVICE);
-				npkts++;
-			} else {
-				/* Pass up the already-filled skbuff. */
-				skb = sp->rx_skbuff[entry];
-				if (skb == NULL) {
-					printk(KERN_ERR "%s: Inconsistent Rx descriptor chain.\n",
-						   dev->name);
-					break;
-				}
-				sp->rx_skbuff[entry] = NULL;
-				skb_put(skb, pkt_len);
-				npkts++;
-				sp->rx_ringp[entry] = NULL;
-				pci_unmap_single(sp->pdev, sp->rx_ring_dma[entry],
-								 PKT_BUF_SZ + sizeof(struct RxFD),
-								 PCI_DMA_FROMDEVICE);
-			}
-			skb->protocol = eth_type_trans(skb, dev);
-			netif_rx(skb);
-			dev->last_rx = jiffies;
-			sp->stats.rx_packets++;
-			sp->stats.rx_bytes += pkt_len;
-		}
-		entry = (++sp->cur_rx) % RX_RING_SIZE;
-		sp->rx_ring_state &= ~RrPostponed;
-		/* Refill the recently taken buffers.
-		   Do it one-by-one to handle traffic bursts better. */
-		if (alloc_ok && speedo_refill_rx_buf(dev, 0) == -1)
-			alloc_ok = 0;
-	}
-
-	/* Try hard to refill the recently taken buffers. */
-	speedo_refill_rx_buffers(dev, 1);
-
-	if (npkts)
-		sp->last_rx_time = jiffies;
-
-	return 0;
-}
-
-static int
-speedo_close(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-	int i;
-
-	netdevice_stop(dev);
-	netif_stop_queue(dev);
-
-	if (netif_msg_ifdown(sp))
-		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
-			   dev->name, ioread16(ioaddr + SCBStatus));
-
-	/* Shut off the media monitoring timer. */
-	del_timer_sync(&sp->timer);
-
-	iowrite16(SCBMaskAll, ioaddr + SCBCmd);
-
-	/* Shutting down the chip nicely fails to disable flow control. So.. */
-	iowrite32(PortPartialReset, ioaddr + SCBPort);
-	ioread32(ioaddr + SCBPort); /* flush posted write */
-	/*
-	 * The chip requires a 10 microsecond quiet period.  Wait here!
-	 */
-	udelay(10);
-
-	free_irq(dev->irq, dev);
-	speedo_show_state(dev);
-
-    /* Free all the skbuffs in the Rx and Tx queues. */
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		struct sk_buff *skb = sp->rx_skbuff[i];
-		sp->rx_skbuff[i] = NULL;
-		/* Clear the Rx descriptors. */
-		if (skb) {
-			pci_unmap_single(sp->pdev,
-					 sp->rx_ring_dma[i],
-					 PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
-			dev_kfree_skb(skb);
-		}
-	}
-
-	for (i = 0; i < TX_RING_SIZE; i++) {
-		struct sk_buff *skb = sp->tx_skbuff[i];
-		sp->tx_skbuff[i] = NULL;
-		/* Clear the Tx descriptors. */
-		if (skb) {
-			pci_unmap_single(sp->pdev,
-					 le32_to_cpu(sp->tx_ring[i].tx_buf_addr0),
-					 skb->len, PCI_DMA_TODEVICE);
-			dev_kfree_skb(skb);
-		}
-	}
-
-	/* Free multicast setting blocks. */
-	for (i = 0; sp->mc_setup_head != NULL; i++) {
-		struct speedo_mc_block *t;
-		t = sp->mc_setup_head->next;
-		kfree(sp->mc_setup_head);
-		sp->mc_setup_head = t;
-	}
-	sp->mc_setup_tail = NULL;
-	if (netif_msg_ifdown(sp))
-		printk(KERN_DEBUG "%s: %d multicast blocks dropped.\n", dev->name, i);
-
-	pci_set_power_state(sp->pdev, PCI_D2);
-
-	return 0;
-}
-
-/* The Speedo-3 has an especially awkward and unusable method of getting
-   statistics out of the chip.  It takes an unpredictable length of time
-   for the dump-stats command to complete.  To avoid a busy-wait loop we
-   update the stats with the previous dump results, and then trigger a
-   new dump.
-
-   Oh, and incoming frames are dropped while executing dump-stats!
-   */
-static struct net_device_stats *
-speedo_get_stats(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-
-	/* Update only if the previous dump finished. */
-	if (sp->lstats->done_marker == le32_to_cpu(0xA007)) {
-		sp->stats.tx_aborted_errors += le32_to_cpu(sp->lstats->tx_coll16_errs);
-		sp->stats.tx_window_errors += le32_to_cpu(sp->lstats->tx_late_colls);
-		sp->stats.tx_fifo_errors += le32_to_cpu(sp->lstats->tx_underruns);
-		sp->stats.tx_fifo_errors += le32_to_cpu(sp->lstats->tx_lost_carrier);
-		/*sp->stats.tx_deferred += le32_to_cpu(sp->lstats->tx_deferred);*/
-		sp->stats.collisions += le32_to_cpu(sp->lstats->tx_total_colls);
-		sp->stats.rx_crc_errors += le32_to_cpu(sp->lstats->rx_crc_errs);
-		sp->stats.rx_frame_errors += le32_to_cpu(sp->lstats->rx_align_errs);
-		sp->stats.rx_over_errors += le32_to_cpu(sp->lstats->rx_resource_errs);
-		sp->stats.rx_fifo_errors += le32_to_cpu(sp->lstats->rx_overrun_errs);
-		sp->stats.rx_length_errors += le32_to_cpu(sp->lstats->rx_runt_errs);
-		sp->lstats->done_marker = 0x0000;
-		if (netif_running(dev)) {
-			unsigned long flags;
-			/* Take a spinlock to make wait_for_cmd_done and sending the
-			   command atomic.  --SAW */
-			spin_lock_irqsave(&sp->lock, flags);
-			wait_for_cmd_done(dev, sp);
-			iowrite8(CUDumpStats, ioaddr + SCBCmd);
-			spin_unlock_irqrestore(&sp->lock, flags);
-		}
-	}
-	return &sp->stats;
-}
-
-static void speedo_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	strncpy(info->driver, "eepro100", sizeof(info->driver)-1);
-	strncpy(info->version, version, sizeof(info->version)-1);
-	if (sp->pdev)
-		strcpy(info->bus_info, pci_name(sp->pdev));
-}
-
-static int speedo_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	spin_lock_irq(&sp->lock);
-	mii_ethtool_gset(&sp->mii_if, ecmd);
-	spin_unlock_irq(&sp->lock);
-	return 0;
-}
-
-static int speedo_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	int res;
-	spin_lock_irq(&sp->lock);
-	res = mii_ethtool_sset(&sp->mii_if, ecmd);
-	spin_unlock_irq(&sp->lock);
-	return res;
-}
-
-static int speedo_nway_reset(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	return mii_nway_restart(&sp->mii_if);
-}
-
-static u32 speedo_get_link(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	return mii_link_ok(&sp->mii_if);
-}
-
-static u32 speedo_get_msglevel(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	return sp->msg_enable;
-}
-
-static void speedo_set_msglevel(struct net_device *dev, u32 v)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	sp->msg_enable = v;
-}
-
-static const struct ethtool_ops ethtool_ops = {
-	.get_drvinfo = speedo_get_drvinfo,
-	.get_settings = speedo_get_settings,
-	.set_settings = speedo_set_settings,
-	.nway_reset = speedo_nway_reset,
-	.get_link = speedo_get_link,
-	.get_msglevel = speedo_get_msglevel,
-	.set_msglevel = speedo_set_msglevel,
-};
-
-static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	struct mii_ioctl_data *data = if_mii(rq);
-	int phy = sp->phy[0] & 0x1f;
-	int saved_acpi;
-	int t;
-
-    switch(cmd) {
-	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
-		data->phy_id = phy;
-
-	case SIOCGMIIREG:		/* Read MII PHY register. */
-		/* FIXME: these operations need to be serialized with MDIO
-		   access from the timeout handler.
-		   They are currently serialized only with MDIO access from the
-		   timer routine.  2000/05/09 SAW */
-		saved_acpi = pci_set_power_state(sp->pdev, PCI_D0);
-		t = del_timer_sync(&sp->timer);
-		data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f);
-		if (t)
-			add_timer(&sp->timer); /* may be set to the past  --SAW */
-		pci_set_power_state(sp->pdev, saved_acpi);
-		return 0;
-
-	case SIOCSMIIREG:		/* Write MII PHY register. */
-		if (!capable(CAP_NET_ADMIN))
-			return -EPERM;
-		saved_acpi = pci_set_power_state(sp->pdev, PCI_D0);
-		t = del_timer_sync(&sp->timer);
-		mdio_write(dev, data->phy_id, data->reg_num, data->val_in);
-		if (t)
-			add_timer(&sp->timer); /* may be set to the past  --SAW */
-		pci_set_power_state(sp->pdev, saved_acpi);
-		return 0;
-	default:
-		return -EOPNOTSUPP;
-	}
-}
-
-/* Set or clear the multicast filter for this adaptor.
-   This is very ugly with Intel chips -- we usually have to execute an
-   entire configuration command, plus process a multicast command.
-   This is complicated.  We must put a large configuration command and
-   an arbitrarily-sized multicast command in the transmit list.
-   To minimize the disruption -- the previous command might have already
-   loaded the link -- we convert the current command block, normally a Tx
-   command, into a no-op and link it to the new command.
-*/
-static void set_rx_mode(struct net_device *dev)
-{
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-	struct descriptor *last_cmd;
-	char new_rx_mode;
-	unsigned long flags;
-	int entry, i;
-
-	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
-		new_rx_mode = 3;
-	} else if ((dev->flags & IFF_ALLMULTI)  ||
-			   dev->mc_count > multicast_filter_limit) {
-		new_rx_mode = 1;
-	} else
-		new_rx_mode = 0;
-
-	if (netif_msg_rx_status(sp))
-		printk(KERN_DEBUG "%s: set_rx_mode %d -> %d\n", dev->name,
-				sp->rx_mode, new_rx_mode);
-
-	if ((int)(sp->cur_tx - sp->dirty_tx) > TX_RING_SIZE - TX_MULTICAST_SIZE) {
-	    /* The Tx ring is full -- don't add anything!  Hope the mode will be
-		 * set again later. */
-		sp->rx_mode = -1;
-		return;
-	}
-
-	if (new_rx_mode != sp->rx_mode) {
-		u8 *config_cmd_data;
-
-		spin_lock_irqsave(&sp->lock, flags);
-		entry = sp->cur_tx++ % TX_RING_SIZE;
-		last_cmd = sp->last_cmd;
-		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
-
-		sp->tx_skbuff[entry] = NULL;			/* Redundant. */
-		sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdConfigure);
-		sp->tx_ring[entry].link =
-			cpu_to_le32(TX_RING_ELEM_DMA(sp, (entry + 1) % TX_RING_SIZE));
-		config_cmd_data = (void *)&sp->tx_ring[entry].tx_desc_addr;
-		/* Construct a full CmdConfig frame. */
-		memcpy(config_cmd_data, i82558_config_cmd, CONFIG_DATA_SIZE);
-		config_cmd_data[1] = (txfifo << 4) | rxfifo;
-		config_cmd_data[4] = rxdmacount;
-		config_cmd_data[5] = txdmacount + 0x80;
-		config_cmd_data[15] |= (new_rx_mode & 2) ? 1 : 0;
-		/* 0x80 doesn't disable FC 0x84 does.
-		   Disable Flow control since we are not ACK-ing any FC interrupts
-		   for now. --Dragan */
-		config_cmd_data[19] = 0x84;
-		config_cmd_data[19] |= sp->mii_if.full_duplex ? 0x40 : 0;
-		config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
-		if (sp->phy[0] & 0x8000) {			/* Use the AUI port instead. */
-			config_cmd_data[15] |= 0x80;
-			config_cmd_data[8] = 0;
-		}
-		/* Trigger the command unit resume. */
-		wait_for_cmd_done(dev, sp);
-		clear_suspend(last_cmd);
-		iowrite8(CUResume, ioaddr + SCBCmd);
-		if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
-			netif_stop_queue(dev);
-			sp->tx_full = 1;
-		}
-		spin_unlock_irqrestore(&sp->lock, flags);
-	}
-
-	if (new_rx_mode == 0  &&  dev->mc_count < 4) {
-		/* The simple case of 0-3 multicast list entries occurs often, and
-		   fits within one tx_ring[] entry. */
-		struct dev_mc_list *mclist;
-		u16 *setup_params, *eaddrs;
-
-		spin_lock_irqsave(&sp->lock, flags);
-		entry = sp->cur_tx++ % TX_RING_SIZE;
-		last_cmd = sp->last_cmd;
-		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
-
-		sp->tx_skbuff[entry] = NULL;
-		sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdMulticastList);
-		sp->tx_ring[entry].link =
-			cpu_to_le32(TX_RING_ELEM_DMA(sp, (entry + 1) % TX_RING_SIZE));
-		sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
-		setup_params = (u16 *)&sp->tx_ring[entry].tx_desc_addr;
-		*setup_params++ = cpu_to_le16(dev->mc_count*6);
-		/* Fill in the multicast addresses. */
-		for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
-			 i++, mclist = mclist->next) {
-			eaddrs = (u16 *)mclist->dmi_addr;
-			*setup_params++ = *eaddrs++;
-			*setup_params++ = *eaddrs++;
-			*setup_params++ = *eaddrs++;
-		}
-
-		wait_for_cmd_done(dev, sp);
-		clear_suspend(last_cmd);
-		/* Immediately trigger the command unit resume. */
-		iowrite8(CUResume, ioaddr + SCBCmd);
-
-		if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
-			netif_stop_queue(dev);
-			sp->tx_full = 1;
-		}
-		spin_unlock_irqrestore(&sp->lock, flags);
-	} else if (new_rx_mode == 0) {
-		struct dev_mc_list *mclist;
-		u16 *setup_params, *eaddrs;
-		struct speedo_mc_block *mc_blk;
-		struct descriptor *mc_setup_frm;
-		int i;
-
-		mc_blk = kmalloc(sizeof(*mc_blk) + 2 + multicast_filter_limit*6,
-						 GFP_ATOMIC);
-		if (mc_blk == NULL) {
-			printk(KERN_ERR "%s: Failed to allocate a setup frame.\n",
-				   dev->name);
-			sp->rx_mode = -1; /* We failed, try again. */
-			return;
-		}
-		mc_blk->next = NULL;
-		mc_blk->len = 2 + multicast_filter_limit*6;
-		mc_blk->frame_dma =
-			pci_map_single(sp->pdev, &mc_blk->frame, mc_blk->len,
-					PCI_DMA_TODEVICE);
-		mc_setup_frm = &mc_blk->frame;
-
-		/* Fill the setup frame. */
-		if (netif_msg_ifup(sp))
-			printk(KERN_DEBUG "%s: Constructing a setup frame at %p.\n",
-				   dev->name, mc_setup_frm);
-		mc_setup_frm->cmd_status =
-			cpu_to_le32(CmdSuspend | CmdIntr | CmdMulticastList);
-		/* Link set below. */
-		setup_params = (u16 *)&mc_setup_frm->params;
-		*setup_params++ = cpu_to_le16(dev->mc_count*6);
-		/* Fill in the multicast addresses. */
-		for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
-			 i++, mclist = mclist->next) {
-			eaddrs = (u16 *)mclist->dmi_addr;
-			*setup_params++ = *eaddrs++;
-			*setup_params++ = *eaddrs++;
-			*setup_params++ = *eaddrs++;
-		}
-
-		/* Disable interrupts while playing with the Tx Cmd list. */
-		spin_lock_irqsave(&sp->lock, flags);
-
-		if (sp->mc_setup_tail)
-			sp->mc_setup_tail->next = mc_blk;
-		else
-			sp->mc_setup_head = mc_blk;
-		sp->mc_setup_tail = mc_blk;
-		mc_blk->tx = sp->cur_tx;
-
-		entry = sp->cur_tx++ % TX_RING_SIZE;
-		last_cmd = sp->last_cmd;
-		sp->last_cmd = mc_setup_frm;
-
-		/* Change the command to a NoOp, pointing to the CmdMulti command. */
-		sp->tx_skbuff[entry] = NULL;
-		sp->tx_ring[entry].status = cpu_to_le32(CmdNOp);
-		sp->tx_ring[entry].link = cpu_to_le32(mc_blk->frame_dma);
-
-		/* Set the link in the setup frame. */
-		mc_setup_frm->link =
-			cpu_to_le32(TX_RING_ELEM_DMA(sp, (entry + 1) % TX_RING_SIZE));
-
-		pci_dma_sync_single_for_device(sp->pdev, mc_blk->frame_dma,
-									   mc_blk->len, PCI_DMA_TODEVICE);
-
-		wait_for_cmd_done(dev, sp);
-		clear_suspend(last_cmd);
-		/* Immediately trigger the command unit resume. */
-		iowrite8(CUResume, ioaddr + SCBCmd);
-
-		if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
-			netif_stop_queue(dev);
-			sp->tx_full = 1;
-		}
-		spin_unlock_irqrestore(&sp->lock, flags);
-
-		if (netif_msg_rx_status(sp))
-			printk(" CmdMCSetup frame length %d in entry %d.\n",
-				   dev->mc_count, entry);
-	}
-
-	sp->rx_mode = new_rx_mode;
-}
-
-#ifdef CONFIG_PM
-static int eepro100_suspend(struct pci_dev *pdev, pm_message_t state)
-{
-	struct net_device *dev = pci_get_drvdata (pdev);
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-
-	pci_save_state(pdev);
-
-	if (!netif_running(dev))
-		return 0;
-
-	del_timer_sync(&sp->timer);
-
-	netif_device_detach(dev);
-	iowrite32(PortPartialReset, ioaddr + SCBPort);
-
-	/* XXX call pci_set_power_state ()? */
-	pci_disable_device(pdev);
-	pci_set_power_state (pdev, PCI_D3hot);
-	return 0;
-}
-
-static int eepro100_resume(struct pci_dev *pdev)
-{
-	struct net_device *dev = pci_get_drvdata (pdev);
-	struct speedo_private *sp = netdev_priv(dev);
-	void __iomem *ioaddr = sp->regs;
-	int rc;
-
-	pci_set_power_state(pdev, PCI_D0);
-	pci_restore_state(pdev);
-
-	rc = pci_enable_device(pdev);
-	if (rc)
-		return rc;
-
-	pci_set_master(pdev);
-
-	if (!netif_running(dev))
-		return 0;
-
-	/* I'm absolutely uncertain if this part of code may work.
-	   The problems are:
-	    - correct hardware reinitialization;
-		- correct driver behavior between different steps of the
-		  reinitialization;
-		- serialization with other driver calls.
-	   2000/03/08  SAW */
-	iowrite16(SCBMaskAll, ioaddr + SCBCmd);
-	speedo_resume(dev);
-	netif_device_attach(dev);
-	sp->rx_mode = -1;
-	sp->flow_ctrl = sp->partner = 0;
-	set_rx_mode(dev);
-	sp->timer.expires = RUN_AT(2*HZ);
-	add_timer(&sp->timer);
-	return 0;
-}
-#endif /* CONFIG_PM */
-
-static void __devexit eepro100_remove_one (struct pci_dev *pdev)
-{
-	struct net_device *dev = pci_get_drvdata (pdev);
-	struct speedo_private *sp = netdev_priv(dev);
-
-	unregister_netdev(dev);
-
-	release_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
-	release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
-
-	pci_iounmap(pdev, sp->regs);
-	pci_free_consistent(pdev, TX_RING_SIZE * sizeof(struct TxFD)
-								+ sizeof(struct speedo_stats),
-						sp->tx_ring, sp->tx_ring_dma);
-	pci_disable_device(pdev);
-	free_netdev(dev);
-}
-
-static struct pci_device_id eepro100_pci_tbl[] = {
-	{ PCI_VENDOR_ID_INTEL, 0x1229, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1209, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1029, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1030, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1031, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1032, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1033, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1034, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1035, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1036, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1037, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1038, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1039, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x103A, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x103B, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x103C, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x103D, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x103E, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1050, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1059, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x1227, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x2449, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x2459, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x245D, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x5200, PCI_ANY_ID, PCI_ANY_ID, },
-	{ PCI_VENDOR_ID_INTEL, 0x5201, PCI_ANY_ID, PCI_ANY_ID, },
-	{ 0,}
-};
-MODULE_DEVICE_TABLE(pci, eepro100_pci_tbl);
-
-static struct pci_driver eepro100_driver = {
-	.name		= "eepro100",
-	.id_table	= eepro100_pci_tbl,
-	.probe		= eepro100_init_one,
-	.remove		= __devexit_p(eepro100_remove_one),
-#ifdef CONFIG_PM
-	.suspend	= eepro100_suspend,
-	.resume		= eepro100_resume,
-#endif /* CONFIG_PM */
-};
-
-static int __init eepro100_init_module(void)
-{
-#ifdef MODULE
-	printk(version);
-#endif
-	return pci_register_driver(&eepro100_driver);
-}
-
-static void __exit eepro100_cleanup_module(void)
-{
-	pci_unregister_driver(&eepro100_driver);
-}
-
-module_init(eepro100_init_module);
-module_exit(eepro100_cleanup_module);
-
-/*
- * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- *  c-indent-level: 4
- *  c-basic-offset: 4
- *  tab-width: 4
- * End:
- */


^ permalink raw reply

* [2.6 patch] net/unix/af_unix.c: make code static
From: Adrian Bunk @ 2007-07-29 14:58 UTC (permalink / raw)
  To: Miklos Szeredi; +Cc: netdev, linux-kernel

The following code can now become static:
- struct unix_socket_table
- unix_table_lock

Signed-off-by: Adrian Bunk <bunk@stusta.de>

---

 include/net/af_unix.h |   29 -----------------------------
 net/unix/af_unix.c    |   30 ++++++++++++++++++++++++++++--
 2 files changed, 28 insertions(+), 31 deletions(-)

--- linux-2.6.23-rc1-mm1/include/net/af_unix.h.old	2007-07-26 21:17:33.000000000 +0200
+++ linux-2.6.23-rc1-mm1/include/net/af_unix.h	2007-07-26 21:20:27.000000000 +0200
@@ -12,37 +12,8 @@
 
 #define UNIX_HASH_SIZE	256
 
-extern struct hlist_head unix_socket_table[UNIX_HASH_SIZE + 1];
-extern spinlock_t unix_table_lock;
-
 extern atomic_t unix_tot_inflight;
 
-static inline struct sock *first_unix_socket(int *i)
-{
-	for (*i = 0; *i <= UNIX_HASH_SIZE; (*i)++) {
-		if (!hlist_empty(&unix_socket_table[*i]))
-			return __sk_head(&unix_socket_table[*i]);
-	}
-	return NULL;
-}
-
-static inline struct sock *next_unix_socket(int *i, struct sock *s)
-{
-	struct sock *next = sk_next(s);
-	/* More in this chain? */
-	if (next)
-		return next;
-	/* Look for next non-empty chain. */
-	for ((*i)++; *i <= UNIX_HASH_SIZE; (*i)++) {
-		if (!hlist_empty(&unix_socket_table[*i]))
-			return __sk_head(&unix_socket_table[*i]);
-	}
-	return NULL;
-}
-
-#define forall_unix_sockets(i, s) \
-	for (s = first_unix_socket(&(i)); s; s = next_unix_socket(&(i),(s)))
-
 struct unix_address {
 	atomic_t	refcnt;
 	int		len;
--- linux-2.6.23-rc1-mm1/net/unix/af_unix.c.old	2007-07-26 21:17:48.000000000 +0200
+++ linux-2.6.23-rc1-mm1/net/unix/af_unix.c	2007-07-26 21:20:24.000000000 +0200
@@ -118,14 +118,40 @@
 
 int sysctl_unix_max_dgram_qlen __read_mostly = 10;
 
-struct hlist_head unix_socket_table[UNIX_HASH_SIZE + 1];
-DEFINE_SPINLOCK(unix_table_lock);
+static struct hlist_head unix_socket_table[UNIX_HASH_SIZE + 1];
+static DEFINE_SPINLOCK(unix_table_lock);
 static atomic_t unix_nr_socks = ATOMIC_INIT(0);
 
 #define unix_sockets_unbound	(&unix_socket_table[UNIX_HASH_SIZE])
 
 #define UNIX_ABSTRACT(sk)	(unix_sk(sk)->addr->hash != UNIX_HASH_SIZE)
 
+static struct sock *first_unix_socket(int *i)
+{
+	for (*i = 0; *i <= UNIX_HASH_SIZE; (*i)++) {
+		if (!hlist_empty(&unix_socket_table[*i]))
+			return __sk_head(&unix_socket_table[*i]);
+	}
+	return NULL;
+}
+
+static struct sock *next_unix_socket(int *i, struct sock *s)
+{
+	struct sock *next = sk_next(s);
+	/* More in this chain? */
+	if (next)
+		return next;
+	/* Look for next non-empty chain. */
+	for ((*i)++; *i <= UNIX_HASH_SIZE; (*i)++) {
+		if (!hlist_empty(&unix_socket_table[*i]))
+			return __sk_head(&unix_socket_table[*i]);
+	}
+	return NULL;
+}
+
+#define forall_unix_sockets(i, s) \
+	for (s = first_unix_socket(&(i)); s; s = next_unix_socket(&(i),(s)))
+
 #ifdef CONFIG_SECURITY_NETWORK
 static void unix_get_secdata(struct scm_cookie *scm, struct sk_buff *skb)
 {


^ permalink raw reply

* [2.6 patch] make pktgen.c:get_ipsec_sa() static and non-inline
From: Adrian Bunk @ 2007-07-29 14:58 UTC (permalink / raw)
  To: Jamal Hadi Salim, Robert Olsson; +Cc: linux-kernel, netdev

Non-static inline code usually doesn't makes sense.

In this case making is static and non-inline is the correct solution.

Signed-off-by: Adrian Bunk <bunk@stusta.de>

---
--- linux-2.6.23-rc1-mm1/net/core/pktgen.c.old	2007-07-26 20:12:01.000000000 +0200
+++ linux-2.6.23-rc1-mm1/net/core/pktgen.c	2007-07-26 20:12:13.000000000 +0200
@@ -2156,8 +2156,7 @@
 /* If there was already an IPSEC SA, we keep it as is, else
  * we go look for it ...
 */
-inline
-void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
+static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
 {
 	struct xfrm_state *x = pkt_dev->flows[flow].x;
 	if (!x) {


^ permalink raw reply

* [2.6 patch] make nf_ct_ipv6_skip_exthdr() static
From: Adrian Bunk @ 2007-07-29 14:58 UTC (permalink / raw)
  To: Yasuyuki Kozakai, Patrick McHardy; +Cc: netdev, netfilter-devel, linux-kernel

nf_ct_ipv6_skip_exthdr() can now become static.

Signed-off-by: Adrian Bunk <bunk@stusta.de>

---

 include/net/netfilter/ipv6/nf_conntrack_ipv6.h |    3 ---
 net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c |    4 ++--
 2 files changed, 2 insertions(+), 5 deletions(-)

--- linux-2.6.23-rc1-mm1/include/net/netfilter/ipv6/nf_conntrack_ipv6.h.old	2007-07-26 20:17:47.000000000 +0200
+++ linux-2.6.23-rc1-mm1/include/net/netfilter/ipv6/nf_conntrack_ipv6.h	2007-07-26 20:17:53.000000000 +0200
@@ -7,9 +7,6 @@
 extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6;
 extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6;
 
-extern int nf_ct_ipv6_skip_exthdr(const struct sk_buff *skb, int start,
-				  u8 *nexthdrp, int len);
-
 extern int nf_ct_frag6_init(void);
 extern void nf_ct_frag6_cleanup(void);
 extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb);
--- linux-2.6.23-rc1-mm1/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c.old	2007-07-26 20:18:03.000000000 +0200
+++ linux-2.6.23-rc1-mm1/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c	2007-07-26 20:18:27.000000000 +0200
@@ -86,8 +86,8 @@
  *        - Note also special handling of AUTH header. Thanks to IPsec wizards.
  */
 
-int nf_ct_ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
-			   int len)
+static int nf_ct_ipv6_skip_exthdr(const struct sk_buff *skb, int start,
+				  u8 *nexthdrp, int len)
 {
 	u8 nexthdr = *nexthdrp;
 

^ permalink raw reply

* [-mm patch] e1000: #if 0 two functions
From: Adrian Bunk @ 2007-07-29 14:58 UTC (permalink / raw)
  To: Andrew Morton
  Cc: auke-jan.h.kok, Peter Oruba, e1000-devel, jgarzik,
	jesse.brandeburg, linux-kernel, john.ronciak, jeffrey.t.kirsher,
	netdev, Stephen Hemminger, cramerj

e1000_{read,write}_pci_cfg() are no longer used.

Signed-off-by: Adrian Bunk <bunk@stusta.de>

---

This patch has been sent on:
- 1 Jul 2007
- 5 Jun 2007

 drivers/net/e1000/e1000_hw.h   |    2 --
 drivers/net/e1000/e1000_main.c |    4 ++++
 2 files changed, 4 insertions(+), 2 deletions(-)

--- linux-2.6.22-rc3-mm1/drivers/net/e1000/e1000_hw.h.old	2007-06-04 22:03:05.000000000 +0200
+++ linux-2.6.22-rc3-mm1/drivers/net/e1000/e1000_hw.h	2007-06-04 22:03:14.000000000 +0200
@@ -421,8 +421,6 @@ void e1000_tbi_adjust_stats(struct e1000
 void e1000_get_bus_info(struct e1000_hw *hw);
 void e1000_pci_set_mwi(struct e1000_hw *hw);
 void e1000_pci_clear_mwi(struct e1000_hw *hw);
-void e1000_read_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value);
-void e1000_write_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value);
 int32_t e1000_read_pcie_cap_reg(struct e1000_hw *hw, uint32_t reg, uint16_t *value);
 void e1000_pcix_set_mmrbc(struct e1000_hw *hw, int mmrbc);
 int e1000_pcix_get_mmrbc(struct e1000_hw *hw);
--- linux-2.6.22-rc3-mm1/drivers/net/e1000/e1000_main.c.old	2007-06-04 22:03:24.000000000 +0200
+++ linux-2.6.22-rc3-mm1/drivers/net/e1000/e1000_main.c	2007-06-04 22:03:40.000000000 +0200
@@ -4888,6 +4888,8 @@ e1000_pci_clear_mwi(struct e1000_hw *hw)
 	pci_clear_mwi(adapter->pdev);
 }
 
+#if 0
+
 void
 e1000_read_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t *value)
 {
@@ -4904,6 +4906,8 @@ e1000_write_pci_cfg(struct e1000_hw *hw,
 	pci_write_config_word(adapter->pdev, reg, *value);
 }
 
+#endif  /*  0  */
+
 int
 e1000_pcix_get_mmrbc(struct e1000_hw *hw)
 {



-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

^ permalink raw reply

* [2.6 patch] drivers/net/cxgb3/xgmac.c: remove dead code
From: Adrian Bunk @ 2007-07-29 14:55 UTC (permalink / raw)
  To: Divy Le Ray, Jeff Garzik; +Cc: netdev, linux-kernel

This patch removes dead code ("tx_xcnt" can never be != 0 at this place) 
spotted by the Coverity checker.

Signed-off-by: Adrian Bunk <bunk@stusta.de>

---

 drivers/net/cxgb3/xgmac.c |    5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

--- linux-2.6.22-rc6-mm1/drivers/net/cxgb3/xgmac.c.old	2007-07-24 13:55:33.000000000 +0200
+++ linux-2.6.22-rc6-mm1/drivers/net/cxgb3/xgmac.c	2007-07-24 13:57:06.000000000 +0200
@@ -510,38 +510,35 @@ int t3b2_mac_watchdog_task(struct cmac *
 	if (tx_mcnt == mac->tx_mcnt) {
 		tx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap,
 						A_XGM_TX_SPI4_SOP_EOP_CNT +
 					       	mac->offset)));
 		if (tx_xcnt == 0) {
 			t3_write_reg(adap, A_TP_PIO_ADDR,
 				     A_TP_TX_DROP_CNT_CH0 + macidx(mac));
 			tx_tcnt = (G_TXDROPCNTCH0RCVD(t3_read_reg(adap,
 						      A_TP_PIO_DATA)));
 		} else {
 			goto rxcheck;
 		}
 	} else {
 		mac->toggle_cnt = 0;
 		goto rxcheck;
 	}
 
-	if (((tx_tcnt != mac->tx_tcnt) &&
-	     (tx_xcnt == 0) && (mac->tx_xcnt == 0)) ||
-	    ((mac->tx_mcnt == tx_mcnt) &&
-	     (tx_xcnt != 0) && (mac->tx_xcnt != 0))) {
+	if ((tx_tcnt != mac->tx_tcnt) && (mac->tx_xcnt == 0))  {
 		if (mac->toggle_cnt > 4) {
 			status = 2;
 			goto out;
 		} else {
 			status = 1;
 			goto out;
 		}
 	} else {
 		mac->toggle_cnt = 0;
 		goto rxcheck;
 	}
 
 rxcheck:
 	if (rx_mcnt != mac->rx_mcnt)
 		rx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap,
 						A_XGM_RX_SPI4_SOP_EOP_CNT +
 						mac->offset)));

^ permalink raw reply

* Re: source interface ping bug ?
From: nano bug @ 2007-07-29 11:48 UTC (permalink / raw)
  To: netdev
In-Reply-To: <d39c36500707270130j3eac7f44qb3a4c87496a1296b@mail.gmail.com>

Any news about this ?

On 7/27/07, nano bug <linnewbye@gmail.com> wrote:
> Hello there,
>
> I'm facing the following issue : when I try to ping using source
> interface instead of a source ip address the ping utility starts to
> send arp requests instead of icmp requests though the ip address I'm
> pinging it's not in the subnets directly connected to my linux box.
> I've noticed this situation since I upgraded from kernel 2.6.20 to
> 2.6.21. On 2.6.20 and lower I haven't had this problem. Now I upgraded
> to 2.6.22 but it's the same. I'm using latest iproute and iputils.
> Here is an output of tcpdump when I try to ping an outside ip address,
> like for example www.yahoo.com, using source interface :
>
> root@darkstar:~# uname -a
> Linux darkstar 2.6.22 #1 Thu Jul 26 21:22:11 EEST 2007 i686 Pentium II
> (Deschutes) GenuineIntel GNU/Linux
>
> root@darkstar:~# ip -V
> ip utility, iproute2-ss070710
> root@darkstar:~#
>
>
> root@darkstar:~# ip address show dev eth2
> 3: eth2: <BROADCAST,MULTICAST,NOTRAILERS,UP,LOWER_UP> mtu 1500 qdisc
> pfifo_fast qlen 1000
>    link/ether 00:90:27:0f:79:f3 brd ff:ff:ff:ff:ff:ff
>    inet 86.106.19.75/23 brd 86.106.19.255 scope global eth2
> root@darkstar:~# ip route get 87.248.113.14 from 86.106.19.75 oif eth2
> 87.248.113.14 from 86.106.19.75 via 86.106.18.1 dev eth2
>    cache  mtu 1500 advmss 1460 hoplimit 64
> root@darkstar:~#
>
>
> root@darkstar:~/iputils# ./ping -V
> ping utility, iputils-sss20070202
> root@darkstar:~/iputils# ./ping -I 86.106.19.75 87.248.113.14 -c 2
> PING 87.248.113.14 (87.248.113.14) from 86.106.19.75 : 56(84) bytes of data.
> 64 bytes from 87.248.113.14: icmp_seq=1 ttl=51 time=60.5 ms
> 64 bytes from 87.248.113.14: icmp_seq=2 ttl=51 time=63.2 ms
>
> --- 87.248.113.14 ping statistics ---
> 2 packets transmitted, 2 received, 0% packet loss, time 999ms
> rtt min/avg/max/mdev = 60.574/61.924/63.274/1.350 ms
> root@darkstar:~/iputils#
>
>
> root@darkstar:~# tcpdump -i eth2 -vvv -n host 87.248.113.14 and host
> 86.106.19.75
> tcpdump: listening on eth2, link-type EN10MB (Ethernet), capture size 96 bytes
> 01:18:09.572603 IP (tos 0x0, ttl  64, id 0, offset 0, flags [DF],
> proto: ICMP (1), length: 84) 86.106.19.75 > 87.248.113.14: ICMP echo
> request, id 27166, seq 1, length 64
> 01:18:09.632861 IP (tos 0x0, ttl  51, id 6100, offset 0, flags [none],
> proto: ICMP (1), length: 84) 87.248.113.14 > 86.106.19.75: ICMP echo
> reply, id 27166, seq 1, length 64
> 01:18:10.572746 IP (tos 0x0, ttl  64, id 0, offset 0, flags [DF],
> proto: ICMP (1), length: 84) 86.106.19.75 > 87.248.113.14: ICMP echo
> request, id 27166, seq 2, length 64
> 01:18:10.634951 IP (tos 0x0, ttl  51, id 8790, offset 0, flags [none],
> proto: ICMP (1), length: 84) 87.248.113.14 > 86.106.19.75: ICMP echo
> reply, id 27166, seq 2, length 64
>
>
>
> using source interface :
>
> root@darkstar:~/iputils# ./ping -I eth2 87.248.113.14
> PING 87.248.113.14 (87.248.113.14) from 86.106.19.75 eth2: 56(84) bytes of data.
> From 86.106.19.75 icmp_seq=1 Destination Host Unreachable
> From 86.106.19.75 icmp_seq=2 Destination Host Unreachable
> From 86.106.19.75 icmp_seq=3 Destination Host Unreachable
> From 86.106.19.75 icmp_seq=5 Destination Host Unreachable
> From 86.106.19.75 icmp_seq=6 Destination Host Unreachable
> From 86.106.19.75 icmp_seq=7 Destination Host Unreachable
> From 86.106.19.75 icmp_seq=9 Destination Host Unreachable
> From 86.106.19.75 icmp_seq=10 Destination Host Unreachable
> From 86.106.19.75 icmp_seq=11 Destination Host Unreachable
>
> --- 87.248.113.14 ping statistics ---
> 13 packets transmitted, 0 received, +9 errors, 100% packet loss, time 12006ms
> , pipe 3
> root@darkstar:~/iputils#
>
>
> root@darkstar:~# tcpdump -i eth2 -vvv -n host 87.248.113.14 and host
> 86.106.19.75
> tcpdump: listening on eth2, link-type EN10MB (Ethernet), capture size 96 bytes
> 01:19:24.292911 arp who-has 87.248.113.14 tell 86.106.19.75
> 01:19:25.292897 arp who-has 87.248.113.14 tell 86.106.19.75
> 01:19:26.292901 arp who-has 87.248.113.14 tell 86.106.19.75
> 01:19:27.302906 arp who-has 87.248.113.14 tell 86.106.19.75
> 01:19:28.302911 arp who-has 87.248.113.14 tell 86.106.19.75
> 01:19:29.302912 arp who-has 87.248.113.14 tell 86.106.19.75
> 01:19:31.302917 arp who-has 87.248.113.14 tell 86.106.19.75
> 01:19:32.302921 arp who-has 87.248.113.14 tell 86.106.19.75
> 01:19:33.302923 arp who-has 87.248.113.14 tell 86.106.19.75
> 01:19:35.302932 arp who-has 87.248.113.14 tell 86.106.19.75
> 01:19:36.302932 arp who-has 87.248.113.14 tell 86.106.19.75
> 01:19:37.302939 arp who-has 87.248.113.14 tell 86.106.19.75
>
> 12 packets captured
> 12 packets received by filter
> 0 packets dropped by kernel
> root@darkstar:~#
>
>
> There is one exception though, it works when using eth0. I'm
> administrating multiple linux boxes with 2 or 3 ethernet cards and if
> I try pinging with eth0 it does send icmp like it should but when
> specifying eth1 or eth2 is sends arp requests. The distro I'm using is
> Slackware 12.0. I already reported this to the iputils maintainer and
> he recommended to report it here. Thanks in advance.
>
> Regards.
>

^ permalink raw reply

* [PATCH FINAL] Merge the Sonics Silicon Backplane subsystem
From: Michael Buesch @ 2007-07-29 11:24 UTC (permalink / raw)
  To: Andrew Morton
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA, linux-kernel,
	Gary Zambrano, bcm43xx-dev-0fE9KPoRgkgATYTw5x5z8w

The Sonics Silicon Backplane is a mini-bus used on
various Broadcom chips and embedded devices.
Devices using the SSB include b44, bcm43xx and various
Broadcom based wireless routers.
A b44 and bcm43xx port and a SSB based OHCI driver is available.

Signed-off-by: Michael Buesch <mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
Acked-by: John Linville <linville-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org>

--

Andrew, please put this patch into -mm.
This is the final patch to merge ssb, which means future
cleanups and fixes will come incremental based on this one.

Please also schedule this patch for merge into mainline
when the next merge window opens. I'll also send out a
remind mail when that happens.

Please also leave the patch
avoid-ssb-compililation-on-s390.patch
in -mm and also push that to mainline when the merge window opens.

diff --git a/drivers/Makefile b/drivers/Makefile
index a9e4c5f..2b079ed 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -87,3 +87,4 @@ obj-$(CONFIG_DMA_ENGINE)	+= dma/
 obj-$(CONFIG_HID)		+= hid/
 obj-$(CONFIG_PPC_PS3)		+= ps3/
 obj-$(CONFIG_OF)		+= of/
+obj-$(CONFIG_SSB)		+= ssb/
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 3e1c442..7bdae47 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -58,6 +58,8 @@ source "drivers/power/Kconfig"
 
 source "drivers/hwmon/Kconfig"
 
+source "drivers/ssb/Kconfig"
+
 source "drivers/mfd/Kconfig"
 
 source "drivers/media/Kconfig"
diff --git a/drivers/ssb/Makefile b/drivers/ssb/Makefile
new file mode 100644
index 0000000..045aff0
--- /dev/null
+++ b/drivers/ssb/Makefile
@@ -0,0 +1,13 @@
+# core
+ssb-y					+= main.o scan.o
+
+# host support
+ssb-$(CONFIG_SSB_PCIHOST)		+= pci.o pcihost_wrapper.o
+ssb-$(CONFIG_SSB_PCMCIAHOST)		+= pcmcia.o
+
+# built-in drivers
+ssb-y					+= driver_chipcommon.o
+ssb-$(CONFIG_SSB_DRIVER_MIPS)		+= driver_mipscore.o
+ssb-$(CONFIG_SSB_DRIVER_PCICORE)	+= driver_pcicore.o
+
+obj-$(CONFIG_SSB)			+= ssb.o
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
new file mode 100644
index 0000000..34a9411
--- /dev/null
+++ b/drivers/ssb/Kconfig
@@ -0,0 +1,92 @@
+menu "Sonics Silicon Backplane"
+
+config SSB
+	tristate "Sonics Silicon Backplane support"
+	depends on EXPERIMENTAL
+	help
+	  Support for the Sonics Silicon Backplane bus
+
+	  The module will be called ssb
+
+	  If unsure, say M
+
+config SSB_PCIHOST
+	bool "Support for SSB on PCI-bus host"
+	depends on SSB && PCI
+	default y
+	help
+	  Support for a Sonics Silicon Backplane on top
+	  of a PCI device.
+
+	  If unsure, say Y
+
+config SSB_PCMCIAHOST
+	bool "Support for SSB on PCMCIA-bus host"
+	depends on SSB && PCMCIA
+	help
+	  Support for a Sonics Silicon Backplane on top
+	  of a PCMCIA device.
+
+	  If unsure, say N
+
+config SSB_SILENT
+	bool "No SSB kernel messages"
+	depends on SSB
+	help
+	  This option turns off all Sonics Silicon Backplane printks.
+	  Note that you won't be able to identify problems, once
+	  messages are turned off.
+	  This might only be desired for production kernels on
+	  embedded devices to reduce the kernel size.
+
+	  Say N
+
+config SSB_DEBUG
+	bool "SSB debugging"
+	depends on SSB && !SSB_SILENT
+	help
+	  This turns on additional runtime checks and debugging
+	  messages. Turn this on for SSB troubleshooting.
+
+	  If unsure, say N
+
+config SSB_SERIAL
+	bool
+	depends on SSB
+	# ChipCommon and ExtIf serial support routines.
+
+config SSB_DRIVER_PCICORE
+	bool "SSB PCI core driver"
+	depends on SSB && SSB_PCIHOST
+	help
+	  Driver for the Sonics Silicon Backplane attached
+	  Broadcom PCI core.
+
+	  If unsure, say Y
+
+config SSB_PCICORE_HOSTMODE
+	bool "Hostmode support for SSB PCI core"
+	depends on SSB_DRIVER_PCICORE && SSB_DRIVER_MIPS
+	help
+	  PCIcore hostmode operation (external PCI bus).
+
+config SSB_DRIVER_MIPS
+	bool "SSB Broadcom MIPS core driver"
+	depends on SSB && MIPS
+	select SSB_SERIAL
+	help
+	  Driver for the Sonics Silicon Backplane attached
+	  Broadcom MIPS core.
+
+	  If unsure, say N
+
+config SSB_DRIVER_EXTIF
+	bool "SSB Broadcom EXTIF core driver"
+	depends on SSB_DRIVER_MIPS
+	help
+	  Driver for the Sonics Silicon Backplane attached
+	  Broadcom EXTIF core.
+
+	  If unsure, say N
+
+endmenu
diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
new file mode 100644
index 0000000..8e5491c
--- /dev/null
+++ b/drivers/ssb/driver_chipcommon.c
@@ -0,0 +1,394 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom ChipCommon core driver
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/ssb/ssb_regs.h>
+#include <linux/pci.h>
+
+#include "ssb_private.h"
+
+
+/* Clock sources */
+enum ssb_clksrc {
+	/* PCI clock */
+	SSB_CHIPCO_CLKSRC_PCI,
+	/* Crystal slow clock oscillator */
+	SSB_CHIPCO_CLKSRC_XTALOS,
+	/* Low power oscillator */
+	SSB_CHIPCO_CLKSRC_LOPWROS,
+};
+
+
+static inline u32 chipco_read32(struct ssb_chipcommon *cc,
+				u16 offset)
+{
+	return ssb_read32(cc->dev, offset);
+}
+
+static inline void chipco_write32(struct ssb_chipcommon *cc,
+				  u16 offset,
+				  u32 value)
+{
+	ssb_write32(cc->dev, offset, value);
+}
+
+void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc,
+			      enum ssb_clkmode mode)
+{
+	struct ssb_device *ccdev = cc->dev;
+	struct ssb_bus *bus;
+	u32 tmp;
+
+	if (!ccdev)
+		return;
+	bus = ccdev->bus;
+	/* chipcommon cores prior to rev6 don't support dynamic clock control */
+	if (ccdev->id.revision < 6)
+		return;
+	/* chipcommon cores rev10 are a whole new ball game */
+	if (ccdev->id.revision >= 10)
+		return;
+	if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL))
+		return;
+
+	switch (mode) {
+	case SSB_CLKMODE_SLOW:
+		tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+		tmp |= SSB_CHIPCO_SLOWCLKCTL_FSLOW;
+		chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
+		break;
+	case SSB_CLKMODE_FAST:
+		ssb_pci_xtal(bus, SSB_GPIO_XTAL, 1); /* Force crystal on */
+		tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+		tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW;
+		tmp |= SSB_CHIPCO_SLOWCLKCTL_IPLL;
+		chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
+		break;
+	case SSB_CLKMODE_DYNAMIC:
+		tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+		tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW;
+		tmp &= ~SSB_CHIPCO_SLOWCLKCTL_IPLL;
+		tmp &= ~SSB_CHIPCO_SLOWCLKCTL_ENXTAL;
+		if ((tmp & SSB_CHIPCO_SLOWCLKCTL_SRC) != SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL)
+			tmp |= SSB_CHIPCO_SLOWCLKCTL_ENXTAL;
+		chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
+
+		/* for dynamic control, we have to release our xtal_pu "force on" */
+		if (tmp & SSB_CHIPCO_SLOWCLKCTL_ENXTAL)
+			ssb_pci_xtal(bus, SSB_GPIO_XTAL, 0);
+		break;
+	default:
+		SSB_WARN_ON(1);
+	}
+}
+
+/* Get the Slow Clock Source */
+static enum ssb_clksrc chipco_pctl_get_slowclksrc(struct ssb_chipcommon *cc)
+{
+	struct ssb_bus *bus = cc->dev->bus;
+	u32 uninitialized_var(tmp);
+
+	if (cc->dev->id.revision < 6) {
+		if (bus->bustype == SSB_BUSTYPE_SSB ||
+		    bus->bustype == SSB_BUSTYPE_PCMCIA)
+			return SSB_CHIPCO_CLKSRC_XTALOS;
+		if (bus->bustype == SSB_BUSTYPE_PCI) {
+			pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &tmp);
+			if (tmp & 0x10)
+				return SSB_CHIPCO_CLKSRC_PCI;
+			return SSB_CHIPCO_CLKSRC_XTALOS;
+		}
+	}
+	if (cc->dev->id.revision < 10) {
+		tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+		tmp &= 0x7;
+		if (tmp == 0)
+			return SSB_CHIPCO_CLKSRC_LOPWROS;
+		if (tmp == 1)
+			return SSB_CHIPCO_CLKSRC_XTALOS;
+		if (tmp == 2)
+			return SSB_CHIPCO_CLKSRC_PCI;
+	}
+
+	return SSB_CHIPCO_CLKSRC_XTALOS;
+}
+
+/* Get maximum or minimum (depending on get_max flag) slowclock frequency. */
+static int chipco_pctl_clockfreqlimit(struct ssb_chipcommon *cc, int get_max)
+{
+	int uninitialized_var(limit);
+	enum ssb_clksrc clocksrc;
+	int divisor = 1;
+	u32 tmp;
+
+	clocksrc = chipco_pctl_get_slowclksrc(cc);
+	if (cc->dev->id.revision < 6) {
+		switch (clocksrc) {
+		case SSB_CHIPCO_CLKSRC_PCI:
+			divisor = 64;
+			break;
+		case SSB_CHIPCO_CLKSRC_XTALOS:
+			divisor = 32;
+			break;
+		default:
+			SSB_WARN_ON(1);
+		}
+	} else if (cc->dev->id.revision < 10) {
+		switch (clocksrc) {
+		case SSB_CHIPCO_CLKSRC_LOPWROS:
+			break;
+		case SSB_CHIPCO_CLKSRC_XTALOS:
+		case SSB_CHIPCO_CLKSRC_PCI:
+			tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+			divisor = (tmp >> 16) + 1;
+			divisor *= 4;
+			break;
+		}
+	} else {
+		tmp = chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL);
+		divisor = (tmp >> 16) + 1;
+		divisor *= 4;
+	}
+
+	switch (clocksrc) {
+	case SSB_CHIPCO_CLKSRC_LOPWROS:
+		if (get_max)
+			limit = 43000;
+		else
+			limit = 25000;
+		break;
+	case SSB_CHIPCO_CLKSRC_XTALOS:
+		if (get_max)
+			limit = 20200000;
+		else
+			limit = 19800000;
+		break;
+	case SSB_CHIPCO_CLKSRC_PCI:
+		if (get_max)
+			limit = 34000000;
+		else
+			limit = 25000000;
+		break;
+	}
+	limit /= divisor;
+
+	return limit;
+}
+
+static void chipco_powercontrol_init(struct ssb_chipcommon *cc)
+{
+	struct ssb_bus *bus = cc->dev->bus;
+
+	if (bus->chip_id == 0x4321) {
+		if (bus->chip_rev == 0)
+			chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0x3A4);
+		else if (bus->chip_rev == 1)
+			chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0xA4);
+	}
+
+	if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL))
+		return;
+
+	if (cc->dev->id.revision >= 10) {
+		/* Set Idle Power clock rate to 1Mhz */
+		chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL,
+			       (chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) &
+				0x0000FFFF) | 0x00040000);
+	} else {
+		int maxfreq;
+
+		maxfreq = chipco_pctl_clockfreqlimit(cc, 1);
+		chipco_write32(cc, SSB_CHIPCO_PLLONDELAY,
+			       (maxfreq * 150 + 999999) / 1000000);
+		chipco_write32(cc, SSB_CHIPCO_FREFSELDELAY,
+			       (maxfreq * 15 + 999999) / 1000000);
+	}
+}
+
+static void calc_fast_powerup_delay(struct ssb_chipcommon *cc)
+{
+	struct ssb_bus *bus = cc->dev->bus;
+	int minfreq;
+	unsigned int tmp;
+	u32 pll_on_delay;
+
+	if (bus->bustype != SSB_BUSTYPE_PCI)
+		return;
+	if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL))
+		return;
+
+	minfreq = chipco_pctl_clockfreqlimit(cc, 0);
+	pll_on_delay = chipco_read32(cc, SSB_CHIPCO_PLLONDELAY);
+	tmp = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq;
+	SSB_WARN_ON(tmp & ~0xFFFF);
+
+	cc->fast_pwrup_delay = tmp;
+}
+
+void ssb_chipcommon_init(struct ssb_chipcommon *cc)
+{
+	if (!cc->dev)
+		return; /* We don't have a ChipCommon */
+	chipco_powercontrol_init(cc);
+	ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
+	calc_fast_powerup_delay(cc);
+}
+
+void ssb_chipco_suspend(struct ssb_chipcommon *cc, pm_message_t state)
+{
+	if (!cc->dev)
+		return;
+	ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW);
+}
+
+void ssb_chipco_resume(struct ssb_chipcommon *cc)
+{
+	if (!cc->dev)
+		return;
+	chipco_powercontrol_init(cc);
+	ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
+}
+
+void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc,
+				 u32 *plltype, u32 *n, u32 *m)
+{
+	*n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N);
+	*plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT);
+	switch (*plltype) {
+	case SSB_PLLTYPE_6: /* 100/200 or 120/240 only */
+		*m = chipco_read32(cc, SSB_CHIPCO_CLOCK_MIPS);
+		break;
+	case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */
+		if (cc->dev->bus->chip_id != 0x5365) {
+			*m = chipco_read32(cc, SSB_CHIPCO_CLOCK_M2);
+			break;
+		}
+		/* Fallthough */
+	default:
+		*m = chipco_read32(cc, SSB_CHIPCO_CLOCK_SB);
+	}
+}
+
+void ssb_chipco_timing_init(struct ssb_chipcommon *cc,
+			    unsigned long ns)
+{
+	struct ssb_device *dev = cc->dev;
+	struct ssb_bus *bus = dev->bus;
+	u32 tmp;
+
+	/* set register for external IO to control LED. */
+	chipco_write32(cc, SSB_CHIPCO_PROG_CFG, 0x11);
+	tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT;		/* Waitcount-3 = 10ns */
+	tmp |= DIV_ROUND_UP(40, ns) << SSB_PROG_WCNT_1_SHIFT;	/* Waitcount-1 = 40ns */
+	tmp |= DIV_ROUND_UP(240, ns);				/* Waitcount-0 = 240ns */
+	chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp);	/* 0x01020a0c for a 100Mhz clock */
+
+	/* Set timing for the flash */
+	tmp = DIV_ROUND_UP(10, ns) << SSB_FLASH_WCNT_3_SHIFT;	/* Waitcount-3 = 10nS */
+	tmp |= DIV_ROUND_UP(10, ns) << SSB_FLASH_WCNT_1_SHIFT;	/* Waitcount-1 = 10nS */
+	tmp |= DIV_ROUND_UP(120, ns);				/* Waitcount-0 = 120nS */
+	if ((bus->chip_id == 0x5365) ||
+	    (dev->id.revision < 9))
+		chipco_write32(cc, SSB_CHIPCO_FLASH_WAITCNT, tmp);
+	if ((bus->chip_id == 0x5365) ||
+	    (dev->id.revision < 9) ||
+	    ((bus->chip_id == 0x5350) && (bus->chip_rev == 0)))
+		chipco_write32(cc, SSB_CHIPCO_PCMCIA_MEMWAIT, tmp);
+
+	if (bus->chip_id == 0x5350) {
+		/* Enable EXTIF */
+		tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT;	  /* Waitcount-3 = 10ns */
+		tmp |= DIV_ROUND_UP(20, ns) << SSB_PROG_WCNT_2_SHIFT;  /* Waitcount-2 = 20ns */
+		tmp |= DIV_ROUND_UP(100, ns) << SSB_PROG_WCNT_1_SHIFT; /* Waitcount-1 = 100ns */
+		tmp |= DIV_ROUND_UP(120, ns);			  /* Waitcount-0 = 120ns */
+		chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp); /* 0x01020a0c for a 100Mhz clock */
+	}
+}
+
+
+#ifdef CONFIG_SSB_SERIAL
+int ssb_chipco_serial_init(struct ssb_chipcommon *cc,
+			   struct ssb_serial_port *ports)
+{
+	struct ssb_bus *bus = cc->dev->bus;
+	int nr_ports = 0;
+	u32 plltype;
+	unsigned int irq;
+	u32 baud_base, div;
+	u32 i, n;
+
+	plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT);
+	irq = ssb_mips_irq(cc->dev);
+
+	if (plltype == SSB_PLLTYPE_1) {
+		/* PLL clock */
+		baud_base = ssb_calc_clock_rate(plltype,
+						chipco_read32(cc, SSB_CHIPCO_CLOCK_N),
+						chipco_read32(cc, SSB_CHIPCO_CLOCK_M2));
+		div = 1;
+	} else {
+		if (cc->dev->id.revision >= 11) {
+			/* Fixed ALP clock */
+			baud_base = 20000000;
+			div = 1;
+			/* Set the override bit so we don't divide it */
+			chipco_write32(cc, SSB_CHIPCO_CORECTL,
+				       SSB_CHIPCO_CORECTL_UARTCLK0);
+		} else if (cc->dev->id.revision >= 3) {
+			/* Internal backplane clock */
+			baud_base = ssb_clockspeed(bus);
+			div = 2; /* Minimum divisor */
+			chipco_write32(cc, SSB_CHIPCO_CLKDIV,
+				       (chipco_read32(cc, SSB_CHIPCO_CLKDIV)
+					& ~SSB_CHIPCO_CLKDIV_UART) | div);
+		} else {
+			/* Fixed internal backplane clock */
+			baud_base = 88000000;
+			div = 48;
+		}
+
+		/* Clock source depends on strapping if UartClkOverride is unset */
+		if ((cc->dev->id.revision > 0) &&
+		    !(chipco_read32(cc, SSB_CHIPCO_CORECTL) & SSB_CHIPCO_CORECTL_UARTCLK0)) {
+			if ((cc->capabilities & SSB_CHIPCO_CAP_UARTCLK) ==
+			    SSB_CHIPCO_CAP_UARTCLK_INT) {
+				/* Internal divided backplane clock */
+				baud_base /= div;
+			} else {
+				/* Assume external clock of 1.8432 MHz */
+				baud_base = 1843200;
+			}
+		}
+	}
+
+	/* Determine the registers of the UARTs */
+	n = (cc->capabilities & SSB_CHIPCO_CAP_NRUART);
+	for (i = 0; i < n; i++) {
+		void __iomem *cc_mmio;
+		void __iomem *uart_regs;
+
+		cc_mmio = cc->dev->bus->mmio + (cc->dev->core_index * SSB_CORE_SIZE);
+		uart_regs = cc_mmio + SSB_CHIPCO_UART0_DATA;
+		/* Offset changed at after rev 0 */
+		if (cc->dev->id.revision == 0)
+			uart_regs += (i * 8);
+		else
+			uart_regs += (i * 256);
+
+		nr_ports++;
+		ports[i].regs = uart_regs;
+		ports[i].irq = irq;
+		ports[i].baud_base = baud_base;
+		ports[i].reg_shift = 0;
+	}
+
+	return nr_ports;
+}
+#endif /* CONFIG_SSB_SERIAL */
diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c
new file mode 100644
index 0000000..3f09598
--- /dev/null
+++ b/drivers/ssb/driver_mipscore.c
@@ -0,0 +1,258 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom MIPS core driver
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+
+#include "ssb_private.h"
+
+
+static inline u32 mips_read32(struct ssb_mipscore *mcore,
+			      u16 offset)
+{
+	return ssb_read32(mcore->dev, offset);
+}
+
+static inline void mips_write32(struct ssb_mipscore *mcore,
+				u16 offset,
+				u32 value)
+{
+	ssb_write32(mcore->dev, offset, value);
+}
+
+static const u32 ipsflag_irq_mask[] = {
+	0,
+	SSB_IPSFLAG_IRQ1,
+	SSB_IPSFLAG_IRQ2,
+	SSB_IPSFLAG_IRQ3,
+	SSB_IPSFLAG_IRQ4,
+};
+
+static const u32 ipsflag_irq_shift[] = {
+	0,
+	SSB_IPSFLAG_IRQ1_SHIFT,
+	SSB_IPSFLAG_IRQ2_SHIFT,
+	SSB_IPSFLAG_IRQ3_SHIFT,
+	SSB_IPSFLAG_IRQ4_SHIFT,
+};
+
+static inline u32 ssb_irqflag(struct ssb_device *dev)
+{
+	return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG;
+}
+
+/* Get the MIPS IRQ assignment for a specified device.
+ * If unassigned, 0 is returned.
+ */
+unsigned int ssb_mips_irq(struct ssb_device *dev)
+{
+	struct ssb_bus *bus = dev->bus;
+	u32 irqflag;
+	u32 ipsflag;
+	u32 tmp;
+	unsigned int irq;
+
+	irqflag = ssb_irqflag(dev);
+	ipsflag = ssb_read32(bus->mipscore.dev, SSB_IPSFLAG);
+	for (irq = 1; irq <= 4; irq++) {
+		tmp = ((ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]);
+		if (tmp == irqflag)
+			break;
+	}
+	if (irq	== 5)
+		irq = 0;
+
+	return irq;
+}
+
+static void clear_irq(struct ssb_bus *bus, unsigned int irq)
+{
+	struct ssb_device *dev = bus->mipscore.dev;
+
+	/* Clear the IRQ in the MIPScore backplane registers */
+	if (irq == 0) {
+		ssb_write32(dev, SSB_INTVEC, 0);
+	} else {
+		ssb_write32(dev, SSB_IPSFLAG,
+			    ssb_read32(dev, SSB_IPSFLAG) |
+			    ipsflag_irq_mask[irq]);
+	}
+}
+
+static void set_irq(struct ssb_device *dev, unsigned int irq)
+{
+	unsigned int oldirq = ssb_mips_irq(dev);
+	struct ssb_bus *bus = dev->bus;
+	struct ssb_device *mdev = bus->mipscore.dev;
+	u32 irqflag = ssb_irqflag(dev);
+
+	dev->irq = irq + 2;
+
+	ssb_dprintk(KERN_INFO PFX
+		    "set_irq: core 0x%04x, irq %d => %d\n",
+		    dev->id.coreid, oldirq, irq);
+	/* clear the old irq */
+	if (oldirq == 0)
+		ssb_write32(mdev, SSB_INTVEC, (~(1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)));
+	else
+		clear_irq(bus, oldirq);
+
+	/* assign the new one */
+	if (irq == 0)
+		ssb_write32(mdev, SSB_INTVEC, ((1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)));
+
+	irqflag <<= ipsflag_irq_shift[irq];
+	irqflag |= (ssb_read32(mdev, SSB_IPSFLAG) & ~ipsflag_irq_mask[irq]);
+	ssb_write32(mdev, SSB_IPSFLAG, irqflag);
+}
+
+/* XXX: leave here or move into separate extif driver? */
+static int ssb_extif_serial_init(struct ssb_device *dev, struct ssb_serial_ports *ports)
+{
+
+}
+
+
+static void ssb_mips_serial_init(struct ssb_mipscore *mcore)
+{
+	struct ssb_bus *bus = mcore->dev->bus;
+
+	//TODO if (EXTIF available
+#if 0
+		extifregs_t *eir = (extifregs_t *) regs;
+		sbconfig_t *sb;
+
+		/* Determine external UART register base */
+		sb = (sbconfig_t *)((ulong) eir + SBCONFIGOFF);
+		base = EXTIF_CFGIF_BASE(sb_base(R_REG(&sb->sbadmatch1)));
+
+		/* Determine IRQ */
+		irq = sb_irq(sbh);
+
+		/* Disable GPIO interrupt initially */
+		W_REG(&eir->gpiointpolarity, 0);
+		W_REG(&eir->gpiointmask, 0);
+
+		/* Search for external UARTs */
+		n = 2;
+		for (i = 0; i < 2; i++) {
+			regs = (void *) REG_MAP(base + (i * 8), 8);
+			if (BCMINIT(serial_exists)(regs)) {
+				/* Set GPIO 1 to be the external UART IRQ */
+				W_REG(&eir->gpiointmask, 2);
+				if (add)
+					add(regs, irq, 13500000, 0);
+			}
+		}
+
+		/* Add internal UART if enabled */
+		if (R_REG(&eir->corecontrol) & CC_UE)
+			if (add)
+				add((void *) &eir->uartdata, irq, sb_clock(sbh), 2);
+
+#endif
+	if (bus->extif.dev)
+		mcore->nr_serial_ports = ssb_extif_serial_init(&bus->extif, mcore->serial_ports);
+	else if (bus->chipco.dev)
+		mcore->nr_serial_ports = ssb_chipco_serial_init(&bus->chipco, mcore->serial_ports);
+	else
+		mcore->nr_serial_ports = 0;
+}
+
+static void ssb_mips_flash_detect(struct ssb_mipscore *mcore)
+{
+	struct ssb_bus *bus = mcore->dev->bus;
+
+	if (bus->chipco.dev) {
+		mcore->flash_window = 0x1c000000;
+		mcore->flash_window_size = 0x800000;
+	} else {
+		mcore->flash_window = 0x1fc00000;
+		mcore->flash_window_size = 0x400000;
+	}
+}
+
+
+static void ssb_cpu_clock(struct ssb_mipscore *mcore)
+{
+}
+
+void ssb_mipscore_init(struct ssb_mipscore *mcore)
+{
+	struct ssb_bus *bus = mcore->dev->bus;
+	struct ssb_device *dev;
+	unsigned long hz, ns;
+	unsigned int irq, i;
+
+	if (!mcore->dev)
+		return; /* We don't have a MIPS core */
+
+	ssb_dprintk(KERN_INFO PFX "Initializing MIPS core...\n");
+
+	hz = ssb_clockspeed(bus);
+	if (!hz)
+		hz = 100000000;
+	ns = 1000000000 / hz;
+
+//TODO
+#if 0
+	if (have EXTIF) {
+		/* Initialize extif so we can get to the LEDs and external UART */
+		W_REG(&eir->prog_config, CF_EN);
+
+		/* Set timing for the flash */
+		tmp = CEIL(10, ns) << FW_W3_SHIFT;	/* W3 = 10nS */
+		tmp = tmp | (CEIL(40, ns) << FW_W1_SHIFT); /* W1 = 40nS */
+		tmp = tmp | CEIL(120, ns);		/* W0 = 120nS */
+		W_REG(&eir->prog_waitcount, tmp);	/* 0x01020a0c for a 100Mhz clock */
+
+		/* Set programmable interface timing for external uart */
+		tmp = CEIL(10, ns) << FW_W3_SHIFT;	/* W3 = 10nS */
+		tmp = tmp | (CEIL(20, ns) << FW_W2_SHIFT); /* W2 = 20nS */
+		tmp = tmp | (CEIL(100, ns) << FW_W1_SHIFT); /* W1 = 100nS */
+		tmp = tmp | CEIL(120, ns);		/* W0 = 120nS */
+		W_REG(&eir->prog_waitcount, tmp);
+	}
+#endif
+	if (bus->chipco.dev)
+		ssb_chipco_timing_init(&bus->chipco, ns);
+
+	/* Assign IRQs to all cores on the bus, start with irq line 2, because serial usually takes 1 */
+	for (irq = 2, i = 0; i < bus->nr_devices; i++) {
+		dev = &(bus->devices[i]);
+		dev->irq = ssb_mips_irq(dev) + 2;
+		switch (dev->id.coreid) {
+		case SSB_DEV_USB11_HOST:
+			/* shouldn't need a separate irq line for non-4710, most of them have a proper
+			 * external usb controller on the pci */
+			if ((bus->chip_id == 0x4710) && (irq <= 4)) {
+				set_irq(dev, irq++);
+				break;
+			}
+			/* fallthrough */
+		case SSB_DEV_PCI:
+		case SSB_DEV_ETHERNET:
+		case SSB_DEV_80211:
+		case SSB_DEV_USB20_HOST:
+			/* These devices get their own IRQ line if available, the rest goes on IRQ0 */
+			if (irq <= 4) {
+				set_irq(dev, irq++);
+				break;
+			}
+		}
+	}
+
+	ssb_mips_serial_init(mcore);
+	ssb_mips_flash_detect(mcore);
+}
diff --git a/drivers/ssb/driver_pcicore.c b/drivers/ssb/driver_pcicore.c
new file mode 100644
index 0000000..e11b103
--- /dev/null
+++ b/drivers/ssb/driver_pcicore.c
@@ -0,0 +1,568 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom PCI-core driver
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "ssb_private.h"
+
+
+static inline
+u32 pcicore_read32(struct ssb_pcicore *pc, u16 offset)
+{
+	return ssb_read32(pc->dev, offset);
+}
+
+static inline
+void pcicore_write32(struct ssb_pcicore *pc, u16 offset, u32 value)
+{
+	ssb_write32(pc->dev, offset, value);
+}
+
+/**************************************************
+ * Code for hostmode operation.
+ **************************************************/
+
+#ifdef CONFIG_SSB_PCICORE_HOSTMODE
+
+#include <asm/paccess.h>
+/* Probe a 32bit value on the bus and catch bus exceptions.
+ * Returns nonzero on a bus exception.
+ * This is MIPS specific */
+#define mips_busprobe32(val, addr)	get_dbe((val), ((u32 *)(addr)))
+
+/* Assume one-hot slot wiring */
+#define SSB_PCI_SLOT_MAX	16
+
+/* Global lock is OK, as we won't have more than one extpci anyway. */
+static DEFINE_SPINLOCK(cfgspace_lock);
+/* 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;
+		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)
+{
+	if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)
+		return;
+
+	ssb_printk(KERN_INFO "PCI: fixing up bridge\n");
+
+	/* Enable PCI bridge bus mastering and memory space */
+	pci_set_master(dev);
+	pcibios_enable_device(dev, ~0);
+
+	/* Enable PCI bridge BAR1 prefetch and burst */
+	pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3);
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_fixup_pcibridge);
+
+int __init pcibios_map_irq(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)
+{
+	u32 addr = 0;
+	u32 tmp;
+
+	if (unlikely(pc->cardbusmode && dev > 1))
+		goto out;
+	if (bus == 0) {
+		/* Type 0 transaction */
+		if (unlikely(dev >= SSB_PCI_SLOT_MAX))
+			goto out;
+		/* Slide the window */
+		tmp = SSB_PCICORE_SBTOPCI_CFG0;
+		tmp |= ((1 << (dev + 16)) & SSB_PCICORE_SBTOPCI1_MASK);
+		pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, tmp);
+		/* Calculate the address */
+		addr = SSB_PCI_CFG;
+		addr |= ((1 << (dev + 16)) & ~SSB_PCICORE_SBTOPCI1_MASK);
+		addr |= (func << 8);
+		addr |= (off & ~3);
+	} else {
+		/* Type 1 transaction */
+		pcicore_write32(pc, SSB_PCICORE_SBTOPCI1,
+				SSB_PCICORE_SBTOPCI_CFG1);
+		/* Calculate the address */
+		addr = SSB_PCI_CFG;
+		addr |= (bus << 16);
+		addr |= (dev << 11);
+		addr |= (func << 8);
+		addr |= (off & ~3);
+	}
+out:
+	return addr;
+}
+
+static int ssb_extpci_read_config(struct ssb_pcicore *pc,
+				  unsigned int bus, unsigned int dev,
+				  unsigned int func, unsigned int off,
+				  void *buf, int len)
+{
+	int err = -EINVAL;
+	u32 addr, val;
+	void __iomem *mmio;
+
+	SSB_WARN_ON(!pc->hostmode);
+	if (unlikely(len != 1 && len != 2 && len != 4))
+		goto out;
+	addr = get_cfgspace_addr(pc, bus, dev, func, off);
+	if (unlikely(!addr))
+		goto out;
+	err = -ENOMEM;
+	mmio = ioremap_nocache(addr, len);
+	if (!mmio)
+		goto out;
+
+	if (mips_busprobe32(val, mmio)) {
+		val = 0xffffffff;
+		goto unmap;
+	}
+
+	val = readl(mmio);
+	val >>= (8 * (off & 3));
+
+	switch (len) {
+	case 1:
+		*((u8 *)buf) = (u8)val;
+		break;
+	case 2:
+		*((u16 *)buf) = (u16)val;
+		break;
+	case 4:
+		*((u32 *)buf) = (u32)val;
+		break;
+	}
+	err = 0;
+unmap:
+	iounmap(mmio);
+out:
+	return err;
+}
+
+static int ssb_extpci_write_config(struct ssb_pcicore *pc,
+				   unsigned int bus, unsigned int dev,
+				   unsigned int func, unsigned int off,
+				   const void *buf, int len)
+{
+	int err = -EINVAL;
+	u32 addr, val = 0;
+	void __iomem *mmio;
+
+	SSB_WARN_ON(!pc->hostmode);
+	if (unlikely(len != 1 && len != 2 && len != 4))
+		goto out;
+	addr = get_cfgspace_addr(pc, bus, dev, func, off);
+	if (unlikely(!addr))
+		goto out;
+	err = -ENOMEM;
+	mmio = ioremap_nocache(addr, len);
+	if (!mmio)
+		goto out;
+
+	if (mips_busprobe32(val, mmio)) {
+		val = 0xffffffff;
+		goto unmap;
+	}
+
+	switch (len) {
+	case 1:
+		val = readl(mmio);
+		val &= ~(0xFF << (8 * (off & 3)));
+		val |= *((const u8 *)buf) << (8 * (off & 3));
+		break;
+	case 2:
+		val = readl(mmio);
+		val &= ~(0xFFFF << (8 * (off & 3)));
+		val |= *((const u16 *)buf) << (8 * (off & 3));
+		break;
+	case 4:
+		val = *((const u32 *)buf);
+		break;
+	}
+	writel(*((const u32 *)buf), mmio);
+
+	err = 0;
+unmap:
+	iounmap(mmio);
+out:
+	return err;
+}
+
+static int ssb_pcicore_read_config(struct pci_bus *bus, unsigned int devfn,
+				   int reg, int size, u32 *val)
+{
+	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&cfgspace_lock, flags);
+	err = ssb_extpci_read_config(extpci_core, bus->number, PCI_SLOT(devfn),
+				     PCI_FUNC(devfn), reg, val, size);
+	spin_unlock_irqrestore(&cfgspace_lock, flags);
+
+	return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
+}
+
+static int ssb_pcicore_write_config(struct pci_bus *bus, unsigned int devfn,
+				    int reg, int size, u32 val)
+{
+	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&cfgspace_lock, flags);
+	err = ssb_extpci_write_config(extpci_core, bus->number, PCI_SLOT(devfn),
+				      PCI_FUNC(devfn), reg, &val, size);
+	spin_unlock_irqrestore(&cfgspace_lock, flags);
+
+	return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops ssb_pcicore_pciops = {
+	.read	= ssb_pcicore_read_config,
+	.write	= ssb_pcicore_write_config,
+};
+
+static struct resource ssb_pcicore_mem_resource = {
+	.name	= "SSB PCIcore external memory",
+	.start	= SSB_PCI_DMA,
+	.end	= SSB_PCI_DMA + SSB_PCI_DMA_SZ - 1,
+	.flags	= IORESOURCE_MEM,
+};
+
+static struct resource ssb_pcicore_io_resource = {
+	.name	= "SSB PCIcore external I/O",
+	.start	= 0x100,
+	.end	= 0x7FF,
+	.flags	= IORESOURCE_IO,
+};
+
+static struct pci_controller ssb_pcicore_controller = {
+	.pci_ops	= &ssb_pcicore_pciops,
+	.io_resource	= &ssb_pcicore_io_resource,
+	.mem_resource	= &ssb_pcicore_mem_resource,
+	.mem_offset	= 0x24000000,
+};
+
+static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
+{
+	u32 val;
+
+	if (WARN_ON(extpci_core))
+		return;
+	extpci_core = pc;
+
+	ssb_dprintk(KERN_INFO PFX "PCIcore in host mode found\n");
+	/* Reset devices on the external PCI bus */
+	val = SSB_PCICORE_CTL_RST_OE;
+	val |= SSB_PCICORE_CTL_CLK_OE;
+	pcicore_write32(pc, SSB_PCICORE_CTL, val);
+	val |= SSB_PCICORE_CTL_CLK; /* Clock on */
+	pcicore_write32(pc, SSB_PCICORE_CTL, val);
+	udelay(150); /* Assertion time demanded by the PCI standard */
+	val |= SSB_PCICORE_CTL_RST; /* Deassert RST# */
+	pcicore_write32(pc, SSB_PCICORE_CTL, val);
+	udelay(1); /* Assertion time demanded by the PCI standard */
+
+	/*TODO cardbus mode */
+
+	/* 64MB I/O window */
+	pcicore_write32(pc, SSB_PCICORE_SBTOPCI0,
+			SSB_PCICORE_SBTOPCI_IO);
+	/* 64MB config space */
+	pcicore_write32(pc, SSB_PCICORE_SBTOPCI1,
+			SSB_PCICORE_SBTOPCI_CFG0);
+	/* 1GB memory window */
+	pcicore_write32(pc, SSB_PCICORE_SBTOPCI2,
+			SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA);
+
+	/* Enable PCI bridge BAR0 prefetch and burst */
+	val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+	ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2);
+	/* Clear error conditions */
+	val = 0;
+	ssb_extpci_write_config(pc, 0, 0, 0, PCI_STATUS, &val, 2);
+
+	/* Enable PCI interrupts */
+	pcicore_write32(pc, SSB_PCICORE_IMASK,
+			SSB_PCICORE_IMASK_INTA);
+
+	/* Ok, ready to run, register it to the system.
+	 * The following needs change, if we want to port hostmode
+	 * to non-MIPS platform. */
+	set_io_port_base((unsigned long)ioremap_nocache(SSB_PCI_MEM, 0x04000000));
+	register_pci_controller(&ssb_pcicore_controller);
+}
+
+static int pcicore_is_in_hostmode(struct ssb_pcicore *pc)
+{
+	struct ssb_bus *bus = pc->dev->bus;
+	u16 chipid_top;
+	u32 tmp;
+
+	chipid_top = (bus->chip_id & 0xFF00);
+	if (chipid_top != 0x4700 &&
+	    chipid_top != 0x5300)
+		return 0;
+
+	if (bus->sprom.r1.boardflags_lo & SSB_PCICORE_BFL_NOPCI)
+		return 0;
+
+	/* The 200-pin BCM4712 package does not bond out PCI. Even when
+	 * PCI is bonded out, some boards may leave the pins floating. */
+	if (bus->chip_id == 0x4712) {
+		if (bus->chip_package == SSB_CHIPPACK_BCM4712S)
+			return 0;
+		if (bus->chip_package == SSB_CHIPPACK_BCM4712M)
+			return 0;
+	}
+	if (bus->chip_id == 0x5350)
+		return 0;
+
+	return !mips_busprobe32(tmp, (bus->mmio + (pc->dev->core_index * SSB_CORE_SIZE)));
+}
+#endif /* CONFIG_SSB_PCICORE_HOSTMODE */
+
+
+/**************************************************
+ * Generic and Clientmode operation code.
+ **************************************************/
+
+static void ssb_pcicore_init_clientmode(struct ssb_pcicore *pc)
+{
+	/* Disable PCI interrupts. */
+	ssb_write32(pc->dev, SSB_INTVEC, 0);
+}
+
+void ssb_pcicore_init(struct ssb_pcicore *pc)
+{
+	struct ssb_device *dev = pc->dev;
+	struct ssb_bus *bus;
+
+	if (!dev)
+		return;
+	bus = dev->bus;
+	if (!ssb_device_is_enabled(dev))
+		ssb_device_enable(dev, 0);
+
+#ifdef CONFIG_SSB_PCICORE_HOSTMODE
+	pc->hostmode = pcicore_is_in_hostmode(pc);
+	if (pc->hostmode)
+		ssb_pcicore_init_hostmode(pc);
+#endif /* CONFIG_SSB_PCICORE_HOSTMODE */
+	if (!pc->hostmode)
+		ssb_pcicore_init_clientmode(pc);
+}
+
+static u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address)
+{
+	pcicore_write32(pc, 0x130, address);
+	return pcicore_read32(pc, 0x134);
+}
+
+static void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data)
+{
+	pcicore_write32(pc, 0x130, address);
+	pcicore_write32(pc, 0x134, data);
+}
+
+static void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device,
+				u8 address, u16 data)
+{
+	const u16 mdio_control = 0x128;
+	const u16 mdio_data = 0x12C;
+	u32 v;
+	int i;
+
+	v = 0x80; /* Enable Preamble Sequence */
+	v |= 0x2; /* MDIO Clock Divisor */
+	pcicore_write32(pc, mdio_control, v);
+
+	v = (1 << 30); /* Start of Transaction */
+	v |= (1 << 28); /* Write Transaction */
+	v |= (1 << 17); /* Turnaround */
+	v |= (u32)device << 22;
+	v |= (u32)address << 18;
+	v |= data;
+	pcicore_write32(pc, mdio_data, v);
+	/* Wait for the device to complete the transaction */
+	udelay(10);
+	for (i = 0; i < 10; i++) {
+		v = pcicore_read32(pc, mdio_control);
+		if (v & 0x100 /* Trans complete */)
+			break;
+		msleep(1);
+	}
+	pcicore_write32(pc, mdio_control, 0);
+}
+
+static void ssb_broadcast_value(struct ssb_device *dev,
+				u32 address, u32 data)
+{
+	/* This is used for both, PCI and ChipCommon core, so be careful. */
+	BUILD_BUG_ON(SSB_PCICORE_BCAST_ADDR != SSB_CHIPCO_BCAST_ADDR);
+	BUILD_BUG_ON(SSB_PCICORE_BCAST_DATA != SSB_CHIPCO_BCAST_DATA);
+
+	ssb_write32(dev, SSB_PCICORE_BCAST_ADDR, address);
+	ssb_read32(dev, SSB_PCICORE_BCAST_ADDR); /* flush */
+	ssb_write32(dev, SSB_PCICORE_BCAST_DATA, data);
+	ssb_read32(dev, SSB_PCICORE_BCAST_DATA); /* flush */
+}
+
+static void ssb_commit_settings(struct ssb_bus *bus)
+{
+	struct ssb_device *dev;
+
+	dev = bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev;
+	if (WARN_ON(!dev))
+		return;
+	/* This forces an update of the cached registers. */
+	ssb_broadcast_value(dev, 0xFD8, 0);
+}
+
+int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
+				   struct ssb_device *dev)
+{
+	struct ssb_device *pdev = pc->dev;
+	struct ssb_bus *bus;
+	int err = 0;
+	u32 tmp;
+
+	might_sleep();
+
+	if (!pdev)
+		goto out;
+	bus = pdev->bus;
+
+	/* Enable interrupts for this device. */
+	if (bus->host_pci &&
+	    ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE))) {
+		u32 coremask;
+
+		/* Calculate the "coremask" for the device. */
+		coremask = (1 << dev->core_index);
+
+		err = pci_read_config_dword(bus->host_pci, SSB_PCI_IRQMASK, &tmp);
+		if (err)
+			goto out;
+		tmp |= coremask << 8;
+		err = pci_write_config_dword(bus->host_pci, SSB_PCI_IRQMASK, tmp);
+		if (err)
+			goto out;
+	} else {
+		u32 intvec;
+
+		intvec = ssb_read32(pdev, SSB_INTVEC);
+		if ((bus->chip_id & 0xFF00) == 0x4400) {
+			/* Workaround: On the BCM44XX the BPFLAG routing
+			 * bit is wrong. Use a hardcoded constant. */
+			intvec |= 0x00000002;
+		} else {
+			tmp = ssb_read32(dev, SSB_TPSFLAG);
+			tmp &= SSB_TPSFLAG_BPFLAG;
+			intvec |= tmp;
+		}
+		ssb_write32(pdev, SSB_INTVEC, intvec);
+	}
+
+	/* Setup PCIcore operation. */
+	if (pc->setup_done)
+		goto out;
+	if (pdev->id.coreid == SSB_DEV_PCI) {
+		tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2);
+		tmp |= SSB_PCICORE_SBTOPCI_PREF;
+		tmp |= SSB_PCICORE_SBTOPCI_BURST;
+		pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp);
+
+		if (pdev->id.revision < 5) {
+			tmp = ssb_read32(pdev, SSB_IMCFGLO);
+			tmp &= ~SSB_IMCFGLO_SERTO;
+			tmp |= 2;
+			tmp &= ~SSB_IMCFGLO_REQTO;
+			tmp |= 3 << SSB_IMCFGLO_REQTO_SHIFT;
+			ssb_write32(pdev, SSB_IMCFGLO, tmp);
+			ssb_commit_settings(bus);
+		} else if (pdev->id.revision >= 11) {
+			tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2);
+			tmp |= SSB_PCICORE_SBTOPCI_MRM;
+			pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp);
+		}
+	} else {
+		WARN_ON(pdev->id.coreid != SSB_DEV_PCIE);
+		//TODO: Better make defines for all these magic PCIE values.
+		if ((pdev->id.revision == 0) || (pdev->id.revision == 1)) {
+			/* TLP Workaround register. */
+			tmp = ssb_pcie_read(pc, 0x4);
+			tmp |= 0x8;
+			ssb_pcie_write(pc, 0x4, tmp);
+		}
+		if (pdev->id.revision == 0) {
+			const u8 serdes_rx_device = 0x1F;
+
+			ssb_pcie_mdio_write(pc, serdes_rx_device,
+					    2 /* Timer */, 0x8128);
+			ssb_pcie_mdio_write(pc, serdes_rx_device,
+					    6 /* CDR */, 0x0100);
+			ssb_pcie_mdio_write(pc, serdes_rx_device,
+					    7 /* CDR BW */, 0x1466);
+		} else if (pdev->id.revision == 1) {
+			/* DLLP Link Control register. */
+			tmp = ssb_pcie_read(pc, 0x100);
+			tmp |= 0x40;
+			ssb_pcie_write(pc, 0x100, tmp);
+		}
+	}
+	pc->setup_done = 1;
+out:
+	return err;
+}
+EXPORT_SYMBOL(ssb_pcicore_dev_irqvecs_enable);
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
new file mode 100644
index 0000000..cfe090f
--- /dev/null
+++ b/drivers/ssb/main.c
@@ -0,0 +1,1112 @@
+/*
+ * Sonics Silicon Backplane
+ * Subsystem core
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "ssb_private.h"
+
+#include <linux/delay.h>
+#include <linux/ssb/ssb.h>
+#include <linux/ssb/ssb_regs.h>
+
+#include <linux/pci.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+
+MODULE_DESCRIPTION("Sonics Silicon Backplane driver");
+MODULE_LICENSE("GPL");
+
+
+/* Temporary list of yet-to-be-attached buses */
+static LIST_HEAD(attach_queue);
+/* List if running buses */
+static LIST_HEAD(buses);
+/* Software ID counter */
+static unsigned int next_busnumber;
+/* buses_mutes locks the two buslists and the next_busnumber.
+ * Don't lock this directly, but use ssb_buses_[un]lock() below. */
+static DEFINE_MUTEX(buses_mutex);
+
+/* There are differences in the codeflow, if the bus is
+ * initialized from early boot, as various needed services
+ * are not available early. This is a mechanism to delay
+ * these initializations to after early boot has finished.
+ * It's also used to avoid mutex locking, as that's not
+ * available and needed early. */
+static bool ssb_is_early_boot = 1;
+
+static void ssb_buses_lock(void);
+static void ssb_buses_unlock(void);
+
+
+#ifdef CONFIG_SSB_PCIHOST
+struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev)
+{
+	struct ssb_bus *bus;
+
+	ssb_buses_lock();
+	list_for_each_entry(bus, &buses, list) {
+		if (bus->bustype == SSB_BUSTYPE_PCI &&
+		    bus->host_pci == pdev)
+			goto found;
+	}
+	bus = NULL;
+found:
+	ssb_buses_unlock();
+
+	return bus;
+}
+#endif /* CONFIG_SSB_PCIHOST */
+
+static struct ssb_device *ssb_device_get(struct ssb_device *dev)
+{
+	if (dev)
+		get_device(dev->dev);
+	return dev;
+}
+
+static void ssb_device_put(struct ssb_device *dev)
+{
+	if (dev)
+		put_device(dev->dev);
+}
+
+static int ssb_bus_resume(struct ssb_bus *bus)
+{
+	int err;
+
+	ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1);
+	err = ssb_pcmcia_init(bus);
+	if (err) {
+		/* No need to disable XTAL, as we don't have one on PCMCIA. */
+		return err;
+	}
+	ssb_chipco_resume(&bus->chipco);
+
+	return 0;
+}
+
+static int ssb_device_resume(struct device *dev)
+{
+	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+	struct ssb_driver *ssb_drv;
+	struct ssb_bus *bus;
+	int err = 0;
+
+	bus = ssb_dev->bus;
+	if (bus->suspend_cnt == bus->nr_devices) {
+		err = ssb_bus_resume(bus);
+		if (err)
+			return err;
+	}
+	bus->suspend_cnt--;
+	if (dev->driver) {
+		ssb_drv = drv_to_ssb_drv(dev->driver);
+		if (ssb_drv && ssb_drv->resume)
+			err = ssb_drv->resume(ssb_dev);
+		if (err)
+			goto out;
+	}
+out:
+	return err;
+}
+
+static void ssb_bus_suspend(struct ssb_bus *bus, pm_message_t state)
+{
+	ssb_chipco_suspend(&bus->chipco, state);
+	ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);
+
+	/* Reset HW state information in memory, so that HW is
+	 * completely reinitialized on resume. */
+	bus->mapped_device = NULL;
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+	bus->pcicore.setup_done = 0;
+#endif
+}
+
+static int ssb_device_suspend(struct device *dev, pm_message_t state)
+{
+	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+	struct ssb_driver *ssb_drv;
+	struct ssb_bus *bus;
+	int err = 0;
+
+	if (dev->driver) {
+		ssb_drv = drv_to_ssb_drv(dev->driver);
+		if (ssb_drv && ssb_drv->suspend)
+			err = ssb_drv->suspend(ssb_dev, state);
+		if (err)
+			goto out;
+	}
+
+	bus = ssb_dev->bus;
+	bus->suspend_cnt++;
+	if (bus->suspend_cnt == bus->nr_devices) {
+		/* All devices suspended. Shutdown the bus. */
+		ssb_bus_suspend(bus, state);
+	}
+
+out:
+	return err;
+}
+
+#ifdef CONFIG_SSB_PCIHOST
+int ssb_devices_freeze(struct ssb_bus *bus)
+{
+	struct ssb_device *dev;
+	struct ssb_driver *drv;
+	int err = 0;
+	int i;
+	pm_message_t state = PMSG_FREEZE;
+
+	/* First check that we are capable to freeze all devices. */
+	for (i = 0; i < bus->nr_devices; i++) {
+		dev = &(bus->devices[i]);
+		if (!dev->dev ||
+		    !dev->dev->driver ||
+		    !device_is_registered(dev->dev))
+			continue;
+		drv = drv_to_ssb_drv(dev->dev->driver);
+		if (!drv)
+			continue;
+		if (!drv->suspend) {
+			/* Nope, can't suspend this one. */
+			return -EOPNOTSUPP;
+		}
+	}
+	/* Now suspend all devices */
+	for (i = 0; i < bus->nr_devices; i++) {
+		dev = &(bus->devices[i]);
+		if (!dev->dev ||
+		    !dev->dev->driver ||
+		    !device_is_registered(dev->dev))
+			continue;
+		drv = drv_to_ssb_drv(dev->dev->driver);
+		if (!drv)
+			continue;
+		err = drv->suspend(dev, state);
+		if (err) {
+			ssb_printk(KERN_ERR PFX "Failed to freeze device %s\n",
+				   dev->dev->bus_id);
+			goto err_unwind;
+		}
+	}
+
+	return 0;
+err_unwind:
+	for (i--; i >= 0; i--) {
+		dev = &(bus->devices[i]);
+		if (!dev->dev ||
+		    !dev->dev->driver ||
+		    !device_is_registered(dev->dev))
+			continue;
+		drv = drv_to_ssb_drv(dev->dev->driver);
+		if (!drv)
+			continue;
+		if (drv->resume)
+			drv->resume(dev);
+	}
+	return err;
+}
+
+int ssb_devices_thaw(struct ssb_bus *bus)
+{
+	struct ssb_device *dev;
+	struct ssb_driver *drv;
+	int err;
+	int i;
+
+	for (i = 0; i < bus->nr_devices; i++) {
+		dev = &(bus->devices[i]);
+		if (!dev->dev ||
+		    !dev->dev->driver ||
+		    !device_is_registered(dev->dev))
+			continue;
+		drv = drv_to_ssb_drv(dev->dev->driver);
+		if (!drv)
+			continue;
+		if (SSB_WARN_ON(!drv->resume))
+			continue;
+		err = drv->resume(dev);
+		if (err) {
+			ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n",
+				   dev->dev->bus_id);
+		}
+	}
+
+	return 0;
+}
+#endif /* CONFIG_SSB_PCIHOST */
+
+static void ssb_device_shutdown(struct device *dev)
+{
+	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+	struct ssb_driver *ssb_drv;
+
+	if (!dev->driver)
+		return;
+	ssb_drv = drv_to_ssb_drv(dev->driver);
+	if (ssb_drv && ssb_drv->shutdown)
+		ssb_drv->shutdown(ssb_dev);
+}
+
+static int ssb_device_remove(struct device *dev)
+{
+	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+	struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver);
+
+	if (ssb_drv && ssb_drv->remove)
+		ssb_drv->remove(ssb_dev);
+	ssb_device_put(ssb_dev);
+
+	return 0;
+}
+
+static int ssb_device_probe(struct device *dev)
+{
+	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+	struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver);
+	int err = 0;
+
+	ssb_device_get(ssb_dev);
+	if (ssb_drv && ssb_drv->probe)
+		err = ssb_drv->probe(ssb_dev, &ssb_dev->id);
+	if (err)
+		ssb_device_put(ssb_dev);
+
+	return err;
+}
+
+static int ssb_match_devid(const struct ssb_device_id *tabid,
+			   const struct ssb_device_id *devid)
+{
+	if ((tabid->vendor != devid->vendor) &&
+	    tabid->vendor != SSB_ANY_VENDOR)
+		return 0;
+	if ((tabid->coreid != devid->coreid) &&
+	    tabid->coreid != SSB_ANY_ID)
+		return 0;
+	if ((tabid->revision != devid->revision) &&
+	    tabid->revision != SSB_ANY_REV)
+		return 0;
+	return 1;
+}
+
+static int ssb_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+	struct ssb_driver *ssb_drv = drv_to_ssb_drv(drv);
+	const struct ssb_device_id *id;
+
+	for (id = ssb_drv->id_table;
+	     id->vendor || id->coreid || id->revision;
+	     id++) {
+		if (ssb_match_devid(id, &ssb_dev->id))
+			return 1; /* found */
+	}
+
+	return 0;
+}
+
+static struct bus_type ssb_bustype = {
+	.name		= "ssb",
+	.match		= ssb_bus_match,
+	.probe		= ssb_device_probe,
+	.remove		= ssb_device_remove,
+	.shutdown	= ssb_device_shutdown,
+	.suspend	= ssb_device_suspend,
+	.resume		= ssb_device_resume,
+};
+
+static void ssb_buses_lock(void)
+{
+	/* See the comment at the ssb_is_early_boot definition */
+	if (!ssb_is_early_boot)
+		mutex_lock(&buses_mutex);
+}
+
+static void ssb_buses_unlock(void)
+{
+	/* See the comment at the ssb_is_early_boot definition */
+	if (!ssb_is_early_boot)
+		mutex_unlock(&buses_mutex);
+}
+
+static void ssb_devices_unregister(struct ssb_bus *bus)
+{
+	struct ssb_device *sdev;
+	int i;
+
+	for (i = bus->nr_devices - 1; i >= 0; i--) {
+		sdev = &(bus->devices[i]);
+		if (sdev->dev)
+			device_unregister(sdev->dev);
+	}
+}
+
+void ssb_bus_unregister(struct ssb_bus *bus)
+{
+	ssb_buses_lock();
+	ssb_devices_unregister(bus);
+	list_del(&bus->list);
+	ssb_buses_unlock();
+
+	/* ssb_pcmcia_exit(bus); */
+	ssb_pci_exit(bus);
+	ssb_iounmap(bus);
+}
+EXPORT_SYMBOL(ssb_bus_unregister);
+
+static void ssb_release_dev(struct device *dev)
+{
+	struct __ssb_dev_wrapper *devwrap;
+
+	devwrap = container_of(dev, struct __ssb_dev_wrapper, dev);
+	kfree(devwrap);
+}
+
+static int ssb_devices_register(struct ssb_bus *bus)
+{
+	struct ssb_device *sdev;
+	struct device *dev;
+	struct __ssb_dev_wrapper *devwrap;
+	int i, err = 0;
+	int dev_idx = 0;
+
+	for (i = 0; i < bus->nr_devices; i++) {
+		sdev = &(bus->devices[i]);
+
+		/* We don't register SSB-system devices to the kernel,
+		 * as the drivers for them are built into SSB. */
+		switch (sdev->id.coreid) {
+		case SSB_DEV_CHIPCOMMON:
+		case SSB_DEV_PCI:
+		case SSB_DEV_PCIE:
+		case SSB_DEV_PCMCIA:
+		case SSB_DEV_MIPS:
+		case SSB_DEV_MIPS_3302:
+		case SSB_DEV_EXTIF:
+			continue;
+		}
+
+		devwrap = kzalloc(sizeof(*devwrap), GFP_KERNEL);
+		if (!devwrap) {
+			ssb_printk(KERN_ERR PFX
+				   "Could not allocate device\n");
+			err = -ENOMEM;
+			goto error;
+		}
+		dev = &devwrap->dev;
+		devwrap->sdev = sdev;
+
+		dev->release = ssb_release_dev;
+		dev->bus = &ssb_bustype;
+		snprintf(dev->bus_id, sizeof(dev->bus_id),
+			 "ssb%u:%d", bus->busnumber, dev_idx);
+
+		switch (bus->bustype) {
+		case SSB_BUSTYPE_PCI:
+#ifdef CONFIG_SSB_PCIHOST
+			sdev->irq = bus->host_pci->irq;
+			dev->parent = &bus->host_pci->dev;
+#endif
+			break;
+		case SSB_BUSTYPE_PCMCIA:
+#ifdef CONFIG_SSB_PCMCIAHOST
+			dev->parent = &bus->host_pcmcia->dev;
+#endif
+			break;
+		case SSB_BUSTYPE_SSB:
+			break;
+		}
+
+		sdev->dev = dev;
+		err = device_register(dev);
+		if (err) {
+			ssb_printk(KERN_ERR PFX
+				   "Could not register %s\n",
+				   dev->bus_id);
+			/* Set dev to NULL to not unregister
+			 * dev on error unwinding. */
+			sdev->dev = NULL;
+			kfree(devwrap);
+			goto error;
+		}
+		dev_idx++;
+	}
+
+	return 0;
+error:
+	/* Unwind the already registered devices. */
+	ssb_devices_unregister(bus);
+	return err;
+}
+
+/* Needs ssb_buses_lock() */
+static int ssb_attach_queued_buses(void)
+{
+	struct ssb_bus *bus, *n;
+	int err = 0;
+	int drop_them_all = 0;
+
+	list_for_each_entry_safe(bus, n, &attach_queue, list) {
+		if (drop_them_all) {
+			list_del(&bus->list);
+			continue;
+		}
+		/* Can't init the PCIcore in ssb_bus_register(), as that
+		 * is too early in boot for embedded systems
+		 * (no udelay() available). So do it here in attach stage.
+		 */
+		ssb_pcicore_init(&bus->pcicore);
+
+		err = ssb_devices_register(bus);
+		if (err) {
+			drop_them_all = 1;
+			list_del(&bus->list);
+			continue;
+		}
+		list_move_tail(&bus->list, &buses);
+	}
+
+	return err;
+}
+
+static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset)
+{
+	struct ssb_bus *bus = dev->bus;
+
+	offset += dev->core_index * SSB_CORE_SIZE;
+	return readw(bus->mmio + offset);
+}
+
+static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset)
+{
+	struct ssb_bus *bus = dev->bus;
+
+	offset += dev->core_index * SSB_CORE_SIZE;
+	return readl(bus->mmio + offset);
+}
+
+static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value)
+{
+	struct ssb_bus *bus = dev->bus;
+
+	offset += dev->core_index * SSB_CORE_SIZE;
+	writew(value, bus->mmio + offset);
+}
+
+static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value)
+{
+	struct ssb_bus *bus = dev->bus;
+
+	offset += dev->core_index * SSB_CORE_SIZE;
+	writel(value, bus->mmio + offset);
+}
+
+/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */
+static const struct ssb_bus_ops ssb_ssb_ops = {
+	.read16		= ssb_ssb_read16,
+	.read32		= ssb_ssb_read32,
+	.write16	= ssb_ssb_write16,
+	.write32	= ssb_ssb_write32,
+};
+
+static int ssb_fetch_invariants(struct ssb_bus *bus,
+				ssb_invariants_func_t get_invariants)
+{
+	struct ssb_init_invariants iv;
+	int err;
+
+	memset(&iv, 0, sizeof(iv));
+	err = get_invariants(bus, &iv);
+	if (err)
+		goto out;
+	memcpy(&bus->boardinfo, &iv.boardinfo, sizeof(iv.boardinfo));
+	memcpy(&bus->sprom, &iv.sprom, sizeof(iv.sprom));
+out:
+	return err;
+}
+
+static int ssb_bus_register(struct ssb_bus *bus,
+			    ssb_invariants_func_t get_invariants,
+			    unsigned long baseaddr)
+{
+	int err;
+
+	spin_lock_init(&bus->bar_lock);
+	INIT_LIST_HEAD(&bus->list);
+
+	/* Powerup the bus */
+	err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1);
+	if (err)
+		goto out;
+	ssb_buses_lock();
+	bus->busnumber = next_busnumber;
+	/* Scan for devices (cores) */
+	err = ssb_bus_scan(bus, baseaddr);
+	if (err)
+		goto err_disable_xtal;
+
+	/* Init PCI-host device (if any) */
+	err = ssb_pci_init(bus);
+	if (err)
+		goto err_unmap;
+	/* Init PCMCIA-host device (if any) */
+	err = ssb_pcmcia_init(bus);
+	if (err)
+		goto err_pci_exit;
+
+	/* Initialize basic system devices (if available) */
+	ssb_chipcommon_init(&bus->chipco);
+	ssb_mipscore_init(&bus->mipscore);
+	err = ssb_fetch_invariants(bus, get_invariants);
+	if (err)
+		goto err_pcmcia_exit;
+
+	/* Queue it for attach.
+	 * See the comment at the ssb_is_early_boot definition. */
+	list_add_tail(&bus->list, &attach_queue);
+	if (!ssb_is_early_boot) {
+		/* This is not early boot, so we must attach the bus now */
+		err = ssb_attach_queued_buses();
+		if (err)
+			goto err_dequeue;
+	}
+	next_busnumber++;
+	ssb_buses_unlock();
+
+out:
+	return err;
+
+err_dequeue:
+	list_del(&bus->list);
+err_pcmcia_exit:
+/*	ssb_pcmcia_exit(bus); */
+err_pci_exit:
+	ssb_pci_exit(bus);
+err_unmap:
+	ssb_iounmap(bus);
+err_disable_xtal:
+	ssb_buses_unlock();
+	ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);
+	return err;
+}
+
+#ifdef CONFIG_SSB_PCIHOST
+int ssb_bus_pcibus_register(struct ssb_bus *bus,
+			    struct pci_dev *host_pci)
+{
+	int err;
+
+	bus->bustype = SSB_BUSTYPE_PCI;
+	bus->host_pci = host_pci;
+	bus->ops = &ssb_pci_ops;
+
+	err = ssb_bus_register(bus, ssb_pci_get_invariants, 0);
+	if (!err) {
+		ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on "
+			   "PCI device %s\n", host_pci->dev.bus_id);
+	}
+
+	return err;
+}
+EXPORT_SYMBOL(ssb_bus_pcibus_register);
+#endif /* CONFIG_SSB_PCIHOST */
+
+#ifdef CONFIG_SSB_PCMCIAHOST
+int ssb_bus_pcmciabus_register(struct ssb_bus *bus,
+			       struct pcmcia_device *pcmcia_dev,
+			       unsigned long baseaddr)
+{
+	int err;
+
+	bus->bustype = SSB_BUSTYPE_PCMCIA;
+	bus->host_pcmcia = pcmcia_dev;
+	bus->ops = &ssb_pcmcia_ops;
+
+	err = ssb_bus_register(bus, ssb_pcmcia_get_invariants, baseaddr);
+	if (!err) {
+		ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on "
+			   "PCMCIA device %s\n", pcmcia_dev->devname);
+	}
+
+	return err;
+}
+EXPORT_SYMBOL(ssb_bus_pcmciabus_register);
+#endif /* CONFIG_SSB_PCMCIAHOST */
+
+int ssb_bus_ssbbus_register(struct ssb_bus *bus,
+			    unsigned long baseaddr,
+			    ssb_invariants_func_t get_invariants)
+{
+	int err;
+
+	bus->bustype = SSB_BUSTYPE_SSB;
+	bus->ops = &ssb_ssb_ops;
+
+	err = ssb_bus_register(bus, get_invariants, baseaddr);
+	if (!err) {
+		ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found at "
+			   "address 0x%08lX\n", baseaddr);
+	}
+
+	return err;
+}
+
+int __ssb_driver_register(struct ssb_driver *drv, struct module *owner)
+{
+	drv->drv.name = drv->name;
+	drv->drv.bus = &ssb_bustype;
+	drv->drv.owner = owner;
+
+	return driver_register(&drv->drv);
+}
+EXPORT_SYMBOL(__ssb_driver_register);
+
+void ssb_driver_unregister(struct ssb_driver *drv)
+{
+	driver_unregister(&drv->drv);
+}
+EXPORT_SYMBOL(ssb_driver_unregister);
+
+void ssb_set_devtypedata(struct ssb_device *dev, void *data)
+{
+	struct ssb_bus *bus = dev->bus;
+	struct ssb_device *ent;
+	int i;
+
+	for (i = 0; i < bus->nr_devices; i++) {
+		ent = &(bus->devices[i]);
+		if (ent->id.vendor != dev->id.vendor)
+			continue;
+		if (ent->id.coreid != dev->id.coreid)
+			continue;
+
+		ent->devtypedata = data;
+	}
+}
+EXPORT_SYMBOL(ssb_set_devtypedata);
+
+static u32 clkfactor_f6_resolve(u32 v)
+{
+	/* map the magic values */
+	switch (v) {
+	case SSB_CHIPCO_CLK_F6_2:
+		return 2;
+	case SSB_CHIPCO_CLK_F6_3:
+		return 3;
+	case SSB_CHIPCO_CLK_F6_4:
+		return 4;
+	case SSB_CHIPCO_CLK_F6_5:
+		return 5;
+	case SSB_CHIPCO_CLK_F6_6:
+		return 6;
+	case SSB_CHIPCO_CLK_F6_7:
+		return 7;
+	}
+	return 0;
+}
+
+/* Calculate the speed the backplane would run at a given set of clockcontrol values */
+u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m)
+{
+	u32 n1, n2, clock, m1, m2, m3, mc;
+
+	n1 = (n & SSB_CHIPCO_CLK_N1);
+	n2 = ((n & SSB_CHIPCO_CLK_N2) >> SSB_CHIPCO_CLK_N2_SHIFT);
+
+	switch (plltype) {
+	case SSB_PLLTYPE_6: /* 100/200 or 120/240 only */
+		if (m & SSB_CHIPCO_CLK_T6_MMASK)
+			return SSB_CHIPCO_CLK_T6_M0;
+		return SSB_CHIPCO_CLK_T6_M1;
+	case SSB_PLLTYPE_1: /* 48Mhz base, 3 dividers */
+	case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */
+	case SSB_PLLTYPE_4: /* 48Mhz, 4 dividers */
+	case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */
+		n1 = clkfactor_f6_resolve(n1);
+		n2 += SSB_CHIPCO_CLK_F5_BIAS;
+		break;
+	case SSB_PLLTYPE_2: /* 48Mhz, 4 dividers */
+		n1 += SSB_CHIPCO_CLK_T2_BIAS;
+		n2 += SSB_CHIPCO_CLK_T2_BIAS;
+		SSB_WARN_ON(!((n1 >= 2) && (n1 <= 7)));
+		SSB_WARN_ON(!((n2 >= 5) && (n2 <= 23)));
+		break;
+	case SSB_PLLTYPE_5: /* 25Mhz, 4 dividers */
+		return 100000000;
+	default:
+		SSB_WARN_ON(1);
+	}
+
+	switch (plltype) {
+	case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */
+	case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */
+		clock = SSB_CHIPCO_CLK_BASE2 * n1 * n2;
+		break;
+	default:
+		clock = SSB_CHIPCO_CLK_BASE1 * n1 * n2;
+	}
+	if (!clock)
+		return 0;
+
+	m1 = (m & SSB_CHIPCO_CLK_M1);
+	m2 = ((m & SSB_CHIPCO_CLK_M2) >> SSB_CHIPCO_CLK_M2_SHIFT);
+	m3 = ((m & SSB_CHIPCO_CLK_M3) >> SSB_CHIPCO_CLK_M3_SHIFT);
+	mc = ((m & SSB_CHIPCO_CLK_MC) >> SSB_CHIPCO_CLK_MC_SHIFT);
+
+	switch (plltype) {
+	case SSB_PLLTYPE_1: /* 48Mhz base, 3 dividers */
+	case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */
+	case SSB_PLLTYPE_4: /* 48Mhz, 4 dividers */
+	case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */
+		m1 = clkfactor_f6_resolve(m1);
+		if ((plltype == SSB_PLLTYPE_1) ||
+		    (plltype == SSB_PLLTYPE_3))
+			m2 += SSB_CHIPCO_CLK_F5_BIAS;
+		else
+			m2 = clkfactor_f6_resolve(m2);
+		m3 = clkfactor_f6_resolve(m3);
+
+		switch (mc) {
+		case SSB_CHIPCO_CLK_MC_BYPASS:
+			return clock;
+		case SSB_CHIPCO_CLK_MC_M1:
+			return (clock / m1);
+		case SSB_CHIPCO_CLK_MC_M1M2:
+			return (clock / (m1 * m2));
+		case SSB_CHIPCO_CLK_MC_M1M2M3:
+			return (clock / (m1 * m2 * m3));
+		case SSB_CHIPCO_CLK_MC_M1M3:
+			return (clock / (m1 * m3));
+		}
+		return 0;
+	case SSB_PLLTYPE_2:
+		m1 += SSB_CHIPCO_CLK_T2_BIAS;
+		m2 += SSB_CHIPCO_CLK_T2M2_BIAS;
+		m3 += SSB_CHIPCO_CLK_T2_BIAS;
+		SSB_WARN_ON(!((m1 >= 2) && (m1 <= 7)));
+		SSB_WARN_ON(!((m2 >= 3) && (m2 <= 10)));
+		SSB_WARN_ON(!((m3 >= 2) && (m3 <= 7)));
+
+		if (!(mc & SSB_CHIPCO_CLK_T2MC_M1BYP))
+			clock /= m1;
+		if (!(mc & SSB_CHIPCO_CLK_T2MC_M2BYP))
+			clock /= m2;
+		if (!(mc & SSB_CHIPCO_CLK_T2MC_M3BYP))
+			clock /= m3;
+		return clock;
+	default:
+		SSB_WARN_ON(1);
+	}
+	return 0;
+}
+
+/* Get the current speed the backplane is running at */
+u32 ssb_clockspeed(struct ssb_bus *bus)
+{
+	u32 rate;
+	u32 plltype;
+	u32 clkctl_n, clkctl_m;
+
+	//TODO if EXTIF: PLLTYPE == 1, read n from clockcontrol_n, m from clockcontrol_sb
+
+	if (bus->chipco.dev) {
+		ssb_chipco_get_clockcontrol(&bus->chipco, &plltype,
+					    &clkctl_n, &clkctl_m);
+	} else
+		return 0;
+
+	if (bus->chip_id == 0x5365) {
+		rate = 100000000;
+	} else {
+		rate = ssb_calc_clock_rate(plltype, clkctl_n, clkctl_m);
+		if (plltype == SSB_PLLTYPE_3) /* 25Mhz, 2 dividers */
+			rate /= 2;
+	}
+
+	return rate;
+}
+EXPORT_SYMBOL(ssb_clockspeed);
+
+static u32 ssb_tmslow_reject_bitmask(struct ssb_device *dev)
+{
+	/* The REJECT bit changed position in TMSLOW between
+	 * Backplane revisions. */
+	switch (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_SSBREV) {
+	case SSB_IDLOW_SSBREV_22:
+		return SSB_TMSLOW_REJECT_22;
+	case SSB_IDLOW_SSBREV_23:
+		return SSB_TMSLOW_REJECT_23;
+	default:
+		WARN_ON(1);
+	}
+	return (SSB_TMSLOW_REJECT_22 | SSB_TMSLOW_REJECT_23);
+}
+
+int ssb_device_is_enabled(struct ssb_device *dev)
+{
+	u32 val;
+	u32 reject;
+
+	reject = ssb_tmslow_reject_bitmask(dev);
+	val = ssb_read32(dev, SSB_TMSLOW);
+	val &= SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET | reject;
+
+	return (val == SSB_TMSLOW_CLOCK);
+}
+EXPORT_SYMBOL(ssb_device_is_enabled);
+
+static void ssb_flush_tmslow(struct ssb_device *dev)
+{
+	/* Make _really_ sure the device has finished the TMSLOW
+	 * register write transaction, as we risk running into
+	 * a machine check exception otherwise.
+	 * Do this by reading the register back to commit the
+	 * PCI write and delay an additional usec for the device
+	 * to react to the change. */
+	ssb_read32(dev, SSB_TMSLOW);
+	udelay(1);
+}
+
+void ssb_device_enable(struct ssb_device *dev, u32 core_specific_flags)
+{
+	u32 val;
+
+	ssb_device_disable(dev, core_specific_flags);
+	ssb_write32(dev, SSB_TMSLOW,
+		    SSB_TMSLOW_RESET | SSB_TMSLOW_CLOCK |
+		    SSB_TMSLOW_FGC | core_specific_flags);
+	ssb_flush_tmslow(dev);
+
+	/* Clear SERR if set. This is a hw bug workaround. */
+	if (ssb_read32(dev, SSB_TMSHIGH) & SSB_TMSHIGH_SERR)
+		ssb_write32(dev, SSB_TMSHIGH, 0);
+
+	val = ssb_read32(dev, SSB_IMSTATE);
+	if (val & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) {
+		val &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO);
+		ssb_write32(dev, SSB_IMSTATE, val);
+	}
+
+	ssb_write32(dev, SSB_TMSLOW,
+		    SSB_TMSLOW_CLOCK | SSB_TMSLOW_FGC |
+		    core_specific_flags);
+	ssb_flush_tmslow(dev);
+
+	ssb_write32(dev, SSB_TMSLOW, SSB_TMSLOW_CLOCK |
+		    core_specific_flags);
+	ssb_flush_tmslow(dev);
+}
+EXPORT_SYMBOL(ssb_device_enable);
+
+/* Wait for a bit in a register to get set or unset.
+ * timeout is in units of ten-microseconds */
+static int ssb_wait_bit(struct ssb_device *dev, u16 reg, u32 bitmask,
+			int timeout, int set)
+{
+	int i;
+	u32 val;
+
+	for (i = 0; i < timeout; i++) {
+		val = ssb_read32(dev, reg);
+		if (set) {
+			if (val & bitmask)
+				return 0;
+		} else {
+			if (!(val & bitmask))
+				return 0;
+		}
+		udelay(10);
+	}
+	printk(KERN_ERR PFX "Timeout waiting for bitmask %08X on "
+			    "register %04X to %s.\n",
+	       bitmask, reg, (set ? "set" : "clear"));
+
+	return -ETIMEDOUT;
+}
+
+void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags)
+{
+	u32 reject;
+
+	if (ssb_read32(dev, SSB_TMSLOW) & SSB_TMSLOW_RESET)
+		return;
+
+	reject = ssb_tmslow_reject_bitmask(dev);
+	ssb_write32(dev, SSB_TMSLOW, reject | SSB_TMSLOW_CLOCK);
+	ssb_wait_bit(dev, SSB_TMSLOW, reject, 1000, 1);
+	ssb_wait_bit(dev, SSB_TMSHIGH, SSB_TMSHIGH_BUSY, 1000, 0);
+	ssb_write32(dev, SSB_TMSLOW,
+		    SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
+		    reject | SSB_TMSLOW_RESET |
+		    core_specific_flags);
+	ssb_flush_tmslow(dev);
+
+	ssb_write32(dev, SSB_TMSLOW,
+		    reject | SSB_TMSLOW_RESET |
+		    core_specific_flags);
+	ssb_flush_tmslow(dev);
+}
+EXPORT_SYMBOL(ssb_device_disable);
+
+u32 ssb_dma_translation(struct ssb_device *dev)
+{
+	switch (dev->bus->bustype) {
+	case SSB_BUSTYPE_SSB:
+		return 0;
+	case SSB_BUSTYPE_PCI:
+	case SSB_BUSTYPE_PCMCIA:
+		return SSB_PCI_DMA;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(ssb_dma_translation);
+
+int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask)
+{
+	struct device *dev = ssb_dev->dev;
+
+#ifdef CONFIG_SSB_PCIHOST
+	if (ssb_dev->bus->bustype == SSB_BUSTYPE_PCI &&
+	    !dma_supported(dev, mask))
+		return -EIO;
+#endif
+	dev->coherent_dma_mask = mask;
+	dev->dma_mask = &dev->coherent_dma_mask;
+
+	return 0;
+}
+EXPORT_SYMBOL(ssb_dma_set_mask);
+
+int ssb_bus_may_powerdown(struct ssb_bus *bus)
+{
+	struct ssb_chipcommon *cc;
+	int err;
+
+	/* On buses where more than one core may be working
+	 * at a time, we must not powerdown stuff if there are
+	 * still cores that may want to run. */
+	if (bus->bustype == SSB_BUSTYPE_SSB)
+		return 0;
+
+	cc = &bus->chipco;
+	ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW);
+	err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);
+	if (err)
+		goto error;
+
+	return 0;
+error:
+	ssb_printk(KERN_ERR PFX "Bus powerdown failed\n");
+	return err;
+}
+EXPORT_SYMBOL(ssb_bus_may_powerdown);
+
+int ssb_bus_powerup(struct ssb_bus *bus, bool dynamic_pctl)
+{
+	struct ssb_chipcommon *cc;
+	int err;
+	enum ssb_clkmode mode;
+
+	err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1);
+	if (err)
+		goto error;
+	cc = &bus->chipco;
+	mode = dynamic_pctl ? SSB_CLKMODE_DYNAMIC : SSB_CLKMODE_FAST;
+	ssb_chipco_set_clockmode(cc, mode);
+
+	return 0;
+error:
+	ssb_printk(KERN_ERR PFX "Bus powerup failed\n");
+	return err;
+}
+EXPORT_SYMBOL(ssb_bus_powerup);
+
+u32 ssb_admatch_base(u32 adm)
+{
+	u32 base = 0;
+
+	switch (adm & SSB_ADM_TYPE) {
+	case SSB_ADM_TYPE0:
+		base = (adm & SSB_ADM_BASE0);
+		break;
+	case SSB_ADM_TYPE1:
+		SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */
+		base = (adm & SSB_ADM_BASE1);
+		break;
+	case SSB_ADM_TYPE2:
+		SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */
+		base = (adm & SSB_ADM_BASE2);
+		break;
+	default:
+		SSB_WARN_ON(1);
+	}
+
+	return base;
+}
+EXPORT_SYMBOL(ssb_admatch_base);
+
+u32 ssb_admatch_size(u32 adm)
+{
+	u32 size = 0;
+
+	switch (adm & SSB_ADM_TYPE) {
+	case SSB_ADM_TYPE0:
+		size = ((adm & SSB_ADM_SZ0) >> SSB_ADM_SZ0_SHIFT);
+		break;
+	case SSB_ADM_TYPE1:
+		SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */
+		size = ((adm & SSB_ADM_SZ1) >> SSB_ADM_SZ1_SHIFT);
+		break;
+	case SSB_ADM_TYPE2:
+		SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */
+		size = ((adm & SSB_ADM_SZ2) >> SSB_ADM_SZ2_SHIFT);
+		break;
+	default:
+		SSB_WARN_ON(1);
+	}
+	size = (1 << (size + 1));
+
+	return size;
+}
+EXPORT_SYMBOL(ssb_admatch_size);
+
+static int __init ssb_modinit(void)
+{
+	int err;
+
+	/* See the comment at the ssb_is_early_boot definition */
+	ssb_is_early_boot = 0;
+	err = bus_register(&ssb_bustype);
+	if (err)
+		return err;
+
+	/* Maybe we already registered some buses at early boot.
+	 * Check for this and attach them
+	 */
+	ssb_buses_lock();
+	err = ssb_attach_queued_buses();
+	ssb_buses_unlock();
+	if (err)
+		bus_unregister(&ssb_bustype);
+
+	return err;
+}
+subsys_initcall(ssb_modinit);
+
+static void __exit ssb_modexit(void)
+{
+	bus_unregister(&ssb_bustype);
+}
+module_exit(ssb_modexit)
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
new file mode 100644
index 0000000..b3ec4c8
--- /dev/null
+++ b/drivers/ssb/pci.c
@@ -0,0 +1,704 @@
+/*
+ * Sonics Silicon Backplane PCI-Hostbus related functions.
+ *
+ * Copyright (C) 2005-2006 Michael Buesch <mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
+ * Copyright (C) 2005 Martin Langer <martin-langer-Mmb7MZpHnFY@public.gmane.org>
+ * Copyright (C) 2005 Stefano Brivio <st3-sGOZH3hwPm2sTnJN9+BGXg@public.gmane.org>
+ * Copyright (C) 2005 Danny van Dyk <kugelfang-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
+ * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi-xegAWGFB9mQXXHkOk0aIfQ@public.gmane.org>
+ *
+ * Derived from the Broadcom 4400 device driver.
+ * Copyright (C) 2002 David S. Miller (davem-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org)
+ * Fixed by Pekka Pietikainen (pp-YuCZbdju05vHOG6cAo2yLw@public.gmane.org)
+ * Copyright (C) 2006 Broadcom Corporation.
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/ssb/ssb_regs.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "ssb_private.h"
+
+
+/* Lowlevel coreswitching */
+int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
+{
+	int err;
+	int attempts = 0;
+	u32 cur_core;
+
+	while (1) {
+		err = pci_write_config_dword(bus->host_pci, SSB_BAR0_WIN,
+					     (coreidx * SSB_CORE_SIZE)
+					     + SSB_ENUM_BASE);
+		if (err)
+			goto error;
+		err = pci_read_config_dword(bus->host_pci, SSB_BAR0_WIN,
+					    &cur_core);
+		if (err)
+			goto error;
+		cur_core = (cur_core - SSB_ENUM_BASE)
+			   / SSB_CORE_SIZE;
+		if (cur_core == coreidx)
+			break;
+
+		if (attempts++ > SSB_BAR0_MAX_RETRIES)
+			goto error;
+		udelay(10);
+	}
+	return 0;
+error:
+	ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx);
+	return -ENODEV;
+}
+
+int ssb_pci_switch_core(struct ssb_bus *bus,
+			struct ssb_device *dev)
+{
+	int err;
+	unsigned long flags;
+
+	ssb_dprintk(KERN_INFO PFX
+		    "Switching to %s core, index %d\n",
+		    ssb_core_name(dev->id.coreid),
+		    dev->core_index);
+
+	spin_lock_irqsave(&bus->bar_lock, flags);
+	err = ssb_pci_switch_coreidx(bus, dev->core_index);
+	if (!err)
+		bus->mapped_device = dev;
+	spin_unlock_irqrestore(&bus->bar_lock, flags);
+
+	return err;
+}
+
+/* Enable/disable the on board crystal oscillator and/or PLL. */
+int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on)
+{
+	int err;
+	u32 in, out, outenable;
+	u16 pci_status;
+
+	if (bus->bustype != SSB_BUSTYPE_PCI)
+		return 0;
+
+	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_IN, &in);
+	if (err)
+		goto err_pci;
+	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &out);
+	if (err)
+		goto err_pci;
+	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, &outenable);
+	if (err)
+		goto err_pci;
+
+	outenable |= what;
+
+	if (turn_on) {
+		/* Avoid glitching the clock if GPRS is already using it.
+		 * We can't actually read the state of the PLLPD so we infer it
+		 * by the value of XTAL_PU which *is* readable via gpioin.
+		 */
+		if (!(in & SSB_GPIO_XTAL)) {
+			if (what & SSB_GPIO_XTAL) {
+				/* Turn the crystal on */
+				out |= SSB_GPIO_XTAL;
+				if (what & SSB_GPIO_PLL)
+					out |= SSB_GPIO_PLL;
+				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
+				if (err)
+					goto err_pci;
+				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE,
+							     outenable);
+				if (err)
+					goto err_pci;
+				msleep(1);
+			}
+			if (what & SSB_GPIO_PLL) {
+				/* Turn the PLL on */
+				out &= ~SSB_GPIO_PLL;
+				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
+				if (err)
+					goto err_pci;
+				msleep(5);
+			}
+		}
+
+		err = pci_read_config_word(bus->host_pci, PCI_STATUS, &pci_status);
+		if (err)
+			goto err_pci;
+		pci_status &= ~PCI_STATUS_SIG_TARGET_ABORT;
+		err = pci_write_config_word(bus->host_pci, PCI_STATUS, pci_status);
+		if (err)
+			goto err_pci;
+	} else {
+		if (what & SSB_GPIO_XTAL) {
+			/* Turn the crystal off */
+			out &= ~SSB_GPIO_XTAL;
+		}
+		if (what & SSB_GPIO_PLL) {
+			/* Turn the PLL off */
+			out |= SSB_GPIO_PLL;
+		}
+		err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
+		if (err)
+			goto err_pci;
+		err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, outenable);
+		if (err)
+			goto err_pci;
+	}
+
+out:
+	return err;
+
+err_pci:
+	printk(KERN_ERR PFX "Error: ssb_pci_xtal() could not access PCI config space!\n");
+	err = -EBUSY;
+	goto out;
+}
+
+/* Get the word-offset for a SSB_SPROM_XXX define. */
+#define SPOFF(offset)	(((offset) - SSB_SPROM_BASE) / sizeof(u16))
+/* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
+#define SPEX(_outvar, _offset, _mask, _shift)	\
+	out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
+
+static inline u8 ssb_crc8(u8 crc, u8 data)
+{
+	/* Polynomial:   x^8 + x^7 + x^6 + x^4 + x^2 + 1   */
+	static const u8 t[] = {
+		0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
+		0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
+		0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
+		0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
+		0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
+		0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
+		0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
+		0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
+		0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
+		0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
+		0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
+		0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
+		0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
+		0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
+		0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
+		0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
+		0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
+		0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
+		0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
+		0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
+		0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
+		0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
+		0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
+		0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
+		0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
+		0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
+		0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
+		0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
+		0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
+		0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
+		0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
+		0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
+	};
+	return t[crc ^ data];
+}
+
+static u8 ssb_sprom_crc(const u16 *sprom)
+{
+	int word;
+	u8 crc = 0xFF;
+
+	for (word = 0; word < SSB_SPROMSIZE_WORDS - 1; word++) {
+		crc = ssb_crc8(crc, sprom[word] & 0x00FF);
+		crc = ssb_crc8(crc, (sprom[word] & 0xFF00) >> 8);
+	}
+	crc = ssb_crc8(crc, sprom[SPOFF(SSB_SPROM_REVISION)] & 0x00FF);
+	crc ^= 0xFF;
+
+	return crc;
+}
+
+static int sprom_check_crc(const u16 *sprom)
+{
+	u8 crc;
+	u8 expected_crc;
+	u16 tmp;
+
+	crc = ssb_sprom_crc(sprom);
+	tmp = sprom[SPOFF(SSB_SPROM_REVISION)] & SSB_SPROM_REVISION_CRC;
+	expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
+	if (crc != expected_crc)
+		return -EPROTO;
+
+	return 0;
+}
+
+static void sprom_do_read(struct ssb_bus *bus, u16 *sprom)
+{
+	int i;
+
+	for (i = 0; i < SSB_SPROMSIZE_WORDS; i++)
+		sprom[i] = readw(bus->mmio + SSB_SPROM_BASE + (i * 2));
+}
+
+static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
+{
+	struct pci_dev *pdev = bus->host_pci;
+	int i, err;
+	u32 spromctl;
+
+	ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n");
+	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
+	if (err)
+		goto err_ctlreg;
+	spromctl |= SSB_SPROMCTL_WE;
+	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
+	if (err)
+		goto err_ctlreg;
+	ssb_printk(KERN_NOTICE PFX "[ 0%%");
+	msleep(500);
+	for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) {
+		if (i == SSB_SPROMSIZE_WORDS / 4)
+			ssb_printk("25%%");
+		else if (i == SSB_SPROMSIZE_WORDS / 2)
+			ssb_printk("50%%");
+		else if (i == (SSB_SPROMSIZE_WORDS / 4) * 3)
+			ssb_printk("75%%");
+		else if (i % 2)
+			ssb_printk(".");
+		writew(sprom[i], bus->mmio + SSB_SPROM_BASE + (i * 2));
+		mmiowb();
+		msleep(20);
+	}
+	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
+	if (err)
+		goto err_ctlreg;
+	spromctl &= ~SSB_SPROMCTL_WE;
+	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
+	if (err)
+		goto err_ctlreg;
+	msleep(500);
+	ssb_printk("100%% ]\n");
+	ssb_printk(KERN_NOTICE PFX "SPROM written.\n");
+
+	return 0;
+err_ctlreg:
+	ssb_printk(KERN_ERR PFX "Could not access SPROM control register.\n");
+	return err;
+}
+
+static void sprom_extract_r1(struct ssb_sprom_r1 *out, const u16 *in)
+{
+	int i;
+	u16 v;
+
+	SPEX(pci_spid, SSB_SPROM1_SPID, 0xFFFF, 0);
+	SPEX(pci_svid, SSB_SPROM1_SVID, 0xFFFF, 0);
+	SPEX(pci_pid, SSB_SPROM1_PID, 0xFFFF, 0);
+	for (i = 0; i < 3; i++) {
+		v = in[SPOFF(SSB_SPROM1_IL0MAC) + i];
+		*(((u16 *)out->il0mac) + i) = cpu_to_be16(v);
+	}
+	for (i = 0; i < 3; i++) {
+		v = in[SPOFF(SSB_SPROM1_ET0MAC) + i];
+		*(((u16 *)out->et0mac) + i) = cpu_to_be16(v);
+	}
+	for (i = 0; i < 3; i++) {
+		v = in[SPOFF(SSB_SPROM1_ET1MAC) + i];
+		*(((u16 *)out->et1mac) + i) = cpu_to_be16(v);
+	}
+	SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0);
+	SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A,
+	     SSB_SPROM1_ETHPHY_ET1A_SHIFT);
+	SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14);
+	SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15);
+	SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0);
+	SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE,
+	     SSB_SPROM1_BINF_CCODE_SHIFT);
+	SPEX(antenna_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA,
+	     SSB_SPROM1_BINF_ANTA_SHIFT);
+	SPEX(antenna_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG,
+	     SSB_SPROM1_BINF_ANTBG_SHIFT);
+	SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0);
+	SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0);
+	SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0);
+	SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0);
+	SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0);
+	SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0);
+	SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0);
+	SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1,
+	     SSB_SPROM1_GPIOA_P1_SHIFT);
+	SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0);
+	SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3,
+	     SSB_SPROM1_GPIOB_P3_SHIFT);
+	SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A,
+	     SSB_SPROM1_MAXPWR_A_SHIFT);
+	SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0);
+	SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A,
+	     SSB_SPROM1_ITSSI_A_SHIFT);
+	SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0);
+	SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0);
+	SPEX(antenna_gain_a, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_A, 0);
+	SPEX(antenna_gain_bg, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_BG,
+	     SSB_SPROM1_AGAIN_BG_SHIFT);
+	for (i = 0; i < 4; i++) {
+		v = in[SPOFF(SSB_SPROM1_OEM) + i];
+		*(((u16 *)out->oem) + i) = cpu_to_le16(v);
+	}
+}
+
+static void sprom_extract_r2(struct ssb_sprom_r2 *out, const u16 *in)
+{
+	int i;
+	u16 v;
+
+	SPEX(boardflags_hi, SSB_SPROM2_BFLHI,  0xFFFF, 0);
+	SPEX(maxpwr_a_hi, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_HI, 0);
+	SPEX(maxpwr_a_lo, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_LO,
+	     SSB_SPROM2_MAXP_A_LO_SHIFT);
+	SPEX(pa1lob0, SSB_SPROM2_PA1LOB0, 0xFFFF, 0);
+	SPEX(pa1lob1, SSB_SPROM2_PA1LOB1, 0xFFFF, 0);
+	SPEX(pa1lob2, SSB_SPROM2_PA1LOB2, 0xFFFF, 0);
+	SPEX(pa1hib0, SSB_SPROM2_PA1HIB0, 0xFFFF, 0);
+	SPEX(pa1hib1, SSB_SPROM2_PA1HIB1, 0xFFFF, 0);
+	SPEX(pa1hib2, SSB_SPROM2_PA1HIB2, 0xFFFF, 0);
+	SPEX(ofdm_pwr_off, SSB_SPROM2_OPO, SSB_SPROM2_OPO_VALUE, 0);
+	for (i = 0; i < 4; i++) {
+		v = in[SPOFF(SSB_SPROM2_CCODE) + i];
+		*(((u16 *)out->country_str) + i) = cpu_to_le16(v);
+	}
+}
+
+static void sprom_extract_r3(struct ssb_sprom_r3 *out, const u16 *in)
+{
+	out->ofdmapo  = (in[SPOFF(SSB_SPROM3_OFDMAPO) + 0] & 0xFF00) >> 8;
+	out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 0] & 0x00FF) << 8;
+	out->ofdmapo <<= 16;
+	out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 1] & 0xFF00) >> 8;
+	out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 1] & 0x00FF) << 8;
+
+	out->ofdmalpo  = (in[SPOFF(SSB_SPROM3_OFDMALPO) + 0] & 0xFF00) >> 8;
+	out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 0] & 0x00FF) << 8;
+	out->ofdmalpo <<= 16;
+	out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 1] & 0xFF00) >> 8;
+	out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 1] & 0x00FF) << 8;
+
+	out->ofdmahpo  = (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 0] & 0xFF00) >> 8;
+	out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 0] & 0x00FF) << 8;
+	out->ofdmahpo <<= 16;
+	out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 1] & 0xFF00) >> 8;
+	out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 1] & 0x00FF) << 8;
+
+	SPEX(gpioldc_on_cnt, SSB_SPROM3_GPIOLDC, SSB_SPROM3_GPIOLDC_ON,
+	     SSB_SPROM3_GPIOLDC_ON_SHIFT);
+	SPEX(gpioldc_off_cnt, SSB_SPROM3_GPIOLDC, SSB_SPROM3_GPIOLDC_OFF,
+	     SSB_SPROM3_GPIOLDC_OFF_SHIFT);
+	SPEX(cckpo_1M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_1M, 0);
+	SPEX(cckpo_2M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_2M,
+	     SSB_SPROM3_CCKPO_2M_SHIFT);
+	SPEX(cckpo_55M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_55M,
+	     SSB_SPROM3_CCKPO_55M_SHIFT);
+	SPEX(cckpo_11M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_11M,
+	     SSB_SPROM3_CCKPO_11M_SHIFT);
+
+	out->ofdmgpo  = (in[SPOFF(SSB_SPROM3_OFDMGPO) + 0] & 0xFF00) >> 8;
+	out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 0] & 0x00FF) << 8;
+	out->ofdmgpo <<= 16;
+	out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 1] & 0xFF00) >> 8;
+	out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 1] & 0x00FF) << 8;
+}
+
+static int sprom_extract(struct ssb_bus *bus,
+			 struct ssb_sprom *out, const u16 *in)
+{
+	memset(out, 0, sizeof(*out));
+
+	SPEX(revision, SSB_SPROM_REVISION, SSB_SPROM_REVISION_REV, 0);
+	SPEX(crc, SSB_SPROM_REVISION, SSB_SPROM_REVISION_CRC,
+	     SSB_SPROM_REVISION_CRC_SHIFT);
+
+	if ((bus->chip_id & 0xFF00) == 0x4400) {
+		/* Workaround: The BCM44XX chip has a stupid revision
+		 * number stored in the SPROM.
+		 * Always extract r1. */
+		sprom_extract_r1(&out->r1, in);
+	} else {
+		if (out->revision == 0)
+			goto unsupported;
+		if (out->revision >= 1 && out->revision <= 3)
+			sprom_extract_r1(&out->r1, in);
+		if (out->revision >= 2 && out->revision <= 3)
+			sprom_extract_r2(&out->r2, in);
+		if (out->revision == 3)
+			sprom_extract_r3(&out->r3, in);
+		if (out->revision >= 4)
+			goto unsupported;
+	}
+
+	return 0;
+unsupported:
+	ssb_printk(KERN_WARNING PFX "Unsupported SPROM revision %d "
+		   "detected. Will extract v1\n", out->revision);
+	sprom_extract_r1(&out->r1, in);
+	return 0;
+}
+
+static int ssb_pci_sprom_get(struct ssb_bus *bus,
+			     struct ssb_sprom *sprom)
+{
+	int err = -ENOMEM;
+	u16 *buf;
+
+	buf = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL);
+	if (!buf)
+		goto out;
+	sprom_do_read(bus, buf);
+	err = sprom_check_crc(buf);
+	if (err) {
+		ssb_printk(KERN_WARNING PFX
+			   "WARNING: Invalid SPROM CRC (corrupt SPROM)\n");
+	}
+	err = sprom_extract(bus, sprom, buf);
+
+	kfree(buf);
+out:
+	return err;
+}
+
+static void ssb_pci_get_boardinfo(struct ssb_bus *bus,
+				  struct ssb_boardinfo *bi)
+{
+	pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID,
+			     &bi->vendor);
+	pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID,
+			     &bi->type);
+	pci_read_config_word(bus->host_pci, PCI_REVISION_ID,
+			     &bi->rev);
+}
+
+int ssb_pci_get_invariants(struct ssb_bus *bus,
+			   struct ssb_init_invariants *iv)
+{
+	int err;
+
+	err = ssb_pci_sprom_get(bus, &iv->sprom);
+	if (err)
+		goto out;
+	ssb_pci_get_boardinfo(bus, &iv->boardinfo);
+
+out:
+	return err;
+}
+
+static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset)
+{
+	struct ssb_bus *bus = dev->bus;
+
+	if (unlikely(bus->mapped_device != dev)) {
+		if (unlikely(ssb_pci_switch_core(bus, dev)))
+			return 0xFFFF;
+	}
+	return readw(bus->mmio + offset);
+}
+
+static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset)
+{
+	struct ssb_bus *bus = dev->bus;
+
+	if (unlikely(bus->mapped_device != dev)) {
+		if (unlikely(ssb_pci_switch_core(bus, dev)))
+			return 0xFFFFFFFF;
+	}
+	return readl(bus->mmio + offset);
+}
+
+static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value)
+{
+	struct ssb_bus *bus = dev->bus;
+
+	if (unlikely(bus->mapped_device != dev)) {
+		if (unlikely(ssb_pci_switch_core(bus, dev)))
+			return;
+	}
+	writew(value, bus->mmio + offset);
+}
+
+static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value)
+{
+	struct ssb_bus *bus = dev->bus;
+
+	if (unlikely(bus->mapped_device != dev)) {
+		if (unlikely(ssb_pci_switch_core(bus, dev)))
+			return;
+	}
+	writel(value, bus->mmio + offset);
+}
+
+/* Not "static", as it's used in main.c */
+const struct ssb_bus_ops ssb_pci_ops = {
+	.read16		= ssb_pci_read16,
+	.read32		= ssb_pci_read32,
+	.write16	= ssb_pci_write16,
+	.write32	= ssb_pci_write32,
+};
+
+static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len)
+{
+	int i, pos = 0;
+
+	for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) {
+		pos += snprintf(buf + pos, buf_len - pos - 1,
+				"%04X", swab16(sprom[i]) & 0xFFFF);
+	}
+	pos += snprintf(buf + pos, buf_len - pos - 1, "\n");
+
+	return pos + 1;
+}
+
+static int hex2sprom(u16 *sprom, const char *dump, size_t len)
+{
+	char tmp[5] = { 0 };
+	int cnt = 0;
+	unsigned long parsed;
+
+	if (len < SSB_SPROMSIZE_BYTES * 2)
+		return -EINVAL;
+
+	while (cnt < SSB_SPROMSIZE_WORDS) {
+		memcpy(tmp, dump, 4);
+		dump += 4;
+		parsed = simple_strtoul(tmp, NULL, 16);
+		sprom[cnt++] = swab16((u16)parsed);
+	}
+
+	return 0;
+}
+
+static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
+	struct ssb_bus *bus;
+	u16 *sprom;
+	int err = -ENODEV;
+	ssize_t count = 0;
+
+	bus = ssb_pci_dev_to_bus(pdev);
+	if (!bus)
+		goto out;
+	err = -ENOMEM;
+	sprom = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL);
+	if (!sprom)
+		goto out;
+
+	/* Use interruptible locking, as the SPROM write might
+	 * be holding the lock for several seconds. So allow userspace
+	 * to cancel operation. */
+	err = -ERESTARTSYS;
+	if (mutex_lock_interruptible(&bus->pci_sprom_mutex))
+		goto out_kfree;
+	sprom_do_read(bus, sprom);
+	mutex_unlock(&bus->pci_sprom_mutex);
+
+	count = sprom2hex(sprom, buf, PAGE_SIZE);
+	err = 0;
+
+out_kfree:
+	kfree(sprom);
+out:
+	return err ? err : count;
+}
+
+static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
+	struct ssb_bus *bus;
+	u16 *sprom;
+	int res = 0, err = -ENODEV;
+
+	bus = ssb_pci_dev_to_bus(pdev);
+	if (!bus)
+		goto out;
+	err = -ENOMEM;
+	sprom = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL);
+	if (!sprom)
+		goto out;
+	err = hex2sprom(sprom, buf, count);
+	if (err) {
+		err = -EINVAL;
+		goto out_kfree;
+	}
+	err = sprom_check_crc(sprom);
+	if (err) {
+		err = -EINVAL;
+		goto out_kfree;
+	}
+
+	/* Use interruptible locking, as the SPROM write might
+	 * be holding the lock for several seconds. So allow userspace
+	 * to cancel operation. */
+	err = -ERESTARTSYS;
+	if (mutex_lock_interruptible(&bus->pci_sprom_mutex))
+		goto out_kfree;
+	err = ssb_devices_freeze(bus);
+	if (err == -EOPNOTSUPP) {
+		ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. "
+			   "No suspend support. Is CONFIG_PM enabled?\n");
+		goto out_unlock;
+	}
+	if (err) {
+		ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n");
+		goto out_unlock;
+	}
+	res = sprom_do_write(bus, sprom);
+	err = ssb_devices_thaw(bus);
+	if (err)
+		ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n");
+out_unlock:
+	mutex_unlock(&bus->pci_sprom_mutex);
+out_kfree:
+	kfree(sprom);
+out:
+	if (res)
+		return res;
+	return err ? err : count;
+}
+
+static DEVICE_ATTR(ssb_sprom, 0600,
+		   ssb_pci_attr_sprom_show,
+		   ssb_pci_attr_sprom_store);
+
+void ssb_pci_exit(struct ssb_bus *bus)
+{
+	struct pci_dev *pdev;
+
+	if (bus->bustype != SSB_BUSTYPE_PCI)
+		return;
+
+	pdev = bus->host_pci;
+	device_remove_file(&pdev->dev, &dev_attr_ssb_sprom);
+}
+
+int ssb_pci_init(struct ssb_bus *bus)
+{
+	struct pci_dev *pdev;
+	int err;
+
+	if (bus->bustype != SSB_BUSTYPE_PCI)
+		return 0;
+
+	pdev = bus->host_pci;
+	mutex_init(&bus->pci_sprom_mutex);
+	err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom);
+	if (err)
+		goto out;
+
+out:
+	return err;
+}
diff --git a/drivers/ssb/pcihost_wrapper.c b/drivers/ssb/pcihost_wrapper.c
new file mode 100644
index 0000000..82a10ab
--- /dev/null
+++ b/drivers/ssb/pcihost_wrapper.c
@@ -0,0 +1,104 @@
+/*
+ * Sonics Silicon Backplane
+ * PCI Hostdevice wrapper
+ *
+ * Copyright (c) 2005 Martin Langer <martin-langer-Mmb7MZpHnFY@public.gmane.org>
+ * Copyright (c) 2005 Stefano Brivio <st3-sGOZH3hwPm2sTnJN9+BGXg@public.gmane.org>
+ * Copyright (c) 2005 Danny van Dyk <kugelfang-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
+ * Copyright (c) 2005 Andreas Jaggi <andreas.jaggi-xegAWGFB9mQXXHkOk0aIfQ@public.gmane.org>
+ * Copyright (c) 2005-2007 Michael Buesch <mbuesch-KuiJ5kEpwI6ELgA04lAiVw@public.gmane.org>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/pci.h>
+#include <linux/ssb/ssb.h>
+
+
+#ifdef CONFIG_PM
+static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state)
+{
+	pci_save_state(dev);
+	pci_disable_device(dev);
+	pci_set_power_state(dev, pci_choose_state(dev, state));
+
+	return 0;
+}
+
+static int ssb_pcihost_resume(struct pci_dev *dev)
+{
+	int err;
+
+	pci_set_power_state(dev, 0);
+	err = pci_enable_device(dev);
+	if (err)
+		return err;
+	pci_restore_state(dev);
+
+	return 0;
+}
+#else /* CONFIG_PM */
+# define ssb_pcihost_suspend	NULL
+# define ssb_pcihost_resume	NULL
+#endif /* CONFIG_PM */
+
+static int ssb_pcihost_probe(struct pci_dev *dev,
+			     const struct pci_device_id *id)
+{
+	struct ssb_bus *ssb;
+	int err = -ENOMEM;
+	const char *name;
+
+	ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
+	if (!ssb)
+		goto out;
+	err = pci_enable_device(dev);
+	if (err)
+		goto err_kfree_ssb;
+	name = dev->dev.bus_id;
+	if (dev->driver && dev->driver->name)
+		name = dev->driver->name;
+	err = pci_request_regions(dev, name);
+	if (err)
+		goto err_pci_disable;
+	pci_set_master(dev);
+
+	err = ssb_bus_pcibus_register(ssb, dev);
+	if (err)
+		goto err_pci_release_regions;
+
+	pci_set_drvdata(dev, ssb);
+
+out:
+	return err;
+
+err_pci_release_regions:
+	pci_release_regions(dev);
+err_pci_disable:
+	pci_disable_device(dev);
+err_kfree_ssb:
+	kfree(ssb);
+	return err;
+}
+
+static void ssb_pcihost_remove(struct pci_dev *dev)
+{
+	struct ssb_bus *ssb = pci_get_drvdata(dev);
+
+	ssb_bus_unregister(ssb);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+	kfree(ssb);
+	pci_set_drvdata(dev, NULL);
+}
+
+int ssb_pcihost_register(struct pci_driver *driver)
+{
+	driver->probe = ssb_pcihost_probe;
+	driver->remove = ssb_pcihost_remove;
+	driver->suspend = ssb_pcihost_suspend;
+	driver->resume = ssb_pcihost_resume;
+
+	return pci_register_driver(driver);
+}
+EXPORT_SYMBOL(ssb_pcihost_register);
diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c
new file mode 100644
index 0000000..b102738
--- /dev/null
+++ b/drivers/ssb/pcmcia.c
@@ -0,0 +1,265 @@
+/*
+ * Sonics Silicon Backplane
+ * PCMCIA-Hostbus related functions
+ *
+ * Copyright 2006 Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
+ * Copyright 2007 Michael Buesch <mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/delay.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include "ssb_private.h"
+
+
+int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
+			      u8 coreidx)
+{
+	struct pcmcia_device *pdev = bus->host_pcmcia;
+	int err;
+	int attempts = 0;
+	u32 cur_core;
+	conf_reg_t reg;
+	u32 addr;
+	u32 read_addr;
+
+	addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
+	while (1) {
+		reg.Action = CS_WRITE;
+		reg.Offset = 0x2E;
+		reg.Value = (addr & 0x0000F000) >> 12;
+		err = pcmcia_access_configuration_register(pdev, &reg);
+		if (err != CS_SUCCESS)
+			goto error;
+		reg.Offset = 0x30;
+		reg.Value = (addr & 0x00FF0000) >> 16;
+		err = pcmcia_access_configuration_register(pdev, &reg);
+		if (err != CS_SUCCESS)
+			goto error;
+		reg.Offset = 0x32;
+		reg.Value = (addr & 0xFF000000) >> 24;
+		err = pcmcia_access_configuration_register(pdev, &reg);
+		if (err != CS_SUCCESS)
+			goto error;
+
+		read_addr = 0;
+
+		reg.Action = CS_READ;
+		reg.Offset = 0x2E;
+		err = pcmcia_access_configuration_register(pdev, &reg);
+		if (err != CS_SUCCESS)
+			goto error;
+		read_addr |= (reg.Value & 0xF) << 12;
+		reg.Offset = 0x30;
+		err = pcmcia_access_configuration_register(pdev, &reg);
+		if (err != CS_SUCCESS)
+			goto error;
+		read_addr |= reg.Value << 16;
+		reg.Offset = 0x32;
+		err = pcmcia_access_configuration_register(pdev, &reg);
+		if (err != CS_SUCCESS)
+			goto error;
+		read_addr |= reg.Value << 24;
+
+		cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE;
+		if (cur_core == coreidx)
+			break;
+
+		if (attempts++ > SSB_BAR0_MAX_RETRIES)
+			goto error;
+		udelay(10);
+	}
+
+	return 0;
+error:
+	ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx);
+	return -ENODEV;
+}
+
+int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+			   struct ssb_device *dev)
+{
+	int err;
+	unsigned long flags;
+
+	ssb_dprintk(KERN_INFO PFX
+		    "Switching to %s core, index %d\n",
+		    ssb_core_name(dev->id.coreid),
+		    dev->core_index);
+
+	spin_lock_irqsave(&bus->bar_lock, flags);
+	err = ssb_pcmcia_switch_coreidx(bus, dev->core_index);
+	if (!err)
+		bus->mapped_device = dev;
+	spin_unlock_irqrestore(&bus->bar_lock, flags);
+
+	return err;
+}
+
+int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg)
+{
+	int attempts = 0;
+	unsigned long flags;
+	conf_reg_t reg;
+	int res, err = 0;
+
+	SSB_WARN_ON((seg != 0) && (seg != 1));
+	reg.Offset = 0x34;
+	reg.Function = 0;
+	spin_lock_irqsave(&bus->bar_lock, flags);
+	while (1) {
+		reg.Action = CS_WRITE;
+		reg.Value = seg;
+		res = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
+		if (unlikely(res != CS_SUCCESS))
+			goto error;
+		reg.Value = 0xFF;
+		reg.Action = CS_READ;
+		res = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
+		if (unlikely(res != CS_SUCCESS))
+			goto error;
+
+		if (reg.Value == seg)
+			break;
+
+		if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES))
+			goto error;
+		udelay(10);
+	}
+	bus->mapped_pcmcia_seg = seg;
+out_unlock:
+	spin_unlock_irqrestore(&bus->bar_lock, flags);
+	return err;
+error:
+	ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n");
+	err = -ENODEV;
+	goto out_unlock;
+}
+
+/* These are the main device register access functions.
+ * do_select_core is inline to have the likely hotpath inline.
+ * All unlikely codepaths are out-of-line. */
+static inline int do_select_core(struct ssb_bus *bus,
+				 struct ssb_device *dev,
+				 u16 *offset)
+{
+	int err;
+	u8 need_seg = (*offset >= 0x800) ? 1 : 0;
+
+	if (unlikely(dev != bus->mapped_device)) {
+		err = ssb_pcmcia_switch_core(bus, dev);
+		if (unlikely(err))
+			return err;
+	}
+	if (unlikely(need_seg != bus->mapped_pcmcia_seg)) {
+		err = ssb_pcmcia_switch_segment(bus, need_seg);
+		if (unlikely(err))
+			return err;
+	}
+	if (need_seg == 1)
+		*offset -= 0x800;
+
+	return 0;
+}
+
+static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset)
+{
+	struct ssb_bus *bus = dev->bus;
+	u16 x;
+
+	if (unlikely(do_select_core(bus, dev, &offset)))
+		return 0xFFFF;
+	x = readw(bus->mmio + offset);
+
+	return x;
+}
+
+static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset)
+{
+	struct ssb_bus *bus = dev->bus;
+	u32 x;
+
+	if (unlikely(do_select_core(bus, dev, &offset)))
+		return 0xFFFFFFFF;
+	x = readl(bus->mmio + offset);
+
+	return x;
+}
+
+static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value)
+{
+	struct ssb_bus *bus = dev->bus;
+
+	if (unlikely(do_select_core(bus, dev, &offset)))
+		return;
+	writew(value, bus->mmio + offset);
+}
+
+static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value)
+{
+	struct ssb_bus *bus = dev->bus;
+
+	if (unlikely(do_select_core(bus, dev, &offset)))
+		return;
+	readw(bus->mmio + offset);
+	writew(value >> 16, bus->mmio + offset + 2);
+	readw(bus->mmio + offset);
+	writew(value, bus->mmio + offset);
+}
+
+/* Not "static", as it's used in main.c */
+const struct ssb_bus_ops ssb_pcmcia_ops = {
+	.read16		= ssb_pcmcia_read16,
+	.read32		= ssb_pcmcia_read32,
+	.write16	= ssb_pcmcia_write16,
+	.write32	= ssb_pcmcia_write32,
+};
+
+int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
+			      struct ssb_init_invariants *iv)
+{
+	//TODO
+	return 0;
+}
+
+int ssb_pcmcia_init(struct ssb_bus *bus)
+{
+	conf_reg_t reg;
+	int err;
+
+	if (bus->bustype != SSB_BUSTYPE_PCMCIA)
+		return 0;
+
+	/* Switch segment to a known state and sync
+	 * bus->mapped_pcmcia_seg with hardware state. */
+	ssb_pcmcia_switch_segment(bus, 0);
+
+	/* Init IRQ routing */
+	reg.Action = CS_READ;
+	reg.Function = 0;
+	if (bus->chip_id == 0x4306)
+		reg.Offset = 0x00;
+	else
+		reg.Offset = 0x80;
+	err = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
+	if (err != CS_SUCCESS)
+		goto error;
+	reg.Action = CS_WRITE;
+	reg.Value |= 0x04 | 0x01;
+	err = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
+	if (err != CS_SUCCESS)
+		goto error;
+
+	return 0;
+error:
+	return -ENODEV;
+}
diff --git a/drivers/ssb/scan.c b/drivers/ssb/scan.c
new file mode 100644
index 0000000..96258c6
--- /dev/null
+++ b/drivers/ssb/scan.c
@@ -0,0 +1,413 @@
+/*
+ * Sonics Silicon Backplane
+ * Bus scanning
+ *
+ * Copyright (C) 2005-2007 Michael Buesch <mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
+ * Copyright (C) 2005 Martin Langer <martin-langer-Mmb7MZpHnFY@public.gmane.org>
+ * Copyright (C) 2005 Stefano Brivio <st3-sGOZH3hwPm2sTnJN9+BGXg@public.gmane.org>
+ * Copyright (C) 2005 Danny van Dyk <kugelfang-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>
+ * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi-xegAWGFB9mQXXHkOk0aIfQ@public.gmane.org>
+ * Copyright (C) 2006 Broadcom Corporation.
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/ssb/ssb_regs.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#include "ssb_private.h"
+
+
+const char *ssb_core_name(u16 coreid)
+{
+	switch (coreid) {
+	case SSB_DEV_CHIPCOMMON:
+		return "ChipCommon";
+	case SSB_DEV_ILINE20:
+		return "ILine 20";
+	case SSB_DEV_SDRAM:
+		return "SDRAM";
+	case SSB_DEV_PCI:
+		return "PCI";
+	case SSB_DEV_MIPS:
+		return "MIPS";
+	case SSB_DEV_ETHERNET:
+		return "Fast Ethernet";
+	case SSB_DEV_V90:
+		return "V90";
+	case SSB_DEV_USB11_HOSTDEV:
+		return "USB 1.1 Hostdev";
+	case SSB_DEV_ADSL:
+		return "ADSL";
+	case SSB_DEV_ILINE100:
+		return "ILine 100";
+	case SSB_DEV_IPSEC:
+		return "IPSEC";
+	case SSB_DEV_PCMCIA:
+		return "PCMCIA";
+	case SSB_DEV_INTERNAL_MEM:
+		return "Internal Memory";
+	case SSB_DEV_MEMC_SDRAM:
+		return "MEMC SDRAM";
+	case SSB_DEV_EXTIF:
+		return "EXTIF";
+	case SSB_DEV_80211:
+		return "IEEE 802.11";
+	case SSB_DEV_MIPS_3302:
+		return "MIPS 3302";
+	case SSB_DEV_USB11_HOST:
+		return "USB 1.1 Host";
+	case SSB_DEV_USB11_DEV:
+		return "USB 1.1 Device";
+	case SSB_DEV_USB20_HOST:
+		return "USB 2.0 Host";
+	case SSB_DEV_USB20_DEV:
+		return "USB 2.0 Device";
+	case SSB_DEV_SDIO_HOST:
+		return "SDIO Host";
+	case SSB_DEV_ROBOSWITCH:
+		return "Roboswitch";
+	case SSB_DEV_PARA_ATA:
+		return "PATA";
+	case SSB_DEV_SATA_XORDMA:
+		return "SATA XOR-DMA";
+	case SSB_DEV_ETHERNET_GBIT:
+		return "GBit Ethernet";
+	case SSB_DEV_PCIE:
+		return "PCI-E";
+	case SSB_DEV_MIMO_PHY:
+		return "MIMO PHY";
+	case SSB_DEV_SRAM_CTRLR:
+		return "SRAM Controller";
+	case SSB_DEV_MINI_MACPHY:
+		return "Mini MACPHY";
+	case SSB_DEV_ARM_1176:
+		return "ARM 1176";
+	case SSB_DEV_ARM_7TDMI:
+		return "ARM 7TDMI";
+	}
+	return "UNKNOWN";
+}
+
+static u16 pcidev_to_chipid(struct pci_dev *pci_dev)
+{
+	u16 chipid_fallback = 0;
+
+	switch (pci_dev->device) {
+	case 0x4301:
+		chipid_fallback = 0x4301;
+		break;
+	case 0x4305 ... 0x4307:
+		chipid_fallback = 0x4307;
+		break;
+	case 0x4403:
+		chipid_fallback = 0x4402;
+		break;
+	case 0x4610 ... 0x4615:
+		chipid_fallback = 0x4610;
+		break;
+	case 0x4710 ... 0x4715:
+		chipid_fallback = 0x4710;
+		break;
+	case 0x4320 ... 0x4325:
+		chipid_fallback = 0x4309;
+		break;
+	case PCI_DEVICE_ID_BCM4401:
+	case PCI_DEVICE_ID_BCM4401B0:
+	case PCI_DEVICE_ID_BCM4401B1:
+		chipid_fallback = 0x4401;
+		break;
+	default:
+		ssb_printk(KERN_ERR PFX
+			   "PCI-ID not in fallback list\n");
+	}
+
+	return chipid_fallback;
+}
+
+static u8 chipid_to_nrcores(u16 chipid)
+{
+	switch (chipid) {
+	case 0x5365:
+		return 7;
+	case 0x4306:
+		return 6;
+	case 0x4310:
+		return 8;
+	case 0x4307:
+	case 0x4301:
+		return 5;
+	case 0x4401:
+	case 0x4402:
+		return 3;
+	case 0x4710:
+	case 0x4610:
+	case 0x4704:
+		return 9;
+	default:
+		ssb_printk(KERN_ERR PFX
+			   "CHIPID not in nrcores fallback list\n");
+	}
+
+	return 1;
+}
+
+static u32 scan_read32(struct ssb_bus *bus, u8 current_coreidx,
+		       u16 offset)
+{
+	switch (bus->bustype) {
+	case SSB_BUSTYPE_SSB:
+		offset += current_coreidx * SSB_CORE_SIZE;
+		break;
+	case SSB_BUSTYPE_PCI:
+		break;
+	case SSB_BUSTYPE_PCMCIA:
+		if (offset >= 0x800) {
+			ssb_pcmcia_switch_segment(bus, 1);
+			offset -= 0x800;
+		} else
+			ssb_pcmcia_switch_segment(bus, 0);
+		break;
+	}
+	return readl(bus->mmio + offset);
+}
+
+static int scan_switchcore(struct ssb_bus *bus, u8 coreidx)
+{
+	switch (bus->bustype) {
+	case SSB_BUSTYPE_SSB:
+		break;
+	case SSB_BUSTYPE_PCI:
+		return ssb_pci_switch_coreidx(bus, coreidx);
+	case SSB_BUSTYPE_PCMCIA:
+		return ssb_pcmcia_switch_coreidx(bus, coreidx);
+	}
+	return 0;
+}
+
+void ssb_iounmap(struct ssb_bus *bus)
+{
+	switch (bus->bustype) {
+	case SSB_BUSTYPE_SSB:
+	case SSB_BUSTYPE_PCMCIA:
+		iounmap(bus->mmio);
+		break;
+	case SSB_BUSTYPE_PCI:
+#ifdef CONFIG_SSB_PCIHOST
+		pci_iounmap(bus->host_pci, bus->mmio);
+#else
+		SSB_BUG_ON(1); /* Can't reach this code. */
+#endif
+		break;
+	}
+	bus->mmio = NULL;
+	bus->mapped_device = NULL;
+}
+
+static void __iomem *ssb_ioremap(struct ssb_bus *bus,
+				 unsigned long baseaddr)
+{
+	void __iomem *mmio = NULL;
+
+	switch (bus->bustype) {
+	case SSB_BUSTYPE_SSB:
+		/* Only map the first core for now. */
+		/* fallthrough... */
+	case SSB_BUSTYPE_PCMCIA:
+		mmio = ioremap(baseaddr, SSB_CORE_SIZE);
+		break;
+	case SSB_BUSTYPE_PCI:
+#ifdef CONFIG_SSB_PCIHOST
+		mmio = pci_iomap(bus->host_pci, 0, ~0UL);
+#else
+		SSB_BUG_ON(1); /* Can't reach this code. */
+#endif
+		break;
+	}
+
+	return mmio;
+}
+
+static int we_support_multiple_80211_cores(struct ssb_bus *bus)
+{
+	/* More than one 802.11 core is only supported by special chips.
+	 * There are chips with two 802.11 cores, but with dangling
+	 * pins on the second core. Be careful and reject them here.
+	 */
+
+#ifdef CONFIG_SSB_PCIHOST
+	if (bus->bustype == SSB_BUSTYPE_PCI) {
+		if (bus->host_pci->vendor == PCI_VENDOR_ID_BROADCOM &&
+		    bus->host_pci->device == 0x4324)
+			return 1;
+	}
+#endif /* CONFIG_SSB_PCIHOST */
+	return 0;
+}
+
+int ssb_bus_scan(struct ssb_bus *bus,
+		 unsigned long baseaddr)
+{
+	int err = -ENOMEM;
+	void __iomem *mmio;
+	u32 idhi, cc, rev, tmp;
+	int dev_i, i;
+	struct ssb_device *dev;
+	int nr_80211_cores = 0;
+
+	mmio = ssb_ioremap(bus, baseaddr);
+	if (!mmio)
+		goto out;
+	bus->mmio = mmio;
+
+	err = scan_switchcore(bus, 0); /* Switch to first core */
+	if (err)
+		goto err_unmap;
+
+	idhi = scan_read32(bus, 0, SSB_IDHIGH);
+	cc = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT;
+	rev = (idhi & SSB_IDHIGH_RCLO);
+	rev |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT;
+
+	bus->nr_devices = 0;
+	if (cc == SSB_DEV_CHIPCOMMON) {
+		tmp = scan_read32(bus, 0, SSB_CHIPCO_CHIPID);
+
+		bus->chip_id = (tmp & SSB_CHIPCO_IDMASK);
+		bus->chip_rev = (tmp & SSB_CHIPCO_REVMASK) >>
+				SSB_CHIPCO_REVSHIFT;
+		bus->chip_package = (tmp & SSB_CHIPCO_PACKMASK) >>
+				    SSB_CHIPCO_PACKSHIFT;
+		if (rev >= 4) {
+			bus->nr_devices = (tmp & SSB_CHIPCO_NRCORESMASK) >>
+					  SSB_CHIPCO_NRCORESSHIFT;
+		}
+		tmp = scan_read32(bus, 0, SSB_CHIPCO_CAP);
+		bus->chipco.capabilities = tmp;
+	} else {
+		if (bus->bustype == SSB_BUSTYPE_PCI) {
+			bus->chip_id = pcidev_to_chipid(bus->host_pci);
+			pci_read_config_word(bus->host_pci, PCI_REVISION_ID,
+					     &bus->chip_rev);
+			bus->chip_package = 0;
+		} else {
+			bus->chip_id = 0x4710;
+			bus->chip_rev = 0;
+			bus->chip_package = 0;
+		}
+	}
+	if (!bus->nr_devices)
+		bus->nr_devices = chipid_to_nrcores(bus->chip_id);
+	if (bus->nr_devices > ARRAY_SIZE(bus->devices)) {
+		ssb_printk(KERN_ERR PFX
+			   "More than %d ssb cores found (%d)\n",
+			   SSB_MAX_NR_CORES, bus->nr_devices);
+		goto err_unmap;
+	}
+	if (bus->bustype == SSB_BUSTYPE_SSB) {
+		/* Now that we know the number of cores,
+		 * remap the whole IO space for all cores.
+		 */
+		err = -ENOMEM;
+		iounmap(mmio);
+		mmio = ioremap(baseaddr, SSB_CORE_SIZE * bus->nr_devices);
+		if (!mmio)
+			goto out;
+		bus->mmio = mmio;
+	}
+
+	/* Fetch basic information about each core/device */
+	for (i = 0, dev_i = 0; i < bus->nr_devices; i++) {
+		err = scan_switchcore(bus, i);
+		if (err)
+			goto err_unmap;
+		dev = &(bus->devices[dev_i]);
+
+		idhi = scan_read32(bus, i, SSB_IDHIGH);
+		dev->id.coreid = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT;
+		dev->id.revision = (idhi & SSB_IDHIGH_RCLO);
+		dev->id.revision |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT;
+		dev->id.vendor = (idhi & SSB_IDHIGH_VC) >> SSB_IDHIGH_VC_SHIFT;
+		dev->core_index = i;
+		dev->bus = bus;
+		dev->ops = bus->ops;
+
+		ssb_dprintk(KERN_INFO PFX
+			    "Core %d found: %s "
+			    "(cc 0x%03X, rev 0x%02X, vendor 0x%04X)\n",
+			    i, ssb_core_name(dev->id.coreid),
+			    dev->id.coreid, dev->id.revision, dev->id.vendor);
+
+		switch (dev->id.coreid) {
+		case SSB_DEV_80211:
+			nr_80211_cores++;
+			if (nr_80211_cores > 1) {
+				if (!we_support_multiple_80211_cores(bus)) {
+					ssb_dprintk(KERN_INFO PFX "Ignoring additional "
+						    "802.11 core\n");
+					continue;
+				}
+			}
+			break;
+		case SSB_DEV_EXTIF:
+#ifdef CONFIG_SSB_DRIVER_EXTIF
+			if (bus->extif.dev) {
+				ssb_printk(KERN_WARNING PFX
+					   "WARNING: Multiple EXTIFs found\n");
+				break;
+			}
+			bus->extif.dev = dev;
+#endif /* CONFIG_SSB_DRIVER_EXTIF */
+			break;
+		case SSB_DEV_CHIPCOMMON:
+			if (bus->chipco.dev) {
+				ssb_printk(KERN_WARNING PFX
+					   "WARNING: Multiple ChipCommon found\n");
+				break;
+			}
+			bus->chipco.dev = dev;
+			break;
+		case SSB_DEV_MIPS:
+		case SSB_DEV_MIPS_3302:
+#ifdef CONFIG_SSB_DRIVER_MIPS
+			if (bus->mipscore.dev) {
+				ssb_printk(KERN_WARNING PFX
+					   "WARNING: Multiple MIPS cores found\n");
+				break;
+			}
+			bus->mipscore.dev = dev;
+#endif /* CONFIG_SSB_DRIVER_MIPS */
+			break;
+		case SSB_DEV_PCI:
+		case SSB_DEV_PCIE:
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+			if (bus->pcicore.dev) {
+				ssb_printk(KERN_WARNING PFX
+					   "WARNING: Multiple PCI(E) cores found\n");
+				break;
+			}
+			bus->pcicore.dev = dev;
+#endif /* CONFIG_SSB_DRIVER_PCICORE */
+			break;
+		default:
+			break;
+		}
+
+		dev_i++;
+	}
+	bus->nr_devices = dev_i;
+
+	err = 0;
+out:
+	return err;
+err_unmap:
+	ssb_iounmap(bus);
+	goto out;
+}
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
new file mode 100644
index 0000000..4aa77b4
--- /dev/null
+++ b/drivers/ssb/ssb_private.h
@@ -0,0 +1,122 @@
+#ifndef LINUX_SSB_PRIVATE_H_
+#define LINUX_SSB_PRIVATE_H_
+
+#include <linux/ssb/ssb.h>
+#include <linux/types.h>
+
+
+#define PFX	"ssb: "
+
+#ifdef CONFIG_SSB_SILENT
+# define ssb_printk(fmt, x...)	do { /* nothing */ } while (0)
+#else
+# define ssb_printk		printk
+#endif /* CONFIG_SSB_SILENT */
+
+/* dprintk: Debugging printk; vanishes for non-debug compilation */
+#ifdef CONFIG_SSB_DEBUG
+# define ssb_dprintk(fmt, x...)	ssb_printk(fmt , ##x)
+#else
+# define ssb_dprintk(fmt, x...)	do { /* nothing */ } while (0)
+#endif
+
+#ifdef CONFIG_SSB_DEBUG
+# define SSB_WARN_ON(x)		WARN_ON(x)
+# define SSB_BUG_ON(x)		BUG_ON(x)
+#else
+static inline int __ssb_do_nothing(int x) { return x; }
+# define SSB_WARN_ON(x)		__ssb_do_nothing(unlikely(!!(x)))
+# define SSB_BUG_ON(x)		__ssb_do_nothing(unlikely(!!(x)))
+#endif
+
+
+/* pci.c */
+#ifdef CONFIG_SSB_PCIHOST
+extern int ssb_pci_switch_core(struct ssb_bus *bus,
+			       struct ssb_device *dev);
+extern int ssb_pci_switch_coreidx(struct ssb_bus *bus,
+				  u8 coreidx);
+extern int ssb_pci_xtal(struct ssb_bus *bus, u32 what,
+			int turn_on);
+extern int ssb_pci_get_invariants(struct ssb_bus *bus,
+				  struct ssb_init_invariants *iv);
+extern void ssb_pci_exit(struct ssb_bus *bus);
+extern int ssb_pci_init(struct ssb_bus *bus);
+extern const struct ssb_bus_ops ssb_pci_ops;
+
+#else /* CONFIG_SSB_PCIHOST */
+
+static inline int ssb_pci_switch_core(struct ssb_bus *bus,
+				      struct ssb_device *dev)
+{
+	return 0;
+}
+static inline int ssb_pci_switch_coreidx(struct ssb_bus *bus,
+					 u8 coreidx)
+{
+	return 0;
+}
+static inline int ssb_pci_xtal(struct ssb_bus *bus, u32 what,
+			       int turn_on)
+{
+	return 0;
+}
+static inline void ssb_pci_exit(struct ssb_bus *bus)
+{
+}
+static inline int ssb_pci_init(struct ssb_bus *bus)
+{
+	return 0;
+}
+#endif /* CONFIG_SSB_PCIHOST */
+
+
+/* pcmcia.c */
+#ifdef CONFIG_SSB_PCMCIAHOST
+extern int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+				  struct ssb_device *dev);
+extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
+				     u8 coreidx);
+extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
+				     u8 seg);
+extern int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
+				     struct ssb_init_invariants *iv);
+extern int ssb_pcmcia_init(struct ssb_bus *bus);
+extern const struct ssb_bus_ops ssb_pcmcia_ops;
+#else /* CONFIG_SSB_PCMCIAHOST */
+static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+					 struct ssb_device *dev)
+{
+	return 0;
+}
+static inline int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
+					    u8 coreidx)
+{
+	return 0;
+}
+static inline int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
+					    u8 seg)
+{
+	return 0;
+}
+static inline int ssb_pcmcia_init(struct ssb_bus *bus)
+{
+	return 0;
+}
+#endif /* CONFIG_SSB_PCMCIAHOST */
+
+
+/* scan.c */
+extern const char *ssb_core_name(u16 coreid);
+extern int ssb_bus_scan(struct ssb_bus *bus,
+			unsigned long baseaddr);
+extern void ssb_iounmap(struct ssb_bus *ssb);
+
+
+/* core.c */
+extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m);
+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);
+
+#endif /* LINUX_SSB_PRIVATE_H_ */
diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h
new file mode 100644
index 0000000..d58c24b
--- /dev/null
+++ b/include/linux/ssb/ssb_driver_chipcommon.h
@@ -0,0 +1,385 @@
+#ifndef LINUX_SSB_CHIPCO_H_
+#define LINUX_SSB_CHIPCO_H_
+
+/* SonicsSiliconBackplane CHIPCOMMON core hardware definitions
+ *
+ * The chipcommon core provides chip identification, SB control,
+ * jtag, 0/1/2 uarts, clock frequency control, a watchdog interrupt timer,
+ * gpio interface, extbus, and support for serial and parallel flashes.
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, Michael Buesch <mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
+ *
+ * Licensed under the GPL version 2. See COPYING for details.
+ */
+
+/** ChipCommon core registers. **/
+
+#define SSB_CHIPCO_CHIPID		0x0000
+#define  SSB_CHIPCO_IDMASK		0x0000FFFF
+#define  SSB_CHIPCO_REVMASK		0x000F0000
+#define  SSB_CHIPCO_REVSHIFT		16
+#define  SSB_CHIPCO_PACKMASK		0x00F00000
+#define  SSB_CHIPCO_PACKSHIFT		20
+#define  SSB_CHIPCO_NRCORESMASK		0x0F000000
+#define  SSB_CHIPCO_NRCORESSHIFT	24
+#define SSB_CHIPCO_CAP	 		0x0004		/* Capabilities */
+#define  SSB_CHIPCO_CAP_NRUART		0x00000003	/* # of UARTs */
+#define  SSB_CHIPCO_CAP_MIPSEB		0x00000004	/* MIPS in BigEndian Mode */
+#define  SSB_CHIPCO_CAP_UARTCLK		0x00000018	/* UART clock select */
+#define   SSB_CHIPCO_CAP_UARTCLK_INT	0x00000008	/* UARTs are driven by internal divided clock */
+#define  SSB_CHIPCO_CAP_UARTGPIO	0x00000020	/* UARTs on GPIO 15-12 */
+#define  SSB_CHIPCO_CAP_EXTBUS		0x000000C0	/* External buses present */
+#define  SSB_CHIPCO_CAP_FLASHT		0x00000700	/* Flash Type */
+#define   SSB_CHIPCO_FLASHT_NONE	0x00000000	/* No flash */
+#define   SSB_CHIPCO_FLASHT_STSER	0x00000100	/* ST serial flash */
+#define   SSB_CHIPCO_FLASHT_ATSER	0x00000200	/* Atmel serial flash */
+#define	  SSB_CHIPCO_FLASHT_PARA	0x00000700	/* Parallel flash */
+#define  SSB_CHIPCO_CAP_PLLT		0x00038000	/* PLL Type */
+#define   SSB_PLLTYPE_NONE		0x00000000
+#define   SSB_PLLTYPE_1			0x00010000	/* 48Mhz base, 3 dividers */
+#define   SSB_PLLTYPE_2			0x00020000	/* 48Mhz, 4 dividers */
+#define   SSB_PLLTYPE_3			0x00030000	/* 25Mhz, 2 dividers */
+#define   SSB_PLLTYPE_4			0x00008000	/* 48Mhz, 4 dividers */
+#define   SSB_PLLTYPE_5			0x00018000	/* 25Mhz, 4 dividers */
+#define   SSB_PLLTYPE_6			0x00028000	/* 100/200 or 120/240 only */
+#define   SSB_PLLTYPE_7			0x00038000	/* 25Mhz, 4 dividers */
+#define  SSB_CHIPCO_CAP_PCTL		0x00040000	/* Power Control */
+#define  SSB_CHIPCO_CAP_OTPS		0x00380000	/* OTP size */
+#define  SSB_CHIPCO_CAP_OTPS_SHIFT	19
+#define  SSB_CHIPCO_CAP_OTPS_BASE	5
+#define  SSB_CHIPCO_CAP_JTAGM		0x00400000	/* JTAG master present */
+#define  SSB_CHIPCO_CAP_BROM		0x00800000	/* Internal boot ROM active */
+#define  SSB_CHIPCO_CAP_64BIT		0x08000000	/* 64-bit Backplane */
+#define SSB_CHIPCO_CORECTL		0x0008
+#define  SSB_CHIPCO_CORECTL_UARTCLK0	0x00000001	/* Drive UART with internal clock */
+#define	 SSB_CHIPCO_CORECTL_SE		0x00000002	/* sync clk out enable (corerev >= 3) */
+#define SSB_CHIPCO_BIST			0x000C
+#define SSB_CHIPCO_OTPS			0x0010		/* OTP status */
+#define	 SSB_CHIPCO_OTPS_PROGFAIL	0x80000000
+#define	 SSB_CHIPCO_OTPS_PROTECT	0x00000007
+#define	 SSB_CHIPCO_OTPS_HW_PROTECT	0x00000001
+#define	 SSB_CHIPCO_OTPS_SW_PROTECT	0x00000002
+#define	 SSB_CHIPCO_OTPS_CID_PROTECT	0x00000004
+#define SSB_CHIPCO_OTPC			0x0014		/* OTP control */
+#define	 SSB_CHIPCO_OTPC_RECWAIT	0xFF000000
+#define	 SSB_CHIPCO_OTPC_PROGWAIT	0x00FFFF00
+#define	 SSB_CHIPCO_OTPC_PRW_SHIFT	8
+#define	 SSB_CHIPCO_OTPC_MAXFAIL	0x00000038
+#define	 SSB_CHIPCO_OTPC_VSEL		0x00000006
+#define	 SSB_CHIPCO_OTPC_SELVL		0x00000001
+#define SSB_CHIPCO_OTPP			0x0018		/* OTP prog */
+#define	 SSB_CHIPCO_OTPP_COL		0x000000FF
+#define	 SSB_CHIPCO_OTPP_ROW		0x0000FF00
+#define	 SSB_CHIPCO_OTPP_ROW_SHIFT	8
+#define	 SSB_CHIPCO_OTPP_READERR	0x10000000
+#define	 SSB_CHIPCO_OTPP_VALUE		0x20000000
+#define	 SSB_CHIPCO_OTPP_READ		0x40000000
+#define	 SSB_CHIPCO_OTPP_START		0x80000000
+#define	 SSB_CHIPCO_OTPP_BUSY		0x80000000
+#define SSB_CHIPCO_IRQSTAT		0x0020
+#define SSB_CHIPCO_IRQMASK		0x0024
+#define	 SSB_CHIPCO_IRQ_GPIO		0x00000001	/* gpio intr */
+#define	 SSB_CHIPCO_IRQ_EXT		0x00000002	/* ro: ext intr pin (corerev >= 3) */
+#define	 SSB_CHIPCO_IRQ_WDRESET		0x80000000	/* watchdog reset occurred */
+#define SSB_CHIPCO_CHIPCTL		0x0028		/* Rev >= 11 only */
+#define SSB_CHIPCO_CHIPSTAT		0x002C		/* Rev >= 11 only */
+#define SSB_CHIPCO_JCMD			0x0030		/* Rev >= 10 only */
+#define  SSB_CHIPCO_JCMD_START		0x80000000
+#define  SSB_CHIPCO_JCMD_BUSY		0x80000000
+#define  SSB_CHIPCO_JCMD_PAUSE		0x40000000
+#define  SSB_CHIPCO_JCMD0_ACC_MASK	0x0000F000
+#define  SSB_CHIPCO_JCMD0_ACC_IRDR	0x00000000
+#define  SSB_CHIPCO_JCMD0_ACC_DR	0x00001000
+#define  SSB_CHIPCO_JCMD0_ACC_IR	0x00002000
+#define  SSB_CHIPCO_JCMD0_ACC_RESET	0x00003000
+#define  SSB_CHIPCO_JCMD0_ACC_IRPDR	0x00004000
+#define  SSB_CHIPCO_JCMD0_ACC_PDR	0x00005000
+#define  SSB_CHIPCO_JCMD0_IRW_MASK	0x00000F00
+#define  SSB_CHIPCO_JCMD_ACC_MASK	0x000F0000	/* Changes for corerev 11 */
+#define  SSB_CHIPCO_JCMD_ACC_IRDR	0x00000000
+#define  SSB_CHIPCO_JCMD_ACC_DR		0x00010000
+#define  SSB_CHIPCO_JCMD_ACC_IR		0x00020000
+#define  SSB_CHIPCO_JCMD_ACC_RESET	0x00030000
+#define  SSB_CHIPCO_JCMD_ACC_IRPDR	0x00040000
+#define  SSB_CHIPCO_JCMD_ACC_PDR	0x00050000
+#define  SSB_CHIPCO_JCMD_IRW_MASK	0x00001F00
+#define  SSB_CHIPCO_JCMD_IRW_SHIFT	8
+#define  SSB_CHIPCO_JCMD_DRW_MASK	0x0000003F
+#define SSB_CHIPCO_JIR			0x0034		/* Rev >= 10 only */
+#define SSB_CHIPCO_JDR			0x0038		/* Rev >= 10 only */
+#define SSB_CHIPCO_JCTL			0x003C		/* Rev >= 10 only */
+#define  SSB_CHIPCO_JCTL_FORCE_CLK	4		/* Force clock */
+#define  SSB_CHIPCO_JCTL_EXT_EN		2		/* Enable external targets */
+#define  SSB_CHIPCO_JCTL_EN		1		/* Enable Jtag master */
+#define SSB_CHIPCO_FLASHCTL		0x0040
+#define  SSB_CHIPCO_FLASHCTL_START	0x80000000
+#define  SSB_CHIPCO_FLASHCTL_BUSY	SSB_CHIPCO_FLASHCTL_START
+#define SSB_CHIPCO_FLASHADDR		0x0044
+#define SSB_CHIPCO_FLASHDATA		0x0048
+#define SSB_CHIPCO_BCAST_ADDR		0x0050
+#define SSB_CHIPCO_BCAST_DATA		0x0054
+#define SSB_CHIPCO_GPIOIN		0x0060
+#define SSB_CHIPCO_GPIOOUT		0x0064
+#define SSB_CHIPCO_GPIOOUTEN		0x0068
+#define SSB_CHIPCO_GPIOCTL		0x006C
+#define SSB_CHIPCO_GPIOPOL		0x0070
+#define SSB_CHIPCO_GPIOIRQ		0x0074
+#define SSB_CHIPCO_WATCHDOG		0x0080
+#define SSB_CHIPCO_GPIOTIMER		0x0088		/* LED powersave (corerev >= 16) */
+#define  SSB_CHIPCO_GPIOTIMER_ONTIME_SHIFT	16
+#define SSB_CHIPCO_GPIOTOUTM		0x008C		/* LED powersave (corerev >= 16) */
+#define SSB_CHIPCO_CLOCK_N		0x0090
+#define SSB_CHIPCO_CLOCK_SB		0x0094
+#define SSB_CHIPCO_CLOCK_PCI		0x0098
+#define SSB_CHIPCO_CLOCK_M2		0x009C
+#define SSB_CHIPCO_CLOCK_MIPS		0x00A0
+#define SSB_CHIPCO_CLKDIV		0x00A4		/* Rev >= 3 only */
+#define	 SSB_CHIPCO_CLKDIV_SFLASH	0x0F000000
+#define	 SSB_CHIPCO_CLKDIV_SFLASH_SHIFT	24
+#define	 SSB_CHIPCO_CLKDIV_OTP		0x000F0000
+#define	 SSB_CHIPCO_CLKDIV_OTP_SHIFT	16
+#define	 SSB_CHIPCO_CLKDIV_JTAG		0x00000F00
+#define	 SSB_CHIPCO_CLKDIV_JTAG_SHIFT	8
+#define	 SSB_CHIPCO_CLKDIV_UART		0x000000FF
+#define SSB_CHIPCO_PLLONDELAY		0x00B0		/* Rev >= 4 only */
+#define SSB_CHIPCO_FREFSELDELAY		0x00B4		/* Rev >= 4 only */
+#define SSB_CHIPCO_SLOWCLKCTL		0x00B8		/* 6 <= Rev <= 9 only */
+#define  SSB_CHIPCO_SLOWCLKCTL_SRC	0x00000007	/* slow clock source mask */
+#define	  SSB_CHIPCO_SLOWCLKCTL_SRC_LPO		0x00000000	/* source of slow clock is LPO */
+#define   SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL	0x00000001	/* source of slow clock is crystal */
+#define	  SSB_CHIPCO_SLOECLKCTL_SRC_PCI		0x00000002	/* source of slow clock is PCI */
+#define  SSB_CHIPCO_SLOWCLKCTL_LPOFREQ	0x00000200	/* LPOFreqSel, 1: 160Khz, 0: 32KHz */
+#define  SSB_CHIPCO_SLOWCLKCTL_LPOPD	0x00000400	/* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */
+#define  SSB_CHIPCO_SLOWCLKCTL_FSLOW	0x00000800	/* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */
+#define  SSB_CHIPCO_SLOWCLKCTL_IPLL	0x00001000	/* IgnorePllOffReq, 1/0: power logic ignores/honors PLL clock disable requests from core */
+#define  SSB_CHIPCO_SLOWCLKCTL_ENXTAL	0x00002000	/* XtalControlEn, 1/0: power logic does/doesn't disable crystal when appropriate */
+#define  SSB_CHIPCO_SLOWCLKCTL_XTALPU	0x00004000	/* XtalPU (RO), 1/0: crystal running/disabled */
+#define  SSB_CHIPCO_SLOWCLKCTL_CLKDIV	0xFFFF0000	/* ClockDivider (SlowClk = 1/(4+divisor)) */
+#define  SSB_CHIPCO_SLOWCLKCTL_CLKDIV_SHIFT	16
+#define SSB_CHIPCO_SYSCLKCTL		0x00C0		/* Rev >= 3 only */
+#define	 SSB_CHIPCO_SYSCLKCTL_IDLPEN	0x00000001	/* ILPen: Enable Idle Low Power */
+#define	 SSB_CHIPCO_SYSCLKCTL_ALPEN	0x00000002	/* ALPen: Enable Active Low Power */
+#define	 SSB_CHIPCO_SYSCLKCTL_PLLEN	0x00000004	/* ForcePLLOn */
+#define	 SSB_CHIPCO_SYSCLKCTL_FORCEALP	0x00000008	/* Force ALP (or HT if ALPen is not set */
+#define	 SSB_CHIPCO_SYSCLKCTL_FORCEHT	0x00000010	/* Force HT */
+#define  SSB_CHIPCO_SYSCLKCTL_CLKDIV	0xFFFF0000	/* ClkDiv  (ILP = 1/(4+divisor)) */
+#define  SSB_CHIPCO_SYSCLKCTL_CLKDIV_SHIFT	16
+#define SSB_CHIPCO_CLKSTSTR		0x00C4		/* Rev >= 3 only */
+#define SSB_CHIPCO_PCMCIA_CFG		0x0100
+#define SSB_CHIPCO_PCMCIA_MEMWAIT	0x0104
+#define SSB_CHIPCO_PCMCIA_ATTRWAIT	0x0108
+#define SSB_CHIPCO_PCMCIA_IOWAIT	0x010C
+#define SSB_CHIPCO_IDE_CFG		0x0110
+#define SSB_CHIPCO_IDE_MEMWAIT		0x0114
+#define SSB_CHIPCO_IDE_ATTRWAIT		0x0118
+#define SSB_CHIPCO_IDE_IOWAIT		0x011C
+#define SSB_CHIPCO_PROG_CFG		0x0120
+#define SSB_CHIPCO_PROG_WAITCNT		0x0124
+#define SSB_CHIPCO_FLASH_CFG		0x0128
+#define SSB_CHIPCO_FLASH_WAITCNT	0x012C
+#define SSB_CHIPCO_UART0_DATA		0x0300
+#define SSB_CHIPCO_UART0_IMR		0x0304
+#define SSB_CHIPCO_UART0_FCR		0x0308
+#define SSB_CHIPCO_UART0_LCR		0x030C
+#define SSB_CHIPCO_UART0_MCR		0x0310
+#define SSB_CHIPCO_UART0_LSR		0x0314
+#define SSB_CHIPCO_UART0_MSR		0x0318
+#define SSB_CHIPCO_UART0_SCRATCH	0x031C
+#define SSB_CHIPCO_UART1_DATA		0x0400
+#define SSB_CHIPCO_UART1_IMR		0x0404
+#define SSB_CHIPCO_UART1_FCR		0x0408
+#define SSB_CHIPCO_UART1_LCR		0x040C
+#define SSB_CHIPCO_UART1_MCR		0x0410
+#define SSB_CHIPCO_UART1_LSR		0x0414
+#define SSB_CHIPCO_UART1_MSR		0x0418
+#define SSB_CHIPCO_UART1_SCRATCH	0x041C
+
+
+
+/** Clockcontrol masks and values **/
+
+/* SSB_CHIPCO_CLOCK_N */
+#define	SSB_CHIPCO_CLK_N1		0x0000003F	/* n1 control */
+#define	SSB_CHIPCO_CLK_N2		0x00003F00	/* n2 control */
+#define	SSB_CHIPCO_CLK_N2_SHIFT		8
+#define	SSB_CHIPCO_CLK_PLLC		0x000F0000	/* pll control */
+#define	SSB_CHIPCO_CLK_PLLC_SHIFT	16
+
+/* SSB_CHIPCO_CLOCK_SB/PCI/UART */
+#define	SSB_CHIPCO_CLK_M1		0x0000003F	/* m1 control */
+#define	SSB_CHIPCO_CLK_M2		0x00003F00	/* m2 control */
+#define	SSB_CHIPCO_CLK_M2_SHIFT		8
+#define	SSB_CHIPCO_CLK_M3		0x003F0000	/* m3 control */
+#define	SSB_CHIPCO_CLK_M3_SHIFT		16
+#define	SSB_CHIPCO_CLK_MC		0x1F000000	/* mux control */
+#define	SSB_CHIPCO_CLK_MC_SHIFT		24
+
+/* N3M Clock control magic field values */
+#define	SSB_CHIPCO_CLK_F6_2		0x02		/* A factor of 2 in */
+#define	SSB_CHIPCO_CLK_F6_3		0x03		/* 6-bit fields like */
+#define	SSB_CHIPCO_CLK_F6_4		0x05		/* N1, M1 or M3 */
+#define	SSB_CHIPCO_CLK_F6_5		0x09
+#define	SSB_CHIPCO_CLK_F6_6		0x11
+#define	SSB_CHIPCO_CLK_F6_7		0x21
+
+#define	SSB_CHIPCO_CLK_F5_BIAS		5		/* 5-bit fields get this added */
+
+#define	SSB_CHIPCO_CLK_MC_BYPASS	0x08
+#define	SSB_CHIPCO_CLK_MC_M1		0x04
+#define	SSB_CHIPCO_CLK_MC_M1M2		0x02
+#define	SSB_CHIPCO_CLK_MC_M1M2M3	0x01
+#define	SSB_CHIPCO_CLK_MC_M1M3		0x11
+
+/* Type 2 Clock control magic field values */
+#define	SSB_CHIPCO_CLK_T2_BIAS		2		/* n1, n2, m1 & m3 bias */
+#define	SSB_CHIPCO_CLK_T2M2_BIAS	3		/* m2 bias */
+
+#define	SSB_CHIPCO_CLK_T2MC_M1BYP	1
+#define	SSB_CHIPCO_CLK_T2MC_M2BYP	2
+#define	SSB_CHIPCO_CLK_T2MC_M3BYP	4
+
+/* Type 6 Clock control magic field values */
+#define	SSB_CHIPCO_CLK_T6_MMASK		1		/* bits of interest in m */
+#define	SSB_CHIPCO_CLK_T6_M0		120000000	/* sb clock for m = 0 */
+#define	SSB_CHIPCO_CLK_T6_M1		100000000	/* sb clock for m = 1 */
+#define	SSB_CHIPCO_CLK_SB2MIPS_T6(sb)	(2 * (sb))
+
+/* Common clock base */
+#define	SSB_CHIPCO_CLK_BASE1		24000000	/* Half the clock freq */
+#define SSB_CHIPCO_CLK_BASE2		12500000	/* Alternate crystal on some PLL's */
+
+/* Clock control values for 200Mhz in 5350 */
+#define	SSB_CHIPCO_CLK_5350_N		0x0311
+#define	SSB_CHIPCO_CLK_5350_M		0x04020009
+
+
+/** Bits in the config registers **/
+
+#define	SSB_CHIPCO_CFG_EN		0x0001		/* Enable */
+#define	SSB_CHIPCO_CFG_EXTM		0x000E		/* Extif Mode */
+#define	 SSB_CHIPCO_CFG_EXTM_ASYNC	0x0002		/* Async/Parallel flash */
+#define	 SSB_CHIPCO_CFG_EXTM_SYNC	0x0004		/* Synchronous */
+#define	 SSB_CHIPCO_CFG_EXTM_PCMCIA	0x0008		/* PCMCIA */
+#define	 SSB_CHIPCO_CFG_EXTM_IDE	0x000A		/* IDE */
+#define	SSB_CHIPCO_CFG_DS16		0x0010		/* Data size, 0=8bit, 1=16bit */
+#define	SSB_CHIPCO_CFG_CLKDIV		0x0060		/* Sync: Clock divisor */
+#define	SSB_CHIPCO_CFG_CLKEN		0x0080		/* Sync: Clock enable */
+#define	SSB_CHIPCO_CFG_BSTRO		0x0100		/* Sync: Size/Bytestrobe */
+
+
+/** Flash-specific control/status values */
+
+/* flashcontrol opcodes for ST flashes */
+#define SSB_CHIPCO_FLASHCTL_ST_WREN	0x0006		/* Write Enable */
+#define SSB_CHIPCO_FLASHCTL_ST_WRDIS	0x0004		/* Write Disable */
+#define SSB_CHIPCO_FLASHCTL_ST_RDSR	0x0105		/* Read Status Register */
+#define SSB_CHIPCO_FLASHCTL_ST_WRSR	0x0101		/* Write Status Register */
+#define SSB_CHIPCO_FLASHCTL_ST_READ	0x0303		/* Read Data Bytes */
+#define SSB_CHIPCO_FLASHCTL_ST_PP	0x0302		/* Page Program */
+#define SSB_CHIPCO_FLASHCTL_ST_SE	0x02D8		/* Sector Erase */
+#define SSB_CHIPCO_FLASHCTL_ST_BE	0x00C7		/* Bulk Erase */
+#define SSB_CHIPCO_FLASHCTL_ST_DP	0x00B9		/* Deep Power-down */
+#define SSB_CHIPCO_FLASHCTL_ST_RSIG	0x03AB		/* Read Electronic Signature */
+
+/* Status register bits for ST flashes */
+#define SSB_CHIPCO_FLASHSTA_ST_WIP	0x01		/* Write In Progress */
+#define SSB_CHIPCO_FLASHSTA_ST_WEL	0x02		/* Write Enable Latch */
+#define SSB_CHIPCO_FLASHSTA_ST_BP	0x1C		/* Block Protect */
+#define SSB_CHIPCO_FLASHSTA_ST_BP_SHIFT	2
+#define SSB_CHIPCO_FLASHSTA_ST_SRWD	0x80		/* Status Register Write Disable */
+
+/* flashcontrol opcodes for Atmel flashes */
+#define SSB_CHIPCO_FLASHCTL_AT_READ		0x07E8
+#define SSB_CHIPCO_FLASHCTL_AT_PAGE_READ	0x07D2
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_READ	/* FIXME */
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_READ	/* FIXME */
+#define SSB_CHIPCO_FLASHCTL_AT_STATUS		0x01D7
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_WRITE	0x0384
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_WRITE	0x0387
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_ERASE_PRGM	0x0283	/* Erase program */
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_ERASE_PRGM	0x0286	/* Erase program */
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_PROGRAM	0x0288
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_PROGRAM	0x0289
+#define SSB_CHIPCO_FLASHCTL_AT_PAGE_ERASE	0x0281
+#define SSB_CHIPCO_FLASHCTL_AT_BLOCK_ERASE	0x0250
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_WRER_PRGM	0x0382	/* Write erase program */
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_WRER_PRGM	0x0385	/* Write erase program */
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_LOAD	0x0253
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_LOAD	0x0255
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_COMPARE	0x0260
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_COMPARE	0x0261
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_REPROGRAM	0x0258
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_REPROGRAM	0x0259
+
+/* Status register bits for Atmel flashes */
+#define SSB_CHIPCO_FLASHSTA_AT_READY	0x80
+#define SSB_CHIPCO_FLASHSTA_AT_MISMATCH	0x40
+#define SSB_CHIPCO_FLASHSTA_AT_ID	0x38
+#define SSB_CHIPCO_FLASHSTA_AT_ID_SHIFT	3
+
+
+/** OTP **/
+
+/* OTP regions */
+#define	SSB_CHIPCO_OTP_HW_REGION	SSB_CHIPCO_OTPS_HW_PROTECT
+#define	SSB_CHIPCO_OTP_SW_REGION	SSB_CHIPCO_OTPS_SW_PROTECT
+#define	SSB_CHIPCO_OTP_CID_REGION	SSB_CHIPCO_OTPS_CID_PROTECT
+
+/* OTP regions (Byte offsets from otp size) */
+#define	SSB_CHIPCO_OTP_SWLIM_OFF	(-8)
+#define	SSB_CHIPCO_OTP_CIDBASE_OFF	0
+#define	SSB_CHIPCO_OTP_CIDLIM_OFF	8
+
+/* Predefined OTP words (Word offset from otp size) */
+#define	SSB_CHIPCO_OTP_BOUNDARY_OFF	(-4)
+#define	SSB_CHIPCO_OTP_HWSIGN_OFF	(-3)
+#define	SSB_CHIPCO_OTP_SWSIGN_OFF	(-2)
+#define	SSB_CHIPCO_OTP_CIDSIGN_OFF	(-1)
+
+#define	SSB_CHIPCO_OTP_CID_OFF		0
+#define	SSB_CHIPCO_OTP_PKG_OFF		1
+#define	SSB_CHIPCO_OTP_FID_OFF		2
+#define	SSB_CHIPCO_OTP_RSV_OFF		3
+#define	SSB_CHIPCO_OTP_LIM_OFF		4
+
+#define	SSB_CHIPCO_OTP_SIGNATURE	0x578A
+#define	SSB_CHIPCO_OTP_MAGIC		0x4E56
+
+
+struct ssb_device;
+struct ssb_serial_port;
+
+struct ssb_chipcommon {
+	struct ssb_device *dev;
+	u32 capabilities;
+	/* Fast Powerup Delay constant */
+	u16 fast_pwrup_delay;
+};
+
+extern void ssb_chipcommon_init(struct ssb_chipcommon *cc);
+
+#include <linux/pm.h>
+extern void ssb_chipco_suspend(struct ssb_chipcommon *cc, pm_message_t state);
+extern void ssb_chipco_resume(struct ssb_chipcommon *cc);
+
+extern void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc,
+					u32 *plltype, u32 *n, u32 *m);
+extern void ssb_chipco_timing_init(struct ssb_chipcommon *cc,
+				   unsigned long ns_per_cycle);
+
+enum ssb_clkmode {
+	SSB_CLKMODE_SLOW,
+	SSB_CLKMODE_FAST,
+	SSB_CLKMODE_DYNAMIC,
+};
+
+extern void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc,
+				     enum ssb_clkmode mode);
+
+#ifdef CONFIG_SSB_SERIAL
+extern int ssb_chipco_serial_init(struct ssb_chipcommon *cc,
+				  struct ssb_serial_port *ports);
+#endif /* CONFIG_SSB_SERIAL */
+
+#endif /* LINUX_SSB_CHIPCO_H_ */
diff --git a/include/linux/ssb/ssb_driver_extif.h b/include/linux/ssb/ssb_driver_extif.h
new file mode 100644
index 0000000..00de749
--- /dev/null
+++ b/include/linux/ssb/ssb_driver_extif.h
@@ -0,0 +1,160 @@
+/*
+ * Hardware-specific External Interface I/O core definitions
+ * for the BCM47xx family of SiliconBackplane-based chips.
+ *
+ * The External Interface core supports a total of three external chip selects
+ * supporting external interfaces. One of the external chip selects is
+ * used for Flash, one is used for PCMCIA, and the other may be
+ * programmed to support either a synchronous interface or an
+ * asynchronous interface. The asynchronous interface can be used to
+ * support external devices such as UARTs and the BCM2019 Bluetooth
+ * baseband processor.
+ * The external interface core also contains 2 on-chip 16550 UARTs, clock
+ * frequency control, a watchdog interrupt timer, and a GPIO interface.
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, Michael Buesch
+ *
+ * Licensed under the GPL version 2. See COPYING for details.
+ */
+#ifndef LINUX_SSB_EXTIFCORE_H_
+#define LINUX_SSB_EXTIFCORE_H_
+
+struct ssb_extif {
+	struct ssb_device *dev;
+};
+
+/* external interface address space */
+#define	SSB_EXTIF_PCMCIA_MEMBASE(x)	(x)
+#define	SSB_EXTIF_PCMCIA_IOBASE(x)	((x) + 0x100000)
+#define	SSB_EXTIF_PCMCIA_CFGBASE(x)	((x) + 0x200000)
+#define	SSB_EXTIF_CFGIF_BASE(x)		((x) + 0x800000)
+#define	SSB_EXTIF_FLASH_BASE(x)		((x) + 0xc00000)
+
+#define SSB_EXTIF_NR_GPIOOUT		5
+/* GPIO NOTE:
+ * The multiple instances of output and output enable registers
+ * are present to allow driver software for multiple cores to control
+ * gpio outputs without needing to share a single register pair.
+ * Use the following helper macro to get a register offset value.
+ */
+#define SSB_EXTIF_GPIO_OUT(index)	({		\
+	BUILD_BUG_ON(index >= SSB_EXTIF_NR_GPIOOUT);	\
+	SSB_EXTIF_GPIO_OUT_BASE + ((index) * 8);	\
+					})
+#define SSB_EXTIF_GPIO_OUTEN(index)	({		\
+	BUILD_BUG_ON(index >= SSB_EXTIF_NR_GPIOOUT);	\
+	SSB_EXTIF_GPIO_OUTEN_BASE + ((index) * 8);	\
+					})
+
+/** EXTIF core registers **/
+
+#define SSB_EXTIF_CTL			0x0000
+#define  SSB_EXTIF_CTL_UARTEN		(1 << 0) /* UART enable */
+#define SSB_EXTIF_EXTSTAT		0x0004
+#define  SSB_EXTIF_EXTSTAT_EMODE	(1 << 0) /* Endian mode (ro) */
+#define  SSB_EXTIF_EXTSTAT_EIRQPIN	(1 << 1) /* External interrupt pin (ro) */
+#define  SSB_EXTIF_EXTSTAT_GPIOIRQPIN	(1 << 2) /* GPIO interrupt pin (ro) */
+#define SSB_EXTIF_PCMCIA_CFG		0x0010
+#define SSB_EXTIF_PCMCIA_MEMWAIT	0x0014
+#define SSB_EXTIF_PCMCIA_ATTRWAIT	0x0018
+#define SSB_EXTIF_PCMCIA_IOWAIT		0x001C
+#define SSB_EXTIF_PROG_CFG		0x0020
+#define SSB_EXTIF_PROG_WAITCNT		0x0024
+#define SSB_EXTIF_FLASH_CFG		0x0028
+#define SSB_EXTIF_FLASH_WAITCNT		0x002C
+#define SSB_EXTIF_WATCHDOG		0x0040
+#define SSB_EXTIF_CLOCK_N		0x0044
+#define SSB_EXTIF_CLOCK_SB		0x0048
+#define SSB_EXTIF_CLOCK_PCI		0x004C
+#define SSB_EXTIF_CLOCK_MII		0x0050
+#define SSB_EXTIF_GPIO_IN		0x0060
+#define SSB_EXTIF_GPIO_OUT_BASE		0x0064
+#define SSB_EXTIF_GPIO_OUTEN_BASE	0x0068
+#define SSB_EXTIF_EJTAG_OUTEN		0x0090
+#define SSB_EXTIF_GPIO_INTPOL		0x0094
+#define SSB_EXTIF_GPIO_INTMASK		0x0098
+#define SSB_EXTIF_UART_DATA		0x0300
+#define SSB_EXTIF_UART_TIMER		0x0310
+#define SSB_EXTIF_UART_FCR		0x0320
+#define SSB_EXTIF_UART_LCR		0x0330
+#define SSB_EXTIF_UART_MCR		0x0340
+#define SSB_EXTIF_UART_LSR		0x0350
+#define SSB_EXTIF_UART_MSR		0x0360
+#define SSB_EXTIF_UART_SCRATCH		0x0370
+
+
+
+
+/* pcmcia/prog/flash_config */
+#define	SSB_EXTCFG_EN			(1 << 0)	/* enable */
+#define	SSB_EXTCFG_MODE			0xE		/* mode */
+#define	SSB_EXTCFG_MODE_SHIFT		1
+#define	 SSB_EXTCFG_MODE_FLASH		0x0		/* flash/asynchronous mode */
+#define	 SSB_EXTCFG_MODE_SYNC		0x2		/* synchronous mode */
+#define	 SSB_EXTCFG_MODE_PCMCIA		0x4		/* pcmcia mode */
+#define	SSB_EXTCFG_DS16			(1 << 4)	/* destsize:  0=8bit, 1=16bit */
+#define	SSB_EXTCFG_BSWAP		(1 << 5)	/* byteswap */
+#define	SSB_EXTCFG_CLKDIV		0xC0		/* clock divider */
+#define	SSB_EXTCFG_CLKDIV_SHIFT		6
+#define	 SSB_EXTCFG_CLKDIV_2		0x0		/* backplane/2 */
+#define	 SSB_EXTCFG_CLKDIV_3		0x40		/* backplane/3 */
+#define	 SSB_EXTCFG_CLKDIV_4		0x80		/* backplane/4 */
+#define	SSB_EXTCFG_CLKEN		(1 << 8)	/* clock enable */
+#define	SSB_EXTCFG_STROBE		(1 << 9)	/* size/bytestrobe (synch only) */
+
+/* pcmcia_memwait */
+#define	SSB_PCMCIA_MEMW_0		0x0000003F	/* waitcount0 */
+#define	SSB_PCMCIA_MEMW_1		0x00001F00	/* waitcount1 */
+#define	SSB_PCMCIA_MEMW_1_SHIFT		8
+#define	SSB_PCMCIA_MEMW_2		0x001F0000	/* waitcount2 */
+#define	SSB_PCMCIA_MEMW_2_SHIFT		16
+#define	SSB_PCMCIA_MEMW_3		0x1F000000	/* waitcount3 */
+#define	SSB_PCMCIA_MEMW_3_SHIFT		24
+
+/* pcmcia_attrwait */
+#define	SSB_PCMCIA_ATTW_0		0x0000003F	/* waitcount0 */
+#define	SSB_PCMCIA_ATTW_1		0x00001F00	/* waitcount1 */
+#define	SSB_PCMCIA_ATTW_1_SHIFT		8
+#define	SSB_PCMCIA_ATTW_2		0x001F0000	/* waitcount2 */
+#define	SSB_PCMCIA_ATTW_2_SHIFT		16
+#define	SSB_PCMCIA_ATTW_3		0x1F000000	/* waitcount3 */
+#define	SSB_PCMCIA_ATTW_3_SHIFT		24
+
+/* pcmcia_iowait */
+#define	SSB_PCMCIA_IOW_0		0x0000003F	/* waitcount0 */
+#define	SSB_PCMCIA_IOW_1		0x00001F00	/* waitcount1 */
+#define	SSB_PCMCIA_IOW_1_SHIFT		8
+#define	SSB_PCMCIA_IOW_2		0x001F0000	/* waitcount2 */
+#define	SSB_PCMCIA_IOW_2_SHIFT		16
+#define	SSB_PCMCIA_IOW_3		0x1F000000	/* waitcount3 */
+#define	SSB_PCMCIA_IOW_3_SHIFT		24
+
+/* prog_waitcount */
+#define	SSB_PROG_WCNT_0			0x0000001F	/* waitcount0 */
+#define	SSB_PROG_WCNT_1			0x00001F00	/* waitcount1 */
+#define	SSB_PROG_WCNT_1_SHIFT		8
+#define	SSB_PROG_WCNT_2			0x001F0000	/* waitcount2 */
+#define	SSB_PROG_WCNT_2_SHIFT		16
+#define	SSB_PROG_WCNT_3			0x1F000000	/* waitcount3 */
+#define	SSB_PROG_WCNT_3_SHIFT		24
+
+#define SSB_PROG_W0			0x0000000C
+#define SSB_PROG_W1			0x00000A00
+#define SSB_PROG_W2			0x00020000
+#define SSB_PROG_W3			0x01000000
+
+/* flash_waitcount */
+#define	SSB_FLASH_WCNT_0		0x0000001F	/* waitcount0 */
+#define	SSB_FLASH_WCNT_1		0x00001F00	/* waitcount1 */
+#define	SSB_FLASH_WCNT_1_SHIFT		8
+#define	SSB_FLASH_WCNT_2		0x001F0000	/* waitcount2 */
+#define	SSB_FLASH_WCNT_2_SHIFT		16
+#define	SSB_FLASH_WCNT_3		0x1F000000	/* waitcount3 */
+#define	SSB_FLASH_WCNT_3_SHIFT		24
+
+/* watchdog */
+#define SSB_EXTIF_WATCHDOG_CLK		48000000	/* Hz */
+
+
+#endif /* LINUX_SSB_EXTIFCORE_H_ */
diff --git a/include/linux/ssb/ssb_driver_mips.h b/include/linux/ssb/ssb_driver_mips.h
new file mode 100644
index 0000000..cdbb985
--- /dev/null
+++ b/include/linux/ssb/ssb_driver_mips.h
@@ -0,0 +1,44 @@
+#ifndef LINUX_SSB_MIPSCORE_H_
+#define LINUX_SSB_MIPSCORE_H_
+
+#ifdef CONFIG_SSB_DRIVER_MIPS
+
+struct ssb_device;
+
+struct ssb_serial_port {
+	void *regs;
+	unsigned long clockspeed;
+	unsigned int irq;
+	unsigned int baud_base;
+	unsigned int reg_shift;
+};
+
+
+struct ssb_mipscore {
+	struct ssb_device *dev;
+
+	int nr_serial_ports;
+	struct ssb_serial_port serial_ports[4];
+
+	u32 flash_window;
+	u32 flash_window_size;
+};
+
+extern void ssb_mipscore_init(struct ssb_mipscore *mcore);
+
+extern unsigned int ssb_mips_irq(struct ssb_device *dev);
+
+
+#else /* CONFIG_SSB_DRIVER_MIPS */
+
+struct ssb_mipscore {
+};
+
+static inline
+void ssb_mipscore_init(struct ssb_mipscore *mcore)
+{
+}
+
+#endif /* CONFIG_SSB_DRIVER_MIPS */
+
+#endif /* LINUX_SSB_MIPSCORE_H_ */
diff --git a/include/linux/ssb/ssb_driver_pci.h b/include/linux/ssb/ssb_driver_pci.h
new file mode 100644
index 0000000..9cfffb7
--- /dev/null
+++ b/include/linux/ssb/ssb_driver_pci.h
@@ -0,0 +1,106 @@
+#ifndef LINUX_SSB_PCICORE_H_
+#define LINUX_SSB_PCICORE_H_
+
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+
+/* PCI core registers. */
+#define SSB_PCICORE_CTL			0x0000	/* PCI Control */
+#define  SSB_PCICORE_CTL_RST_OE		0x00000001 /* PCI_RESET Output Enable */
+#define  SSB_PCICORE_CTL_RST		0x00000002 /* PCI_RESET driven out to pin */
+#define  SSB_PCICORE_CTL_CLK_OE		0x00000004 /* Clock gate Output Enable */
+#define  SSB_PCICORE_CTL_CLK		0x00000008 /* Gate for clock driven out to pin */
+#define SSB_PCICORE_ARBCTL		0x0010	/* PCI Arbiter Control */
+#define  SSB_PCICORE_ARBCTL_INTERN	0x00000001 /* Use internal arbiter */
+#define  SSB_PCICORE_ARBCTL_EXTERN	0x00000002 /* Use external arbiter */
+#define  SSB_PCICORE_ARBCTL_PARKID	0x00000006 /* Mask, selects which agent is parked on an idle bus */
+#define   SSB_PCICORE_ARBCTL_PARKID_LAST	0x00000000 /* Last requestor */
+#define   SSB_PCICORE_ARBCTL_PARKID_4710	0x00000002 /* 4710 */
+#define   SSB_PCICORE_ARBCTL_PARKID_EXT0	0x00000004 /* External requestor 0 */
+#define   SSB_PCICORE_ARBCTL_PARKID_EXT1	0x00000006 /* External requestor 1 */
+#define SSB_PCICORE_ISTAT		0x0020	/* Interrupt status */
+#define  SSB_PCICORE_ISTAT_INTA		0x00000001 /* PCI INTA# */
+#define  SSB_PCICORE_ISTAT_INTB		0x00000002 /* PCI INTB# */
+#define  SSB_PCICORE_ISTAT_SERR		0x00000004 /* PCI SERR# (write to clear) */
+#define  SSB_PCICORE_ISTAT_PERR		0x00000008 /* PCI PERR# (write to clear) */
+#define  SSB_PCICORE_ISTAT_PME		0x00000010 /* PCI PME# */
+#define SSB_PCICORE_IMASK		0x0024	/* Interrupt mask */
+#define  SSB_PCICORE_IMASK_INTA		0x00000001 /* PCI INTA# */
+#define  SSB_PCICORE_IMASK_INTB		0x00000002 /* PCI INTB# */
+#define  SSB_PCICORE_IMASK_SERR		0x00000004 /* PCI SERR# */
+#define  SSB_PCICORE_IMASK_PERR		0x00000008 /* PCI PERR# */
+#define  SSB_PCICORE_IMASK_PME		0x00000010 /* PCI PME# */
+#define SSB_PCICORE_MBOX		0x0028	/* Backplane to PCI Mailbox */
+#define  SSB_PCICORE_MBOX_F0_0		0x00000100 /* PCI function 0, INT 0 */
+#define  SSB_PCICORE_MBOX_F0_1		0x00000200 /* PCI function 0, INT 1 */
+#define  SSB_PCICORE_MBOX_F1_0		0x00000400 /* PCI function 1, INT 0 */
+#define  SSB_PCICORE_MBOX_F1_1		0x00000800 /* PCI function 1, INT 1 */
+#define  SSB_PCICORE_MBOX_F2_0		0x00001000 /* PCI function 2, INT 0 */
+#define  SSB_PCICORE_MBOX_F2_1		0x00002000 /* PCI function 2, INT 1 */
+#define  SSB_PCICORE_MBOX_F3_0		0x00004000 /* PCI function 3, INT 0 */
+#define  SSB_PCICORE_MBOX_F3_1		0x00008000 /* PCI function 3, INT 1 */
+#define SSB_PCICORE_BCAST_ADDR		0x0050	/* Backplane Broadcast Address */
+#define  SSB_PCICORE_BCAST_ADDR_MASK	0x000000FF
+#define SSB_PCICORE_BCAST_DATA		0x0054	/* Backplane Broadcast Data */
+#define SSB_PCICORE_GPIO_IN		0x0060	/* rev >= 2 only */
+#define SSB_PCICORE_GPIO_OUT		0x0064	/* rev >= 2 only */
+#define SSB_PCICORE_GPIO_ENABLE		0x0068	/* rev >= 2 only */
+#define SSB_PCICORE_GPIO_CTL		0x006C	/* rev >= 2 only */
+#define SSB_PCICORE_SBTOPCI0		0x0100	/* Backplane to PCI translation 0 (sbtopci0) */
+#define  SSB_PCICORE_SBTOPCI0_MASK	0xFC000000
+#define SSB_PCICORE_SBTOPCI1		0x0104	/* Backplane to PCI translation 1 (sbtopci1) */
+#define  SSB_PCICORE_SBTOPCI1_MASK	0xFC000000
+#define SSB_PCICORE_SBTOPCI2		0x0108	/* Backplane to PCI translation 2 (sbtopci2) */
+#define  SSB_PCICORE_SBTOPCI2_MASK	0xC0000000
+
+/* SBtoPCIx */
+#define SSB_PCICORE_SBTOPCI_MEM		0x00000000
+#define SSB_PCICORE_SBTOPCI_IO		0x00000001
+#define SSB_PCICORE_SBTOPCI_CFG0	0x00000002
+#define SSB_PCICORE_SBTOPCI_CFG1	0x00000003
+#define SSB_PCICORE_SBTOPCI_PREF	0x00000004 /* Prefetch enable */
+#define SSB_PCICORE_SBTOPCI_BURST	0x00000008 /* Burst enable */
+#define SSB_PCICORE_SBTOPCI_MRM		0x00000020 /* Memory Read Multiple */
+#define SSB_PCICORE_SBTOPCI_RC		0x00000030 /* Read Command mask (rev >= 11) */
+#define  SSB_PCICORE_SBTOPCI_RC_READ	0x00000000 /* Memory read */
+#define  SSB_PCICORE_SBTOPCI_RC_READL	0x00000010 /* Memory read line */
+#define  SSB_PCICORE_SBTOPCI_RC_READM	0x00000020 /* Memory read multiple */
+
+
+/* PCIcore specific boardflags */
+#define SSB_PCICORE_BFL_NOPCI		0x00000400 /* Board leaves PCI floating */
+
+
+struct ssb_pcicore {
+	struct ssb_device *dev;
+	u8 setup_done:1;
+	u8 hostmode:1;
+	u8 cardbusmode:1;
+};
+
+extern void ssb_pcicore_init(struct ssb_pcicore *pc);
+
+/* Enable IRQ routing for a specific device */
+extern int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
+					  struct ssb_device *dev);
+
+
+#else /* CONFIG_SSB_DRIVER_PCICORE */
+
+
+struct ssb_pcicore {
+};
+
+static inline
+void ssb_pcicore_init(struct ssb_pcicore *pc)
+{
+}
+
+static inline
+int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
+				   struct ssb_device *dev)
+{
+	return 0;
+}
+
+#endif /* CONFIG_SSB_DRIVER_PCICORE */
+#endif /* LINUX_SSB_PCICORE_H_ */
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
new file mode 100644
index 0000000..8326377
--- /dev/null
+++ b/include/linux/ssb/ssb.h
@@ -0,0 +1,432 @@
+#ifndef LINUX_SSB_H_
+#define LINUX_SSB_H_
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+
+#include <linux/ssb/ssb_regs.h>
+
+
+struct pcmcia_device;
+struct ssb_bus;
+struct ssb_driver;
+
+
+struct ssb_sprom_r1 {
+	u16 pci_spid;		/* Subsystem Product ID for PCI */
+	u16 pci_svid;		/* Subsystem Vendor ID for PCI */
+	u16 pci_pid;		/* Product ID for PCI */
+	u8 il0mac[6];		/* MAC address for 802.11b/g */
+	u8 et0mac[6];		/* MAC address for Ethernet */
+	u8 et1mac[6];		/* MAC address for 802.11a */
+	u8 et0phyaddr:5;	/* MII address for enet0 */
+	u8 et1phyaddr:5;	/* MII address for enet1 */
+	u8 et0mdcport:1;	/* MDIO for enet0 */
+	u8 et1mdcport:1;	/* MDIO for enet1 */
+	u8 board_rev;		/* Board revision */
+	u8 country_code:4;	/* Country Code */
+	u8 antenna_a:2;		/* Antenna 0/1 available for A-PHY */
+	u8 antenna_bg:2;	/* Antenna 0/1 available for B-PHY and G-PHY */
+	u16 pa0b0;
+	u16 pa0b1;
+	u16 pa0b2;
+	u16 pa1b0;
+	u16 pa1b1;
+	u16 pa1b2;
+	u8 gpio0;		/* GPIO pin 0 */
+	u8 gpio1;		/* GPIO pin 1 */
+	u8 gpio2;		/* GPIO pin 2 */
+	u8 gpio3;		/* GPIO pin 3 */
+	u16 maxpwr_a;		/* A-PHY Power Amplifier Max Power (in dBm Q5.2) */
+	u16 maxpwr_bg;		/* B/G-PHY Power Amplifier Max Power (in dBm Q5.2) */
+	u8 itssi_a;		/* Idle TSSI Target for A-PHY */
+	u8 itssi_bg;		/* Idle TSSI Target for B/G-PHY */
+	u16 boardflags_lo;	/* Boardflags (low 16 bits) */
+	u8 antenna_gain_a;	/* A-PHY Antenna gain (in dBm Q5.2) */
+	u8 antenna_gain_bg;	/* B/G-PHY Antenna gain (in dBm Q5.2) */
+	u8 oem[8];		/* OEM string (rev 1 only) */
+};
+
+struct ssb_sprom_r2 {
+	u16 boardflags_hi;	/* Boardflags (high 16 bits) */
+	u8 maxpwr_a_lo;		/* A-PHY Max Power Low */
+	u8 maxpwr_a_hi;		/* A-PHY Max Power High */
+	u16 pa1lob0;		/* A-PHY PA Low Settings */
+	u16 pa1lob1;		/* A-PHY PA Low Settings */
+	u16 pa1lob2;		/* A-PHY PA Low Settings */
+	u16 pa1hib0;		/* A-PHY PA High Settings */
+	u16 pa1hib1;		/* A-PHY PA High Settings */
+	u16 pa1hib2;		/* A-PHY PA High Settings */
+	u8 ofdm_pwr_off;	/* OFDM Power Offset from CCK Level */
+	u8 country_str[2];	/* Two char Country Code */
+};
+
+struct ssb_sprom_r3 {
+	u32 ofdmapo;		/* A-PHY OFDM Mid Power Offset */
+	u32 ofdmalpo;		/* A-PHY OFDM Low Power Offset */
+	u32 ofdmahpo;		/* A-PHY OFDM High Power Offset */
+	u8 gpioldc_on_cnt;	/* GPIO LED Powersave Duty Cycle ON count */
+	u8 gpioldc_off_cnt;	/* GPIO LED Powersave Duty Cycle OFF count */
+	u8 cckpo_1M:4;		/* CCK Power Offset for Rate 1M */
+	u8 cckpo_2M:4;		/* CCK Power Offset for Rate 2M */
+	u8 cckpo_55M:4;		/* CCK Power Offset for Rate 5.5M */
+	u8 cckpo_11M:4;		/* CCK Power Offset for Rate 11M */
+	u32 ofdmgpo;		/* G-PHY OFDM Power Offset */
+};
+
+struct ssb_sprom_r4 {
+	/* TODO */
+};
+
+struct ssb_sprom {
+	u8 revision;
+	u8 crc;
+	/* The valid r# fields are selected by the "revision".
+	 * Revision 3 and lower inherit from lower revisions.
+	 */
+	union {
+		struct {
+			struct ssb_sprom_r1 r1;
+			struct ssb_sprom_r2 r2;
+			struct ssb_sprom_r3 r3;
+		};
+		struct ssb_sprom_r4 r4;
+	};
+};
+
+/* Information about the PCB the circuitry is soldered on. */
+struct ssb_boardinfo {
+	u16 vendor;
+	u16 type;
+	u16 rev;
+};
+
+
+struct ssb_device;
+/* Lowlevel read/write operations on the device MMIO.
+ * Internal, don't use that outside of ssb. */
+struct ssb_bus_ops {
+	u16 (*read16)(struct ssb_device *dev, u16 offset);
+	u32 (*read32)(struct ssb_device *dev, u16 offset);
+	void (*write16)(struct ssb_device *dev, u16 offset, u16 value);
+	void (*write32)(struct ssb_device *dev, u16 offset, u32 value);
+};
+
+
+/* Core-ID values. */
+#define SSB_DEV_CHIPCOMMON	0x800
+#define SSB_DEV_ILINE20		0x801
+#define SSB_DEV_SDRAM		0x803
+#define SSB_DEV_PCI		0x804
+#define SSB_DEV_MIPS		0x805
+#define SSB_DEV_ETHERNET	0x806
+#define SSB_DEV_V90		0x807
+#define SSB_DEV_USB11_HOSTDEV	0x808
+#define SSB_DEV_ADSL		0x809
+#define SSB_DEV_ILINE100	0x80A
+#define SSB_DEV_IPSEC		0x80B
+#define SSB_DEV_PCMCIA		0x80D
+#define SSB_DEV_INTERNAL_MEM	0x80E
+#define SSB_DEV_MEMC_SDRAM	0x80F
+#define SSB_DEV_EXTIF		0x811
+#define SSB_DEV_80211		0x812
+#define SSB_DEV_MIPS_3302	0x816
+#define SSB_DEV_USB11_HOST	0x817
+#define SSB_DEV_USB11_DEV	0x818
+#define SSB_DEV_USB20_HOST	0x819
+#define SSB_DEV_USB20_DEV	0x81A
+#define SSB_DEV_SDIO_HOST	0x81B
+#define SSB_DEV_ROBOSWITCH	0x81C
+#define SSB_DEV_PARA_ATA	0x81D
+#define SSB_DEV_SATA_XORDMA	0x81E
+#define SSB_DEV_ETHERNET_GBIT	0x81F
+#define SSB_DEV_PCIE		0x820
+#define SSB_DEV_MIMO_PHY	0x821
+#define SSB_DEV_SRAM_CTRLR	0x822
+#define SSB_DEV_MINI_MACPHY	0x823
+#define SSB_DEV_ARM_1176	0x824
+#define SSB_DEV_ARM_7TDMI	0x825
+
+/* Vendor-ID values */
+#define SSB_VENDOR_BROADCOM	0x4243
+
+struct ssb_device_id {
+	u16 vendor;
+	u16 coreid;
+	u8 revision;
+};
+#define SSB_DEVICE(_vendor, _coreid, _revision)  \
+	{ .vendor = _vendor, .coreid = _coreid, .revision = _revision, }
+#define SSB_DEVTABLE_END  \
+	{ 0, },
+
+#define SSB_ANY_VENDOR		0xFFFF
+#define SSB_ANY_ID		0xFFFF
+#define SSB_ANY_REV		0xFF
+
+/* Some kernel subsystems poke with dev->drvdata, so we must use the
+ * following ugly workaround to get from struct device to struct ssb_device */
+struct __ssb_dev_wrapper {
+	struct device dev;
+	struct ssb_device *sdev;
+};
+
+struct ssb_device {
+	/* Having a copy of the ops pointer in each dev struct
+	 * is an optimization. */
+	const struct ssb_bus_ops *ops;
+
+	struct device *dev;
+	struct ssb_bus *bus;
+	struct ssb_device_id id;
+
+	u8 core_index;
+	unsigned int irq;
+
+	/* Internal-only stuff follows. */
+	void *drvdata;		/* Per-device data */
+	void *devtypedata;	/* Per-devicetype (eg 802.11) data */
+};
+
+/* Go from struct device to struct ssb_device. */
+static inline
+struct ssb_device * dev_to_ssb_dev(struct device *dev)
+{
+	struct __ssb_dev_wrapper *wrap;
+	wrap = container_of(dev, struct __ssb_dev_wrapper, dev);
+	return wrap->sdev;
+}
+
+/* Device specific user data */
+static inline
+void ssb_set_drvdata(struct ssb_device *dev, void *data)
+{
+	dev->drvdata = data;
+}
+static inline
+void * ssb_get_drvdata(struct ssb_device *dev)
+{
+	return dev->drvdata;
+}
+
+/* Devicetype specific user data. This is per device-type (not per device) */
+void ssb_set_devtypedata(struct ssb_device *dev, void *data);
+static inline
+void * ssb_get_devtypedata(struct ssb_device *dev)
+{
+	return dev->devtypedata;
+}
+
+
+struct ssb_driver {
+	const char *name;
+	const struct ssb_device_id *id_table;
+
+	int (*probe)(struct ssb_device *dev, const struct ssb_device_id *id);
+	void (*remove)(struct ssb_device *dev);
+	int (*suspend)(struct ssb_device *dev, pm_message_t state);
+	int (*resume)(struct ssb_device *dev);
+	void (*shutdown)(struct ssb_device *dev);
+
+	struct device_driver drv;
+};
+#define drv_to_ssb_drv(_drv) container_of(_drv, struct ssb_driver, drv)
+
+extern int __ssb_driver_register(struct ssb_driver *drv, struct module *owner);
+static inline int ssb_driver_register(struct ssb_driver *drv)
+{
+	return __ssb_driver_register(drv, THIS_MODULE);
+}
+extern void ssb_driver_unregister(struct ssb_driver *drv);
+
+
+
+
+enum ssb_bustype {
+	SSB_BUSTYPE_SSB,	/* This SSB bus is the system bus */
+	SSB_BUSTYPE_PCI,	/* SSB is connected to PCI bus */
+	SSB_BUSTYPE_PCMCIA,	/* SSB is connected to PCMCIA bus */
+};
+
+/* board_vendor */
+#define SSB_BOARDVENDOR_BCM	0x14E4	/* Broadcom */
+#define SSB_BOARDVENDOR_DELL	0x1028	/* Dell */
+#define SSB_BOARDVENDOR_HP	0x0E11	/* HP */
+/* board_type */
+#define SSB_BOARD_BCM94306MP	0x0418
+#define SSB_BOARD_BCM4309G	0x0421
+#define SSB_BOARD_BCM4306CB	0x0417
+#define SSB_BOARD_BCM4309MP	0x040C
+#define SSB_BOARD_MP4318	0x044A
+#define SSB_BOARD_BU4306	0x0416
+#define SSB_BOARD_BU4309	0x040A
+/* chip_package */
+#define SSB_CHIPPACK_BCM4712S	1	/* Small 200pin 4712 */
+#define SSB_CHIPPACK_BCM4712M	2	/* Medium 225pin 4712 */
+#define SSB_CHIPPACK_BCM4712L	0	/* Large 340pin 4712 */
+
+#include <linux/ssb/ssb_driver_chipcommon.h>
+#include <linux/ssb/ssb_driver_mips.h>
+#include <linux/ssb/ssb_driver_extif.h>
+#include <linux/ssb/ssb_driver_pci.h>
+
+struct ssb_bus {
+	/* The MMIO area. */
+	void __iomem *mmio;
+
+	const struct ssb_bus_ops *ops;
+
+	/* The core in the basic address register window. (PCI bus only) */
+	struct ssb_device *mapped_device;
+	/* Currently mapped PCMCIA segment. (bustype == SSB_BUSTYPE_PCMCIA only) */
+	u8 mapped_pcmcia_seg;
+	/* Lock for core and segment switching. */
+	spinlock_t bar_lock;
+
+	/* The bus this backplane is running on. */
+	enum ssb_bustype bustype;
+	/* Pointer to the PCI bus (only valid if bustype == SSB_BUSTYPE_PCI). */
+	struct pci_dev *host_pci;
+	/* Pointer to the PCMCIA device (only if bustype == SSB_BUSTYPE_PCMCIA). */
+	struct pcmcia_device *host_pcmcia;
+
+#ifdef CONFIG_SSB_PCIHOST
+	/* Mutex to protect the SPROM writing. */
+	struct mutex pci_sprom_mutex;
+#endif
+
+	/* ID information about the Chip. */
+	u16 chip_id;
+	u16 chip_rev;
+	u8 chip_package;
+
+	/* List of devices (cores) on the backplane. */
+	struct ssb_device devices[SSB_MAX_NR_CORES];
+	u8 nr_devices;
+
+	/* Reference count. Number of suspended devices. */
+	u8 suspend_cnt;
+
+	/* Software ID number for this bus. */
+	unsigned int busnumber;
+
+	/* The ChipCommon device (if available). */
+	struct ssb_chipcommon chipco;
+	/* The PCI-core device (if available). */
+	struct ssb_pcicore pcicore;
+	/* The MIPS-core device (if available). */
+	struct ssb_mipscore mipscore;
+	/* The EXTif-core device (if available). */
+	struct ssb_extif extif;
+
+	/* The following structure elements are not available in early
+	 * SSB initialization. Though, they are available for regular
+	 * registered drivers at any stage. So be careful when
+	 * using them in the ssb core code. */
+
+	/* ID information about the PCB. */
+	struct ssb_boardinfo boardinfo;
+	/* Contents of the SPROM. */
+	struct ssb_sprom sprom;
+
+	/* Internal. */
+	struct list_head list;
+};
+
+/* The initialization-invariants. */
+struct ssb_init_invariants {
+	struct ssb_boardinfo boardinfo;
+	struct ssb_sprom sprom;
+};
+/* Type of function to fetch the invariants. */
+typedef int (*ssb_invariants_func_t)(struct ssb_bus *bus,
+				     struct ssb_init_invariants *iv);
+
+/* Register a SSB system bus. get_invariants() is called after the
+ * basic system devices are initialized.
+ * The invariants are usually fetched from some NVRAM.
+ * Put the invariants into the struct pointed to by iv. */
+extern int ssb_bus_ssbbus_register(struct ssb_bus *bus,
+				   unsigned long baseaddr,
+				   ssb_invariants_func_t get_invariants);
+#ifdef CONFIG_SSB_PCIHOST
+extern int ssb_bus_pcibus_register(struct ssb_bus *bus,
+				   struct pci_dev *host_pci);
+#endif /* CONFIG_SSB_PCIHOST */
+#ifdef CONFIG_SSB_PCMCIAHOST
+extern int ssb_bus_pcmciabus_register(struct ssb_bus *bus,
+				      struct pcmcia_device *pcmcia_dev,
+				      unsigned long baseaddr);
+#endif /* CONFIG_SSB_PCMCIAHOST */
+
+extern void ssb_bus_unregister(struct ssb_bus *bus);
+
+extern u32 ssb_clockspeed(struct ssb_bus *bus);
+
+/* Is the device enabled in hardware? */
+int ssb_device_is_enabled(struct ssb_device *dev);
+/* Enable a device and pass device-specific SSB_TMSLOW flags.
+ * If no device-specific flags are available, use 0. */
+void ssb_device_enable(struct ssb_device *dev, u32 core_specific_flags);
+/* Disable a device in hardware and pass SSB_TMSLOW flags (if any). */
+void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags);
+
+
+/* Device MMIO register read/write functions. */
+static inline u16 ssb_read16(struct ssb_device *dev, u16 offset)
+{
+	return dev->ops->read16(dev, offset);
+}
+static inline u32 ssb_read32(struct ssb_device *dev, u16 offset)
+{
+	return dev->ops->read32(dev, offset);
+}
+static inline void ssb_write16(struct ssb_device *dev, u16 offset, u16 value)
+{
+	dev->ops->write16(dev, offset, value);
+}
+static inline void ssb_write32(struct ssb_device *dev, u16 offset, u32 value)
+{
+	dev->ops->write32(dev, offset, value);
+}
+
+
+/* Translation (routing) bits that need to be ORed to DMA
+ * addresses before they are given to a device. */
+extern u32 ssb_dma_translation(struct ssb_device *dev);
+#define SSB_DMA_TRANSLATION_MASK	0xC0000000
+#define SSB_DMA_TRANSLATION_SHIFT	30
+
+extern int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask);
+
+
+#ifdef CONFIG_SSB_PCIHOST
+/* PCI-host wrapper driver */
+extern int ssb_pcihost_register(struct pci_driver *driver);
+static inline void ssb_pcihost_unregister(struct pci_driver *driver)
+{
+	pci_unregister_driver(driver);
+}
+#endif /* CONFIG_SSB_PCIHOST */
+
+
+/* If a driver is shutdown or suspended, call this to signal
+ * that the bus may be completely powered down. SSB will decide,
+ * if it's really time to power down the bus, based on if there
+ * are other devices that want to run. */
+extern int ssb_bus_may_powerdown(struct ssb_bus *bus);
+/* Before initializing and enabling a device, call this to power-up the bus.
+ * If you want to allow use of dynamic-power-control, pass the flag.
+ * Otherwise static always-on powercontrol will be used. */
+extern int ssb_bus_powerup(struct ssb_bus *bus, bool dynamic_pctl);
+
+
+/* Various helper functions */
+extern u32 ssb_admatch_base(u32 adm);
+extern u32 ssb_admatch_size(u32 adm);
+
+
+#endif /* LINUX_SSB_H_ */
diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h
new file mode 100644
index 0000000..66751a6
--- /dev/null
+++ b/include/linux/ssb/ssb_regs.h
@@ -0,0 +1,292 @@
+#ifndef LINUX_SSB_REGS_H_
+#define LINUX_SSB_REGS_H_
+
+
+/* SiliconBackplane Address Map.
+ * All regions may not exist on all chips.
+ */
+#define SSB_SDRAM_BASE		0x00000000	/* Physical SDRAM */
+#define SSB_PCI_MEM		0x08000000	/* Host Mode sb2pcitranslation0 (64 MB) */
+#define SSB_PCI_CFG		0x0c000000	/* Host Mode sb2pcitranslation1 (64 MB) */
+#define	SSB_SDRAM_SWAPPED	0x10000000	/* Byteswapped Physical SDRAM */
+#define SSB_ENUM_BASE    	0x18000000	/* Enumeration space base */
+#define	SSB_ENUM_LIMIT		0x18010000	/* Enumeration space limit */
+
+#define	SSB_FLASH2		0x1c000000	/* Flash Region 2 (region 1 shadowed here) */
+#define	SSB_FLASH2_SZ		0x02000000	/* Size of Flash Region 2 */
+
+#define	SSB_EXTIF_BASE		0x1f000000	/* External Interface region base address */
+#define	SSB_FLASH1		0x1fc00000	/* Flash Region 1 */
+#define	SSB_FLASH1_SZ		0x00400000	/* Size of Flash Region 1 */
+
+#define SSB_PCI_DMA		0x40000000	/* Client Mode sb2pcitranslation2 (1 GB) */
+#define SSB_PCI_DMA_SZ		0x40000000	/* Client Mode sb2pcitranslation2 size in bytes */
+#define SSB_PCIE_DMA_L32	0x00000000	/* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), low 32 bits */
+#define SSB_PCIE_DMA_H32	0x80000000	/* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), high 32 bits */
+#define	SSB_EUART		(SSB_EXTIF_BASE + 0x00800000)
+#define	SSB_LED			(SSB_EXTIF_BASE + 0x00900000)
+
+
+/* Enumeration space constants */
+#define SSB_CORE_SIZE		0x1000	/* Size of a core MMIO area */
+#define SSB_MAX_NR_CORES	((SSB_ENUM_LIMIT - SSB_ENUM_BASE) / SSB_CORE_SIZE)
+
+
+/* mips address */
+#define	SSB_EJTAG		0xff200000	/* MIPS EJTAG space (2M) */
+
+
+/* SSB PCI config space registers. */
+#define SSB_PMCSR		0x44
+#define  SSB_PE			0x100
+#define	SSB_BAR0_WIN		0x80	/* Backplane address space 0 */
+#define	SSB_BAR1_WIN		0x84	/* Backplane address space 1 */
+#define	SSB_SPROMCTL		0x88	/* SPROM control */
+#define  SSB_SPROMCTL_WE	0x10	/* SPROM write enable */
+#define	SSB_BAR1_CONTROL	0x8c	/* Address space 1 burst control */
+#define SSB_PCI_IRQS		0x90	/* PCI interrupts */
+#define SSB_PCI_IRQMASK		0x94	/* PCI IRQ control and mask (pcirev >= 6 only) */
+#define SSB_BACKPLANE_IRQS	0x98	/* Backplane Interrupts */
+#define SSB_GPIO_IN		0xB0	/* GPIO Input (pcirev >= 3 only) */
+#define SSB_GPIO_OUT		0xB4	/* GPIO Output (pcirev >= 3 only) */
+#define SSB_GPIO_OUT_ENABLE	0xB8	/* GPIO Output Enable/Disable (pcirev >= 3 only) */
+#define  SSB_GPIO_SCS		0x10	/* PCI config space bit 4 for 4306c0 slow clock source */
+#define  SSB_GPIO_HWRAD		0x20	/* PCI config space GPIO 13 for hw radio disable */
+#define  SSB_GPIO_XTAL		0x40	/* PCI config space GPIO 14 for Xtal powerup */
+#define  SSB_GPIO_PLL		0x80	/* PCI config space GPIO 15 for PLL powerdown */
+
+
+#define SSB_BAR0_MAX_RETRIES	50
+
+/* Silicon backplane configuration register definitions */
+#define SSB_IPSFLAG		0x0F08
+#define	 SSB_IPSFLAG_IRQ1	0x0000003F /* which sbflags get routed to mips interrupt 1 */
+#define	 SSB_IPSFLAG_IRQ1_SHIFT	0
+#define	 SSB_IPSFLAG_IRQ2	0x00003F00 /* which sbflags get routed to mips interrupt 2 */
+#define	 SSB_IPSFLAG_IRQ2_SHIFT	8
+#define	 SSB_IPSFLAG_IRQ3	0x003F0000 /* which sbflags get routed to mips interrupt 3 */
+#define	 SSB_IPSFLAG_IRQ3_SHIFT	16
+#define	 SSB_IPSFLAG_IRQ4	0x3F000000 /* which sbflags get routed to mips interrupt 4 */
+#define	 SSB_IPSFLAG_IRQ4_SHIFT	24
+#define SSB_TPSFLAG		0x0F18
+#define  SSB_TPSFLAG_BPFLAG	0x0000003F /* Backplane flag # */
+#define  SSB_TPSFLAG_ALWAYSIRQ	0x00000040 /* IRQ is always sent on the Backplane */
+#define SSB_TMERRLOGA		0x0F48
+#define SSB_TMERRLOG		0x0F50
+#define SSB_ADMATCH3		0x0F60
+#define SSB_ADMATCH2		0x0F68
+#define SSB_ADMATCH1		0x0F70
+#define SSB_IMSTATE		0x0F90     /* SB Initiator Agent State */
+#define  SSB_IMSTATE_PC		0x0000000f /* Pipe Count */
+#define  SSB_IMSTATE_AP_MASK	0x00000030 /* Arbitration Priority */
+#define  SSB_IMSTATE_AP_BOTH	0x00000000 /* Use both timeslices and token */
+#define  SSB_IMSTATE_AP_TS	0x00000010 /* Use timeslices only */
+#define  SSB_IMSTATE_AP_TK	0x00000020 /* Use token only */
+#define  SSB_IMSTATE_AP_RSV	0x00000030 /* Reserved */
+#define  SSB_IMSTATE_IBE	0x00020000 /* In Band Error */
+#define  SSB_IMSTATE_TO		0x00040000 /* Timeout */
+#define SSB_INTVEC		0x0F94     /* SB Interrupt Mask */
+#define  SSB_INTVEC_PCI		0x00000001 /* Enable interrupts for PCI */
+#define  SSB_INTVEC_ENET0	0x00000002 /* Enable interrupts for enet 0 */
+#define  SSB_INTVEC_ILINE20	0x00000004 /* Enable interrupts for iline20 */
+#define  SSB_INTVEC_CODEC	0x00000008 /* Enable interrupts for v90 codec */
+#define  SSB_INTVEC_USB		0x00000010 /* Enable interrupts for usb */
+#define  SSB_INTVEC_EXTIF	0x00000020 /* Enable interrupts for external i/f */
+#define  SSB_INTVEC_ENET1	0x00000040 /* Enable interrupts for enet 1 */
+#define SSB_TMSLOW		0x0F98     /* SB Target State Low */
+#define  SSB_TMSLOW_RESET	0x00000001 /* Reset */
+#define  SSB_TMSLOW_REJECT_22	0x00000002 /* Reject (Backplane rev 2.2) */
+#define  SSB_TMSLOW_REJECT_23	0x00000004 /* Reject (Backplane rev 2.3) */
+#define  SSB_TMSLOW_CLOCK	0x00010000 /* Clock Enable */
+#define  SSB_TMSLOW_FGC		0x00020000 /* Force Gated Clocks On */
+#define  SSB_TMSLOW_PE		0x40000000 /* Power Management Enable */
+#define  SSB_TMSLOW_BE		0x80000000 /* BIST Enable */
+#define SSB_TMSHIGH		0x0F9C     /* SB Target State High */
+#define  SSB_TMSHIGH_SERR	0x00000001 /* S-error */
+#define  SSB_TMSHIGH_INT	0x00000002 /* Interrupt */
+#define  SSB_TMSHIGH_BUSY	0x00000004 /* Busy */
+#define  SSB_TMSHIGH_TO		0x00000020 /* Timeout. Backplane rev >= 2.3 only */
+#define  SSB_TMSHIGH_COREFL	0x1FFF0000 /* Core specific flags */
+#define  SSB_TMSHIGH_COREFL_SHIFT	16
+#define  SSB_TMSHIGH_DMA64	0x10000000 /* 64bit DMA supported */
+#define  SSB_TMSHIGH_GCR	0x20000000 /* Gated Clock Request */
+#define  SSB_TMSHIGH_BISTF	0x40000000 /* BIST Failed */
+#define  SSB_TMSHIGH_BISTD	0x80000000 /* BIST Done */
+#define SSB_BWA0		0x0FA0
+#define SSB_IMCFGLO		0x0FA8
+#define  SSB_IMCFGLO_SERTO	0x00000007 /* Service timeout */
+#define  SSB_IMCFGLO_REQTO	0x00000070 /* Request timeout */
+#define  SSB_IMCFGLO_REQTO_SHIFT	4
+#define  SSB_IMCFGLO_CONNID	0x00FF0000 /* Connection ID */
+#define  SSB_IMCFGLO_CONNID_SHIFT	16
+#define SSB_IMCFGHI		0x0FAC
+#define SSB_ADMATCH0		0x0FB0
+#define SSB_TMCFGLO		0x0FB8
+#define SSB_TMCFGHI		0x0FBC
+#define SSB_BCONFIG		0x0FC0
+#define SSB_BSTATE		0x0FC8
+#define SSB_ACTCFG		0x0FD8
+#define SSB_FLAGST		0x0FE8
+#define SSB_IDLOW		0x0FF8
+#define  SSB_IDLOW_CFGSP	0x00000003 /* Config Space */
+#define  SSB_IDLOW_ADDRNGE	0x00000038 /* Address Ranges supported */
+#define  SSB_IDLOW_ADDRNGE_SHIFT	3
+#define  SSB_IDLOW_SYNC		0x00000040
+#define  SSB_IDLOW_INITIATOR	0x00000080
+#define  SSB_IDLOW_MIBL		0x00000F00 /* Minimum Backplane latency */
+#define  SSB_IDLOW_MIBL_SHIFT	8
+#define  SSB_IDLOW_MABL		0x0000F000 /* Maximum Backplane latency */
+#define  SSB_IDLOW_MABL_SHIFT	12
+#define  SSB_IDLOW_TIF		0x00010000 /* This Initiator is first */
+#define  SSB_IDLOW_CCW		0x000C0000 /* Cycle counter width */
+#define  SSB_IDLOW_CCW_SHIFT	18
+#define  SSB_IDLOW_TPT		0x00F00000 /* Target ports */
+#define  SSB_IDLOW_TPT_SHIFT	20
+#define  SSB_IDLOW_INITP	0x0F000000 /* Initiator ports */
+#define  SSB_IDLOW_INITP_SHIFT	24
+#define  SSB_IDLOW_SSBREV	0xF0000000 /* Sonics Backplane Revision code */
+#define  SSB_IDLOW_SSBREV_22	0x00000000 /* <= 2.2 */
+#define  SSB_IDLOW_SSBREV_23	0x10000000 /* 2.3 */
+#define SSB_IDHIGH		0x0FFC     /* SB Identification High */
+#define  SSB_IDHIGH_RCLO	0x0000000F /* Revision Code (low part) */
+#define  SSB_IDHIGH_CC		0x00008FF0 /* Core Code */
+#define  SSB_IDHIGH_CC_SHIFT	4
+#define  SSB_IDHIGH_RCHI	0x00007000 /* Revision Code (high part) */
+#define  SSB_IDHIGH_RCHI_SHIFT	8	   /* yes, shift 8 is right */
+#define  SSB_IDHIGH_VC		0xFFFF0000 /* Vendor Code */
+#define  SSB_IDHIGH_VC_SHIFT	16
+
+/* SPROM shadow area. If not otherwise noted, fields are
+ * two bytes wide. Note that the SPROM can _only_ be read
+ * in two-byte quantinies.
+ */
+#define SSB_SPROMSIZE_WORDS		64
+#define SSB_SPROMSIZE_BYTES		(SSB_SPROMSIZE_WORDS * sizeof(u16))
+#define SSB_SPROM_BASE			0x1000
+#define SSB_SPROM_REVISION		0x107E
+#define  SSB_SPROM_REVISION_REV		0x00FF	/* SPROM Revision number */
+#define  SSB_SPROM_REVISION_CRC		0xFF00	/* SPROM CRC8 value */
+#define  SSB_SPROM_REVISION_CRC_SHIFT	8
+/* SPROM Revision 1 */
+#define SSB_SPROM1_SPID			0x1004	/* Subsystem Product ID for PCI */
+#define SSB_SPROM1_SVID			0x1006	/* Subsystem Vendor ID for PCI */
+#define SSB_SPROM1_PID			0x1008	/* Product ID for PCI */
+#define SSB_SPROM1_IL0MAC		0x1048	/* 6 bytes MAC address for 802.11b/g */
+#define SSB_SPROM1_ET0MAC		0x104E	/* 6 bytes MAC address for Ethernet */
+#define SSB_SPROM1_ET1MAC		0x1054	/* 6 bytes MAC address for 802.11a */
+#define SSB_SPROM1_ETHPHY		0x105A	/* Ethernet PHY settings */
+#define  SSB_SPROM1_ETHPHY_ET0A		0x001F	/* MII Address for enet0 */
+#define  SSB_SPROM1_ETHPHY_ET1A		0x03E0	/* MII Address for enet1 */
+#define  SSB_SPROM1_ETHPHY_ET1A_SHIFT	5
+#define  SSB_SPROM1_ETHPHY_ET0M		(1<<14)	/* MDIO for enet0 */
+#define  SSB_SPROM1_ETHPHY_ET1M		(1<<15)	/* MDIO for enet1 */
+#define SSB_SPROM1_BINF			0x105C	/* Board info */
+#define  SSB_SPROM1_BINF_BREV		0x00FF	/* Board Revision */
+#define  SSB_SPROM1_BINF_CCODE		0x0F00	/* Country Code */
+#define  SSB_SPROM1_BINF_CCODE_SHIFT	8
+#define  SSB_SPROM1_BINF_ANTA		0x3000	/* Available A-PHY antennas */
+#define  SSB_SPROM1_BINF_ANTA_SHIFT	12
+#define  SSB_SPROM1_BINF_ANTBG		0xC000	/* Available B-PHY antennas */
+#define  SSB_SPROM1_BINF_ANTBG_SHIFT	14
+#define SSB_SPROM1_PA0B0		0x105E
+#define SSB_SPROM1_PA0B1		0x1060
+#define SSB_SPROM1_PA0B2		0x1062
+#define SSB_SPROM1_GPIOA		0x1064	/* General Purpose IO pins 0 and 1 */
+#define  SSB_SPROM1_GPIOA_P0		0x00FF	/* Pin 0 */
+#define  SSB_SPROM1_GPIOA_P1		0xFF00	/* Pin 1 */
+#define  SSB_SPROM1_GPIOA_P1_SHIFT	8
+#define SSB_SPROM1_GPIOB		0x1066	/* General Purpuse IO pins 2 and 3 */
+#define  SSB_SPROM1_GPIOB_P2		0x00FF	/* Pin 2 */
+#define  SSB_SPROM1_GPIOB_P3		0xFF00	/* Pin 3 */
+#define  SSB_SPROM1_GPIOB_P3_SHIFT	8
+#define SSB_SPROM1_MAXPWR		0x1068	/* Power Amplifier Max Power */
+#define  SSB_SPROM1_MAXPWR_BG		0x00FF	/* B-PHY and G-PHY (in dBm Q5.2) */
+#define  SSB_SPROM1_MAXPWR_A		0xFF00	/* A-PHY (in dBm Q5.2) */
+#define  SSB_SPROM1_MAXPWR_A_SHIFT	8
+#define SSB_SPROM1_PA1B0		0x106A
+#define SSB_SPROM1_PA1B1		0x106C
+#define SSB_SPROM1_PA1B2		0x106E
+#define SSB_SPROM1_ITSSI		0x1070	/* Idle TSSI Target */
+#define  SSB_SPROM1_ITSSI_BG		0x00FF	/* B-PHY and G-PHY*/
+#define  SSB_SPROM1_ITSSI_A		0xFF00	/* A-PHY */
+#define  SSB_SPROM1_ITSSI_A_SHIFT	8
+#define SSB_SPROM1_BFLLO		0x1072	/* Boardflags (low 16 bits) */
+#define SSB_SPROM1_AGAIN		0x1074	/* Antenna Gain (in dBm Q5.2) */
+#define  SSB_SPROM1_AGAIN_A		0x00FF	/* A-PHY */
+#define  SSB_SPROM1_AGAIN_BG		0xFF00	/* B-PHY and G-PHY */
+#define  SSB_SPROM1_AGAIN_BG_SHIFT	8
+#define SSB_SPROM1_OEM			0x1076	/* 8 bytes OEM string (rev 1 only) */
+/* SPROM Revision 2 (inherits from rev 1) */
+#define SSB_SPROM2_BFLHI		0x1038	/* Boardflags (high 16 bits) */
+#define SSB_SPROM2_MAXP_A		0x103A	/* A-PHY Max Power */
+#define  SSB_SPROM2_MAXP_A_HI		0x00FF	/* Max Power High */
+#define  SSB_SPROM2_MAXP_A_LO		0xFF00	/* Max Power Low */
+#define  SSB_SPROM2_MAXP_A_LO_SHIFT	8
+#define SSB_SPROM2_PA1LOB0		0x103C	/* A-PHY PowerAmplifier Low Settings */
+#define SSB_SPROM2_PA1LOB1		0x103E	/* A-PHY PowerAmplifier Low Settings */
+#define SSB_SPROM2_PA1LOB2		0x1040	/* A-PHY PowerAmplifier Low Settings */
+#define SSB_SPROM2_PA1HIB0		0x1042	/* A-PHY PowerAmplifier High Settings */
+#define SSB_SPROM2_PA1HIB1		0x1044	/* A-PHY PowerAmplifier High Settings */
+#define SSB_SPROM2_PA1HIB2		0x1046	/* A-PHY PowerAmplifier High Settings */
+#define SSB_SPROM2_OPO			0x1078	/* OFDM Power Offset from CCK Level */
+#define  SSB_SPROM2_OPO_VALUE		0x00FF
+#define  SSB_SPROM2_OPO_UNUSED		0xFF00
+#define SSB_SPROM2_CCODE		0x107C	/* Two char Country Code */
+/* SPROM Revision 3 (inherits from rev 2) */
+#define SSB_SPROM3_OFDMAPO		0x102C	/* A-PHY OFDM Mid Power Offset (4 bytes, BigEndian) */
+#define SSB_SPROM3_OFDMALPO		0x1030	/* A-PHY OFDM Low Power Offset (4 bytes, BigEndian) */
+#define SSB_SPROM3_OFDMAHPO		0x1034	/* A-PHY OFDM High Power Offset (4 bytes, BigEndian) */
+#define SSB_SPROM3_GPIOLDC		0x1042	/* GPIO LED Powersave Duty Cycle (4 bytes, BigEndian) */
+#define  SSB_SPROM3_GPIOLDC_OFF		0x0000FF00	/* Off Count */
+#define  SSB_SPROM3_GPIOLDC_OFF_SHIFT	8
+#define  SSB_SPROM3_GPIOLDC_ON		0x00FF0000	/* On Count */
+#define  SSB_SPROM3_GPIOLDC_ON_SHIFT	16
+#define SSB_SPROM3_CCKPO		0x1078	/* CCK Power Offset */
+#define  SSB_SPROM3_CCKPO_1M		0x000F	/* 1M Rate PO */
+#define  SSB_SPROM3_CCKPO_2M		0x00F0	/* 2M Rate PO */
+#define  SSB_SPROM3_CCKPO_2M_SHIFT	4
+#define  SSB_SPROM3_CCKPO_55M		0x0F00	/* 5.5M Rate PO */
+#define  SSB_SPROM3_CCKPO_55M_SHIFT	8
+#define  SSB_SPROM3_CCKPO_11M		0xF000	/* 11M Rate PO */
+#define  SSB_SPROM3_CCKPO_11M_SHIFT	12
+#define  SSB_SPROM3_OFDMGPO		0x107A	/* G-PHY OFDM Power Offset (4 bytes, BigEndian) */
+
+/* Values for SSB_SPROM1_BINF_CCODE */
+enum {
+	SSB_SPROM1CCODE_WORLD = 0,
+	SSB_SPROM1CCODE_THAILAND,
+	SSB_SPROM1CCODE_ISRAEL,
+	SSB_SPROM1CCODE_JORDAN,
+	SSB_SPROM1CCODE_CHINA,
+	SSB_SPROM1CCODE_JAPAN,
+	SSB_SPROM1CCODE_USA_CANADA_ANZ,
+	SSB_SPROM1CCODE_EUROPE,
+	SSB_SPROM1CCODE_USA_LOW,
+	SSB_SPROM1CCODE_JAPAN_HIGH,
+	SSB_SPROM1CCODE_ALL,
+	SSB_SPROM1CCODE_NONE,
+};
+
+/* Address-Match values and masks (SSB_ADMATCHxxx) */
+#define SSB_ADM_TYPE			0x00000003	/* Address type */
+#define  SSB_ADM_TYPE0			0
+#define  SSB_ADM_TYPE1			1
+#define  SSB_ADM_TYPE2			2
+#define SSB_ADM_AD64			0x00000004
+#define SSB_ADM_SZ0			0x000000F8	/* Type0 size */
+#define SSB_ADM_SZ0_SHIFT		3
+#define SSB_ADM_SZ1			0x000001F8	/* Type1 size */
+#define SSB_ADM_SZ1_SHIFT		3
+#define SSB_ADM_SZ2			0x000001F8	/* Type2 size */
+#define SSB_ADM_SZ2_SHIFT		3
+#define SSB_ADM_EN			0x00000400	/* Enable */
+#define SSB_ADM_NEG			0x00000800	/* Negative decode */
+#define SSB_ADM_BASE0			0xFFFFFF00	/* Type0 base address */
+#define SSB_ADM_BASE0_SHIFT		8
+#define SSB_ADM_BASE1			0xFFFFF000	/* Type1 base address for the core */
+#define SSB_ADM_BASE1_SHIFT		12
+#define SSB_ADM_BASE2			0xFFFF0000	/* Type2 base address for the core */
+#define SSB_ADM_BASE2_SHIFT		16
+
+
+#endif /* LINUX_SSB_REGS_H_ */
diff --git a/MAINTAINERS b/MAINTAINERS
index 845fbf4..aee5182 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3298,6 +3347,12 @@
 L:	netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
 S:	Maintained
 
+SONICS SILICON BACKPLANE DRIVER (SSB)
+P:	Michael Buesch
+M:	mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org
+L:	netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S:	Maintained
+
 SONY VAIO CONTROL DEVICE DRIVER
 P:	Mattia Dongili
 M:	malattia-k2GhghHVRtY@public.gmane.org

^ permalink raw reply related

* Re: TCP SACK issue, hung connection, tcpdump included
From: Ilpo Järvinen @ 2007-07-29  9:28 UTC (permalink / raw)
  To: Willy Tarreau; +Cc: Darryl L. Miles, linux-kernel, Netdev
In-Reply-To: <20070729085427.GA22784@1wt.eu>

[-- Attachment #1: Type: TEXT/PLAIN, Size: 3481 bytes --]

On Sun, 29 Jul 2007, Willy Tarreau wrote:

> On Sun, Jul 29, 2007 at 11:26:00AM +0300, Ilpo Järvinen wrote:
> > On Sun, 29 Jul 2007, Willy Tarreau wrote:
> > 
> > > On Sun, Jul 29, 2007 at 06:59:26AM +0100, Darryl L. Miles wrote:
> > > > CLIENT = Linux 2.6.20.1-smp [Customer build]
> > > > SERVER = Linux 2.6.9-55.ELsmp [Red Hat Enterprise Linux AS release 4 
> > > > (Nahant Update 5)]
> > > > 
> > > > The problems start around time index 09:21:39.860302 when the CLIENT issues 
> > > > a TCP packet with SACK option set (seemingly for a data segment which has 
> > > > already been seen) from that point on the connection hangs.
> > 
> > ...That's DSACK and it's being correctly sent. To me, it seems unlikely to 
> > be the cause for this breakage...
> > 
> > > Where was the capture taken ? on CLIENT or on SERVER (I suspect client from
> > > the timers) ? 
> > 
> > ...I would guess the same based on SYN timestamps (and from the DSACK 
> > timestamps)...
> > 
> > > A possible, but very unlikely reason would be an MTU limitation
> > > somewhere, because the segment which never gets correctly ACKed is also the
> > > largest one in this trace.
> > 
> > Limitation for 48 byte segments? You have to be kidding... :-) But yes,
> > it seems that one of the directions is dropping packets for some reason 
> > though I would not assume MTU limitation... Or did you mean some other 
> > segment?
> 
> No, I was talking about the 1448 bytes segments. But in fact I don't
> believe it much because the SACKs are always retransmitted just afterwards.

Ah, but it's ACKed correctly right below it...:

[...snip...]
> > > > 09:21:39.490740 IP SERVER.ssh > CLIENT.50727: P 18200:18464(264) ack 2991 
> > > > win 2728 <nop,nop,timestamp 7692910 800001727>
> > > > 09:21:39.490775 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > > > <nop,nop,timestamp 800001755 7692910>
> > > > 09:21:39.860245 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > > > win 2728 <nop,nop,timestamp 7693293 800001749>

...segment below snd_una arrived => snd_una remains 18464, receiver 
generates a duplicate ACK:
 
> > > > 09:21:39.860302 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > > > <nop,nop,timestamp 800001847 7692910,nop,nop,sack sack 1 {12408:13856} >

The cumulative ACK field of it covers _everything_ below 18464 (i.e., it 
ACKs them), including the 1448 bytes in 12408:13856... In addition, the 
SACK block is DSACK information [RFC2883] telling explicitly the address 
of the received duplicate block. However, if this ACK doesn't reach the 
SERVER TCP, RTO is triggered and the first not yet cumulatively ACKed 
segment is retransmitted (I guess cumulative ACKs up to 12408 arrived 
without problems to the SERVER):

> > > > 09:21:40.453440 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > > > win 2728 <nop,nop,timestamp 7693887 800001749>
[...snip...]

> BTW, some information are missing. It would have been better if the trace
> had been read with tcpdump -Svv. We would have got seq numbers and ttl.
> Also, we do not know if there's a firewall between both sides. Sometimes,
> some IDS identify attacks in crypted traffic and kill connections. It
> might have been the case here, with the connection closed one way on an
> intermediate firewall.

Yeah, firewall or some other issue, I'd say it's quite unlikely a bug in 
TCP because behavior to both directions indicate client -> sender 
blackhole independently of each other...


-- 
 i.

^ permalink raw reply

* Re: TCP SACK issue, hung connection, tcpdump included
From: David Miller @ 2007-07-29  8:56 UTC (permalink / raw)
  To: ilpo.jarvinen; +Cc: w, darryl-mailinglists, linux-kernel, netdev
In-Reply-To: <Pine.LNX.4.64.0707291052300.10340@kivilampi-30.cs.helsinki.fi>

From: "Ilpo_Järvinen" <ilpo.jarvinen@helsinki.fi>
Date: Sun, 29 Jul 2007 11:26:00 +0300 (EEST)

> Is this reproducable? Can you somehow verify that the packets CLIENT is 
> sending are indeed received by the SERVER...?

One possibility is drops due to checksum errors on the receiver, this
tends to pop up from time to time, let's see some SNMP statistics.

^ permalink raw reply

* Re: TCP SACK issue, hung connection, tcpdump included
From: Willy Tarreau @ 2007-07-29  8:54 UTC (permalink / raw)
  To: Ilpo Järvinen; +Cc: Darryl L. Miles, linux-kernel, Netdev
In-Reply-To: <Pine.LNX.4.64.0707291052300.10340@kivilampi-30.cs.helsinki.fi>

On Sun, Jul 29, 2007 at 11:26:00AM +0300, Ilpo Järvinen wrote:
> On Sun, 29 Jul 2007, Willy Tarreau wrote:
> 
> > On Sun, Jul 29, 2007 at 06:59:26AM +0100, Darryl L. Miles wrote:
> > > CLIENT = Linux 2.6.20.1-smp [Customer build]
> > > SERVER = Linux 2.6.9-55.ELsmp [Red Hat Enterprise Linux AS release 4 
> > > (Nahant Update 5)]
> > > 
> > > The problems start around time index 09:21:39.860302 when the CLIENT issues 
> > > a TCP packet with SACK option set (seemingly for a data segment which has 
> > > already been seen) from that point on the connection hangs.
> 
> ...That's DSACK and it's being correctly sent. To me, it seems unlikely to 
> be the cause for this breakage...
> 
> > Where was the capture taken ? on CLIENT or on SERVER (I suspect client from
> > the timers) ? 
> 
> ...I would guess the same based on SYN timestamps (and from the DSACK 
> timestamps)...
> 
> > A possible, but very unlikely reason would be an MTU limitation
> > somewhere, because the segment which never gets correctly ACKed is also the
> > largest one in this trace.
> 
> Limitation for 48 byte segments? You have to be kidding... :-) But yes,
> it seems that one of the directions is dropping packets for some reason 
> though I would not assume MTU limitation... Or did you mean some other 
> segment?

No, I was talking about the 1448 bytes segments. But in fact I don't
believe it much because the SACKs are always retransmitted just afterwards.

BTW, some information are missing. It would have been better if the trace
had been read with tcpdump -Svv. We would have got seq numbers and ttl.
Also, we do not know if there's a firewall between both sides. Sometimes,
some IDS identify attacks in crypted traffic and kill connections. It
might have been the case here, with the connection closed one way on an
intermediate firewall.

Regards,
Willy


^ permalink raw reply

* Re: [linux-usb-devel] [PATCH] USB Pegasus driver - avoid a potential NULL pointer dereference.
From: Oliver Neukum @ 2007-07-29  8:49 UTC (permalink / raw)
  To: linux-usb-devel
  Cc: Jesper Juhl, Satyam Sharma, Greg Kroah-Hartman, netdev,
	Linux Kernel Mailing List, Petko Manolov
In-Reply-To: <9a8748490707281655u66e50bbfqba58687b579a85fe@mail.gmail.com>

Am Sonntag 29 Juli 2007 schrieb Jesper Juhl:
> On 29/07/07, Satyam Sharma <satyam.sharma@gmail.com> wrote:
> > Hi,
> >
> > On 7/29/07, Jesper Juhl <jesper.juhl@gmail.com> wrote:
> > > Hello,
> > >
> > > This patch makes sure we don't dereference a NULL pointer in
> > > drivers/net/usb/pegasus.c::write_bulk_callback() in the initial
> > > struct net_device *net = pegasus->net; assignment.
> > > The existing code checks if 'pegasus' is NULL and bails out if
> > > it is, so we better not touch that pointer until after that check.
> > > [...]
> > > diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c
> > > index a05fd97..04cba6b 100644
> > > --- a/drivers/net/usb/pegasus.c
> > > +++ b/drivers/net/usb/pegasus.c
> > > @@ -768,11 +768,13 @@ done:
> > >  static void write_bulk_callback(struct urb *urb)
> > >  {
> > >         pegasus_t *pegasus = urb->context;
> > > -       struct net_device *net = pegasus->net;
> > > +       struct net_device *net;
> > >
> > >         if (!pegasus)
> > >                 return;
> > >
> > > +       net = pegasus->net;
> > > +
> > >         if (!netif_device_present(net) || !netif_running(net))
> > >                 return;
> >
> > Is it really possible that we're called into this function with
> > urb->context == NULL? If not, I'd suggest let's just get rid of
> > the check itself, instead.
> >
> I'm not sure. I am not very familiar with this code. I just figured
> that moving the assignment is potentially a little safer and it is
> certainly no worse than the current code, so that's a safe and
> potentially benneficial change. Removing the check may be safe but I'm
> not certain enough to make that call...

pegasus == NULL there would be a kernel bug. Silently ignoring
it, like the code now wants to do is bad. As the oops has never been
reported, I figure turning it into an explicit debugging test is overkill,
so removal seems to be the best option.

	Regards
		Oliver

^ permalink raw reply

* Re: TCP SACK issue, hung connection, tcpdump included
From: Jan Engelhardt @ 2007-07-29  8:39 UTC (permalink / raw)
  To: Willy Tarreau; +Cc: Darryl L. Miles, linux-kernel, netdev
In-Reply-To: <20070729064511.GA18718@1wt.eu>


On Jul 29 2007 08:45, Willy Tarreau wrote:
>On Sun, Jul 29, 2007 at 06:59:26AM +0100, Darryl L. Miles wrote:
>> CLIENT = Linux 2.6.20.1-smp [Customer build]
>> SERVER = Linux 2.6.9-55.ELsmp [Red Hat Enterprise Linux AS release 4 
>> (Nahant Update 5)]
>> 
>> The problems start around time index 09:21:39.860302 when the CLIENT issues 
>> a TCP packet with SACK option set (seemingly for a data segment which has 
>> already been seen) from that point on the connection hangs.
>
>Where was the capture taken ? on CLIENT or on SERVER (I suspect client from
>the timers) ? A possible, but very unlikely reason would be an MTU limitation
>somewhere, because the segment which never gets correctly ACKed is also the
>largest one in this trace.[...]

I had something similar (and most strangely enough, it only seemed to happen
when the source and/or destination address was outside a given subnet). It
boiled down to that some switch acted really strange and God knows why. A
tcpdump on *both* sides (server+client) each showed that the *other* peer sent
a FIN/RST. Well in the end I settled with setting sys.net.ipv4.tcp_sack=0,
no idea if the broken switch got replaced in the meantime.
Well, just letting you know that it is not limited to computers.


	Jan
-- 

^ permalink raw reply

* Re: TCP SACK issue, hung connection, tcpdump included
From: Ilpo Järvinen @ 2007-07-29  8:26 UTC (permalink / raw)
  To: Willy Tarreau; +Cc: Darryl L. Miles, linux-kernel, Netdev
In-Reply-To: <20070729064511.GA18718@1wt.eu>

On Sun, 29 Jul 2007, Willy Tarreau wrote:

> On Sun, Jul 29, 2007 at 06:59:26AM +0100, Darryl L. Miles wrote:
> > CLIENT = Linux 2.6.20.1-smp [Customer build]
> > SERVER = Linux 2.6.9-55.ELsmp [Red Hat Enterprise Linux AS release 4 
> > (Nahant Update 5)]
> > 
> > The problems start around time index 09:21:39.860302 when the CLIENT issues 
> > a TCP packet with SACK option set (seemingly for a data segment which has 
> > already been seen) from that point on the connection hangs.

...That's DSACK and it's being correctly sent. To me, it seems unlikely to 
be the cause for this breakage...

> Where was the capture taken ? on CLIENT or on SERVER (I suspect client from
> the timers) ? 

...I would guess the same based on SYN timestamps (and from the DSACK 
timestamps)...

> A possible, but very unlikely reason would be an MTU limitation
> somewhere, because the segment which never gets correctly ACKed is also the
> largest one in this trace.

Limitation for 48 byte segments? You have to be kidding... :-) But yes,
it seems that one of the directions is dropping packets for some reason 
though I would not assume MTU limitation... Or did you mean some other 
segment?

> Also, if everything is right with the packets on the server side, then
> it would means that it's the RHEL kernel which is buggy (which does not
> mean that the same bug does not exist in mainline).

...There are two independed signs that server side is not getting the 
packets (ack field is not advanced nor do the retransmissions of an old 
data cease) so IMHO that would the first thing to rule out.

> > Full dump available via email.
> > 
> > 
> > 09:12:13.444999 IP CLIENT.50727 > SERVER.ssh: S 2919512080:2919512080(0) 
> > win 5840 <mss 1460,sackOK,timestamp 799860259 0,nop,wscale 7>
> > 09:12:13.535643 IP SERVER.ssh > CLIENT.50727: S 492909547:492909547(0) ack 
> > 2919512081 win 5792 <mss 1460,sackOK,timestamp 7126976 799860259,nop,wscale 
> > 2>
> > 09:12:13.535717 IP CLIENT.50727 > SERVER.ssh: . ack 1 win 46 
> > <nop,nop,timestamp 799860282 7126976>
> > 09:12:13.665481 IP SERVER.ssh > CLIENT.50727: P 1:24(23) ack 1 win 1448 
> > <nop,nop,timestamp 7127074 799860282>
> > 09:12:13.665895 IP CLIENT.50727 > SERVER.ssh: . ack 24 win 46 
> > <nop,nop,timestamp 799860314 7127074>
> > 09:12:13.666044 IP CLIENT.50727 > SERVER.ssh: P 1:23(22) ack 24 win 46 
> > <nop,nop,timestamp 799860314 7127074>
> > 09:12:13.776318 IP SERVER.ssh > CLIENT.50727: . ack 23 win 1448 
> > <nop,nop,timestamp 7127216 799860314>
> > 
> > ...SNIPPED SUCCESSFUL TRAFFIC...
> > 
> > 09:21:39.490740 IP SERVER.ssh > CLIENT.50727: P 18200:18464(264) ack 2991 
> > win 2728 <nop,nop,timestamp 7692910 800001727>
> > 09:21:39.490775 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800001755 7692910>
> > 09:21:39.860245 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 7693293 800001749>
> > 09:21:39.860302 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800001847 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:21:40.453440 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 7693887 800001749>
> > 09:21:40.453495 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800001996 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:21:41.641821 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 7695075 800001749>
> > 09:21:41.641938 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800002293 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:21:44.017552 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 7697451 800001749>
> > 09:21:44.017622 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800002887 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:21:48.770051 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 7702203 800001749>
> > 09:21:48.770099 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800004075 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:21:58.274061 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 7711707 800001749>
> > 09:21:58.274180 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800006450 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:22:17.282035 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 7730715 800001749>
> > 09:22:17.282153 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800011202 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:22:55.298955 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 7768731 800001749>
> > 09:22:55.299023 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800020705 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:24:11.329853 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 7844763 800001749>
> > 09:24:11.329923 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800039711 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:26:11.331578 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 7964763 800001749>
> > 09:26:11.331699 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800069708 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:26:13.011885 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800070128 7692910>
> > 09:26:13.309032 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800070202 7692910>
> > 09:26:13.901048 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800070350 7692910>
> > 09:26:15.085103 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800070646 7692910>
> > 09:26:17.453195 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800071238 7692910>
> > 09:26:22.189378 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800072422 7692910>
> > 09:26:31.661696 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800074790 7692910>
> > 09:26:50.610374 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800079526 7692910>
> > 09:27:28.499729 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800088998 7692910>
> > 09:28:11.331256 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 8084763 800001749>
> > 09:28:11.331354 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800099704 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:28:44.286495 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800107942 7692910>
> > 09:30:11.330959 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 8204763 800001749>
> > 09:30:11.331074 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800129701 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:30:44.298756 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800137942 7692910>
> > 09:32:11.331661 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 8324763 800001749>
> > 09:32:11.331727 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800159698 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:32:44.311051 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800167942 7692910>
> > 09:34:11.332375 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 8444763 800001749>
> > 09:34:11.332447 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800189695 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:34:44.323298 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800197942 7692910>
> > 09:36:11.332073 IP SERVER.ssh > CLIENT.50727: . 12408:13856(1448) ack 2991 
> > win 2728 <nop,nop,timestamp 8564763 800001749>
> > 09:36:11.332166 IP CLIENT.50727 > SERVER.ssh: . ack 18464 win 378 
> > <nop,nop,timestamp 800219692 7692910,nop,nop,sack sack 1 {12408:13856} >
> > 09:36:44.335591 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800227942 7692910>
> > 09:38:44.351950 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800257942 7692910>
> > 09:40:44.368172 IP CLIENT.50727 > SERVER.ssh: P 2991:3039(48) ack 18464 win 
> > 378 <nop,nop,timestamp 800287943 7692910>
> > 
> > At the end of the dump here the connection is dropped by the client side.

...There a limit on how many times a retransmission of a segment is tried, 
and in the dump both directions are trying to retransmit (and then they 
finally gave up), perhaps CLIENT->SERVER direction drops all packets after 
09:21:39.490740 (or even before that), so that no cumulative ACK for seq 
18464 ever reaches SERVER (which is still having snd_una @ 12408 I guess) 
nor the retransmissions of 2991:3039 from the CLIENT.

Is this reproducable? Can you somehow verify that the packets CLIENT is 
sending are indeed received by the SERVER...?

-- 
 i.

^ permalink raw reply

* Re: [PATCH 0/7] SFQ: backport some features from ESFQ
From: David Miller @ 2007-07-29  7:19 UTC (permalink / raw)
  To: bugfood-ml; +Cc: netdev
In-Reply-To: <46AC3EFB.6040504@fatooh.org>

From: Corey Hickey <bugfood-ml@fatooh.org>
Date: Sun, 29 Jul 2007 00:17:15 -0700

> Corey Hickey wrote:
> > Hello,
> > 
> > This set of patches adds some of ESFQ's modifications to the original 
> > SFQ. Thus far, I have received support for this approach rather than for 
> > trying to get ESFQ included as a separate qdisc.
> 
> Crud; I wasn't expecting git-send-email to thread the messages that way. 
> I guess I should have used --no-chain-reply-to. If I ought re-send this 
> series with that option, somebody please say the word and I'll do so.

It's not necessary, let some feedback come in first.

^ permalink raw reply

* Re: [PATCH 4/4] l2cap: don't mangle cmd.len
From: David Miller @ 2007-07-29  7:18 UTC (permalink / raw)
  To: marcel; +Cc: viro, linux-kernel, netdev
In-Reply-To: <1185547286.13228.18.camel@violet>

From: Marcel Holtmann <marcel@holtmann.org>
Date: Fri, 27 Jul 2007 16:41:26 +0200

> > Since nobody uses it after we convert it to host-endian,
> > no need to do that at all.  At that point l2cap is endian-clean.
> > 
> > Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> 
> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

Also applied, thanks a lot.

^ permalink raw reply

* Re: [PATCH 3/4] pass (host-endian) cmd length as explicit argument to l2cap_conf_req()
From: David Miller @ 2007-07-29  7:17 UTC (permalink / raw)
  To: marcel; +Cc: viro, linux-kernel, netdev
In-Reply-To: <1185547283.13228.17.camel@violet>

From: Marcel Holtmann <marcel@holtmann.org>
Date: Fri, 27 Jul 2007 16:41:22 +0200

> > Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> 
> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

Applied.

^ permalink raw reply

* Re: [PATCH 0/7] SFQ: backport some features from ESFQ
From: Corey Hickey @ 2007-07-29  7:17 UTC (permalink / raw)
  To: netdev
In-Reply-To: <11856929352537-git-send-email-bugfood-ml@fatooh.org>

Corey Hickey wrote:
> Hello,
> 
> This set of patches adds some of ESFQ's modifications to the original 
> SFQ. Thus far, I have received support for this approach rather than for 
> trying to get ESFQ included as a separate qdisc.

Crud; I wasn't expecting git-send-email to thread the messages that way. 
I guess I should have used --no-chain-reply-to. If I ought re-send this 
series with that option, somebody please say the word and I'll do so.

-Corey

^ permalink raw reply

* Re: [PATCH 2/4] l2cap: endianness annotations
From: David Miller @ 2007-07-29  7:16 UTC (permalink / raw)
  To: marcel; +Cc: viro, linux-kernel, netdev
In-Reply-To: <1185547281.13228.16.camel@violet>

From: Marcel Holtmann <marcel@holtmann.org>
Date: Fri, 27 Jul 2007 16:41:21 +0200

> > no code changes, just documenting existing types
> > 
> > Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> 
> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

Applied.

^ permalink raw reply

* Re: [PATCH 1/4] fix endianness bug in l2cap_sock_listen()
From: David Miller @ 2007-07-29  7:16 UTC (permalink / raw)
  To: marcel; +Cc: viro, linux-kernel, netdev
In-Reply-To: <1185547278.13228.15.camel@violet>

From: Marcel Holtmann <marcel@holtmann.org>
Date: Fri, 27 Jul 2007 16:41:18 +0200

> > We loop through psm values, calling __l2cap_get_sock_by_addr(psm, ...)
> > until we get NULL; then we set ->psm of our socket to htobs(psm).
> > IOW, we find unused psm value and put it into our socket.  So far, so
> > good, but...  __l2cap_get_sock_by_addr() compares its argument with
> > ->psm of sockets.  IOW, the entire thing works correctly only on
> > little-endian.  On big-endian we'll get "no socket with such psm"
> > on the first iteration, since we won't find a socket with ->psm == 0x1001.
> > We will happily conclude that 0x1001 is unused and slap htobs(0x1001)
> > (i.e. 0x110) into ->psm of our socket.  Of course, the next time around
> > the same thing will repeat and we'll just get a fsckload of sockets
> > with the same ->psm assigned.
> > 
> > Fix: pass htobs(psm) to __l2cap_get_sock_by_addr() there.  All other
> > callers are already passing little-endian values and all places that
> > store something in ->psm are storing little-endian.
> > 
> > Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> 
> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>

Applied.

> Dave, all four patches are good and should go in sooner than later. If
> you want me to put them into my tree first, then I can do that or you
> can apply them directly. You choice.

I'll take care of these, thanks.

^ permalink raw reply

* [PATCH] [iproute2] SFQ: Support changing depth and divisor.
From: Corey Hickey @ 2007-07-29  7:08 UTC (permalink / raw)
  To: netdev; +Cc: Corey Hickey
In-Reply-To: <1185692936479-git-send-email-bugfood-ml@fatooh.org>

This can safely be applied either before or after the kernel
patches because the tc_sfq_qopt struct is unchanged:

- old kernels will ignore the parameters from new iproute2
- new kernels will use the same default parameters
---
 include/linux/pkt_sched.h |    9 ---------
 tc/q_sfq.c                |   21 ++++++++++++++++++++-
 2 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index d10f353..37946d4 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -139,15 +139,6 @@ struct tc_sfq_qopt
 	unsigned	flows;		/* Maximal number of flows  */
 };
 
-/*
- *  NOTE: limit, divisor and flows are hardwired to code at the moment.
- *
- *	limit=flows=128, divisor=1024;
- *
- *	The only reason for this is efficiency, it is possible
- *	to change these parameters in compile time.
- */
-
 /* RED section */
 
 enum
diff --git a/tc/q_sfq.c b/tc/q_sfq.c
index 05385cf..7754db7 100644
--- a/tc/q_sfq.c
+++ b/tc/q_sfq.c
@@ -25,7 +25,7 @@
 
 static void explain(void)
 {
-	fprintf(stderr, "Usage: ... sfq [ limit NUMBER ] [ perturb SECS ] [ quantum BYTES ]\n");
+	fprintf(stderr, "Usage: ... sfq [ limit NUMBER ] [ depth FLOWS ] [ divisor HASHBITS ] [ perturb SECS ] [ quantum BYTES ]\n");
 }
 
 #define usage() return(-1)
@@ -63,6 +63,25 @@ static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl
 				return -1;
 			}
 			ok++;
+		} else if (strcmp(*argv, "depth") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&opt.flows, *argv, 0)) {
+				fprintf(stderr, "Illegal \"depth\"\n");
+				return -1;
+			}
+			ok++;
+		} else if (strcmp(*argv, "divisor") == 0) {
+			NEXT_ARG();
+			if (get_unsigned(&opt.divisor, *argv, 0)) {
+				fprintf(stderr, "Illegal \"divisor\"\n");
+				return -1;
+			}
+			if (opt.divisor >= 15) {
+				fprintf(stderr, "Illegal \"divisor\", must be < 15\n");
+				return -1;
+			}
+			opt.divisor = 1<<opt.divisor;
+			ok++;
 		} else if (strcmp(*argv, "help") == 0) {
 			explain();
 			return -1;
-- 
1.5.2.4


^ permalink raw reply related

* [PATCH 6/7] Make qdisc changeable.
From: Corey Hickey @ 2007-07-29  7:08 UTC (permalink / raw)
  To: netdev; +Cc: Corey Hickey
In-Reply-To: <1185692936660-git-send-email-bugfood-ml@fatooh.org>

Re-implement sfq_change() and enable Qdisc_opts.change so "tc qdisc
change" will work.

Signed-off-by: Corey Hickey <bugfood-ml@fatooh.org>
---
 net/sched/sch_sfq.c |   51 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 50 insertions(+), 1 deletions(-)

diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 3e67a68..4cd523f 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -490,6 +490,55 @@ static int sfq_init(struct Qdisc *sch, struct rtattr *opt)
 	return 0;
 }
 
+static int sfq_change(struct Qdisc *sch, struct rtattr *opt)
+{
+	struct sfq_sched_data *q = qdisc_priv(sch);
+	struct sfq_sched_data tmp;
+	struct sk_buff *skb;
+	int err;
+	
+	/* set up tmp queue */
+	memset(&tmp, 0, sizeof(struct sfq_sched_data));
+	tmp.quantum = psched_mtu(sch->dev); /* default */
+	if ((err = sfq_q_init(&tmp, opt)))
+		return err;
+
+	/* copy packets from the old queue to the tmp queue */
+	sch_tree_lock(sch);
+	while (sch->q.qlen >= tmp.limit - 1)
+		sfq_drop(sch);
+	while ((skb = sfq_q_dequeue(q)) != NULL)
+		sfq_q_enqueue(skb, &tmp, SFQ_TAIL);
+	
+	/* clean up the old queue */
+	sfq_q_destroy(q);
+
+	/* copy elements of the tmp queue into the old queue */
+	q->perturb_period = tmp.perturb_period;
+	q->quantum        = tmp.quantum;
+	q->limit          = tmp.limit;
+	q->depth          = tmp.depth;
+	q->hash_divisor   = tmp.hash_divisor;
+	q->tail           = tmp.tail;
+	q->max_depth      = tmp.max_depth;
+	q->ht    = tmp.ht;
+	q->dep   = tmp.dep;
+	q->next  = tmp.next;
+	q->allot = tmp.allot;
+	q->hash  = tmp.hash;
+	q->qs    = tmp.qs;
+
+	/* finish up */
+	if (q->perturb_period) {
+		q->perturb_timer.expires = jiffies + q->perturb_period;
+		add_timer(&q->perturb_timer);
+	} else {
+		q->perturbation = 0;
+	}
+	sch_tree_unlock(sch);
+	return 0;
+}
+
 static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
 	struct sfq_sched_data *q = qdisc_priv(sch);
@@ -524,7 +573,7 @@ static struct Qdisc_ops sfq_qdisc_ops = {
 	.init		=	sfq_init,
 	.reset		=	sfq_reset,
 	.destroy	=	sfq_destroy,
-	.change		=	NULL,
+	.change		=	sfq_change,
 	.dump		=	sfq_dump,
 	.owner		=	THIS_MODULE,
 };
-- 
1.5.2.4


^ permalink raw reply related

* [PATCH 7/7] Remove comments about hardcoded values.
From: Corey Hickey @ 2007-07-29  7:08 UTC (permalink / raw)
  To: netdev; +Cc: Corey Hickey
In-Reply-To: <11856929364083-git-send-email-bugfood-ml@fatooh.org>

None of these are true anymore (hooray!).

Signed-off-by: Corey Hickey <bugfood-ml@fatooh.org>
---
 include/linux/pkt_sched.h |    8 --------
 net/sched/sch_sfq.c       |   17 +++--------------
 2 files changed, 3 insertions(+), 22 deletions(-)

diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index 268c515..58a0ea6 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -148,14 +148,6 @@ struct tc_sfq_qopt
 	unsigned	flows;		/* Maximal number of flows  */
 };
 
-/*
- *  NOTE: limit, divisor and flows are hardwired to code at the moment.
- *
- *	limit=flows=128, divisor=1024;
- *
- *	The only reason for this is efficiency, it is possible
- *	to change these parameters in compile time.
- */
 
 /* RED section */
 
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 4cd523f..8e84881 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -61,18 +61,7 @@
 
 	We still need true WFQ for top level CSZ, but using WFQ
 	for the best effort traffic is absolutely pointless:
-	SFQ is superior for this purpose.
-
-	IMPLEMENTATION:
-	This implementation limits maximal queue length to 128;
-	maximal mtu to 2^15-1; number of hash buckets to 1024.
-	The only goal of this restrictions was that all data
-	fit into one 4K page :-). Struct sfq_sched_data is
-	organized in anti-cache manner: all the data for a bucket
-	are scattered over different locations. This is not good,
-	but it allowed me to put it into 4K.
-
-	It is easy to increase these values, but not in flight.  */
+	SFQ is superior for this purpose. */
 
 #define SFQ_DEPTH_DEFAULT	128
 #define SFQ_DIVISOR_DEFAULT	1024
@@ -496,7 +485,7 @@ static int sfq_change(struct Qdisc *sch, struct rtattr *opt)
 	struct sfq_sched_data tmp;
 	struct sk_buff *skb;
 	int err;
-	
+
 	/* set up tmp queue */
 	memset(&tmp, 0, sizeof(struct sfq_sched_data));
 	tmp.quantum = psched_mtu(sch->dev); /* default */
@@ -509,7 +498,7 @@ static int sfq_change(struct Qdisc *sch, struct rtattr *opt)
 		sfq_drop(sch);
 	while ((skb = sfq_q_dequeue(q)) != NULL)
 		sfq_q_enqueue(skb, &tmp, SFQ_TAIL);
-	
+
 	/* clean up the old queue */
 	sfq_q_destroy(q);
 
-- 
1.5.2.4


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox