Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h
index a33d87a..7bbf730 100644
--- a/drivers/isdn/hardware/mISDN/hfc_multi.h
+++ b/drivers/isdn/hardware/mISDN/hfc_multi.h
@@ -162,8 +162,8 @@
 	void		(*write_fifo)(struct hfc_multi *hc, u_char *data,
 				int len);
 	u_long		pci_origmembase, plx_origmembase, dsp_origmembase;
-	u_char		*pci_membase; /* PCI memory (MUST BE BYTE POINTER) */
-	u_char		*plx_membase; /* PLX memory */
+	void __iomem	*pci_membase; /* PCI memory */
+	void __iomem	*plx_membase; /* PLX memory */
 	u_char		*dsp_membase; /* DSP on PLX */
 	u_long		pci_iobase; /* PCI IO */
 	struct hfcm_hw	hw;	/* remember data of write-only-registers */
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index 1eac03f..c63e2f4 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -171,9 +171,8 @@
 static int interrupt_registered;
 
 static struct hfc_multi *syncmaster;
-int plxsd_master; /* if we have a master card (yet) */
+static int plxsd_master; /* if we have a master card (yet) */
 static spinlock_t plx_lock; /* may not acquire other lock inside */
-EXPORT_SYMBOL(plx_lock);
 
 #define	TYP_E1		1
 #define	TYP_4S		4
@@ -422,7 +421,7 @@
 #endif
 
 /* write fifo data (REGIO) */
-void
+static void
 write_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
 {
 	outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
@@ -443,7 +442,7 @@
 	}
 }
 /* write fifo data (PCIMEM) */
-void
+static void
 write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
 {
 	while (len>>2) {
@@ -465,7 +464,7 @@
 	}
 }
 /* read fifo data (REGIO) */
-void
+static void
 read_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
 {
 	outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
@@ -487,7 +486,7 @@
 }
 
 /* read fifo data (PCIMEM) */
-void
+static void
 read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
 {
 	while (len>>2) {
@@ -706,7 +705,7 @@
 }
 
 
-void
+static void
 vpm_init(struct hfc_multi *wc)
 {
 	unsigned char reg;
@@ -789,7 +788,8 @@
 	}
 }
 
-void
+#ifdef UNUSED
+static void
 vpm_check(struct hfc_multi *hctmp)
 {
 	unsigned char gpi2;
@@ -799,6 +799,7 @@
 	if ((gpi2 & 0x3) != 0x3)
 		printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2);
 }
+#endif /* UNUSED */
 
 
 /*
@@ -812,7 +813,7 @@
  *
  */
 
-void
+static void
 vpm_echocan_on(struct hfc_multi *hc, int ch, int taps)
 {
 	unsigned int timeslot;
@@ -844,7 +845,7 @@
 	vpm_out(hc, unit, timeslot, 0x7e);
 }
 
-void
+static void
 vpm_echocan_off(struct hfc_multi *hc, int ch)
 {
 	unsigned int timeslot;
@@ -887,8 +888,9 @@
 static inline void
 hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm)
 {
-	struct hfc_multi *hc, *next, *pcmmaster = 0;
-	u_int *plx_acc_32, pv;
+	struct hfc_multi *hc, *next, *pcmmaster = NULL;
+	void __iomem *plx_acc_32;
+	u_int pv;
 	u_long flags;
 
 	spin_lock_irqsave(&HFClock, flags);
@@ -916,7 +918,7 @@
 	/* Disable sync of all cards */
 	list_for_each_entry_safe(hc, next, &HFClist, list) {
 		if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
-			plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+			plx_acc_32 = hc->plx_membase + PLX_GPIOC;
 			pv = readl(plx_acc_32);
 			pv &= ~PLX_SYNC_O_EN;
 			writel(pv, plx_acc_32);
@@ -938,7 +940,7 @@
 			printk(KERN_DEBUG "id=%d (0x%p) = syncronized with "
 				"interface.\n", hc->id, hc);
 		/* Enable new sync master */
-		plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+		plx_acc_32 = hc->plx_membase + PLX_GPIOC;
 		pv = readl(plx_acc_32);
 		pv |= PLX_SYNC_O_EN;
 		writel(pv, plx_acc_32);
@@ -968,7 +970,7 @@
 					    "QUARTZ is automatically "
 					    "enabled by HFC-%dS\n", hc->type);
 			}
-			plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+			plx_acc_32 = hc->plx_membase + PLX_GPIOC;
 			pv = readl(plx_acc_32);
 			pv |= PLX_SYNC_O_EN;
 			writel(pv, plx_acc_32);
@@ -1013,7 +1015,8 @@
 static void
 release_io_hfcmulti(struct hfc_multi *hc)
 {
-	u_int	*plx_acc_32, pv;
+	void __iomem *plx_acc_32;
+	u_int	pv;
 	u_long	plx_flags;
 
 	if (debug & DEBUG_HFCMULTI_INIT)
@@ -1033,7 +1036,7 @@
 			printk(KERN_DEBUG "%s: release PLXSD card %d\n",
 			    __func__, hc->id + 1);
 		spin_lock_irqsave(&plx_lock, plx_flags);
-		plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+		plx_acc_32 = hc->plx_membase + PLX_GPIOC;
 		writel(PLX_GPIOC_INIT, plx_acc_32);
 		pv = readl(plx_acc_32);
 		/* Termination off */
@@ -1055,9 +1058,9 @@
 	test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */
 	pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0);
 	if (hc->pci_membase)
-		iounmap((void *)hc->pci_membase);
+		iounmap(hc->pci_membase);
 	if (hc->plx_membase)
-		iounmap((void *)hc->plx_membase);
+		iounmap(hc->plx_membase);
 	if (hc->pci_iobase)
 		release_region(hc->pci_iobase, 8);
 
@@ -1080,7 +1083,8 @@
 	u_long			flags, val, val2 = 0, rev;
 	int			i, err = 0;
 	u_char			r_conf_en, rval;
-	u_int			*plx_acc_32, pv;
+	void __iomem		*plx_acc_32;
+	u_int			pv;
 	u_long			plx_flags, hfc_flags;
 	int			plx_count;
 	struct hfc_multi	*pos, *next, *plx_last_hc;
@@ -1154,7 +1158,7 @@
 			printk(KERN_DEBUG "%s: initializing PLXSD card %d\n",
 			    __func__, hc->id + 1);
 		spin_lock_irqsave(&plx_lock, plx_flags);
-		plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+		plx_acc_32 = hc->plx_membase + PLX_GPIOC;
 		writel(PLX_GPIOC_INIT, plx_acc_32);
 		pv = readl(plx_acc_32);
 		/* The first and the last cards are terminating the PCM bus */
@@ -1190,8 +1194,7 @@
 					"we disable termination\n",
 				    __func__, plx_last_hc->id + 1);
 			spin_lock_irqsave(&plx_lock, plx_flags);
-			plx_acc_32 = (u_int *)(plx_last_hc->plx_membase
-					+ PLX_GPIOC);
+			plx_acc_32 = plx_last_hc->plx_membase + PLX_GPIOC;
 			pv = readl(plx_acc_32);
 			pv &= ~PLX_TERM_ON;
 			writel(pv, plx_acc_32);
@@ -1240,7 +1243,7 @@
 	/* Speech Design PLX bridge pcm and sync mode */
 	if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
 		spin_lock_irqsave(&plx_lock, plx_flags);
-		plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+		plx_acc_32 = hc->plx_membase + PLX_GPIOC;
 		pv = readl(plx_acc_32);
 		/* Connect PCM */
 		if (hc->hw.r_pcm_md0 & V_PCM_MD) {
@@ -1352,8 +1355,7 @@
 			/* retry with master clock */
 			if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
 				spin_lock_irqsave(&plx_lock, plx_flags);
-				plx_acc_32 = (u_int *)(hc->plx_membase +
-					PLX_GPIOC);
+				plx_acc_32 = hc->plx_membase + PLX_GPIOC;
 				pv = readl(plx_acc_32);
 				pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N;
 				pv |= PLX_SYNC_O_EN;
@@ -1389,7 +1391,7 @@
 		if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip))
 			plxsd_master = 1;
 		spin_lock_irqsave(&plx_lock, plx_flags);
-		plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+		plx_acc_32 = hc->plx_membase + PLX_GPIOC;
 		pv = readl(plx_acc_32);
 		pv |=  PLX_DSP_RES_N;
 		writel(pv, plx_acc_32);
@@ -2586,7 +2588,8 @@
 	struct dchannel		*dch;
 	u_char			r_irq_statech, status, r_irq_misc, r_irq_oview;
 	int			i;
-	u_short			*plx_acc, wval;
+	void __iomem		*plx_acc;
+	u_short			wval;
 	u_char			e1_syncsta, temp;
 	u_long			flags;
 
@@ -2606,7 +2609,7 @@
 
 	if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
 		spin_lock_irqsave(&plx_lock, flags);
-		plx_acc = (u_short *)(hc->plx_membase + PLX_INTCSR);
+		plx_acc = hc->plx_membase + PLX_INTCSR;
 		wval = readw(plx_acc);
 		spin_unlock_irqrestore(&plx_lock, flags);
 		if (!(wval & PLX_INTCSR_LINTI1_STATUS))
@@ -4091,7 +4094,7 @@
 {
 	int	err = -EIO;
 	u_long	flags;
-	u_short	*plx_acc;
+	void	__iomem *plx_acc;
 	u_long	plx_flags;
 
 	if (debug & DEBUG_HFCMULTI_INIT)
@@ -4113,7 +4116,7 @@
 
 	if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
 		spin_lock_irqsave(&plx_lock, plx_flags);
-		plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+		plx_acc = hc->plx_membase + PLX_INTCSR;
 		writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE),
 			plx_acc); /* enable PCI & LINT1 irq */
 		spin_unlock_irqrestore(&plx_lock, plx_flags);
@@ -4162,7 +4165,7 @@
 error:
 	if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
 		spin_lock_irqsave(&plx_lock, plx_flags);
-		plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+		plx_acc = hc->plx_membase + PLX_INTCSR;
 		writew(0x00, plx_acc); /*disable IRQs*/
 		spin_unlock_irqrestore(&plx_lock, plx_flags);
 	}
diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c
index 3306817..751665c 100644
--- a/drivers/isdn/mISDN/core.c
+++ b/drivers/isdn/mISDN/core.c
@@ -26,12 +26,12 @@
 module_param(debug, uint, S_IRUGO | S_IWUSR);
 
 static LIST_HEAD(devices);
-DEFINE_RWLOCK(device_lock);
+static DEFINE_RWLOCK(device_lock);
 static u64		device_ids;
 #define MAX_DEVICE_ID	63
 
 static LIST_HEAD(Bprotocols);
-DEFINE_RWLOCK(bp_lock);
+static DEFINE_RWLOCK(bp_lock);
 
 struct mISDNdevice
 *get_mdevice(u_int id)
@@ -192,7 +192,7 @@
 }
 EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
 
-int
+static int
 mISDNInit(void)
 {
 	int	err;
@@ -224,7 +224,7 @@
 	return err;
 }
 
-void mISDN_cleanup(void)
+static void mISDN_cleanup(void)
 {
 	misdn_sock_cleanup();
 	mISDN_timer_cleanup();
diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c
index 1c2dd56..de3795e 100644
--- a/drivers/isdn/mISDN/dsp_audio.c
+++ b/drivers/isdn/mISDN/dsp_audio.c
@@ -30,7 +30,7 @@
 /* alaw -> ulaw */
 u8 dsp_audio_alaw_to_ulaw[256];
 /* ulaw -> alaw */
-u8 dsp_audio_ulaw_to_alaw[256];
+static u8 dsp_audio_ulaw_to_alaw[256];
 u8 dsp_silence;
 
 
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
index c2f51cc..c884511 100644
--- a/drivers/isdn/mISDN/dsp_cmx.c
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -1540,11 +1540,13 @@
 	schedule_work(&dsp->workq);
 }
 
-u32	samplecount;
+static u32	samplecount;
 struct timer_list dsp_spl_tl;
 u32	dsp_spl_jiffies; /* calculate the next time to fire */
-u32	dsp_start_jiffies; /* jiffies at the time, the calculation begins */
-struct timeval dsp_start_tv; /* time at start of calculation */
+#ifdef UNUSED
+static u32	dsp_start_jiffies; /* jiffies at the time, the calculation begins */
+#endif /* UNUSED */
+static struct timeval dsp_start_tv; /* time at start of calculation */
 
 void
 dsp_cmx_send(void *arg)
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
index 2f10ed8..1dc21d8 100644
--- a/drivers/isdn/mISDN/dsp_core.c
+++ b/drivers/isdn/mISDN/dsp_core.c
@@ -161,7 +161,7 @@
 #include "core.h"
 #include "dsp.h"
 
-const char *mISDN_dsp_revision = "2.0";
+static const char *mISDN_dsp_revision = "2.0";
 
 static int debug;
 static int options;
@@ -631,7 +631,6 @@
 	int			ret = 0;
 	u8			*digits;
 	int			cont;
-	struct			sk_buff *nskb;
 	u_long			flags;
 
 	hh = mISDN_HEAD_P(skb);
@@ -690,6 +689,7 @@
 			digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
 				skb->len, (dsp_options&DSP_OPT_ULAW)?1:0);
 			while (*digits) {
+				struct sk_buff *nskb;
 				if (dsp_debug & DEBUG_DSP_DTMF)
 					printk(KERN_DEBUG "%s: digit"
 					    "(%c) to layer %s\n",
diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c
index 850260a..5ee6651 100644
--- a/drivers/isdn/mISDN/dsp_pipeline.c
+++ b/drivers/isdn/mISDN/dsp_pipeline.c
@@ -249,7 +249,7 @@
 		name = strsep(&tok, "(");
 		args = strsep(&tok, ")");
 		if (args && !*args)
-			args = 0;
+			args = NULL;
 
 		list_for_each_entry_safe(entry, n, &dsp_elements, list)
 			if (!strcmp(entry->elem->name, name)) {
diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c
index 23dd0dd..7a9af66 100644
--- a/drivers/isdn/mISDN/dsp_tones.c
+++ b/drivers/isdn/mISDN/dsp_tones.c
@@ -231,120 +231,120 @@
  * tone sequence definition *
  ****************************/
 
-struct pattern {
+static struct pattern {
 	int tone;
 	u8 *data[10];
 	u32 *siz[10];
 	u32 seq[10];
 } pattern[] = {
 	{TONE_GERMAN_DIALTONE,
-	{DATA_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_OLDDIALTONE,
-	{DATA_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_AMERICAN_DIALTONE,
-	{DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_DIALPBX,
-	{DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0},
-	{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0},
+	{DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, NULL},
+	{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL},
 	{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_OLDDIALPBX,
-	{DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0},
-	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0},
+	{DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL},
+	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL},
 	{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
 
 	{TONE_AMERICAN_DIALPBX,
-	{DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, 0, 0, 0, 0},
-	{SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, 0, 0, 0, 0},
+	{DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, NULL, NULL, NULL, NULL},
+	{SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, NULL, NULL, NULL, NULL},
 	{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_RINGING,
-	{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_OLDRINGING,
-	{DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_AMERICAN_RINGING,
-	{DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_RINGPBX,
-	{DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0},
-	{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0},
+	{DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
 	{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_OLDRINGPBX,
-	{DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
-	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
+	{DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
 	{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_AMERICAN_RINGPBX,
-	{DATA_RI, DATA_S, DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0},
-	{SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0},
+	{DATA_RI, DATA_S, DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
 	{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_BUSY,
-	{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_OLDBUSY,
-	{DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_AMERICAN_BUSY,
-	{DATA_BU, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_BU, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_BU, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_BU, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_HANGUP,
-	{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_OLDHANGUP,
-	{DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_AMERICAN_HANGUP,
-	{DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_SPECIAL_INFO,
-	{DATA_S1, DATA_S2, DATA_S3, DATA_S, 0, 0, 0, 0, 0, 0},
-	{SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, 0, 0, 0, 0, 0, 0},
+	{DATA_S1, DATA_S2, DATA_S3, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
 	{2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_GASSENBESETZT,
-	{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
-	{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} },
 
 	{TONE_GERMAN_AUFSCHALTTON,
-	{DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
-	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
+	{DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
+	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
 	{1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} },
 
 	{0,
-	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+	{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
 	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 };
 
@@ -467,7 +467,7 @@
 
 	/* set next tone */
 	if (pat->data[index] == DATA_S)
-		dsp_tone_hw_message(dsp, 0, 0);
+		dsp_tone_hw_message(dsp, NULL, 0);
 	else
 		dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index]));
 	/* set timer */
diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c
index a2dc457..2ec4b28 100644
--- a/drivers/isdn/mISDN/l1oip_codec.c
+++ b/drivers/isdn/mISDN/l1oip_codec.c
@@ -49,6 +49,7 @@
 #include <linux/vmalloc.h>
 #include <linux/mISDNif.h>
 #include "core.h"
+#include "l1oip.h"
 
 /* definitions of codec. don't use calculations, code may run slower. */
 
diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
index e42150a..0884dd6 100644
--- a/drivers/isdn/mISDN/l1oip_core.c
+++ b/drivers/isdn/mISDN/l1oip_core.c
@@ -469,7 +469,7 @@
 static void
 l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len)
 {
-	u32			id;
+	u32			packet_id;
 	u8			channel;
 	u8			remotecodec;
 	u16			timebase;
@@ -508,7 +508,7 @@
 	}
 
 	/* get id flag */
-	id = (*buf>>4)&1;
+	packet_id = (*buf>>4)&1;
 
 	/* check coding */
 	remotecodec = (*buf) & 0x0f;
@@ -520,11 +520,11 @@
 	buf++;
 	len--;
 
-	/* check id */
-	if (id) {
+	/* check packet_id */
+	if (packet_id) {
 		if (!hc->id) {
 			printk(KERN_WARNING "%s: packet error - packet has id "
-				"0x%x, but we have not\n", __func__, id);
+				"0x%x, but we have not\n", __func__, packet_id);
 			return;
 		}
 		if (len < 4) {
@@ -532,16 +532,16 @@
 				"short for ID value\n", __func__);
 			return;
 		}
-		id = (*buf++) << 24;
-		id += (*buf++) << 16;
-		id += (*buf++) << 8;
-		id += (*buf++);
+		packet_id = (*buf++) << 24;
+		packet_id += (*buf++) << 16;
+		packet_id += (*buf++) << 8;
+		packet_id += (*buf++);
 		len -= 4;
 
-		if (id != hc->id) {
+		if (packet_id != hc->id) {
 			printk(KERN_WARNING "%s: packet error - ID mismatch, "
 				"got 0x%x, we 0x%x\n",
-				__func__, id, hc->id);
+				__func__, packet_id, hc->id);
 			return;
 		}
 	} else {
diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c
index fced1a2..b73e952 100644
--- a/drivers/isdn/mISDN/layer1.c
+++ b/drivers/isdn/mISDN/layer1.c
@@ -18,10 +18,11 @@
 
 #include <linux/module.h>
 #include <linux/mISDNhw.h>
+#include "core.h"
 #include "layer1.h"
 #include "fsm.h"
 
-static int *debug;
+static u_int *debug;
 
 struct layer1 {
 	u_long			Flags;
diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c
index a7915a1..d6e2863 100644
--- a/drivers/isdn/mISDN/layer2.c
+++ b/drivers/isdn/mISDN/layer2.c
@@ -15,10 +15,12 @@
  *
  */
 
+#include <linux/mISDNif.h>
+#include "core.h"
 #include "fsm.h"
 #include "layer2.h"
 
-static int *debug;
+static u_int *debug;
 
 static
 struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL};
@@ -465,7 +467,7 @@
 	    data[0] == RNR : (data[0] & 0xf) == RNR;
 }
 
-int
+static int
 iframe_error(struct layer2 *l2, struct sk_buff *skb)
 {
 	u_int	i;
@@ -483,7 +485,7 @@
 	return 0;
 }
 
-int
+static int
 super_error(struct layer2 *l2, struct sk_buff *skb)
 {
 	if (skb->len != l2addrsize(l2) +
@@ -492,7 +494,7 @@
 	return 0;
 }
 
-int
+static int
 unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp)
 {
 	int rsp = (*skb->data & 0x2) >> 1;
@@ -505,7 +507,7 @@
 	return 0;
 }
 
-int
+static int
 UI_error(struct layer2 *l2, struct sk_buff *skb)
 {
 	int rsp = *skb->data & 0x2;
@@ -518,7 +520,7 @@
 	return 0;
 }
 
-int
+static int
 FRMR_error(struct layer2 *l2, struct sk_buff *skb)
 {
 	u_int	headers = l2addrsize(l2) + 1;
@@ -1065,7 +1067,7 @@
 	}
 }
 
-void
+static void
 enquiry_cr(struct layer2 *l2, u_char typ, u_char cr, u_char pf)
 {
 	struct sk_buff *skb;
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
index e5a20f9..37a2de1 100644
--- a/drivers/isdn/mISDN/socket.c
+++ b/drivers/isdn/mISDN/socket.c
@@ -18,7 +18,7 @@
 #include <linux/mISDNif.h>
 #include "core.h"
 
-static int	*debug;
+static u_int	*debug;
 
 static struct proto mISDN_proto = {
 	.name		= "misdn",
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
index 54cfddc..d55b14a 100644
--- a/drivers/isdn/mISDN/stack.c
+++ b/drivers/isdn/mISDN/stack.c
@@ -36,7 +36,7 @@
 	}
 }
 
-int
+static int
 mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb)
 {
 	_queue_message(ch->st, skb);
diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c
index 6fbae42..5c43d19 100644
--- a/drivers/isdn/mISDN/tei.c
+++ b/drivers/isdn/mISDN/tei.c
@@ -393,7 +393,7 @@
 	return 0;
 }
 
-unsigned int
+static unsigned int
 random_ri(void)
 {
 	u16 x;
@@ -1287,7 +1287,7 @@
 	if (!mgr)
 		return -ENOMEM;
 	INIT_LIST_HEAD(&mgr->layer2);
-	mgr->lock = __RW_LOCK_UNLOCKED(mgr->lock);
+	rwlock_init(&mgr->lock);
 	skb_queue_head_init(&mgr->sendq);
 	mgr->nextid = 1;
 	mgr->lastid = MISDN_ID_NONE;
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
index 875fabe..f2b3218 100644
--- a/drivers/isdn/mISDN/timerdev.c
+++ b/drivers/isdn/mISDN/timerdev.c
@@ -23,8 +23,9 @@
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/mISDNif.h>
+#include "core.h"
 
-static int	*debug;
+static u_int	*debug;
 
 
 struct mISDNtimerdev {
@@ -85,7 +86,7 @@
 }
 
 static ssize_t
-mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off)
+mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
 {
 	struct mISDNtimerdev	*dev = filep->private_data;
 	struct mISDNtimer	*timer;
@@ -115,7 +116,7 @@
 		timer = (struct mISDNtimer *)dev->expired.next;
 		list_del(&timer->list);
 		spin_unlock_irqrestore(&dev->lock, flags);
-		if (put_user(timer->id, (int *)buf))
+		if (put_user(timer->id, (int __user *)buf))
 			ret = -EFAULT;
 		else
 			ret = sizeof(int);
@@ -274,7 +275,7 @@
 };
 
 int
-mISDN_inittimer(int *deb)
+mISDN_inittimer(u_int *deb)
 {
 	int	err;
 
diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile
index e507daa..b89f9be 100644
--- a/drivers/net/sfc/Makefile
+++ b/drivers/net/sfc/Makefile
@@ -1,5 +1,5 @@
-sfc-y			+= efx.o falcon.o tx.o rx.o falcon_xmac.o \
-			   selftest.o ethtool.o xfp_phy.o \
+sfc-y			+= efx.o falcon.o tx.o rx.o falcon_gmac.o \
+			   falcon_xmac.o selftest.o ethtool.o xfp_phy.o \
 			   mdio_10g.o tenxpress.o boards.o sfe4001.o
 sfc-$(CONFIG_SFC_MTD)	+= mtd.o
 
diff --git a/drivers/net/sfc/boards.c b/drivers/net/sfc/boards.c
index edf0262..6490349 100644
--- a/drivers/net/sfc/boards.c
+++ b/drivers/net/sfc/boards.c
@@ -1,6 +1,6 @@
 /****************************************************************************
  * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007 Solarflare Communications Inc.
+ * Copyright 2007-2008 Solarflare Communications Inc.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published
@@ -231,70 +231,40 @@
 /* This will get expanded as board-specific details get moved out of the
  * PHY drivers. */
 struct efx_board_data {
+	enum efx_board_type type;
 	const char *ref_model;
 	const char *gen_type;
 	int (*init) (struct efx_nic *nic);
 };
 
-static int dummy_init(struct efx_nic *nic)
-{
-	return 0;
-}
 
 static struct efx_board_data board_data[] = {
-	[EFX_BOARD_INVALID] =
-	{NULL,	    NULL,                  dummy_init},
-	[EFX_BOARD_SFE4001] =
-	{"SFE4001", "10GBASE-T adapter",   sfe4001_init},
-	[EFX_BOARD_SFE4002] =
-	{"SFE4002", "XFP adapter",         sfe4002_init},
+	{ EFX_BOARD_SFE4001, "SFE4001", "10GBASE-T adapter", sfe4001_init },
+	{ EFX_BOARD_SFE4002, "SFE4002", "XFP adapter", sfe4002_init },
+	{ EFX_BOARD_SFN4111T, "SFN4111T", "100/1000/10GBASE-T adapter",
+	  sfn4111t_init },
 };
 
-int efx_set_board_info(struct efx_nic *efx, u16 revision_info)
+void efx_set_board_info(struct efx_nic *efx, u16 revision_info)
 {
-	int rc = 0;
-	struct efx_board_data *data;
+	struct efx_board_data *data = NULL;
+	int i;
 
-	if (BOARD_TYPE(revision_info) >= EFX_BOARD_MAX) {
-		EFX_ERR(efx, "squashing unknown board type %d\n",
-			BOARD_TYPE(revision_info));
-		revision_info = 0;
-	}
+	efx->board_info.type = BOARD_TYPE(revision_info);
+	efx->board_info.major = BOARD_MAJOR(revision_info);
+	efx->board_info.minor = BOARD_MINOR(revision_info);
 
-	if (BOARD_TYPE(revision_info) == 0) {
-		efx->board_info.major = 0;
-		efx->board_info.minor = 0;
-		/* For early boards that don't have revision info. there is
-		 * only 1 board for each PHY type, so we can work it out, with
-		 * the exception of the PHY-less boards. */
-		switch (efx->phy_type) {
-		case PHY_TYPE_10XPRESS:
-			efx->board_info.type = EFX_BOARD_SFE4001;
-			break;
-		case PHY_TYPE_XFP:
-			efx->board_info.type = EFX_BOARD_SFE4002;
-			break;
-		default:
-			efx->board_info.type = 0;
-			break;
-		}
-	} else {
-		efx->board_info.type = BOARD_TYPE(revision_info);
-		efx->board_info.major = BOARD_MAJOR(revision_info);
-		efx->board_info.minor = BOARD_MINOR(revision_info);
-	}
+	for (i = 0; i < ARRAY_SIZE(board_data); i++)
+		if (board_data[i].type == efx->board_info.type)
+			data = &board_data[i];
 
-	data = &board_data[efx->board_info.type];
-
-	/* Report the board model number or generic type for recognisable
-	 * boards. */
-	if (efx->board_info.type != 0)
+	if (data) {
 		EFX_INFO(efx, "board is %s rev %c%d\n",
 			 (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC)
 			 ? data->ref_model : data->gen_type,
 			 'A' + efx->board_info.major, efx->board_info.minor);
-
-	efx->board_info.init = data->init;
-
-	return rc;
+		efx->board_info.init = data->init;
+	} else {
+		EFX_ERR(efx, "unknown board type %d\n", efx->board_info.type);
+	}
 }
diff --git a/drivers/net/sfc/boards.h b/drivers/net/sfc/boards.h
index c6e01b6..d93c6c6 100644
--- a/drivers/net/sfc/boards.h
+++ b/drivers/net/sfc/boards.h
@@ -1,6 +1,6 @@
 /****************************************************************************
  * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007 Solarflare Communications Inc.
+ * Copyright 2007-2008 Solarflare Communications Inc.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published
@@ -12,14 +12,16 @@
 
 /* Board IDs (must fit in 8 bits) */
 enum efx_board_type {
-	EFX_BOARD_INVALID = 0,
-	EFX_BOARD_SFE4001 = 1,   /* SFE4001 (10GBASE-T) */
+	EFX_BOARD_SFE4001 = 1,
 	EFX_BOARD_SFE4002 = 2,
-	/* Insert new types before here */
-	EFX_BOARD_MAX
+	EFX_BOARD_SFN4111T = 0x51,
 };
 
-extern int efx_set_board_info(struct efx_nic *efx, u16 revision_info);
+extern void efx_set_board_info(struct efx_nic *efx, u16 revision_info);
+
+/* SFE4001 (10GBASE-T) */
 extern int sfe4001_init(struct efx_nic *efx);
+/* SFN4111T (100/1000/10GBASE-T) */
+extern int sfn4111t_init(struct efx_nic *efx);
 
 #endif
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index e5024bb..086629c 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -21,14 +21,12 @@
 #include <linux/ethtool.h>
 #include <linux/topology.h>
 #include "net_driver.h"
-#include "gmii.h"
 #include "ethtool.h"
 #include "tx.h"
 #include "rx.h"
 #include "efx.h"
 #include "mdio_10g.h"
 #include "falcon.h"
-#include "mac.h"
 
 #define EFX_MAX_MTU (9 * 1024)
 
@@ -39,6 +37,12 @@
  */
 static struct workqueue_struct *refill_workqueue;
 
+/* Reset workqueue. If any NIC has a hardware failure then a reset will be
+ * queued onto this work queue. This is not a per-nic work queue, because
+ * efx_reset_work() acquires the rtnl lock, so resets are naturally serialised.
+ */
+static struct workqueue_struct *reset_workqueue;
+
 /**************************************************************************
  *
  * Configurable values
@@ -58,13 +62,15 @@
 /*
  * Use separate channels for TX and RX events
  *
- * Set this to 1 to use separate channels for TX and RX. It allows us to
- * apply a higher level of interrupt moderation to TX events.
+ * Set this to 1 to use separate channels for TX and RX. It allows us
+ * to control interrupt affinity separately for TX and RX.
  *
- * This is forced to 0 for MSI interrupt mode as the interrupt vector
- * is not written
+ * This is only used in MSI-X interrupt mode
  */
-static unsigned int separate_tx_and_rx_channels = true;
+static unsigned int separate_tx_channels;
+module_param(separate_tx_channels, uint, 0644);
+MODULE_PARM_DESC(separate_tx_channels,
+		 "Use separate channels for TX and RX");
 
 /* This is the weight assigned to each of the (per-channel) virtual
  * NAPI devices.
@@ -123,6 +129,10 @@
 module_param(rss_cpus, uint, 0444);
 MODULE_PARM_DESC(rss_cpus, "Number of CPUs to use for Receive-Side Scaling");
 
+static int phy_flash_cfg;
+module_param(phy_flash_cfg, int, 0644);
+MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially");
+
 /**************************************************************************
  *
  * Utility functions and prototypes
@@ -344,6 +354,27 @@
 }
 
 
+static void efx_set_channel_names(struct efx_nic *efx)
+{
+	struct efx_channel *channel;
+	const char *type = "";
+	int number;
+
+	efx_for_each_channel(channel, efx) {
+		number = channel->channel;
+		if (efx->n_channels > efx->n_rx_queues) {
+			if (channel->channel < efx->n_rx_queues) {
+				type = "-rx";
+			} else {
+				type = "-tx";
+				number -= efx->n_rx_queues;
+			}
+		}
+		snprintf(channel->name, sizeof(channel->name),
+			 "%s%s-%d", efx->name, type, number);
+	}
+}
+
 /* Channels are shutdown and reinitialised whilst the NIC is running
  * to propagate configuration changes (mtu, checksum offload), or
  * to clear hardware error conditions
@@ -518,26 +549,8 @@
 
 	/* Status message for kernel log */
 	if (efx->link_up) {
-		struct mii_if_info *gmii = &efx->mii;
-		unsigned adv, lpa;
-		/* NONE here means direct XAUI from the controller, with no
-		 * MDIO-attached device we can query. */
-		if (efx->phy_type != PHY_TYPE_NONE) {
-			adv = gmii_advertised(gmii);
-			lpa = gmii_lpa(gmii);
-		} else {
-			lpa = GM_LPA_10000 | LPA_DUPLEX;
-			adv = lpa;
-		}
-		EFX_INFO(efx, "link up at %dMbps %s-duplex "
-			 "(adv %04x lpa %04x) (MTU %d)%s\n",
-			 (efx->link_options & GM_LPA_10000 ? 10000 :
-			  (efx->link_options & GM_LPA_1000 ? 1000 :
-			   (efx->link_options & GM_LPA_100 ? 100 :
-			    10))),
-			 (efx->link_options & GM_LPA_DUPLEX ?
-			  "full" : "half"),
-			 adv, lpa,
+		EFX_INFO(efx, "link up at %uMbps %s-duplex (MTU %d)%s\n",
+			 efx->link_speed, efx->link_fd ? "full" : "half",
 			 efx->net_dev->mtu,
 			 (efx->promiscuous ? " [PROMISC]" : ""));
 	} else {
@@ -561,10 +574,28 @@
 		netif_addr_unlock_bh(efx->net_dev);
 	}
 
-	falcon_reconfigure_xmac(efx);
+	falcon_deconfigure_mac_wrapper(efx);
+
+	/* Reconfigure the PHY, disabling transmit in mac level loopback. */
+	if (LOOPBACK_INTERNAL(efx))
+		efx->phy_mode |= PHY_MODE_TX_DISABLED;
+	else
+		efx->phy_mode &= ~PHY_MODE_TX_DISABLED;
+	efx->phy_op->reconfigure(efx);
+
+	if (falcon_switch_mac(efx))
+		goto fail;
+
+	efx->mac_op->reconfigure(efx);
 
 	/* Inform kernel of loss/gain of carrier */
 	efx_link_status_changed(efx);
+	return;
+
+fail:
+	EFX_ERR(efx, "failed to reconfigure MAC\n");
+	efx->phy_op->fini(efx);
+	efx->port_initialized = false;
 }
 
 /* Reinitialise the MAC to pick up new PHY settings, even if the port is
@@ -581,10 +612,9 @@
 /* Asynchronous efx_reconfigure_port work item. To speed up efx_flush_all()
  * we don't efx_reconfigure_port() if the port is disabled. Care is taken
  * in efx_stop_all() and efx_start_port() to prevent PHY events being lost */
-static void efx_reconfigure_work(struct work_struct *data)
+static void efx_phy_work(struct work_struct *data)
 {
-	struct efx_nic *efx = container_of(data, struct efx_nic,
-					   reconfigure_work);
+	struct efx_nic *efx = container_of(data, struct efx_nic, phy_work);
 
 	mutex_lock(&efx->mac_lock);
 	if (efx->port_enabled)
@@ -592,6 +622,16 @@
 	mutex_unlock(&efx->mac_lock);
 }
 
+static void efx_mac_work(struct work_struct *data)
+{
+	struct efx_nic *efx = container_of(data, struct efx_nic, mac_work);
+
+	mutex_lock(&efx->mac_lock);
+	if (efx->port_enabled)
+		efx->mac_op->irq(efx);
+	mutex_unlock(&efx->mac_lock);
+}
+
 static int efx_probe_port(struct efx_nic *efx)
 {
 	int rc;
@@ -603,6 +643,9 @@
 	if (rc)
 		goto err;
 
+	if (phy_flash_cfg)
+		efx->phy_mode = PHY_MODE_SPECIAL;
+
 	/* Sanity check MAC address */
 	if (is_valid_ether_addr(efx->mac_address)) {
 		memcpy(efx->net_dev->dev_addr, efx->mac_address, ETH_ALEN);
@@ -631,23 +674,30 @@
 
 	EFX_LOG(efx, "init port\n");
 
-	/* Initialise the MAC and PHY */
-	rc = falcon_init_xmac(efx);
+	rc = efx->phy_op->init(efx);
 	if (rc)
 		return rc;
+	efx->phy_op->reconfigure(efx);
+
+	mutex_lock(&efx->mac_lock);
+	rc = falcon_switch_mac(efx);
+	mutex_unlock(&efx->mac_lock);
+	if (rc)
+		goto fail;
+	efx->mac_op->reconfigure(efx);
 
 	efx->port_initialized = true;
 	efx->stats_enabled = true;
-
-	/* Reconfigure port to program MAC registers */
-	falcon_reconfigure_xmac(efx);
-
 	return 0;
+
+fail:
+	efx->phy_op->fini(efx);
+	return rc;
 }
 
 /* Allow efx_reconfigure_port() to be scheduled, and close the window
  * between efx_stop_port and efx_flush_all whereby a previously scheduled
- * efx_reconfigure_port() may have been cancelled */
+ * efx_phy_work()/efx_mac_work() may have been cancelled */
 static void efx_start_port(struct efx_nic *efx)
 {
 	EFX_LOG(efx, "start port\n");
@@ -656,13 +706,14 @@
 	mutex_lock(&efx->mac_lock);
 	efx->port_enabled = true;
 	__efx_reconfigure_port(efx);
+	efx->mac_op->irq(efx);
 	mutex_unlock(&efx->mac_lock);
 }
 
-/* Prevent efx_reconfigure_work and efx_monitor() from executing, and
- * efx_set_multicast_list() from scheduling efx_reconfigure_work.
- * efx_reconfigure_work can still be scheduled via NAPI processing
- * until efx_flush_all() is called */
+/* Prevent efx_phy_work, efx_mac_work, and efx_monitor() from executing,
+ * and efx_set_multicast_list() from scheduling efx_phy_work. efx_phy_work
+ * and efx_mac_work may still be scheduled via NAPI processing until
+ * efx_flush_all() is called */
 static void efx_stop_port(struct efx_nic *efx)
 {
 	EFX_LOG(efx, "stop port\n");
@@ -685,7 +736,7 @@
 	if (!efx->port_initialized)
 		return;
 
-	falcon_fini_xmac(efx);
+	efx->phy_op->fini(efx);
 	efx->port_initialized = false;
 
 	efx->link_up = false;
@@ -833,26 +884,33 @@
 	if (efx->interrupt_mode == EFX_INT_MODE_MSIX) {
 		struct msix_entry xentries[EFX_MAX_CHANNELS];
 		int wanted_ints;
+		int rx_queues;
 
 		/* We want one RX queue and interrupt per CPU package
 		 * (or as specified by the rss_cpus module parameter).
 		 * We will need one channel per interrupt.
 		 */
-		wanted_ints = rss_cpus ? rss_cpus : efx_wanted_rx_queues();
-		efx->n_rx_queues = min(wanted_ints, max_channels);
+		rx_queues = rss_cpus ? rss_cpus : efx_wanted_rx_queues();
+		wanted_ints = rx_queues + (separate_tx_channels ? 1 : 0);
+		wanted_ints = min(wanted_ints, max_channels);
 
-		for (i = 0; i < efx->n_rx_queues; i++)
+		for (i = 0; i < wanted_ints; i++)
 			xentries[i].entry = i;
-		rc = pci_enable_msix(efx->pci_dev, xentries, efx->n_rx_queues);
+		rc = pci_enable_msix(efx->pci_dev, xentries, wanted_ints);
 		if (rc > 0) {
-			EFX_BUG_ON_PARANOID(rc >= efx->n_rx_queues);
-			efx->n_rx_queues = rc;
+			EFX_ERR(efx, "WARNING: Insufficient MSI-X vectors"
+				" available (%d < %d).\n", rc, wanted_ints);
+			EFX_ERR(efx, "WARNING: Performance may be reduced.\n");
+			EFX_BUG_ON_PARANOID(rc >= wanted_ints);
+			wanted_ints = rc;
 			rc = pci_enable_msix(efx->pci_dev, xentries,
-					     efx->n_rx_queues);
+					     wanted_ints);
 		}
 
 		if (rc == 0) {
-			for (i = 0; i < efx->n_rx_queues; i++)
+			efx->n_rx_queues = min(rx_queues, wanted_ints);
+			efx->n_channels = wanted_ints;
+			for (i = 0; i < wanted_ints; i++)
 				efx->channel[i].irq = xentries[i].vector;
 		} else {
 			/* Fall back to single channel MSI */
@@ -864,6 +922,7 @@
 	/* Try single interrupt MSI */
 	if (efx->interrupt_mode == EFX_INT_MODE_MSI) {
 		efx->n_rx_queues = 1;
+		efx->n_channels = 1;
 		rc = pci_enable_msi(efx->pci_dev);
 		if (rc == 0) {
 			efx->channel[0].irq = efx->pci_dev->irq;
@@ -876,6 +935,7 @@
 	/* Assume legacy interrupts */
 	if (efx->interrupt_mode == EFX_INT_MODE_LEGACY) {
 		efx->n_rx_queues = 1;
+		efx->n_channels = 1 + (separate_tx_channels ? 1 : 0);
 		efx->legacy_irq = efx->pci_dev->irq;
 	}
 }
@@ -900,8 +960,8 @@
 	struct efx_rx_queue *rx_queue;
 
 	efx_for_each_tx_queue(tx_queue, efx) {
-		if (!EFX_INT_MODE_USE_MSI(efx) && separate_tx_and_rx_channels)
-			tx_queue->channel = &efx->channel[1];
+		if (separate_tx_channels)
+			tx_queue->channel = &efx->channel[efx->n_channels-1];
 		else
 			tx_queue->channel = &efx->channel[0];
 		tx_queue->channel->used_flags |= EFX_USED_BY_TX;
@@ -978,6 +1038,7 @@
 			goto fail3;
 		}
 	}
+	efx_set_channel_names(efx);
 
 	return 0;
 
@@ -1043,7 +1104,8 @@
 		cancel_delayed_work_sync(&rx_queue->work);
 
 	/* Stop scheduled port reconfigurations */
-	cancel_work_sync(&efx->reconfigure_work);
+	cancel_work_sync(&efx->mac_work);
+	cancel_work_sync(&efx->phy_work);
 
 }
 
@@ -1080,7 +1142,7 @@
 	 * window to loose phy events */
 	efx_stop_port(efx);
 
-	/* Flush reconfigure_work, refill_workqueue, monitor_work */
+	/* Flush efx_phy_work, efx_mac_work, refill_workqueue, monitor_work */
 	efx_flush_all(efx);
 
 	/* Isolate the MAC from the TX and RX engines, so that queue
@@ -1152,25 +1214,31 @@
 {
 	struct efx_nic *efx = container_of(data, struct efx_nic,
 					   monitor_work.work);
-	int rc = 0;
+	int rc;
 
 	EFX_TRACE(efx, "hardware monitor executing on CPU %d\n",
 		  raw_smp_processor_id());
 
-
 	/* If the mac_lock is already held then it is likely a port
 	 * reconfiguration is already in place, which will likely do
 	 * most of the work of check_hw() anyway. */
-	if (!mutex_trylock(&efx->mac_lock)) {
-		queue_delayed_work(efx->workqueue, &efx->monitor_work,
-				   efx_monitor_interval);
-		return;
+	if (!mutex_trylock(&efx->mac_lock))
+		goto out_requeue;
+	if (!efx->port_enabled)
+		goto out_unlock;
+	rc = efx->board_info.monitor(efx);
+	if (rc) {
+		EFX_ERR(efx, "Board sensor %s; shutting down PHY\n",
+			(rc == -ERANGE) ? "reported fault" : "failed");
+		efx->phy_mode |= PHY_MODE_LOW_POWER;
+		falcon_sim_phy_event(efx);
 	}
+	efx->phy_op->poll(efx);
+	efx->mac_op->poll(efx);
 
-	if (efx->port_enabled)
-		rc = falcon_check_xmac(efx);
+out_unlock:
 	mutex_unlock(&efx->mac_lock);
-
+out_requeue:
 	queue_delayed_work(efx->workqueue, &efx->monitor_work,
 			   efx_monitor_interval);
 }
@@ -1304,7 +1372,7 @@
 	if (!spin_trylock(&efx->stats_lock))
 		return stats;
 	if (efx->stats_enabled) {
-		falcon_update_stats_xmac(efx);
+		efx->mac_op->update_stats(efx);
 		falcon_update_nic_stats(efx);
 	}
 	spin_unlock(&efx->stats_lock);
@@ -1427,7 +1495,7 @@
 		return;
 
 	if (changed)
-		queue_work(efx->workqueue, &efx->reconfigure_work);
+		queue_work(efx->workqueue, &efx->phy_work);
 
 	/* Create and activate new global multicast hash table */
 	falcon_set_multicast_hash(efx);
@@ -1449,17 +1517,21 @@
 #endif
 };
 
+static void efx_update_name(struct efx_nic *efx)
+{
+	strcpy(efx->name, efx->net_dev->name);
+	efx_mtd_rename(efx);
+	efx_set_channel_names(efx);
+}
+
 static int efx_netdev_event(struct notifier_block *this,
 			    unsigned long event, void *ptr)
 {
 	struct net_device *net_dev = ptr;
 
-	if (net_dev->netdev_ops == &efx_netdev_ops && event == NETDEV_CHANGENAME) {
-		struct efx_nic *efx = netdev_priv(net_dev);
-
-		strcpy(efx->name, net_dev->name);
-		efx_mtd_rename(efx);
-	}
+	if (net_dev->netdev_ops == &efx_netdev_ops &&
+	    event == NETDEV_CHANGENAME)
+		efx_update_name(netdev_priv(net_dev));
 
 	return NOTIFY_DONE;
 }
@@ -1468,6 +1540,14 @@
 	.notifier_call = efx_netdev_event,
 };
 
+static ssize_t
+show_phy_type(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+	return sprintf(buf, "%d\n", efx->phy_type);
+}
+static DEVICE_ATTR(phy_type, 0644, show_phy_type, NULL);
+
 static int efx_register_netdev(struct efx_nic *efx)
 {
 	struct net_device *net_dev = efx->net_dev;
@@ -1483,7 +1563,7 @@
 	netif_carrier_off(efx->net_dev);
 
 	/* Clear MAC statistics */
-	falcon_update_stats_xmac(efx);
+	efx->mac_op->update_stats(efx);
 	memset(&efx->mac_stats, 0, sizeof(efx->mac_stats));
 
 	rc = register_netdev(net_dev);
@@ -1491,9 +1571,22 @@
 		EFX_ERR(efx, "could not register net dev\n");
 		return rc;
 	}
-	strcpy(efx->name, net_dev->name);
+
+	rtnl_lock();
+	efx_update_name(efx);
+	rtnl_unlock();
+
+	rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_type);
+	if (rc) {
+		EFX_ERR(efx, "failed to init net dev attributes\n");
+		goto fail_registered;
+	}
 
 	return 0;
+
+fail_registered:
+	unregister_netdev(net_dev);
+	return rc;
 }
 
 static void efx_unregister_netdev(struct efx_nic *efx)
@@ -1513,6 +1606,7 @@
 
 	if (efx_dev_registered(efx)) {
 		strlcpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name));
+		device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_type);
 		unregister_netdev(efx->net_dev);
 	}
 }
@@ -1527,8 +1621,6 @@
  * before reset.  */
 void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd)
 {
-	int rc;
-
 	EFX_ASSERT_RESET_SERIALISED(efx);
 
 	/* The net_dev->get_stats handler is quite slow, and will fail
@@ -1541,9 +1633,7 @@
 	mutex_lock(&efx->mac_lock);
 	mutex_lock(&efx->spi_lock);
 
-	rc = falcon_xmac_get_settings(efx, ecmd);
-	if (rc)
-		EFX_ERR(efx, "could not back up PHY settings\n");
+	efx->phy_op->get_settings(efx, ecmd);
 
 	efx_fini_channels(efx);
 }
@@ -1568,7 +1658,7 @@
 	if (ok) {
 		efx_init_channels(efx);
 
-		if (falcon_xmac_set_settings(efx, ecmd))
+		if (efx->phy_op->set_settings(efx, ecmd))
 			EFX_ERR(efx, "could not restore PHY settings\n");
 	}
 
@@ -1697,7 +1787,7 @@
 
 	efx->reset_pending = method;
 
-	queue_work(efx->reset_workqueue, &efx->reset_work);
+	queue_work(reset_workqueue, &efx->reset_work);
 }
 
 /**************************************************************************
@@ -1731,10 +1821,16 @@
 void efx_port_dummy_op_void(struct efx_nic *efx) {}
 void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink) {}
 
+static struct efx_mac_operations efx_dummy_mac_operations = {
+	.reconfigure	= efx_port_dummy_op_void,
+	.poll		= efx_port_dummy_op_void,
+	.irq		= efx_port_dummy_op_void,
+};
+
 static struct efx_phy_operations efx_dummy_phy_operations = {
 	.init		 = efx_port_dummy_op_int,
 	.reconfigure	 = efx_port_dummy_op_void,
-	.check_hw        = efx_port_dummy_op_int,
+	.poll		 = efx_port_dummy_op_void,
 	.fini		 = efx_port_dummy_op_void,
 	.clear_interrupt = efx_port_dummy_op_void,
 };
@@ -1743,6 +1839,7 @@
 	.init		= efx_port_dummy_op_int,
 	.init_leds	= efx_port_dummy_op_int,
 	.set_fault_led	= efx_port_dummy_op_blink,
+	.monitor	= efx_port_dummy_op_int,
 	.blink		= efx_port_dummy_op_blink,
 	.fini		= efx_port_dummy_op_void,
 };
@@ -1762,7 +1859,7 @@
 	struct efx_channel *channel;
 	struct efx_tx_queue *tx_queue;
 	struct efx_rx_queue *rx_queue;
-	int i, rc;
+	int i;
 
 	/* Initialise common structures */
 	memset(efx, 0, sizeof(*efx));
@@ -1782,9 +1879,11 @@
 	spin_lock_init(&efx->netif_stop_lock);
 	spin_lock_init(&efx->stats_lock);
 	mutex_init(&efx->mac_lock);
+	efx->mac_op = &efx_dummy_mac_operations;
 	efx->phy_op = &efx_dummy_phy_operations;
 	efx->mii.dev = net_dev;
-	INIT_WORK(&efx->reconfigure_work, efx_reconfigure_work);
+	INIT_WORK(&efx->phy_work, efx_phy_work);
+	INIT_WORK(&efx->mac_work, efx_mac_work);
 	atomic_set(&efx->netif_stop_count, 1);
 
 	for (i = 0; i < EFX_MAX_CHANNELS; i++) {
@@ -1831,33 +1930,14 @@
 				  interrupt_mode);
 
 	efx->workqueue = create_singlethread_workqueue("sfc_work");
-	if (!efx->workqueue) {
-		rc = -ENOMEM;
-		goto fail1;
-	}
-
-	efx->reset_workqueue = create_singlethread_workqueue("sfc_reset");
-	if (!efx->reset_workqueue) {
-		rc = -ENOMEM;
-		goto fail2;
-	}
+	if (!efx->workqueue)
+		return -ENOMEM;
 
 	return 0;
-
- fail2:
-	destroy_workqueue(efx->workqueue);
-	efx->workqueue = NULL;
-
- fail1:
-	return rc;
 }
 
 static void efx_fini_struct(struct efx_nic *efx)
 {
-	if (efx->reset_workqueue) {
-		destroy_workqueue(efx->reset_workqueue);
-		efx->reset_workqueue = NULL;
-	}
 	if (efx->workqueue) {
 		destroy_workqueue(efx->workqueue);
 		efx->workqueue = NULL;
@@ -1903,8 +1983,6 @@
 	if (!efx)
 		return;
 
-	efx_mtd_remove(efx);
-
 	/* Mark the NIC as fini, then stop the interface */
 	rtnl_lock();
 	efx->state = STATE_FINI;
@@ -1918,11 +1996,13 @@
 
 	efx_unregister_netdev(efx);
 
+	efx_mtd_remove(efx);
+
 	/* Wait for any scheduled resets to complete. No more will be
 	 * scheduled from this point because efx_stop_all() has been
 	 * called, we are no longer registered with driverlink, and
 	 * the net_device's have been removed. */
-	flush_workqueue(efx->reset_workqueue);
+	cancel_work_sync(&efx->reset_work);
 
 	efx_pci_remove_main(efx);
 
@@ -1983,6 +2063,7 @@
 	efx_fini_port(efx);
  fail5:
  fail4:
+	efx->board_info.fini(efx);
  fail3:
 	efx_fini_napi(efx);
  fail2:
@@ -2036,14 +2117,23 @@
 	 * we're in STATE_INIT. */
 	for (i = 0; i < 5; i++) {
 		rc = efx_pci_probe_main(efx);
-		if (rc == 0)
-			break;
 
 		/* Serialise against efx_reset(). No more resets will be
 		 * scheduled since efx_stop_all() has been called, and we
 		 * have not and never have been registered with either
 		 * the rtnetlink or driverlink layers. */
-		flush_workqueue(efx->reset_workqueue);
+		cancel_work_sync(&efx->reset_work);
+
+		if (rc == 0) {
+			if (efx->reset_pending != RESET_TYPE_NONE) {
+				/* If there was a scheduled reset during
+				 * probe, the NIC is probably hosed anyway */
+				efx_pci_remove_main(efx);
+				rc = -EIO;
+			} else {
+				break;
+			}
+		}
 
 		/* Retry if a recoverably reset event has been scheduled */
 		if ((efx->reset_pending != RESET_TYPE_INVISIBLE) &&
@@ -2061,17 +2151,15 @@
 	/* Switch to the running state before we expose the device to
 	 * the OS.  This is to ensure that the initial gathering of
 	 * MAC stats succeeds. */
-	rtnl_lock();
 	efx->state = STATE_RUNNING;
-	rtnl_unlock();
+
+	efx_mtd_probe(efx); /* allowed to fail */
 
 	rc = efx_register_netdev(efx);
 	if (rc)
 		goto fail5;
 
 	EFX_LOG(efx, "initialisation successful\n");
-
-	efx_mtd_probe(efx); /* allowed to fail */
 	return 0;
 
  fail5:
@@ -2119,6 +2207,11 @@
 		rc = -ENOMEM;
 		goto err_refill;
 	}
+	reset_workqueue = create_singlethread_workqueue("sfc_reset");
+	if (!reset_workqueue) {
+		rc = -ENOMEM;
+		goto err_reset;
+	}
 
 	rc = pci_register_driver(&efx_pci_driver);
 	if (rc < 0)
@@ -2127,6 +2220,8 @@
 	return 0;
 
  err_pci:
+	destroy_workqueue(reset_workqueue);
+ err_reset:
 	destroy_workqueue(refill_workqueue);
  err_refill:
 	unregister_netdevice_notifier(&efx_netdev_notifier);
@@ -2139,6 +2234,7 @@
 	printk(KERN_INFO "Solarflare NET driver unloading\n");
 
 	pci_unregister_driver(&efx_pci_driver);
+	destroy_workqueue(reset_workqueue);
 	destroy_workqueue(refill_workqueue);
 	unregister_netdevice_notifier(&efx_netdev_notifier);
 
diff --git a/drivers/net/sfc/enum.h b/drivers/net/sfc/enum.h
index 41e758e..60cbc6e 100644
--- a/drivers/net/sfc/enum.h
+++ b/drivers/net/sfc/enum.h
@@ -1,6 +1,6 @@
 /****************************************************************************
  * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007 Solarflare Communications Inc.
+ * Copyright 2007-2008 Solarflare Communications Inc.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published
@@ -13,22 +13,24 @@
 /**
  * enum efx_loopback_mode - loopback modes
  * @LOOPBACK_NONE: no loopback
- * @LOOPBACK_XGMII: loopback within MAC at XGMII level
- * @LOOPBACK_XGXS: loopback within MAC at XGXS level
- * @LOOPBACK_XAUI: loopback within MAC at XAUI level
- * @LOOPBACK_PHYXS: loopback within PHY at PHYXS level
- * @LOOPBACK_PCS: loopback within PHY at PCS level
- * @LOOPBACK_PMAPMD: loopback within PHY at PMAPMD level
+ * @LOOPBACK_GMAC: loopback within GMAC at unspecified level
+ * @LOOPBACK_XGMII: loopback within XMAC at XGMII level
+ * @LOOPBACK_XGXS: loopback within XMAC at XGXS level
+ * @LOOPBACK_XAUI: loopback within XMAC at XAUI level
+ * @LOOPBACK_GPHY: loopback within 1G PHY at unspecified level
+ * @LOOPBACK_PHYXS: loopback within 10G PHY at PHYXS level
+ * @LOOPBACK_PCS: loopback within 10G PHY at PCS level
+ * @LOOPBACK_PMAPMD: loopback within 10G PHY at PMAPMD level
  * @LOOPBACK_NETWORK: reflecting loopback (even further than furthest!)
  */
 /* Please keep in order and up-to-date w.r.t the following two #defines */
 enum efx_loopback_mode {
 	LOOPBACK_NONE = 0,
-	LOOPBACK_MAC = 1,
+	LOOPBACK_GMAC = 1,
 	LOOPBACK_XGMII = 2,
 	LOOPBACK_XGXS = 3,
 	LOOPBACK_XAUI = 4,
-	LOOPBACK_PHY = 5,
+	LOOPBACK_GPHY = 5,
 	LOOPBACK_PHYXS = 6,
 	LOOPBACK_PCS = 7,
 	LOOPBACK_PMAPMD = 8,
@@ -45,15 +47,19 @@
 	LOOPBACK_MODE_NAME(efx->loopback_mode)
 
 /* These loopbacks occur within the controller */
-#define LOOPBACKS_10G_INTERNAL ((1 << LOOPBACK_XGMII)| \
-				(1 << LOOPBACK_XGXS) | \
-				(1 << LOOPBACK_XAUI))
+#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_GMAC) |     \
+			    (1 << LOOPBACK_XGMII)|     \
+			    (1 << LOOPBACK_XGXS) |     \
+			    (1 << LOOPBACK_XAUI))
 
 #define LOOPBACK_MASK(_efx)			\
 	(1 << (_efx)->loopback_mode)
 
 #define LOOPBACK_INTERNAL(_efx)				\
-	(!!(LOOPBACKS_10G_INTERNAL & LOOPBACK_MASK(_efx)))
+	(!!(LOOPBACKS_INTERNAL & LOOPBACK_MASK(_efx)))
+
+#define LOOPBACK_CHANGED(_from, _to, _mask)				\
+	(!!((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & (_mask)))
 
 #define LOOPBACK_OUT_OF(_from, _to, _mask)				\
 	((LOOPBACK_MASK(_from) & (_mask)) && !(LOOPBACK_MASK(_to) & (_mask)))
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
index cd92c4d..3aaece6 100644
--- a/drivers/net/sfc/ethtool.c
+++ b/drivers/net/sfc/ethtool.c
@@ -12,24 +12,24 @@
 #include <linux/ethtool.h>
 #include <linux/rtnetlink.h>
 #include "net_driver.h"
+#include "workarounds.h"
 #include "selftest.h"
 #include "efx.h"
 #include "ethtool.h"
 #include "falcon.h"
-#include "gmii.h"
 #include "spi.h"
-#include "mac.h"
+#include "mdio_10g.h"
 
 const char *efx_loopback_mode_names[] = {
 	[LOOPBACK_NONE]		= "NONE",
-	[LOOPBACK_MAC]		= "MAC",
+	[LOOPBACK_GMAC]		= "GMAC",
 	[LOOPBACK_XGMII]	= "XGMII",
 	[LOOPBACK_XGXS]		= "XGXS",
 	[LOOPBACK_XAUI] 	= "XAUI",
-	[LOOPBACK_PHY]		= "PHY",
-	[LOOPBACK_PHYXS]	= "PHY(XS)",
-	[LOOPBACK_PCS]	 	= "PHY(PCS)",
-	[LOOPBACK_PMAPMD]	= "PHY(PMAPMD)",
+	[LOOPBACK_GPHY]		= "GPHY",
+	[LOOPBACK_PHYXS]	= "PHYXS",
+	[LOOPBACK_PCS]	 	= "PCS",
+	[LOOPBACK_PMAPMD]	= "PMA/PMD",
 	[LOOPBACK_NETWORK]	= "NETWORK",
 };
 
@@ -182,12 +182,16 @@
  */
 
 /* Identify device by flashing LEDs */
-static int efx_ethtool_phys_id(struct net_device *net_dev, u32 seconds)
+static int efx_ethtool_phys_id(struct net_device *net_dev, u32 count)
 {
 	struct efx_nic *efx = netdev_priv(net_dev);
 
 	efx->board_info.blink(efx, 1);
-	schedule_timeout_interruptible(seconds * HZ);
+	set_current_state(TASK_INTERRUPTIBLE);
+	if (count)
+		schedule_timeout(count * HZ);
+	else
+		schedule();
 	efx->board_info.blink(efx, 0);
 	return 0;
 }
@@ -197,13 +201,15 @@
 			     struct ethtool_cmd *ecmd)
 {
 	struct efx_nic *efx = netdev_priv(net_dev);
-	int rc;
 
 	mutex_lock(&efx->mac_lock);
-	rc = falcon_xmac_get_settings(efx, ecmd);
+	efx->phy_op->get_settings(efx, ecmd);
 	mutex_unlock(&efx->mac_lock);
 
-	return rc;
+	/* Falcon GMAC does not support 1000Mbps HD */
+	ecmd->supported &= ~SUPPORTED_1000baseT_Half;
+
+	return 0;
 }
 
 /* This must be called with rtnl_lock held. */
@@ -213,8 +219,18 @@
 	struct efx_nic *efx = netdev_priv(net_dev);
 	int rc;
 
+	if (EFX_WORKAROUND_13963(efx) && !ecmd->autoneg)
+		return -EINVAL;
+
+	/* Falcon GMAC does not support 1000Mbps HD */
+	if (ecmd->speed == SPEED_1000 && ecmd->duplex != DUPLEX_FULL) {
+		EFX_LOG(efx, "rejecting unsupported 1000Mbps HD"
+			" setting\n");
+		return -EINVAL;
+	}
+
 	mutex_lock(&efx->mac_lock);
-	rc = falcon_xmac_set_settings(efx, ecmd);
+	rc = efx->phy_op->set_settings(efx, ecmd);
 	mutex_unlock(&efx->mac_lock);
 	if (!rc)
 		efx_reconfigure_port(efx);
@@ -238,10 +254,10 @@
  * @strings:		Ethtool strings, or %NULL
  * @data:		Ethtool test results, or %NULL
  * @test:		Pointer to test result (used only if data != %NULL)
- * @unit_format:	Unit name format (e.g. "channel\%d")
- * @unit_id:		Unit id (e.g. 0 for "channel0")
+ * @unit_format:	Unit name format (e.g. "chan\%d")
+ * @unit_id:		Unit id (e.g. 0 for "chan0")
  * @test_format:	Test name format (e.g. "loopback.\%s.tx.sent")
- * @test_id:		Test id (e.g. "PHY" for "loopback.PHY.tx_sent")
+ * @test_id:		Test id (e.g. "PHYXS" for "loopback.PHYXS.tx_sent")
  *
  * Fill in an individual self-test entry.
  */
@@ -258,18 +274,20 @@
 
 	/* Fill string, if applicable */
 	if (strings) {
-		snprintf(unit_str.name, sizeof(unit_str.name),
-			 unit_format, unit_id);
+		if (strchr(unit_format, '%'))
+			snprintf(unit_str.name, sizeof(unit_str.name),
+				 unit_format, unit_id);
+		else
+			strcpy(unit_str.name, unit_format);
 		snprintf(test_str.name, sizeof(test_str.name),
 			 test_format, test_id);
 		snprintf(strings[test_index].name,
 			 sizeof(strings[test_index].name),
-			 "%-9s%-17s", unit_str.name, test_str.name);
+			 "%-6s %-24s", unit_str.name, test_str.name);
 	}
 }
 
-#define EFX_PORT_NAME "port%d", 0
-#define EFX_CHANNEL_NAME(_channel) "channel%d", _channel->channel
+#define EFX_CHANNEL_NAME(_channel) "chan%d", _channel->channel
 #define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue
 #define EFX_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue
 #define EFX_LOOPBACK_NAME(_mode, _counter)			\
@@ -304,11 +322,11 @@
 	}
 	efx_fill_test(test_index++, strings, data,
 		      &lb_tests->rx_good,
-		      EFX_PORT_NAME,
+		      "rx", 0,
 		      EFX_LOOPBACK_NAME(mode, "rx_good"));
 	efx_fill_test(test_index++, strings, data,
 		      &lb_tests->rx_bad,
-		      EFX_PORT_NAME,
+		      "rx", 0,
 		      EFX_LOOPBACK_NAME(mode, "rx_bad"));
 
 	return test_index;
@@ -356,13 +374,9 @@
 	efx_fill_test(n++, strings, data, &tests->registers,
 		      "core", 0, "registers", NULL);
 	efx_fill_test(n++, strings, data, &tests->phy,
-		      EFX_PORT_NAME, "phy", NULL);
+		      "phy", 0, "bist", NULL);
 
 	/* Loopback tests */
-	efx_fill_test(n++, strings, data, &tests->loopback_speed,
-		      EFX_PORT_NAME, "loopback.speed", NULL);
-	efx_fill_test(n++, strings, data, &tests->loopback_full_duplex,
-		      EFX_PORT_NAME, "loopback.full_duplex", NULL);
 	for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) {
 		if (!(efx->loopback_modes & (1 << mode)))
 			continue;
@@ -554,10 +568,13 @@
 	size_t len;
 	int rc;
 
-	mutex_lock(&efx->spi_lock);
+	rc = mutex_lock_interruptible(&efx->spi_lock);
+	if (rc)
+		return rc;
 	rc = falcon_spi_read(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
 			     eeprom->len, &len, buf);
 	mutex_unlock(&efx->spi_lock);
+
 	eeprom->magic = EFX_ETHTOOL_EEPROM_MAGIC;
 	eeprom->len = len;
 	return rc;
@@ -574,10 +591,13 @@
 	if (eeprom->magic != EFX_ETHTOOL_EEPROM_MAGIC)
 		return -EINVAL;
 
-	mutex_lock(&efx->spi_lock);
+	rc = mutex_lock_interruptible(&efx->spi_lock);
+	if (rc)
+		return rc;
 	rc = falcon_spi_write(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
 			      eeprom->len, &len, buf);
 	mutex_unlock(&efx->spi_lock);
+
 	eeprom->len = len;
 	return rc;
 }
@@ -667,23 +687,52 @@
 				      struct ethtool_pauseparam *pause)
 {
 	struct efx_nic *efx = netdev_priv(net_dev);
-	enum efx_fc_type flow_control = efx->flow_control;
-	int rc;
+	enum efx_fc_type wanted_fc;
+	bool reset;
 
-	flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO);
-	flow_control |= pause->rx_pause ? EFX_FC_RX : 0;
-	flow_control |= pause->tx_pause ? EFX_FC_TX : 0;
-	flow_control |= pause->autoneg ? EFX_FC_AUTO : 0;
+	wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) |
+		     (pause->tx_pause ? EFX_FC_TX : 0) |
+		     (pause->autoneg ? EFX_FC_AUTO : 0));
+
+	if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) {
+		EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n");
+		return -EINVAL;
+	}
+
+	if (!(efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) &&
+	    (wanted_fc & EFX_FC_AUTO)) {
+		EFX_LOG(efx, "PHY does not support flow control "
+			"autonegotiation\n");
+		return -EINVAL;
+	}
+
+	/* TX flow control may automatically turn itself off if the
+	 * link partner (intermittently) stops responding to pause
+	 * frames. There isn't any indication that this has happened,
+	 * so the best we do is leave it up to the user to spot this
+	 * and fix it be cycling transmit flow control on this end. */
+	reset = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX);
+	if (EFX_WORKAROUND_11482(efx) && reset) {
+		if (falcon_rev(efx) >= FALCON_REV_B0) {
+			/* Recover by resetting the EM block */
+			if (efx->link_up)
+				falcon_drain_tx_fifo(efx);
+		} else {
+			/* Schedule a reset to recover */
+			efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
+		}
+	}
 
 	/* Try to push the pause parameters */
 	mutex_lock(&efx->mac_lock);
-	rc = falcon_xmac_set_pause(efx, flow_control);
+
+	efx->wanted_fc = wanted_fc;
+	mdio_clause45_set_pause(efx);
+	__efx_reconfigure_port(efx);
+
 	mutex_unlock(&efx->mac_lock);
 
-	if (!rc)
-		efx_reconfigure_port(efx);
-
-	return rc;
+	return 0;
 }
 
 static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
@@ -691,9 +740,9 @@
 {
 	struct efx_nic *efx = netdev_priv(net_dev);
 
-	pause->rx_pause = !!(efx->flow_control & EFX_FC_RX);
-	pause->tx_pause = !!(efx->flow_control & EFX_FC_TX);
-	pause->autoneg = !!(efx->flow_control & EFX_FC_AUTO);
+	pause->rx_pause = !!(efx->wanted_fc & EFX_FC_RX);
+	pause->tx_pause = !!(efx->wanted_fc & EFX_FC_TX);
+	pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO);
 }
 
 
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index 71e0bed..6884dc8 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -15,11 +15,11 @@
 #include <linux/seq_file.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
+#include <linux/mii.h>
 #include "net_driver.h"
 #include "bitfield.h"
 #include "efx.h"
 #include "mac.h"
-#include "gmii.h"
 #include "spi.h"
 #include "falcon.h"
 #include "falcon_hwdefs.h"
@@ -70,6 +70,20 @@
 #define RX_DC_ENTRIES_ORDER 2
 #define RX_DC_BASE 0x100000
 
+static const unsigned int
+/* "Large" EEPROM device: Atmel AT25640 or similar
+ * 8 KB, 16-bit address, 32 B write block */
+large_eeprom_type = ((13 << SPI_DEV_TYPE_SIZE_LBN)
+		     | (2 << SPI_DEV_TYPE_ADDR_LEN_LBN)
+		     | (5 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)),
+/* Default flash device: Atmel AT25F1024
+ * 128 KB, 24-bit address, 32 KB erase block, 256 B write block */
+default_flash_type = ((17 << SPI_DEV_TYPE_SIZE_LBN)
+		      | (3 << SPI_DEV_TYPE_ADDR_LEN_LBN)
+		      | (0x52 << SPI_DEV_TYPE_ERASE_CMD_LBN)
+		      | (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN)
+		      | (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN));
+
 /* RX FIFO XOFF watermark
  *
  * When the amount of the RX FIFO increases used increases past this
@@ -770,15 +784,18 @@
 			   rx_ev_buf_owner_id_err | rx_ev_eth_crc_err |
 			   rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err);
 
-	/* Count errors that are not in MAC stats. */
+	/* Count errors that are not in MAC stats.  Ignore expected
+	 * checksum errors during self-test. */
 	if (rx_ev_frm_trunc)
 		++rx_queue->channel->n_rx_frm_trunc;
 	else if (rx_ev_tobe_disc)
 		++rx_queue->channel->n_rx_tobe_disc;
-	else if (rx_ev_ip_hdr_chksum_err)
-		++rx_queue->channel->n_rx_ip_hdr_chksum_err;
-	else if (rx_ev_tcp_udp_chksum_err)
-		++rx_queue->channel->n_rx_tcp_udp_chksum_err;
+	else if (!efx->loopback_selftest) {
+		if (rx_ev_ip_hdr_chksum_err)
+			++rx_queue->channel->n_rx_ip_hdr_chksum_err;
+		else if (rx_ev_tcp_udp_chksum_err)
+			++rx_queue->channel->n_rx_tcp_udp_chksum_err;
+	}
 	if (rx_ev_ip_frag_err)
 		++rx_queue->channel->n_rx_ip_frag_err;
 
@@ -809,7 +826,7 @@
 #endif
 
 	if (unlikely(rx_ev_eth_crc_err && EFX_WORKAROUND_10750(efx) &&
-		     efx->phy_type == PHY_TYPE_10XPRESS))
+		     efx->phy_type == PHY_TYPE_SFX7101))
 		tenxpress_crc_err(efx);
 }
 
@@ -893,22 +910,20 @@
 				       efx_qword_t *event)
 {
 	struct efx_nic *efx = channel->efx;
-	bool is_phy_event = false, handled = false;
+	bool handled = false;
 
-	/* Check for interrupt on either port.  Some boards have a
-	 * single PHY wired to the interrupt line for port 1. */
 	if (EFX_QWORD_FIELD(*event, G_PHY0_INTR) ||
 	    EFX_QWORD_FIELD(*event, G_PHY1_INTR) ||
-	    EFX_QWORD_FIELD(*event, XG_PHY_INTR))
-		is_phy_event = true;
+	    EFX_QWORD_FIELD(*event, XG_PHY_INTR) ||
+	    EFX_QWORD_FIELD(*event, XFP_PHY_INTR)) {
+		efx->phy_op->clear_interrupt(efx);
+		queue_work(efx->workqueue, &efx->phy_work);
+		handled = true;
+	}
 
 	if ((falcon_rev(efx) >= FALCON_REV_B0) &&
-	    EFX_QWORD_FIELD(*event, XG_MNT_INTR_B0))
-		is_phy_event = true;
-
-	if (is_phy_event) {
-		efx->phy_op->clear_interrupt(efx);
-		queue_work(efx->workqueue, &efx->reconfigure_work);
+	    EFX_QWORD_FIELD(*event, XG_MNT_INTR_B0)) {
+		queue_work(efx->workqueue, &efx->mac_work);
 		handled = true;
 	}
 
@@ -1151,6 +1166,19 @@
 	falcon_generate_event(channel, &test_event);
 }
 
+void falcon_sim_phy_event(struct efx_nic *efx)
+{
+	efx_qword_t phy_event;
+
+	EFX_POPULATE_QWORD_1(phy_event, EV_CODE, GLOBAL_EV_DECODE);
+	if (EFX_IS10G(efx))
+		EFX_SET_OWORD_FIELD(phy_event, XG_PHY_INTR, 1);
+	else
+		EFX_SET_OWORD_FIELD(phy_event, G_PHY0_INTR, 1);
+
+	falcon_generate_event(&efx->channel[0], &phy_event);
+}
+
 /**************************************************************************
  *
  * Flush handling
@@ -1560,7 +1588,7 @@
 	efx_for_each_channel(channel, efx) {
 		rc = request_irq(channel->irq, falcon_msi_interrupt,
 				 IRQF_PROBE_SHARED, /* Not shared */
-				 efx->name, channel);
+				 channel->name, channel);
 		if (rc) {
 			EFX_ERR(efx, "failed to hook IRQ %d\n", channel->irq);
 			goto fail2;
@@ -1605,32 +1633,45 @@
  **************************************************************************
  */
 
-#define FALCON_SPI_MAX_LEN ((unsigned) sizeof(efx_oword_t))
+#define FALCON_SPI_MAX_LEN sizeof(efx_oword_t)
+
+static int falcon_spi_poll(struct efx_nic *efx)
+{
+	efx_oword_t reg;
+	falcon_read(efx, &reg, EE_SPI_HCMD_REG_KER);
+	return EFX_OWORD_FIELD(reg, EE_SPI_HCMD_CMD_EN) ? -EBUSY : 0;
+}
 
 /* Wait for SPI command completion */
 static int falcon_spi_wait(struct efx_nic *efx)
 {
-	unsigned long timeout = jiffies + DIV_ROUND_UP(HZ, 10);
-	efx_oword_t reg;
-	bool cmd_en, timer_active;
+	/* Most commands will finish quickly, so we start polling at
+	 * very short intervals.  Sometimes the command may have to
+	 * wait for VPD or expansion ROM access outside of our
+	 * control, so we allow up to 100 ms. */
+	unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 10);
+	int i;
+
+	for (i = 0; i < 10; i++) {
+		if (!falcon_spi_poll(efx))
+			return 0;
+		udelay(10);
+	}
 
 	for (;;) {
-		falcon_read(efx, &reg, EE_SPI_HCMD_REG_KER);
-		cmd_en = EFX_OWORD_FIELD(reg, EE_SPI_HCMD_CMD_EN);
-		timer_active = EFX_OWORD_FIELD(reg, EE_WR_TIMER_ACTIVE);
-		if (!cmd_en && !timer_active)
+		if (!falcon_spi_poll(efx))
 			return 0;
 		if (time_after_eq(jiffies, timeout)) {
 			EFX_ERR(efx, "timed out waiting for SPI\n");
 			return -ETIMEDOUT;
 		}
-		cpu_relax();
+		schedule_timeout_uninterruptible(1);
 	}
 }
 
 int falcon_spi_cmd(const struct efx_spi_device *spi,
 		   unsigned int command, int address,
-		   const void *in, void *out, unsigned int len)
+		   const void *in, void *out, size_t len)
 {
 	struct efx_nic *efx = spi->efx;
 	bool addressed = (address >= 0);
@@ -1643,8 +1684,8 @@
 		return -EINVAL;
 	BUG_ON(!mutex_is_locked(&efx->spi_lock));
 
-	/* Check SPI not currently being accessed */
-	rc = falcon_spi_wait(efx);
+	/* Check that previous command is not still running */
+	rc = falcon_spi_poll(efx);
 	if (rc)
 		return rc;
 
@@ -1686,8 +1727,8 @@
 	return 0;
 }
 
-static unsigned int
-falcon_spi_write_limit(const struct efx_spi_device *spi, unsigned int start)
+static size_t
+falcon_spi_write_limit(const struct efx_spi_device *spi, size_t start)
 {
 	return min(FALCON_SPI_MAX_LEN,
 		   (spi->block_size - (start & (spi->block_size - 1))));
@@ -1700,37 +1741,40 @@
 	return command | (((address >> 8) & spi->munge_address) << 3);
 }
 
-int falcon_spi_fast_wait(const struct efx_spi_device *spi)
+/* Wait up to 10 ms for buffered write completion */
+int falcon_spi_wait_write(const struct efx_spi_device *spi)
 {
+	struct efx_nic *efx = spi->efx;
+	unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 100);
 	u8 status;
-	int i, rc;
+	int rc;
 
-	/* Wait up to 1000us for flash/EEPROM to finish a fast operation. */
-	for (i = 0; i < 50; i++) {
-		udelay(20);
-
+	for (;;) {
 		rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL,
 				    &status, sizeof(status));
 		if (rc)
 			return rc;
 		if (!(status & SPI_STATUS_NRDY))
 			return 0;
+		if (time_after_eq(jiffies, timeout)) {
+			EFX_ERR(efx, "SPI write timeout on device %d"
+				" last status=0x%02x\n",
+				spi->device_id, status);
+			return -ETIMEDOUT;
+		}
+		schedule_timeout_uninterruptible(1);
 	}
-	EFX_ERR(spi->efx,
-		"timed out waiting for device %d last status=0x%02x\n",
-		spi->device_id, status);
-	return -ETIMEDOUT;
 }
 
 int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
 		    size_t len, size_t *retlen, u8 *buffer)
 {
-	unsigned int command, block_len, pos = 0;
+	size_t block_len, pos = 0;
+	unsigned int command;
 	int rc = 0;
 
 	while (pos < len) {
-		block_len = min((unsigned int)len - pos,
-				FALCON_SPI_MAX_LEN);
+		block_len = min(len - pos, FALCON_SPI_MAX_LEN);
 
 		command = efx_spi_munge_command(spi, SPI_READ, start + pos);
 		rc = falcon_spi_cmd(spi, command, start + pos, NULL,
@@ -1756,7 +1800,8 @@
 		     size_t len, size_t *retlen, const u8 *buffer)
 {
 	u8 verify_buffer[FALCON_SPI_MAX_LEN];
-	unsigned int command, block_len, pos = 0;
+	size_t block_len, pos = 0;
+	unsigned int command;
 	int rc = 0;
 
 	while (pos < len) {
@@ -1764,7 +1809,7 @@
 		if (rc)
 			break;
 
-		block_len = min((unsigned int)len - pos,
+		block_len = min(len - pos,
 				falcon_spi_write_limit(spi, start + pos));
 		command = efx_spi_munge_command(spi, SPI_WRITE, start + pos);
 		rc = falcon_spi_cmd(spi, command, start + pos,
@@ -1772,7 +1817,7 @@
 		if (rc)
 			break;
 
-		rc = falcon_spi_fast_wait(spi);
+		rc = falcon_spi_wait_write(spi);
 		if (rc)
 			break;
 
@@ -1805,40 +1850,61 @@
  *
  **************************************************************************
  */
-void falcon_drain_tx_fifo(struct efx_nic *efx)
+
+static int falcon_reset_macs(struct efx_nic *efx)
 {
-	efx_oword_t temp;
+	efx_oword_t reg;
 	int count;
 
-	if ((falcon_rev(efx) < FALCON_REV_B0) ||
-	    (efx->loopback_mode != LOOPBACK_NONE))
-		return;
+	if (falcon_rev(efx) < FALCON_REV_B0) {
+		/* It's not safe to use GLB_CTL_REG to reset the
+		 * macs, so instead use the internal MAC resets
+		 */
+		if (!EFX_IS10G(efx)) {
+			EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 1);
+			falcon_write(efx, &reg, GM_CFG1_REG);
+			udelay(1000);
 
-	falcon_read(efx, &temp, MAC0_CTRL_REG_KER);
-	/* There is no point in draining more than once */
-	if (EFX_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0))
-		return;
+			EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 0);
+			falcon_write(efx, &reg, GM_CFG1_REG);
+			udelay(1000);
+			return 0;
+		} else {
+			EFX_POPULATE_OWORD_1(reg, XM_CORE_RST, 1);
+			falcon_write(efx, &reg, XM_GLB_CFG_REG);
+
+			for (count = 0; count < 10000; count++) {
+				falcon_read(efx, &reg, XM_GLB_CFG_REG);
+				if (EFX_OWORD_FIELD(reg, XM_CORE_RST) == 0)
+					return 0;
+				udelay(10);
+			}
+
+			EFX_ERR(efx, "timed out waiting for XMAC core reset\n");
+			return -ETIMEDOUT;
+		}
+	}
 
 	/* MAC stats will fail whilst the TX fifo is draining. Serialise
 	 * the drain sequence with the statistics fetch */
 	spin_lock(&efx->stats_lock);
 
-	EFX_SET_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0, 1);
-	falcon_write(efx, &temp, MAC0_CTRL_REG_KER);
+	falcon_read(efx, &reg, MAC0_CTRL_REG_KER);
+	EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0, 1);
+	falcon_write(efx, &reg, MAC0_CTRL_REG_KER);
 
-	/* Reset the MAC and EM block. */
-	falcon_read(efx, &temp, GLB_CTL_REG_KER);
-	EFX_SET_OWORD_FIELD(temp, RST_XGTX, 1);
-	EFX_SET_OWORD_FIELD(temp, RST_XGRX, 1);
-	EFX_SET_OWORD_FIELD(temp, RST_EM, 1);
-	falcon_write(efx, &temp, GLB_CTL_REG_KER);
+	falcon_read(efx, &reg, GLB_CTL_REG_KER);
+	EFX_SET_OWORD_FIELD(reg, RST_XGTX, 1);
+	EFX_SET_OWORD_FIELD(reg, RST_XGRX, 1);
+	EFX_SET_OWORD_FIELD(reg, RST_EM, 1);
+	falcon_write(efx, &reg, GLB_CTL_REG_KER);
 
 	count = 0;
 	while (1) {
-		falcon_read(efx, &temp, GLB_CTL_REG_KER);
-		if (!EFX_OWORD_FIELD(temp, RST_XGTX) &&
-		    !EFX_OWORD_FIELD(temp, RST_XGRX) &&
-		    !EFX_OWORD_FIELD(temp, RST_EM)) {
+		falcon_read(efx, &reg, GLB_CTL_REG_KER);
+		if (!EFX_OWORD_FIELD(reg, RST_XGTX) &&
+		    !EFX_OWORD_FIELD(reg, RST_XGRX) &&
+		    !EFX_OWORD_FIELD(reg, RST_EM)) {
 			EFX_LOG(efx, "Completed MAC reset after %d loops\n",
 				count);
 			break;
@@ -1855,21 +1921,39 @@
 
 	/* If we've reset the EM block and the link is up, then
 	 * we'll have to kick the XAUI link so the PHY can recover */
-	if (efx->link_up && EFX_WORKAROUND_5147(efx))
+	if (efx->link_up && EFX_IS10G(efx) && EFX_WORKAROUND_5147(efx))
 		falcon_reset_xaui(efx);
+
+	return 0;
+}
+
+void falcon_drain_tx_fifo(struct efx_nic *efx)
+{
+	efx_oword_t reg;
+
+	if ((falcon_rev(efx) < FALCON_REV_B0) ||
+	    (efx->loopback_mode != LOOPBACK_NONE))
+		return;
+
+	falcon_read(efx, &reg, MAC0_CTRL_REG_KER);
+	/* There is no point in draining more than once */
+	if (EFX_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0))
+		return;
+
+	falcon_reset_macs(efx);
 }
 
 void falcon_deconfigure_mac_wrapper(struct efx_nic *efx)
 {
-	efx_oword_t temp;
+	efx_oword_t reg;
 
 	if (falcon_rev(efx) < FALCON_REV_B0)
 		return;
 
 	/* Isolate the MAC -> RX */
-	falcon_read(efx, &temp, RX_CFG_REG_KER);
-	EFX_SET_OWORD_FIELD(temp, RX_INGR_EN_B0, 0);
-	falcon_write(efx, &temp, RX_CFG_REG_KER);
+	falcon_read(efx, &reg, RX_CFG_REG_KER);
+	EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 0);
+	falcon_write(efx, &reg, RX_CFG_REG_KER);
 
 	if (!efx->link_up)
 		falcon_drain_tx_fifo(efx);
@@ -1881,14 +1965,12 @@
 	int link_speed;
 	bool tx_fc;
 
-	if (efx->link_options & GM_LPA_10000)
-		link_speed = 0x3;
-	else if (efx->link_options & GM_LPA_1000)
-		link_speed = 0x2;
-	else if (efx->link_options & GM_LPA_100)
-		link_speed = 0x1;
-	else
-		link_speed = 0x0;
+	switch (efx->link_speed) {
+	case 10000: link_speed = 3; break;
+	case 1000:  link_speed = 2; break;
+	case 100:   link_speed = 1; break;
+	default:    link_speed = 0; break;
+	}
 	/* MAC_LINK_STATUS controls MAC backpressure but doesn't work
 	 * as advertised.  Disable to ensure packets are not
 	 * indefinitely held and TX queue can be flushed at any point
@@ -1914,7 +1996,7 @@
 	/* Transmission of pause frames when RX crosses the threshold is
 	 * covered by RX_XOFF_MAC_EN and XM_TX_CFG_REG:XM_FCNTL.
 	 * Action on receipt of pause frames is controller by XM_DIS_FCNTL */
-	tx_fc = !!(efx->flow_control & EFX_FC_TX);
+	tx_fc = !!(efx->link_fc & EFX_FC_TX);
 	falcon_read(efx, &reg, RX_CFG_REG_KER);
 	EFX_SET_OWORD_FIELD_VER(efx, reg, RX_XOFF_MAC_EN, tx_fc);
 
@@ -1998,7 +2080,8 @@
 	efx_dword_t md_stat;
 	int count;
 
-	for (count = 0; count < 1000; count++) {	/* wait upto 10ms */
+	/* wait upto 50ms - taken max from datasheet */
+	for (count = 0; count < 5000; count++) {
 		falcon_readl(efx, &md_stat, MD_STAT_REG_KER);
 		if (EFX_DWORD_FIELD(md_stat, MD_BSY) == 0) {
 			if (EFX_DWORD_FIELD(md_stat, MD_LNFL) != 0 ||
@@ -2162,10 +2245,14 @@
 static int falcon_probe_phy(struct efx_nic *efx)
 {
 	switch (efx->phy_type) {
-	case PHY_TYPE_10XPRESS:
-		efx->phy_op = &falcon_tenxpress_phy_ops;
+	case PHY_TYPE_SFX7101:
+		efx->phy_op = &falcon_sfx7101_phy_ops;
 		break;
-	case PHY_TYPE_XFP:
+	case PHY_TYPE_SFT9001A:
+	case PHY_TYPE_SFT9001B:
+		efx->phy_op = &falcon_sft9001_phy_ops;
+		break;
+	case PHY_TYPE_QT2022C2:
 		efx->phy_op = &falcon_xfp_phy_ops;
 		break;
 	default:
@@ -2174,10 +2261,59 @@
 		return -1;
 	}
 
-	efx->loopback_modes = LOOPBACKS_10G_INTERNAL | efx->phy_op->loopbacks;
+	if (efx->phy_op->macs & EFX_XMAC)
+		efx->loopback_modes |= ((1 << LOOPBACK_XGMII) |
+					(1 << LOOPBACK_XGXS) |
+					(1 << LOOPBACK_XAUI));
+	if (efx->phy_op->macs & EFX_GMAC)
+		efx->loopback_modes |= (1 << LOOPBACK_GMAC);
+	efx->loopback_modes |= efx->phy_op->loopbacks;
+
 	return 0;
 }
 
+int falcon_switch_mac(struct efx_nic *efx)
+{
+	struct efx_mac_operations *old_mac_op = efx->mac_op;
+	efx_oword_t nic_stat;
+	unsigned strap_val;
+
+	/* Internal loopbacks override the phy speed setting */
+	if (efx->loopback_mode == LOOPBACK_GMAC) {
+		efx->link_speed = 1000;
+		efx->link_fd = true;
+	} else if (LOOPBACK_INTERNAL(efx)) {
+		efx->link_speed = 10000;
+		efx->link_fd = true;
+	}
+
+	efx->mac_op = (EFX_IS10G(efx) ?
+		       &falcon_xmac_operations : &falcon_gmac_operations);
+	if (old_mac_op == efx->mac_op)
+		return 0;
+
+	WARN_ON(!mutex_is_locked(&efx->mac_lock));
+
+	/* Not all macs support a mac-level link state */
+	efx->mac_up = true;
+
+	falcon_read(efx, &nic_stat, NIC_STAT_REG);
+	strap_val = EFX_IS10G(efx) ? 5 : 3;
+	if (falcon_rev(efx) >= FALCON_REV_B0) {
+		EFX_SET_OWORD_FIELD(nic_stat, EE_STRAP_EN, 1);
+		EFX_SET_OWORD_FIELD(nic_stat, EE_STRAP_OVR, strap_val);
+		falcon_write(efx, &nic_stat, NIC_STAT_REG);
+	} else {
+		/* Falcon A1 does not support 1G/10G speed switching
+		 * and must not be used with a PHY that does. */
+		BUG_ON(EFX_OWORD_FIELD(nic_stat, STRAP_PINS) != strap_val);
+	}
+
+
+	EFX_LOG(efx, "selected %cMAC\n", EFX_IS10G(efx) ? 'X' : 'G');
+	return falcon_reset_macs(efx);
+}
+
 /* This call is responsible for hooking in the MAC and PHY operations */
 int falcon_probe_port(struct efx_nic *efx)
 {
@@ -2194,9 +2330,9 @@
 
 	/* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */
 	if (falcon_rev(efx) >= FALCON_REV_B0)
-		efx->flow_control = EFX_FC_RX | EFX_FC_TX;
+		efx->wanted_fc = EFX_FC_RX | EFX_FC_TX;
 	else
-		efx->flow_control = EFX_FC_RX;
+		efx->wanted_fc = EFX_FC_RX;
 
 	/* Allocate buffer for stats */
 	rc = falcon_alloc_buffer(efx, &efx->stats_buffer,
@@ -2253,12 +2389,15 @@
 	__le16 *word, *limit;
 	u32 csum;
 
+	spi = efx->spi_flash ? efx->spi_flash : efx->spi_eeprom;
+	if (!spi)
+		return -EINVAL;
+
 	region = kmalloc(FALCON_NVCONFIG_END, GFP_KERNEL);
 	if (!region)
 		return -ENOMEM;
 	nvconfig = region + NVCONFIG_OFFSET;
 
-	spi = efx->spi_flash ? efx->spi_flash : efx->spi_eeprom;
 	mutex_lock(&efx->spi_lock);
 	rc = falcon_spi_read(spi, 0, FALCON_NVCONFIG_END, NULL, region);
 	mutex_unlock(&efx->spi_lock);
@@ -2327,6 +2466,10 @@
 	  EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) },
 	{ DP_CTRL_REG,
 	  EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) },
+	{ GM_CFG2_REG,
+	  EFX_OWORD32(0x00007337, 0x00000000, 0x00000000, 0x00000000) },
+	{ GMF_CFG0_REG,
+	  EFX_OWORD32(0x00001F1F, 0x00000000, 0x00000000, 0x00000000) },
 	{ XM_GLB_CFG_REG,
 	  EFX_OWORD32(0x00000C68, 0x00000000, 0x00000000, 0x00000000) },
 	{ XM_TX_CFG_REG,
@@ -2547,7 +2690,7 @@
 	struct efx_spi_device *spi_device;
 
 	if (device_type != 0) {
-		spi_device = kmalloc(sizeof(*spi_device), GFP_KERNEL);
+		spi_device = kzalloc(sizeof(*spi_device), GFP_KERNEL);
 		if (!spi_device)
 			return -ENOMEM;
 		spi_device->device_id = device_id;
@@ -2652,6 +2795,7 @@
 static int falcon_probe_nic_variant(struct efx_nic *efx)
 {
 	efx_oword_t altera_build;
+	efx_oword_t nic_stat;
 
 	falcon_read(efx, &altera_build, ALTERA_BUILD_REG_KER);
 	if (EFX_OWORD_FIELD(altera_build, VER_ALL)) {
@@ -2659,27 +2803,20 @@
 		return -ENODEV;
 	}
 
+	falcon_read(efx, &nic_stat, NIC_STAT_REG);
+
 	switch (falcon_rev(efx)) {
 	case FALCON_REV_A0:
 	case 0xff:
 		EFX_ERR(efx, "Falcon rev A0 not supported\n");
 		return -ENODEV;
 
-	case FALCON_REV_A1:{
-		efx_oword_t nic_stat;
-
-		falcon_read(efx, &nic_stat, NIC_STAT_REG);
-
+	case FALCON_REV_A1:
 		if (EFX_OWORD_FIELD(nic_stat, STRAP_PCIE) == 0) {
 			EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n");
 			return -ENODEV;
 		}
-		if (!EFX_OWORD_FIELD(nic_stat, STRAP_10G)) {
-			EFX_ERR(efx, "1G mode not supported\n");
-			return -ENODEV;
-		}
 		break;
-	}
 
 	case FALCON_REV_B0:
 		break;
@@ -2689,6 +2826,9 @@
 		return -ENODEV;
 	}
 
+	/* Initial assumed speed */
+	efx->link_speed = EFX_OWORD_FIELD(nic_stat, STRAP_10G) ? 10000 : 1000;
+
 	return 0;
 }
 
@@ -2696,80 +2836,37 @@
 static void falcon_probe_spi_devices(struct efx_nic *efx)
 {
 	efx_oword_t nic_stat, gpio_ctl, ee_vpd_cfg;
-	bool has_flash, has_eeprom, boot_is_external;
+	int boot_dev;
 
 	falcon_read(efx, &gpio_ctl, GPIO_CTL_REG_KER);
 	falcon_read(efx, &nic_stat, NIC_STAT_REG);
 	falcon_read(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER);
 
-	has_flash = EFX_OWORD_FIELD(nic_stat, SF_PRST);
-	has_eeprom = EFX_OWORD_FIELD(nic_stat, EE_PRST);
-	boot_is_external = EFX_OWORD_FIELD(gpio_ctl, BOOTED_USING_NVDEVICE);
-
-	if (has_flash) {
-		/* Default flash SPI device: Atmel AT25F1024
-		 * 128 KB, 24-bit address, 32 KB erase block,
-		 * 256 B write block
-		 */
-		u32 flash_device_type =
-			(17 << SPI_DEV_TYPE_SIZE_LBN)
-			| (3 << SPI_DEV_TYPE_ADDR_LEN_LBN)
-			| (0x52 << SPI_DEV_TYPE_ERASE_CMD_LBN)
-			| (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN)
-			| (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN);
-
-		falcon_spi_device_init(efx, &efx->spi_flash,
-				       EE_SPI_FLASH, flash_device_type);
-
-		if (!boot_is_external) {
-			/* Disable VPD and set clock dividers to safe
-			 * values for initial programming.
-			 */
-			EFX_LOG(efx, "Booted from internal ASIC settings;"
-				" setting SPI config\n");
-			EFX_POPULATE_OWORD_3(ee_vpd_cfg, EE_VPD_EN, 0,
-					     /* 125 MHz / 7 ~= 20 MHz */
-					     EE_SF_CLOCK_DIV, 7,
-					     /* 125 MHz / 63 ~= 2 MHz */
-					     EE_EE_CLOCK_DIV, 63);
-			falcon_write(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER);
-		}
+	if (EFX_OWORD_FIELD(gpio_ctl, BOOTED_USING_NVDEVICE)) {
+		boot_dev = (EFX_OWORD_FIELD(nic_stat, SF_PRST) ?
+			    EE_SPI_FLASH : EE_SPI_EEPROM);
+		EFX_LOG(efx, "Booted from %s\n",
+			boot_dev == EE_SPI_FLASH ? "flash" : "EEPROM");
+	} else {
+		/* Disable VPD and set clock dividers to safe
+		 * values for initial programming. */
+		boot_dev = -1;
+		EFX_LOG(efx, "Booted from internal ASIC settings;"
+			" setting SPI config\n");
+		EFX_POPULATE_OWORD_3(ee_vpd_cfg, EE_VPD_EN, 0,
+				     /* 125 MHz / 7 ~= 20 MHz */
+				     EE_SF_CLOCK_DIV, 7,
+				     /* 125 MHz / 63 ~= 2 MHz */
+				     EE_EE_CLOCK_DIV, 63);
+		falcon_write(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER);
 	}
 
-	if (has_eeprom) {
-		u32 eeprom_device_type;
-
-		/* If it has no flash, it must have a large EEPROM
-		 * for chip config; otherwise check whether 9-bit
-		 * addressing is used for VPD configuration
-		 */
-		if (has_flash &&
-		    (!boot_is_external ||
-		     EFX_OWORD_FIELD(ee_vpd_cfg, EE_VPD_EN_AD9_MODE))) {
-			/* Default SPI device: Atmel AT25040 or similar
-			 * 512 B, 9-bit address, 8 B write block
-			 */
-			eeprom_device_type =
-				(9 << SPI_DEV_TYPE_SIZE_LBN)
-				| (1 << SPI_DEV_TYPE_ADDR_LEN_LBN)
-				| (3 << SPI_DEV_TYPE_BLOCK_SIZE_LBN);
-		} else {
-			/* "Large" SPI device: Atmel AT25640 or similar
-			 * 8 KB, 16-bit address, 32 B write block
-			 */
-			eeprom_device_type =
-				(13 << SPI_DEV_TYPE_SIZE_LBN)
-				| (2 << SPI_DEV_TYPE_ADDR_LEN_LBN)
-				| (5 << SPI_DEV_TYPE_BLOCK_SIZE_LBN);
-		}
-
-		falcon_spi_device_init(efx, &efx->spi_eeprom,
-				       EE_SPI_EEPROM, eeprom_device_type);
-	}
-
-	EFX_LOG(efx, "flash is %s, EEPROM is %s\n",
-		(has_flash ? "present" : "absent"),
-		(has_eeprom ? "present" : "absent"));
+	if (boot_dev == EE_SPI_FLASH)
+		falcon_spi_device_init(efx, &efx->spi_flash, EE_SPI_FLASH,
+				       default_flash_type);
+	if (boot_dev == EE_SPI_EEPROM)
+		falcon_spi_device_init(efx, &efx->spi_eeprom, EE_SPI_EEPROM,
+				       large_eeprom_type);
 }
 
 int falcon_probe_nic(struct efx_nic *efx)
@@ -2832,10 +2929,10 @@
 		goto fail5;
 
 	/* Initialise I2C adapter */
- 	efx->i2c_adap.owner = THIS_MODULE;
+	efx->i2c_adap.owner = THIS_MODULE;
 	nic_data->i2c_data = falcon_i2c_bit_operations;
 	nic_data->i2c_data.data = efx;
- 	efx->i2c_adap.algo_data = &nic_data->i2c_data;
+	efx->i2c_adap.algo_data = &nic_data->i2c_data;
 	efx->i2c_adap.dev.parent = &efx->pci_dev->dev;
 	strlcpy(efx->i2c_adap.name, "SFC4000 GPIO", sizeof(efx->i2c_adap.name));
 	rc = i2c_bit_add_bus(&efx->i2c_adap);
@@ -2869,20 +2966,18 @@
 	unsigned thresh;
 	int rc;
 
-	/* Set up the address region register. This is only needed
-	 * for the B0 FPGA, but since we are just pushing in the
-	 * reset defaults this may as well be unconditional. */
-	EFX_POPULATE_OWORD_4(temp, ADR_REGION0, 0,
-				   ADR_REGION1, (1 << 16),
-				   ADR_REGION2, (2 << 16),
-				   ADR_REGION3, (3 << 16));
-	falcon_write(efx, &temp, ADR_REGION_REG_KER);
-
 	/* Use on-chip SRAM */
 	falcon_read(efx, &temp, NIC_STAT_REG);
 	EFX_SET_OWORD_FIELD(temp, ONCHIP_SRAM, 1);
 	falcon_write(efx, &temp, NIC_STAT_REG);
 
+	/* Set the source of the GMAC clock */
+	if (falcon_rev(efx) == FALCON_REV_B0) {
+		falcon_read(efx, &temp, GPIO_CTL_REG_KER);
+		EFX_SET_OWORD_FIELD(temp, GPIO_USE_NIC_CLK, true);
+		falcon_write(efx, &temp, GPIO_CTL_REG_KER);
+	}
+
 	/* Set buffer table mode */
 	EFX_POPULATE_OWORD_1(temp, BUF_TBL_MODE, BUF_TBL_MODE_FULL);
 	falcon_write(efx, &temp, BUF_TBL_CFG_REG_KER);
diff --git a/drivers/net/sfc/falcon.h b/drivers/net/sfc/falcon.h
index be025ba..7869c3d 100644
--- a/drivers/net/sfc/falcon.h
+++ b/drivers/net/sfc/falcon.h
@@ -12,6 +12,7 @@
 #define EFX_FALCON_H
 
 #include "net_driver.h"
+#include "efx.h"
 
 /*
  * Falcon hardware control
@@ -65,6 +66,7 @@
 extern void falcon_remove_port(struct efx_nic *efx);
 
 /* MAC/PHY */
+extern int falcon_switch_mac(struct efx_nic *efx);
 extern bool falcon_xaui_link_ok(struct efx_nic *efx);
 extern int falcon_dma_stats(struct efx_nic *efx,
 			    unsigned int done_offset);
@@ -77,6 +79,7 @@
 extern void falcon_enable_interrupts(struct efx_nic *efx);
 extern void falcon_generate_test_event(struct efx_channel *channel,
 				       unsigned int magic);
+extern void falcon_sim_phy_event(struct efx_nic *efx);
 extern void falcon_generate_interrupt(struct efx_nic *efx);
 extern void falcon_set_int_moderation(struct efx_channel *channel);
 extern void falcon_disable_interrupts(struct efx_nic *efx);
diff --git a/drivers/net/sfc/falcon_gmac.c b/drivers/net/sfc/falcon_gmac.c
new file mode 100644
index 0000000..8865eae
--- /dev/null
+++ b/drivers/net/sfc/falcon_gmac.c
@@ -0,0 +1,229 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005-2006 Fen Systems Ltd.
+ * Copyright 2006-2008 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <linux/delay.h>
+#include "net_driver.h"
+#include "efx.h"
+#include "falcon.h"
+#include "mac.h"
+#include "falcon_hwdefs.h"
+#include "falcon_io.h"
+#include "gmii.h"
+
+/**************************************************************************
+ *
+ * MAC operations
+ *
+ *************************************************************************/
+
+static void falcon_reconfigure_gmac(struct efx_nic *efx)
+{
+	bool loopback, tx_fc, rx_fc, bytemode;
+	int if_mode;
+	unsigned int max_frame_len;
+	efx_oword_t reg;
+
+	/* Configuration register 1 */
+	tx_fc = (efx->link_fc & EFX_FC_TX) || !efx->link_fd;
+	rx_fc = !!(efx->link_fc & EFX_FC_RX);
+	loopback = (efx->loopback_mode == LOOPBACK_GMAC);
+	bytemode = (efx->link_speed == 1000);
+
+	EFX_POPULATE_OWORD_5(reg,
+			     GM_LOOP, loopback,
+			     GM_TX_EN, 1,
+			     GM_TX_FC_EN, tx_fc,
+			     GM_RX_EN, 1,
+			     GM_RX_FC_EN, rx_fc);
+	falcon_write(efx, &reg, GM_CFG1_REG);
+	udelay(10);
+
+	/* Configuration register 2 */
+	if_mode = (bytemode) ? 2 : 1;
+	EFX_POPULATE_OWORD_5(reg,
+			     GM_IF_MODE, if_mode,
+			     GM_PAD_CRC_EN, 1,
+			     GM_LEN_CHK, 1,
+			     GM_FD, efx->link_fd,
+			     GM_PAMBL_LEN, 0x7/*datasheet recommended */);
+
+	falcon_write(efx, &reg, GM_CFG2_REG);
+	udelay(10);
+
+	/* Max frame len register */
+	max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu);
+	EFX_POPULATE_OWORD_1(reg, GM_MAX_FLEN, max_frame_len);
+	falcon_write(efx, &reg, GM_MAX_FLEN_REG);
+	udelay(10);
+
+	/* FIFO configuration register 0 */
+	EFX_POPULATE_OWORD_5(reg,
+			     GMF_FTFENREQ, 1,
+			     GMF_STFENREQ, 1,
+			     GMF_FRFENREQ, 1,
+			     GMF_SRFENREQ, 1,
+			     GMF_WTMENREQ, 1);
+	falcon_write(efx, &reg, GMF_CFG0_REG);
+	udelay(10);
+
+	/* FIFO configuration register 1 */
+	EFX_POPULATE_OWORD_2(reg,
+			     GMF_CFGFRTH, 0x12,
+			     GMF_CFGXOFFRTX, 0xffff);
+	falcon_write(efx, &reg, GMF_CFG1_REG);
+	udelay(10);
+
+	/* FIFO configuration register 2 */
+	EFX_POPULATE_OWORD_2(reg,
+			     GMF_CFGHWM, 0x3f,
+			     GMF_CFGLWM, 0xa);
+	falcon_write(efx, &reg, GMF_CFG2_REG);
+	udelay(10);
+
+	/* FIFO configuration register 3 */
+	EFX_POPULATE_OWORD_2(reg,
+			     GMF_CFGHWMFT, 0x1c,
+			     GMF_CFGFTTH, 0x08);
+	falcon_write(efx, &reg, GMF_CFG3_REG);
+	udelay(10);
+
+	/* FIFO configuration register 4 */
+	EFX_POPULATE_OWORD_1(reg, GMF_HSTFLTRFRM_PAUSE, 1);
+	falcon_write(efx, &reg, GMF_CFG4_REG);
+	udelay(10);
+
+	/* FIFO configuration register 5 */
+	falcon_read(efx, &reg, GMF_CFG5_REG);
+	EFX_SET_OWORD_FIELD(reg, GMF_CFGBYTMODE, bytemode);
+	EFX_SET_OWORD_FIELD(reg, GMF_CFGHDPLX, !efx->link_fd);
+	EFX_SET_OWORD_FIELD(reg, GMF_HSTDRPLT64, !efx->link_fd);
+	EFX_SET_OWORD_FIELD(reg, GMF_HSTFLTRFRMDC_PAUSE, 0);
+	falcon_write(efx, &reg, GMF_CFG5_REG);
+	udelay(10);
+
+	/* MAC address */
+	EFX_POPULATE_OWORD_4(reg,
+			     GM_HWADDR_5, efx->net_dev->dev_addr[5],
+			     GM_HWADDR_4, efx->net_dev->dev_addr[4],
+			     GM_HWADDR_3, efx->net_dev->dev_addr[3],
+			     GM_HWADDR_2, efx->net_dev->dev_addr[2]);
+	falcon_write(efx, &reg, GM_ADR1_REG);
+	udelay(10);
+	EFX_POPULATE_OWORD_2(reg,
+			     GM_HWADDR_1, efx->net_dev->dev_addr[1],
+			     GM_HWADDR_0, efx->net_dev->dev_addr[0]);
+	falcon_write(efx, &reg, GM_ADR2_REG);
+	udelay(10);
+
+	falcon_reconfigure_mac_wrapper(efx);
+}
+
+static void falcon_update_stats_gmac(struct efx_nic *efx)
+{
+	struct efx_mac_stats *mac_stats = &efx->mac_stats;
+	unsigned long old_rx_pause, old_tx_pause;
+	unsigned long new_rx_pause, new_tx_pause;
+	int rc;
+
+	rc = falcon_dma_stats(efx, GDmaDone_offset);
+	if (rc)
+		return;
+
+	/* Pause frames are erroneously counted as errors (SFC bug 3269) */
+	old_rx_pause = mac_stats->rx_pause;
+	old_tx_pause = mac_stats->tx_pause;
+
+	/* Update MAC stats from DMAed values */
+	FALCON_STAT(efx, GRxGoodOct, rx_good_bytes);
+	FALCON_STAT(efx, GRxBadOct, rx_bad_bytes);
+	FALCON_STAT(efx, GRxMissPkt, rx_missed);
+	FALCON_STAT(efx, GRxFalseCRS, rx_false_carrier);
+	FALCON_STAT(efx, GRxPausePkt, rx_pause);
+	FALCON_STAT(efx, GRxBadPkt, rx_bad);
+	FALCON_STAT(efx, GRxUcastPkt, rx_unicast);
+	FALCON_STAT(efx, GRxMcastPkt, rx_multicast);
+	FALCON_STAT(efx, GRxBcastPkt, rx_broadcast);
+	FALCON_STAT(efx, GRxGoodLt64Pkt, rx_good_lt64);
+	FALCON_STAT(efx, GRxBadLt64Pkt, rx_bad_lt64);
+	FALCON_STAT(efx, GRx64Pkt, rx_64);
+	FALCON_STAT(efx, GRx65to127Pkt, rx_65_to_127);
+	FALCON_STAT(efx, GRx128to255Pkt, rx_128_to_255);
+	FALCON_STAT(efx, GRx256to511Pkt, rx_256_to_511);
+	FALCON_STAT(efx, GRx512to1023Pkt, rx_512_to_1023);
+	FALCON_STAT(efx, GRx1024to15xxPkt, rx_1024_to_15xx);
+	FALCON_STAT(efx, GRx15xxtoJumboPkt, rx_15xx_to_jumbo);
+	FALCON_STAT(efx, GRxGtJumboPkt, rx_gtjumbo);
+	FALCON_STAT(efx, GRxFcsErr64to15xxPkt, rx_bad_64_to_15xx);
+	FALCON_STAT(efx, GRxFcsErr15xxtoJumboPkt, rx_bad_15xx_to_jumbo);
+	FALCON_STAT(efx, GRxFcsErrGtJumboPkt, rx_bad_gtjumbo);
+	FALCON_STAT(efx, GTxGoodBadOct, tx_bytes);
+	FALCON_STAT(efx, GTxGoodOct, tx_good_bytes);
+	FALCON_STAT(efx, GTxSglColPkt, tx_single_collision);
+	FALCON_STAT(efx, GTxMultColPkt, tx_multiple_collision);
+	FALCON_STAT(efx, GTxExColPkt, tx_excessive_collision);
+	FALCON_STAT(efx, GTxDefPkt, tx_deferred);
+	FALCON_STAT(efx, GTxLateCol, tx_late_collision);
+	FALCON_STAT(efx, GTxExDefPkt, tx_excessive_deferred);
+	FALCON_STAT(efx, GTxPausePkt, tx_pause);
+	FALCON_STAT(efx, GTxBadPkt, tx_bad);
+	FALCON_STAT(efx, GTxUcastPkt, tx_unicast);
+	FALCON_STAT(efx, GTxMcastPkt, tx_multicast);
+	FALCON_STAT(efx, GTxBcastPkt, tx_broadcast);
+	FALCON_STAT(efx, GTxLt64Pkt, tx_lt64);
+	FALCON_STAT(efx, GTx64Pkt, tx_64);
+	FALCON_STAT(efx, GTx65to127Pkt, tx_65_to_127);
+	FALCON_STAT(efx, GTx128to255Pkt, tx_128_to_255);
+	FALCON_STAT(efx, GTx256to511Pkt, tx_256_to_511);
+	FALCON_STAT(efx, GTx512to1023Pkt, tx_512_to_1023);
+	FALCON_STAT(efx, GTx1024to15xxPkt, tx_1024_to_15xx);
+	FALCON_STAT(efx, GTx15xxtoJumboPkt, tx_15xx_to_jumbo);
+	FALCON_STAT(efx, GTxGtJumboPkt, tx_gtjumbo);
+	FALCON_STAT(efx, GTxNonTcpUdpPkt, tx_non_tcpudp);
+	FALCON_STAT(efx, GTxMacSrcErrPkt, tx_mac_src_error);
+	FALCON_STAT(efx, GTxIpSrcErrPkt, tx_ip_src_error);
+
+	/* Pause frames are erroneously counted as errors (SFC bug 3269) */
+	new_rx_pause = mac_stats->rx_pause;
+	new_tx_pause = mac_stats->tx_pause;
+	mac_stats->rx_bad -= (new_rx_pause - old_rx_pause);
+	mac_stats->tx_bad -= (new_tx_pause - old_tx_pause);
+
+	/* Derive stats that the MAC doesn't provide directly */
+	mac_stats->tx_bad_bytes =
+		mac_stats->tx_bytes - mac_stats->tx_good_bytes;
+	mac_stats->tx_packets =
+		mac_stats->tx_lt64 + mac_stats->tx_64 +
+		mac_stats->tx_65_to_127 + mac_stats->tx_128_to_255 +
+		mac_stats->tx_256_to_511 + mac_stats->tx_512_to_1023 +
+		mac_stats->tx_1024_to_15xx + mac_stats->tx_15xx_to_jumbo +
+		mac_stats->tx_gtjumbo;
+	mac_stats->tx_collision =
+		mac_stats->tx_single_collision +
+		mac_stats->tx_multiple_collision +
+		mac_stats->tx_excessive_collision +
+		mac_stats->tx_late_collision;
+	mac_stats->rx_bytes =
+		mac_stats->rx_good_bytes + mac_stats->rx_bad_bytes;
+	mac_stats->rx_packets =
+		mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64 +
+		mac_stats->rx_64 + mac_stats->rx_65_to_127 +
+		mac_stats->rx_128_to_255 + mac_stats->rx_256_to_511 +
+		mac_stats->rx_512_to_1023 + mac_stats->rx_1024_to_15xx +
+		mac_stats->rx_15xx_to_jumbo + mac_stats->rx_gtjumbo;
+	mac_stats->rx_good = mac_stats->rx_packets - mac_stats->rx_bad;
+	mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64;
+}
+
+struct efx_mac_operations falcon_gmac_operations = {
+	.reconfigure	= falcon_reconfigure_gmac,
+	.update_stats	= falcon_update_stats_gmac,
+	.irq		= efx_port_dummy_op_void,
+	.poll		= efx_port_dummy_op_void,
+};
diff --git a/drivers/net/sfc/falcon_hwdefs.h b/drivers/net/sfc/falcon_hwdefs.h
index 040e70e..bda8d5b 100644
--- a/drivers/net/sfc/falcon_hwdefs.h
+++ b/drivers/net/sfc/falcon_hwdefs.h
@@ -111,12 +111,18 @@
 
 /* NIC status register */
 #define NIC_STAT_REG 0x0200
+#define EE_STRAP_EN_LBN 31
+#define EE_STRAP_EN_WIDTH 1
+#define EE_STRAP_OVR_LBN 24
+#define EE_STRAP_OVR_WIDTH 4
 #define ONCHIP_SRAM_LBN 16
 #define ONCHIP_SRAM_WIDTH 1
 #define SF_PRST_LBN 9
 #define SF_PRST_WIDTH 1
 #define EE_PRST_LBN 8
 #define EE_PRST_WIDTH 1
+#define STRAP_PINS_LBN 0
+#define STRAP_PINS_WIDTH 3
 /* These bit definitions are extrapolated from the list of numerical
  * values for STRAP_PINS.
  */
@@ -130,6 +136,8 @@
 
 /* GPIO control register */
 #define GPIO_CTL_REG_KER 0x0210
+#define GPIO_USE_NIC_CLK_LBN (30)
+#define GPIO_USE_NIC_CLK_WIDTH (1)
 #define GPIO_OUTPUTS_LBN   (16)
 #define GPIO_OUTPUTS_WIDTH (4)
 #define GPIO_INPUTS_LBN (8)
@@ -492,6 +500,107 @@
 #define MAC_MCAST_HASH_REG0_KER 0xca0
 #define MAC_MCAST_HASH_REG1_KER 0xcb0
 
+/* GMAC configuration register 1 */
+#define GM_CFG1_REG 0xe00
+#define GM_SW_RST_LBN 31
+#define GM_SW_RST_WIDTH 1
+#define GM_LOOP_LBN 8
+#define GM_LOOP_WIDTH 1
+#define GM_RX_FC_EN_LBN 5
+#define GM_RX_FC_EN_WIDTH 1
+#define GM_TX_FC_EN_LBN 4
+#define GM_TX_FC_EN_WIDTH 1
+#define GM_RX_EN_LBN 2
+#define GM_RX_EN_WIDTH 1
+#define GM_TX_EN_LBN 0
+#define GM_TX_EN_WIDTH 1
+
+/* GMAC configuration register 2 */
+#define GM_CFG2_REG 0xe10
+#define GM_PAMBL_LEN_LBN 12
+#define GM_PAMBL_LEN_WIDTH 4
+#define GM_IF_MODE_LBN 8
+#define GM_IF_MODE_WIDTH 2
+#define GM_LEN_CHK_LBN 4
+#define GM_LEN_CHK_WIDTH 1
+#define GM_PAD_CRC_EN_LBN 2
+#define GM_PAD_CRC_EN_WIDTH 1
+#define GM_FD_LBN 0
+#define GM_FD_WIDTH 1
+
+/* GMAC maximum frame length register */
+#define GM_MAX_FLEN_REG 0xe40
+#define GM_MAX_FLEN_LBN 0
+#define GM_MAX_FLEN_WIDTH 16
+
+/* GMAC station address register 1 */
+#define GM_ADR1_REG 0xf00
+#define GM_HWADDR_5_LBN 24
+#define GM_HWADDR_5_WIDTH 8
+#define GM_HWADDR_4_LBN 16
+#define GM_HWADDR_4_WIDTH 8
+#define GM_HWADDR_3_LBN 8
+#define GM_HWADDR_3_WIDTH 8
+#define GM_HWADDR_2_LBN 0
+#define GM_HWADDR_2_WIDTH 8
+
+/* GMAC station address register 2 */
+#define GM_ADR2_REG 0xf10
+#define GM_HWADDR_1_LBN 24
+#define GM_HWADDR_1_WIDTH 8
+#define GM_HWADDR_0_LBN 16
+#define GM_HWADDR_0_WIDTH 8
+
+/* GMAC FIFO configuration register 0 */
+#define GMF_CFG0_REG 0xf20
+#define GMF_FTFENREQ_LBN 12
+#define GMF_FTFENREQ_WIDTH 1
+#define GMF_STFENREQ_LBN 11
+#define GMF_STFENREQ_WIDTH 1
+#define GMF_FRFENREQ_LBN 10
+#define GMF_FRFENREQ_WIDTH 1
+#define GMF_SRFENREQ_LBN 9
+#define GMF_SRFENREQ_WIDTH 1
+#define GMF_WTMENREQ_LBN 8
+#define GMF_WTMENREQ_WIDTH 1
+
+/* GMAC FIFO configuration register 1 */
+#define GMF_CFG1_REG 0xf30
+#define GMF_CFGFRTH_LBN 16
+#define GMF_CFGFRTH_WIDTH 5
+#define GMF_CFGXOFFRTX_LBN 0
+#define GMF_CFGXOFFRTX_WIDTH 16
+
+/* GMAC FIFO configuration register 2 */
+#define GMF_CFG2_REG 0xf40
+#define GMF_CFGHWM_LBN 16
+#define GMF_CFGHWM_WIDTH 6
+#define GMF_CFGLWM_LBN 0
+#define GMF_CFGLWM_WIDTH 6
+
+/* GMAC FIFO configuration register 3 */
+#define GMF_CFG3_REG 0xf50
+#define GMF_CFGHWMFT_LBN 16
+#define GMF_CFGHWMFT_WIDTH 6
+#define GMF_CFGFTTH_LBN 0
+#define GMF_CFGFTTH_WIDTH 6
+
+/* GMAC FIFO configuration register 4 */
+#define GMF_CFG4_REG 0xf60
+#define GMF_HSTFLTRFRM_PAUSE_LBN 12
+#define GMF_HSTFLTRFRM_PAUSE_WIDTH 12
+
+/* GMAC FIFO configuration register 5 */
+#define GMF_CFG5_REG 0xf70
+#define GMF_CFGHDPLX_LBN 22
+#define GMF_CFGHDPLX_WIDTH 1
+#define GMF_CFGBYTMODE_LBN 19
+#define GMF_CFGBYTMODE_WIDTH 1
+#define GMF_HSTDRPLT64_LBN 18
+#define GMF_HSTDRPLT64_WIDTH 1
+#define GMF_HSTFLTRFRMDC_PAUSE_LBN 12
+#define GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1
+
 /* XGMAC address register low */
 #define XM_ADR_LO_REG 0x1200
 #define XM_ADR_3_LBN 24
@@ -944,6 +1053,8 @@
 #define XG_MNT_INTR_B0_WIDTH 1
 #define RX_RECOVERY_A1_LBN 11
 #define RX_RECOVERY_A1_WIDTH 1
+#define XFP_PHY_INTR_LBN 10
+#define XFP_PHY_INTR_WIDTH 1
 #define XG_PHY_INTR_LBN 9
 #define XG_PHY_INTR_WIDTH 1
 #define G_PHY1_INTR_LBN 8
@@ -962,54 +1073,103 @@
  **************************************************************************
  *
  */
+
 #define GRxGoodOct_offset 0x0
+#define GRxGoodOct_WIDTH 48
 #define GRxBadOct_offset 0x8
+#define GRxBadOct_WIDTH 48
 #define GRxMissPkt_offset 0x10
+#define GRxMissPkt_WIDTH 32
 #define GRxFalseCRS_offset 0x14
+#define GRxFalseCRS_WIDTH 32
 #define GRxPausePkt_offset 0x18
+#define GRxPausePkt_WIDTH 32
 #define GRxBadPkt_offset 0x1C
+#define GRxBadPkt_WIDTH 32
 #define GRxUcastPkt_offset 0x20
+#define GRxUcastPkt_WIDTH 32
 #define GRxMcastPkt_offset 0x24
+#define GRxMcastPkt_WIDTH 32
 #define GRxBcastPkt_offset 0x28
+#define GRxBcastPkt_WIDTH 32
 #define GRxGoodLt64Pkt_offset 0x2C
+#define GRxGoodLt64Pkt_WIDTH 32
 #define GRxBadLt64Pkt_offset 0x30
+#define GRxBadLt64Pkt_WIDTH 32
 #define GRx64Pkt_offset 0x34
+#define GRx64Pkt_WIDTH 32
 #define GRx65to127Pkt_offset 0x38
+#define GRx65to127Pkt_WIDTH 32
 #define GRx128to255Pkt_offset 0x3C
+#define GRx128to255Pkt_WIDTH 32
 #define GRx256to511Pkt_offset 0x40
+#define GRx256to511Pkt_WIDTH 32
 #define GRx512to1023Pkt_offset 0x44
+#define GRx512to1023Pkt_WIDTH 32
 #define GRx1024to15xxPkt_offset 0x48
+#define GRx1024to15xxPkt_WIDTH 32
 #define GRx15xxtoJumboPkt_offset 0x4C
+#define GRx15xxtoJumboPkt_WIDTH 32
 #define GRxGtJumboPkt_offset 0x50
+#define GRxGtJumboPkt_WIDTH 32
 #define GRxFcsErr64to15xxPkt_offset 0x54
+#define GRxFcsErr64to15xxPkt_WIDTH 32
 #define GRxFcsErr15xxtoJumboPkt_offset 0x58
+#define GRxFcsErr15xxtoJumboPkt_WIDTH 32
 #define GRxFcsErrGtJumboPkt_offset 0x5C
+#define GRxFcsErrGtJumboPkt_WIDTH 32
 #define GTxGoodBadOct_offset 0x80
+#define GTxGoodBadOct_WIDTH 48
 #define GTxGoodOct_offset 0x88
+#define GTxGoodOct_WIDTH 48
 #define GTxSglColPkt_offset 0x90
+#define GTxSglColPkt_WIDTH 32
 #define GTxMultColPkt_offset 0x94
+#define GTxMultColPkt_WIDTH 32
 #define GTxExColPkt_offset 0x98
+#define GTxExColPkt_WIDTH 32
 #define GTxDefPkt_offset 0x9C
+#define GTxDefPkt_WIDTH 32
 #define GTxLateCol_offset 0xA0
+#define GTxLateCol_WIDTH 32
 #define GTxExDefPkt_offset 0xA4
+#define GTxExDefPkt_WIDTH 32
 #define GTxPausePkt_offset 0xA8
+#define GTxPausePkt_WIDTH 32
 #define GTxBadPkt_offset 0xAC
+#define GTxBadPkt_WIDTH 32
 #define GTxUcastPkt_offset 0xB0
+#define GTxUcastPkt_WIDTH 32
 #define GTxMcastPkt_offset 0xB4
+#define GTxMcastPkt_WIDTH 32
 #define GTxBcastPkt_offset 0xB8
+#define GTxBcastPkt_WIDTH 32
 #define GTxLt64Pkt_offset 0xBC
+#define GTxLt64Pkt_WIDTH 32
 #define GTx64Pkt_offset 0xC0
+#define GTx64Pkt_WIDTH 32
 #define GTx65to127Pkt_offset 0xC4
+#define GTx65to127Pkt_WIDTH 32
 #define GTx128to255Pkt_offset 0xC8
+#define GTx128to255Pkt_WIDTH 32
 #define GTx256to511Pkt_offset 0xCC
+#define GTx256to511Pkt_WIDTH 32
 #define GTx512to1023Pkt_offset 0xD0
+#define GTx512to1023Pkt_WIDTH 32
 #define GTx1024to15xxPkt_offset 0xD4
+#define GTx1024to15xxPkt_WIDTH 32
 #define GTx15xxtoJumboPkt_offset 0xD8
+#define GTx15xxtoJumboPkt_WIDTH 32
 #define GTxGtJumboPkt_offset 0xDC
+#define GTxGtJumboPkt_WIDTH 32
 #define GTxNonTcpUdpPkt_offset 0xE0
+#define GTxNonTcpUdpPkt_WIDTH 16
 #define GTxMacSrcErrPkt_offset 0xE4
+#define GTxMacSrcErrPkt_WIDTH 16
 #define GTxIpSrcErrPkt_offset 0xE8
+#define GTxIpSrcErrPkt_WIDTH 16
 #define GDmaDone_offset 0xEC
+#define GDmaDone_WIDTH 32
 
 #define XgRxOctets_offset 0x0
 #define XgRxOctets_WIDTH 48
diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c
index d401231..5a03713 100644
--- a/drivers/net/sfc/falcon_xmac.c
+++ b/drivers/net/sfc/falcon_xmac.c
@@ -15,7 +15,6 @@
 #include "falcon_hwdefs.h"
 #include "falcon_io.h"
 #include "mac.h"
-#include "gmii.h"
 #include "mdio_10g.h"
 #include "phy.h"
 #include "boards.h"
@@ -26,24 +25,6 @@
  * MAC operations
  *
  *************************************************************************/
-static int falcon_reset_xmac(struct efx_nic *efx)
-{
-	efx_oword_t reg;
-	int count;
-
-	EFX_POPULATE_OWORD_1(reg, XM_CORE_RST, 1);
-	falcon_write(efx, &reg, XM_GLB_CFG_REG);
-
-	for (count = 0; count < 10000; count++) {	/* wait upto 100ms */
-		falcon_read(efx, &reg, XM_GLB_CFG_REG);
-		if (EFX_OWORD_FIELD(reg, XM_CORE_RST) == 0)
-			return 0;
-		udelay(10);
-	}
-
-	EFX_ERR(efx, "timed out waiting for XMAC core reset\n");
-	return -ETIMEDOUT;
-}
 
 /* Configure the XAUI driver that is an output from Falcon */
 static void falcon_setup_xaui(struct efx_nic *efx)
@@ -99,31 +80,20 @@
 	return -ETIMEDOUT;
 }
 
-static bool falcon_xgmii_status(struct efx_nic *efx)
-{
-	efx_oword_t reg;
-
-	if (falcon_rev(efx) < FALCON_REV_B0)
-		return true;
-
-	/* The ISR latches, so clear it and re-read */
-	falcon_read(efx, &reg, XM_MGT_INT_REG_B0);
-	falcon_read(efx, &reg, XM_MGT_INT_REG_B0);
-
-	if (EFX_OWORD_FIELD(reg, XM_LCLFLT) ||
-	    EFX_OWORD_FIELD(reg, XM_RMTFLT)) {
-		EFX_INFO(efx, "MGT_INT: "EFX_DWORD_FMT"\n", EFX_DWORD_VAL(reg));
-		return false;
-	}
-
-	return true;
-}
-
 static void falcon_mask_status_intr(struct efx_nic *efx, bool enable)
 {
 	efx_oword_t reg;
 
-	if ((falcon_rev(efx) < FALCON_REV_B0) || LOOPBACK_INTERNAL(efx))
+	if ((falcon_rev(efx) != FALCON_REV_B0) || LOOPBACK_INTERNAL(efx))
+		return;
+
+	/* We expect xgmii faults if the wireside link is up */
+	if (!EFX_WORKAROUND_5147(efx) || !efx->link_up)
+		return;
+
+	/* We can only use this interrupt to signal the negative edge of
+	 * xaui_align [we have to poll the positive edge]. */
+	if (!efx->mac_up)
 		return;
 
 	/* Flush the ISR */
@@ -136,35 +106,7 @@
 	falcon_write(efx, &reg, XM_MGT_INT_MSK_REG_B0);
 }
 
-int falcon_init_xmac(struct efx_nic *efx)
-{
-	int rc;
-
-	/* Initialize the PHY first so the clock is around */
-	rc = efx->phy_op->init(efx);
-	if (rc)
-		goto fail1;
-
-	rc = falcon_reset_xaui(efx);
-	if (rc)
-		goto fail2;
-
-	/* Wait again. Give the PHY and MAC time to come back */
-	schedule_timeout_uninterruptible(HZ / 10);
-
-	rc = falcon_reset_xmac(efx);
-	if (rc)
-		goto fail2;
-
-	falcon_mask_status_intr(efx, true);
-	return 0;
-
- fail2:
-	efx->phy_op->fini(efx);
- fail1:
-	return rc;
-}
-
+/* Get status of XAUI link */
 bool falcon_xaui_link_ok(struct efx_nic *efx)
 {
 	efx_oword_t reg;
@@ -188,18 +130,10 @@
 	EFX_SET_OWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET);
 	falcon_write(efx, &reg, XX_CORE_STAT_REG);
 
-	/* If the link is up, then check the phy side of the xaui link
-	 * (error conditions from the wire side propoagate back through
-	 * the phy to the xaui side). */
-	if (efx->link_up && link_ok) {
+	/* If the link is up, then check the phy side of the xaui link */
+	if (efx->link_up && link_ok)
 		if (efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS))
 			link_ok = mdio_clause45_phyxgxs_lane_sync(efx);
-	}
-
-	/* If the PHY and XAUI links are up, then check the mac's xgmii
-	 * fault state */
-	if (efx->link_up && link_ok)
-		link_ok = falcon_xgmii_status(efx);
 
 	return link_ok;
 }
@@ -208,7 +142,7 @@
 {
 	unsigned int max_frame_len;
 	efx_oword_t reg;
-	bool rx_fc = !!(efx->flow_control & EFX_FC_RX);
+	bool rx_fc = !!(efx->link_fc & EFX_FC_RX);
 
 	/* Configure MAC  - cut-thru mode is hard wired on */
 	EFX_POPULATE_DWORD_3(reg,
@@ -311,70 +245,39 @@
 
 /* Try and bring the Falcon side of the Falcon-Phy XAUI link fails
  * to come back up. Bash it until it comes back up */
-static bool falcon_check_xaui_link_up(struct efx_nic *efx)
+static void falcon_check_xaui_link_up(struct efx_nic *efx, int tries)
 {
-	int max_tries, tries;
-	tries = EFX_WORKAROUND_5147(efx) ? 5 : 1;
-	max_tries = tries;
+	efx->mac_up = falcon_xaui_link_ok(efx);
 
 	if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
-	    (efx->phy_type == PHY_TYPE_NONE) ||
 	    efx_phy_mode_disabled(efx->phy_mode))
-		return false;
+		/* XAUI link is expected to be down */
+		return;
 
-	while (tries) {
-		if (falcon_xaui_link_ok(efx))
-			return true;
-
-		EFX_LOG(efx, "%s Clobbering XAUI (%d tries left).\n",
-			__func__, tries);
+	while (!efx->mac_up && tries) {
+		EFX_LOG(efx, "bashing xaui\n");
 		falcon_reset_xaui(efx);
 		udelay(200);
-		tries--;
-	}
 
-	EFX_LOG(efx, "Failed to bring XAUI link back up in %d tries!\n",
-		max_tries);
-	return false;
+		efx->mac_up = falcon_xaui_link_ok(efx);
+		--tries;
+	}
 }
 
-void falcon_reconfigure_xmac(struct efx_nic *efx)
+static void falcon_reconfigure_xmac(struct efx_nic *efx)
 {
-	bool xaui_link_ok;
-
 	falcon_mask_status_intr(efx, false);
 
-	falcon_deconfigure_mac_wrapper(efx);
-
-	/* Reconfigure the PHY, disabling transmit in mac level loopback. */
-	if (LOOPBACK_INTERNAL(efx))
-		efx->phy_mode |= PHY_MODE_TX_DISABLED;
-	else
-		efx->phy_mode &= ~PHY_MODE_TX_DISABLED;
-	efx->phy_op->reconfigure(efx);
-
 	falcon_reconfigure_xgxs_core(efx);
 	falcon_reconfigure_xmac_core(efx);
 
 	falcon_reconfigure_mac_wrapper(efx);
 
-	/* Ensure XAUI link is up */
-	xaui_link_ok = falcon_check_xaui_link_up(efx);
-
-	if (xaui_link_ok && efx->link_up)
-		falcon_mask_status_intr(efx, true);
+	falcon_check_xaui_link_up(efx, 5);
+	falcon_mask_status_intr(efx, true);
 }
 
-void falcon_fini_xmac(struct efx_nic *efx)
-{
-	/* Isolate the MAC - PHY */
-	falcon_deconfigure_mac_wrapper(efx);
-
-	/* Potentially power down the PHY */
-	efx->phy_op->fini(efx);
-}
-
-void falcon_update_stats_xmac(struct efx_nic *efx)
+static void falcon_update_stats_xmac(struct efx_nic *efx)
 {
 	struct efx_mac_stats *mac_stats = &efx->mac_stats;
 	int rc;
@@ -439,97 +342,35 @@
 		 mac_stats->rx_control * 64);
 }
 
-int falcon_check_xmac(struct efx_nic *efx)
+static void falcon_xmac_irq(struct efx_nic *efx)
 {
-	bool xaui_link_ok;
-	int rc;
+	/* The XGMII link has a transient fault, which indicates either:
+	 *   - there's a transient xgmii fault
+	 *   - falcon's end of the xaui link may need a kick
+	 *   - the wire-side link may have gone down, but the lasi/poll()
+	 *     hasn't noticed yet.
+	 *
+	 * We only want to even bother polling XAUI if we're confident it's
+	 * not (1) or (3). In both cases, the only reliable way to spot this
+	 * is to wait a bit. We do this here by forcing the mac link state
+	 * to down, and waiting for the mac poll to come round and check
+	 */
+	efx->mac_up = false;
+}
 
-	if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
-	    efx_phy_mode_disabled(efx->phy_mode))
-		return 0;
+static void falcon_poll_xmac(struct efx_nic *efx)
+{
+	if (!EFX_WORKAROUND_5147(efx) || !efx->link_up || efx->mac_up)
+		return;
 
 	falcon_mask_status_intr(efx, false);
-	xaui_link_ok = falcon_xaui_link_ok(efx);
-
-	if (EFX_WORKAROUND_5147(efx) && !xaui_link_ok)
-		falcon_reset_xaui(efx);
-
-	/* Call the PHY check_hw routine */
-	rc = efx->phy_op->check_hw(efx);
-
-	/* Unmask interrupt if everything was (and still is) ok */
-	if (xaui_link_ok && efx->link_up)
-		falcon_mask_status_intr(efx, true);
-
-	return rc;
+	falcon_check_xaui_link_up(efx, 1);
+	falcon_mask_status_intr(efx, true);
 }
 
-/* Simulate a PHY event */
-void falcon_xmac_sim_phy_event(struct efx_nic *efx)
-{
-	efx_qword_t phy_event;
-
-	EFX_POPULATE_QWORD_2(phy_event,
-			     EV_CODE, GLOBAL_EV_DECODE,
-			     XG_PHY_INTR, 1);
-	falcon_generate_event(&efx->channel[0], &phy_event);
-}
-
-int falcon_xmac_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
-{
-	mdio_clause45_get_settings(efx, ecmd);
-	ecmd->transceiver = XCVR_INTERNAL;
-	ecmd->phy_address = efx->mii.phy_id;
-	ecmd->autoneg = AUTONEG_DISABLE;
-	ecmd->duplex = DUPLEX_FULL;
-	return 0;
-}
-
-int falcon_xmac_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
-{
-	if (ecmd->transceiver != XCVR_INTERNAL)
-		return -EINVAL;
-	if (ecmd->autoneg != AUTONEG_DISABLE)
-		return -EINVAL;
-	if (ecmd->duplex != DUPLEX_FULL)
-		return -EINVAL;
-
-	return mdio_clause45_set_settings(efx, ecmd);
-}
-
-
-int falcon_xmac_set_pause(struct efx_nic *efx, enum efx_fc_type flow_control)
-{
-	bool reset;
-
-	if (flow_control & EFX_FC_AUTO) {
-		EFX_LOG(efx, "10G does not support flow control "
-			"autonegotiation\n");
-		return -EINVAL;
-	}
-
-	if ((flow_control & EFX_FC_TX) && !(flow_control & EFX_FC_RX))
-		return -EINVAL;
-
-	/* TX flow control may automatically turn itself off if the
-	 * link partner (intermittently) stops responding to pause
-	 * frames. There isn't any indication that this has happened,
-	 * so the best we do is leave it up to the user to spot this
-	 * and fix it be cycling transmit flow control on this end. */
-	reset = ((flow_control & EFX_FC_TX) &&
-		 !(efx->flow_control & EFX_FC_TX));
-	if (EFX_WORKAROUND_11482(efx) && reset) {
-		if (falcon_rev(efx) >= FALCON_REV_B0) {
-			/* Recover by resetting the EM block */
-			if (efx->link_up)
-				falcon_drain_tx_fifo(efx);
-		} else {
-			/* Schedule a reset to recover */
-			efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
-		}
-	}
-
-	efx->flow_control = flow_control;
-
-	return 0;
-}
+struct efx_mac_operations falcon_xmac_operations = {
+	.reconfigure	= falcon_reconfigure_xmac,
+	.update_stats	= falcon_update_stats_xmac,
+	.irq		= falcon_xmac_irq,
+	.poll		= falcon_poll_xmac,
+};
diff --git a/drivers/net/sfc/gmii.h b/drivers/net/sfc/gmii.h
index d25bbd1..dfccaa7 100644
--- a/drivers/net/sfc/gmii.h
+++ b/drivers/net/sfc/gmii.h
@@ -1,7 +1,7 @@
 /****************************************************************************
  * Driver for Solarflare Solarstorm network controllers and boards
  * Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006 Solarflare Communications Inc.
+ * Copyright 2006-2008 Solarflare Communications Inc.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published
@@ -57,139 +57,4 @@
 #define ISR_POLARITY_CHG	0x0002	/* Bit 1 - polarity changed */
 #define ISR_JABBER		0x0001	/* Bit 0 - jabber */
 
-/* Logically extended advertisement register */
-#define GM_ADVERTISE_SLCT		ADVERTISE_SLCT
-#define GM_ADVERTISE_CSMA		ADVERTISE_CSMA
-#define GM_ADVERTISE_10HALF		ADVERTISE_10HALF
-#define GM_ADVERTISE_1000XFULL		ADVERTISE_1000XFULL
-#define GM_ADVERTISE_10FULL		ADVERTISE_10FULL
-#define GM_ADVERTISE_1000XHALF		ADVERTISE_1000XHALF
-#define GM_ADVERTISE_100HALF		ADVERTISE_100HALF
-#define GM_ADVERTISE_1000XPAUSE		ADVERTISE_1000XPAUSE
-#define GM_ADVERTISE_100FULL		ADVERTISE_100FULL
-#define GM_ADVERTISE_1000XPSE_ASYM	ADVERTISE_1000XPSE_ASYM
-#define GM_ADVERTISE_100BASE4		ADVERTISE_100BASE4
-#define GM_ADVERTISE_PAUSE_CAP		ADVERTISE_PAUSE_CAP
-#define GM_ADVERTISE_PAUSE_ASYM		ADVERTISE_PAUSE_ASYM
-#define GM_ADVERTISE_RESV		ADVERTISE_RESV
-#define GM_ADVERTISE_RFAULT		ADVERTISE_RFAULT
-#define GM_ADVERTISE_LPACK		ADVERTISE_LPACK
-#define GM_ADVERTISE_NPAGE		ADVERTISE_NPAGE
-#define GM_ADVERTISE_1000FULL		(ADVERTISE_1000FULL << 8)
-#define GM_ADVERTISE_1000HALF		(ADVERTISE_1000HALF << 8)
-#define GM_ADVERTISE_1000		(GM_ADVERTISE_1000FULL | \
-					 GM_ADVERTISE_1000HALF)
-#define GM_ADVERTISE_FULL		(GM_ADVERTISE_1000FULL | \
-					 ADVERTISE_FULL)
-#define GM_ADVERTISE_ALL		(GM_ADVERTISE_1000FULL | \
-					 GM_ADVERTISE_1000HALF | \
-					 ADVERTISE_ALL)
-
-/* Logically extended link partner ability register */
-#define GM_LPA_SLCT			LPA_SLCT
-#define GM_LPA_10HALF			LPA_10HALF
-#define GM_LPA_1000XFULL		LPA_1000XFULL
-#define GM_LPA_10FULL			LPA_10FULL
-#define GM_LPA_1000XHALF		LPA_1000XHALF
-#define GM_LPA_100HALF			LPA_100HALF
-#define GM_LPA_1000XPAUSE		LPA_1000XPAUSE
-#define GM_LPA_100FULL			LPA_100FULL
-#define GM_LPA_1000XPAUSE_ASYM		LPA_1000XPAUSE_ASYM
-#define GM_LPA_100BASE4			LPA_100BASE4
-#define GM_LPA_PAUSE_CAP		LPA_PAUSE_CAP
-#define GM_LPA_PAUSE_ASYM		LPA_PAUSE_ASYM
-#define GM_LPA_RESV			LPA_RESV
-#define GM_LPA_RFAULT			LPA_RFAULT
-#define GM_LPA_LPACK			LPA_LPACK
-#define GM_LPA_NPAGE			LPA_NPAGE
-#define GM_LPA_1000FULL			(LPA_1000FULL << 6)
-#define GM_LPA_1000HALF			(LPA_1000HALF << 6)
-#define GM_LPA_10000FULL		0x00040000
-#define GM_LPA_10000HALF		0x00080000
-#define GM_LPA_DUPLEX			(GM_LPA_1000FULL | GM_LPA_10000FULL \
-					 | LPA_DUPLEX)
-#define GM_LPA_10			(LPA_10FULL | LPA_10HALF)
-#define GM_LPA_100			LPA_100
-#define GM_LPA_1000			(GM_LPA_1000FULL | GM_LPA_1000HALF)
-#define GM_LPA_10000			(GM_LPA_10000FULL | GM_LPA_10000HALF)
-
-/* Retrieve GMII autonegotiation advertised abilities
- *
- * The MII advertisment register (MII_ADVERTISE) is logically extended
- * to include advertisement bits ADVERTISE_1000FULL and
- * ADVERTISE_1000HALF from MII_CTRL1000.  The result can be tested
- * against the GM_ADVERTISE_xxx constants.
- */
-static inline unsigned int gmii_advertised(struct mii_if_info *gmii)
-{
-	unsigned int advertise;
-	unsigned int ctrl1000;
-
-	advertise = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_ADVERTISE);
-	ctrl1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_CTRL1000);
-	return (((ctrl1000 << 8) & GM_ADVERTISE_1000) | advertise);
-}
-
-/* Retrieve GMII autonegotiation link partner abilities
- *
- * The MII link partner ability register (MII_LPA) is logically
- * extended by adding bits LPA_1000HALF and LPA_1000FULL from
- * MII_STAT1000.  The result can be tested against the GM_LPA_xxx
- * constants.
- */
-static inline unsigned int gmii_lpa(struct mii_if_info *gmii)
-{
-	unsigned int lpa;
-	unsigned int stat1000;
-
-	lpa = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_LPA);
-	stat1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_STAT1000);
-	return (((stat1000 << 6) & GM_LPA_1000) | lpa);
-}
-
-/* Calculate GMII autonegotiated link technology
- *
- * "negotiated" should be the result of gmii_advertised() logically
- * ANDed with the result of gmii_lpa().
- *
- * "tech" will be negotiated with the unused bits masked out.  For
- * example, if both ends of the link are capable of both
- * GM_LPA_1000FULL and GM_LPA_100FULL, GM_LPA_100FULL will be masked
- * out.
- */
-static inline unsigned int gmii_nway_result(unsigned int negotiated)
-{
-	unsigned int other_bits;
-
-	/* Mask out the speed and duplexity bits */
-	other_bits = negotiated & ~(GM_LPA_10 | GM_LPA_100 | GM_LPA_1000);
-
-	if (negotiated & GM_LPA_1000FULL)
-		return (other_bits | GM_LPA_1000FULL);
-	else if (negotiated & GM_LPA_1000HALF)
-		return (other_bits | GM_LPA_1000HALF);
-	else
-		return (other_bits | mii_nway_result(negotiated));
-}
-
-/* Calculate GMII non-autonegotiated link technology
- *
- * This provides an equivalent to gmii_nway_result for the case when
- * autonegotiation is disabled.
- */
-static inline unsigned int gmii_forced_result(unsigned int bmcr)
-{
-	unsigned int result;
-	int full_duplex;
-
-	full_duplex = bmcr & BMCR_FULLDPLX;
-	if (bmcr & BMCR_SPEED1000)
-		result = full_duplex ? GM_LPA_1000FULL : GM_LPA_1000HALF;
-	else if (bmcr & BMCR_SPEED100)
-		result = full_duplex ? GM_LPA_100FULL : GM_LPA_100HALF;
-	else
-		result = full_duplex ? GM_LPA_10FULL : GM_LPA_10HALF;
-	return result;
-}
-
 #endif /* EFX_GMII_H */
diff --git a/drivers/net/sfc/mac.h b/drivers/net/sfc/mac.h
index a31571c..4e70742 100644
--- a/drivers/net/sfc/mac.h
+++ b/drivers/net/sfc/mac.h
@@ -1,7 +1,7 @@
 /****************************************************************************
  * Driver for Solarflare Solarstorm network controllers and boards
  * Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2007 Solarflare Communications Inc.
+ * Copyright 2006-2008 Solarflare Communications Inc.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published
@@ -13,17 +13,7 @@
 
 #include "net_driver.h"
 
-extern int falcon_init_xmac(struct efx_nic *efx);
-extern void falcon_reconfigure_xmac(struct efx_nic *efx);
-extern void falcon_update_stats_xmac(struct efx_nic *efx);
-extern void falcon_fini_xmac(struct efx_nic *efx);
-extern int falcon_check_xmac(struct efx_nic *efx);
-extern void falcon_xmac_sim_phy_event(struct efx_nic *efx);
-extern int falcon_xmac_get_settings(struct efx_nic *efx,
-				    struct ethtool_cmd *ecmd);
-extern int falcon_xmac_set_settings(struct efx_nic *efx,
-				    struct ethtool_cmd *ecmd);
-extern int falcon_xmac_set_pause(struct efx_nic *efx,
-				 enum efx_fc_type pause_params);
+extern struct efx_mac_operations falcon_gmac_operations;
+extern struct efx_mac_operations falcon_xmac_operations;
 
 #endif
diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c
index 19e2521..037601e 100644
--- a/drivers/net/sfc/mdio_10g.c
+++ b/drivers/net/sfc/mdio_10g.c
@@ -47,13 +47,16 @@
 	if (LOOPBACK_INTERNAL(efx))
 		return 0;
 
-	/* Read MMD STATUS2 to check it is responding. */
-	status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT2);
-	if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) &
-	     ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) !=
-	    MDIO_MMDREG_STAT2_PRESENT_VAL) {
-		EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd);
-		return -EIO;
+	if (mmd != MDIO_MMD_AN) {
+		/* Read MMD STATUS2 to check it is responding. */
+		status = mdio_clause45_read(efx, phy_id, mmd,
+					    MDIO_MMDREG_STAT2);
+		if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) &
+		     ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) !=
+		    MDIO_MMDREG_STAT2_PRESENT_VAL) {
+			EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd);
+			return -EIO;
+		}
 	}
 
 	/* Read MMD STATUS 1 to check for fault. */
@@ -121,16 +124,18 @@
 int mdio_clause45_check_mmds(struct efx_nic *efx,
 			     unsigned int mmd_mask, unsigned int fatal_mask)
 {
-	int devices, mmd = 0;
-	int probe_mmd;
+	u32 devices;
+	int mmd = 0, probe_mmd;
 
 	/* Historically we have probed the PHYXS to find out what devices are
 	 * present,but that doesn't work so well if the PHYXS isn't expected
 	 * to exist, if so just find the first item in the list supplied. */
-	probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS0_PHYXS) ? MDIO_MMD_PHYXS :
+	probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS_PHYXS) ? MDIO_MMD_PHYXS :
 	    __ffs(mmd_mask);
-	devices = mdio_clause45_read(efx, efx->mii.phy_id,
-				     probe_mmd, MDIO_MMDREG_DEVS0);
+	devices = (mdio_clause45_read(efx, efx->mii.phy_id,
+				      probe_mmd, MDIO_MMDREG_DEVS0) |
+		   mdio_clause45_read(efx, efx->mii.phy_id,
+				      probe_mmd, MDIO_MMDREG_DEVS1) << 16);
 
 	/* Check all the expected MMDs are present */
 	if (devices < 0) {
@@ -175,14 +180,17 @@
 	else if (efx_phy_mode_disabled(efx->phy_mode))
 		return false;
 	else if (efx->loopback_mode == LOOPBACK_PHYXS)
-		mmd_mask &= ~(MDIO_MMDREG_DEVS0_PHYXS |
-			      MDIO_MMDREG_DEVS0_PCS |
-			      MDIO_MMDREG_DEVS0_PMAPMD);
+		mmd_mask &= ~(MDIO_MMDREG_DEVS_PHYXS |
+			      MDIO_MMDREG_DEVS_PCS |
+			      MDIO_MMDREG_DEVS_PMAPMD |
+			      MDIO_MMDREG_DEVS_AN);
 	else if (efx->loopback_mode == LOOPBACK_PCS)
-		mmd_mask &= ~(MDIO_MMDREG_DEVS0_PCS |
-			      MDIO_MMDREG_DEVS0_PMAPMD);
+		mmd_mask &= ~(MDIO_MMDREG_DEVS_PCS |
+			      MDIO_MMDREG_DEVS_PMAPMD |
+			      MDIO_MMDREG_DEVS_AN);
 	else if (efx->loopback_mode == LOOPBACK_PMAPMD)
-		mmd_mask &= ~MDIO_MMDREG_DEVS0_PMAPMD;
+		mmd_mask &= ~(MDIO_MMDREG_DEVS_PMAPMD |
+			      MDIO_MMDREG_DEVS_AN);
 
 	while (mmd_mask) {
 		if (mmd_mask & 1) {
@@ -203,61 +211,24 @@
 
 void mdio_clause45_transmit_disable(struct efx_nic *efx)
 {
-	int phy_id = efx->mii.phy_id;
-	int ctrl1, ctrl2;
-
-	ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
-					   MDIO_MMDREG_TXDIS);
-	if (efx->phy_mode & PHY_MODE_TX_DISABLED)
-		ctrl2 |= (1 << MDIO_MMDREG_TXDIS_GLOBAL_LBN);
-	else
-		ctrl1 &= ~(1 << MDIO_MMDREG_TXDIS_GLOBAL_LBN);
-	if (ctrl1 != ctrl2)
-		mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
-				    MDIO_MMDREG_TXDIS, ctrl2);
+	mdio_clause45_set_flag(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
+			       MDIO_MMDREG_TXDIS, MDIO_MMDREG_TXDIS_GLOBAL_LBN,
+			       efx->phy_mode & PHY_MODE_TX_DISABLED);
 }
 
 void mdio_clause45_phy_reconfigure(struct efx_nic *efx)
 {
 	int phy_id = efx->mii.phy_id;
-	int ctrl1, ctrl2;
 
-	/* Handle (with debouncing) PMA/PMD loopback */
-	ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
-					   MDIO_MMDREG_CTRL1);
-
-	if (efx->loopback_mode == LOOPBACK_PMAPMD)
-		ctrl2 |= (1 << MDIO_PMAPMD_CTRL1_LBACK_LBN);
-	else
-		ctrl2 &= ~(1 << MDIO_PMAPMD_CTRL1_LBACK_LBN);
-
-	if (ctrl1 != ctrl2)
-		mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
-				    MDIO_MMDREG_CTRL1, ctrl2);
-
-	/* Handle (with debouncing) PCS loopback */
-	ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PCS,
-					   MDIO_MMDREG_CTRL1);
-	if (efx->loopback_mode == LOOPBACK_PCS)
-		ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
-	else
-		ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
-
-	if (ctrl1 != ctrl2)
-		mdio_clause45_write(efx, phy_id, MDIO_MMD_PCS,
-				    MDIO_MMDREG_CTRL1, ctrl2);
-
-	/* Handle (with debouncing) PHYXS network loopback */
-	ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS,
-					   MDIO_MMDREG_CTRL1);
-	if (efx->loopback_mode == LOOPBACK_NETWORK)
-		ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
-	else
-		ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
-
-	if (ctrl1 != ctrl2)
-		mdio_clause45_write(efx, phy_id, MDIO_MMD_PHYXS,
-				    MDIO_MMDREG_CTRL1, ctrl2);
+	mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PMAPMD,
+			       MDIO_MMDREG_CTRL1, MDIO_PMAPMD_CTRL1_LBACK_LBN,
+			       efx->loopback_mode == LOOPBACK_PMAPMD);
+	mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PCS,
+			       MDIO_MMDREG_CTRL1, MDIO_MMDREG_CTRL1_LBACK_LBN,
+			       efx->loopback_mode == LOOPBACK_PCS);
+	mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PHYXS,
+			       MDIO_MMDREG_CTRL1, MDIO_MMDREG_CTRL1_LBACK_LBN,
+			       efx->loopback_mode == LOOPBACK_NETWORK);
 }
 
 static void mdio_clause45_set_mmd_lpower(struct efx_nic *efx,
@@ -265,21 +236,13 @@
 {
 	int phy = efx->mii.phy_id;
 	int stat = mdio_clause45_read(efx, phy, mmd, MDIO_MMDREG_STAT1);
-	int ctrl1, ctrl2;
 
 	EFX_TRACE(efx, "Setting low power mode for MMD %d to %d\n",
 		  mmd, lpower);
 
 	if (stat & (1 << MDIO_MMDREG_STAT1_LPABLE_LBN)) {
-		ctrl1 = ctrl2 = mdio_clause45_read(efx, phy,
-						   mmd, MDIO_MMDREG_CTRL1);
-		if (lpower)
-			ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LPOWER_LBN);
-		else
-			ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LPOWER_LBN);
-		if (ctrl1 != ctrl2)
-			mdio_clause45_write(efx, phy, mmd,
-					    MDIO_MMDREG_CTRL1, ctrl2);
+		mdio_clause45_set_flag(efx, phy, mmd, MDIO_MMDREG_CTRL1,
+				       MDIO_MMDREG_CTRL1_LPOWER_LBN, lpower);
 	}
 }
 
@@ -287,6 +250,7 @@
 				   int low_power, unsigned int mmd_mask)
 {
 	int mmd = 0;
+	mmd_mask &= ~MDIO_MMDREG_DEVS_AN;
 	while (mmd_mask) {
 		if (mmd_mask & 1)
 			mdio_clause45_set_mmd_lpower(efx, low_power, mmd);
@@ -295,78 +259,141 @@
 	}
 }
 
+static u32 mdio_clause45_get_an(struct efx_nic *efx, u16 addr, u32 xnp)
+{
+	int phy_id = efx->mii.phy_id;
+	u32 result = 0;
+	int reg;
+
+	reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, addr);
+	if (reg & ADVERTISE_10HALF)
+		result |= ADVERTISED_10baseT_Half;
+	if (reg & ADVERTISE_10FULL)
+		result |= ADVERTISED_10baseT_Full;
+	if (reg & ADVERTISE_100HALF)
+		result |= ADVERTISED_100baseT_Half;
+	if (reg & ADVERTISE_100FULL)
+		result |= ADVERTISED_100baseT_Full;
+	if (reg & LPA_RESV)
+		result |= xnp;
+
+	return result;
+}
+
 /**
  * mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO.
  * @efx:		Efx NIC
  * @ecmd: 		Buffer for settings
  *
  * On return the 'port', 'speed', 'supported' and 'advertising' fields of
- * ecmd have been filled out based on the PMA type.
+ * ecmd have been filled out.
  */
 void mdio_clause45_get_settings(struct efx_nic *efx,
 				struct ethtool_cmd *ecmd)
 {
-	int pma_type;
+	mdio_clause45_get_settings_ext(efx, ecmd, 0, 0);
+}
 
-	/* If no PMA is present we are presumably talking something XAUI-ish
-	 * like CX4. Which we report as FIBRE (see below) */
-	if ((efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)) == 0) {
-		ecmd->speed = SPEED_10000;
+/**
+ * mdio_clause45_get_settings_ext - Read (some of) the PHY settings over MDIO.
+ * @efx:		Efx NIC
+ * @ecmd: 		Buffer for settings
+ * @xnp:		Advertised Extended Next Page state
+ * @xnp_lpa:		Link Partner's advertised XNP state
+ *
+ * On return the 'port', 'speed', 'supported' and 'advertising' fields of
+ * ecmd have been filled out.
+ */
+void mdio_clause45_get_settings_ext(struct efx_nic *efx,
+				    struct ethtool_cmd *ecmd,
+				    u32 xnp, u32 xnp_lpa)
+{
+	int phy_id = efx->mii.phy_id;
+	int reg;
+
+	ecmd->transceiver = XCVR_INTERNAL;
+	ecmd->phy_address = phy_id;
+
+	reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+				 MDIO_MMDREG_CTRL2);
+	switch (reg & MDIO_PMAPMD_CTRL2_TYPE_MASK) {
+	case MDIO_PMAPMD_CTRL2_10G_BT:
+	case MDIO_PMAPMD_CTRL2_1G_BT:
+	case MDIO_PMAPMD_CTRL2_100_BT:
+	case MDIO_PMAPMD_CTRL2_10_BT:
+		ecmd->port = PORT_TP;
+		ecmd->supported = SUPPORTED_TP;
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+					 MDIO_MMDREG_SPEED);
+		if (reg & (1 << MDIO_MMDREG_SPEED_10G_LBN))
+			ecmd->supported |= SUPPORTED_10000baseT_Full;
+		if (reg & (1 << MDIO_MMDREG_SPEED_1000M_LBN))
+			ecmd->supported |= (SUPPORTED_1000baseT_Full |
+					    SUPPORTED_1000baseT_Half);
+		if (reg & (1 << MDIO_MMDREG_SPEED_100M_LBN))
+			ecmd->supported |= (SUPPORTED_100baseT_Full |
+					    SUPPORTED_100baseT_Half);
+		if (reg & (1 << MDIO_MMDREG_SPEED_10M_LBN))
+			ecmd->supported |= (SUPPORTED_10baseT_Full |
+					    SUPPORTED_10baseT_Half);
+		ecmd->advertising = ADVERTISED_TP;
+		break;
+
+	/* We represent CX4 as fibre in the absence of anything better */
+	case MDIO_PMAPMD_CTRL2_10G_CX4:
+	/* All the other defined modes are flavours of optical */
+	default:
 		ecmd->port = PORT_FIBRE;
 		ecmd->supported = SUPPORTED_FIBRE;
 		ecmd->advertising = ADVERTISED_FIBRE;
-		return;
+		break;
 	}
 
-	pma_type = mdio_clause45_read(efx, efx->mii.phy_id,
-				      MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL2);
-	pma_type &= MDIO_PMAPMD_CTRL2_TYPE_MASK;
+	if (efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) {
+		ecmd->supported |= SUPPORTED_Autoneg;
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
+					 MDIO_MMDREG_CTRL1);
+		if (reg & BMCR_ANENABLE) {
+			ecmd->autoneg = AUTONEG_ENABLE;
+			ecmd->advertising |=
+				ADVERTISED_Autoneg |
+				mdio_clause45_get_an(efx,
+						     MDIO_AN_ADVERTISE, xnp);
+		} else
+			ecmd->autoneg = AUTONEG_DISABLE;
+	} else
+		ecmd->autoneg = AUTONEG_DISABLE;
 
-	switch (pma_type) {
-		/* We represent CX4 as fibre in the absence of anything
-		   better. */
-	case MDIO_PMAPMD_CTRL2_10G_CX4:
-		ecmd->speed = SPEED_10000;
-		ecmd->port = PORT_FIBRE;
-		ecmd->supported = SUPPORTED_FIBRE;
-		ecmd->advertising = ADVERTISED_FIBRE;
-		break;
-		/* 10G Base-T */
-	case MDIO_PMAPMD_CTRL2_10G_BT:
-		ecmd->speed = SPEED_10000;
-		ecmd->port = PORT_TP;
-		ecmd->supported = SUPPORTED_TP | SUPPORTED_10000baseT_Full;
-		ecmd->advertising = (ADVERTISED_FIBRE
-				     | ADVERTISED_10000baseT_Full);
-		break;
-	case MDIO_PMAPMD_CTRL2_1G_BT:
-		ecmd->speed = SPEED_1000;
-		ecmd->port = PORT_TP;
-		ecmd->supported = SUPPORTED_TP | SUPPORTED_1000baseT_Full;
-		ecmd->advertising = (ADVERTISED_FIBRE
-				     | ADVERTISED_1000baseT_Full);
-		break;
-	case MDIO_PMAPMD_CTRL2_100_BT:
-		ecmd->speed = SPEED_100;
-		ecmd->port = PORT_TP;
-		ecmd->supported = SUPPORTED_TP | SUPPORTED_100baseT_Full;
-		ecmd->advertising = (ADVERTISED_FIBRE
-				     | ADVERTISED_100baseT_Full);
-		break;
-	case MDIO_PMAPMD_CTRL2_10_BT:
-		ecmd->speed = SPEED_10;
-		ecmd->port = PORT_TP;
-		ecmd->supported = SUPPORTED_TP | SUPPORTED_10baseT_Full;
-		ecmd->advertising = ADVERTISED_FIBRE | ADVERTISED_10baseT_Full;
-		break;
-	/* All the other defined modes are flavours of
-	 * 10G optical */
-	default:
-		ecmd->speed = SPEED_10000;
-		ecmd->port = PORT_FIBRE;
-		ecmd->supported = SUPPORTED_FIBRE;
-		ecmd->advertising = ADVERTISED_FIBRE;
-		break;
+	/* If AN is enabled and complete, report best common mode */
+	if (ecmd->autoneg &&
+	    (mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, MDIO_MMDREG_STAT1) &
+	     (1 << MDIO_AN_STATUS_AN_DONE_LBN))) {
+		u32 common, lpa;
+		lpa = mdio_clause45_get_an(efx, MDIO_AN_LPA, xnp_lpa);
+		common = ecmd->advertising & lpa;
+		if (common & ADVERTISED_10000baseT_Full) {
+			ecmd->speed = SPEED_10000;
+			ecmd->duplex = DUPLEX_FULL;
+		} else if (common & (ADVERTISED_1000baseT_Full |
+				     ADVERTISED_1000baseT_Half)) {
+			ecmd->speed = SPEED_1000;
+			ecmd->duplex = !!(common & ADVERTISED_1000baseT_Full);
+		} else if (common & (ADVERTISED_100baseT_Full |
+				     ADVERTISED_100baseT_Half)) {
+			ecmd->speed = SPEED_100;
+			ecmd->duplex = !!(common & ADVERTISED_100baseT_Full);
+		} else {
+			ecmd->speed = SPEED_10;
+			ecmd->duplex = !!(common & ADVERTISED_10baseT_Full);
+		}
+	} else {
+		/* Report forced settings */
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+					 MDIO_MMDREG_CTRL1);
+		ecmd->speed = (((reg & BMCR_SPEED1000) ? 100 : 1) *
+			       ((reg & BMCR_SPEED100) ? 100 : 10));
+		ecmd->duplex = (reg & BMCR_FULLDPLX ||
+				ecmd->speed == SPEED_10000);
 	}
 }
 
@@ -374,22 +401,172 @@
  * mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO.
  * @efx:		Efx NIC
  * @ecmd: 		New settings
- *
- * Currently this just enforces that we are _not_ changing the
- * 'port', 'speed', 'supported' or 'advertising' settings as these
- * cannot be changed on any currently supported PHY.
  */
 int mdio_clause45_set_settings(struct efx_nic *efx,
 			       struct ethtool_cmd *ecmd)
 {
-	struct ethtool_cmd tmpcmd;
-	mdio_clause45_get_settings(efx, &tmpcmd);
-	/* None of the current PHYs support more than one mode
-	 * of operation (and only 10GBT ever will), so keep things
-	 * simple for now */
-	if ((ecmd->speed == tmpcmd.speed) && (ecmd->port == tmpcmd.port) &&
-	    (ecmd->supported == tmpcmd.supported) &&
-	    (ecmd->advertising == tmpcmd.advertising))
+	int phy_id = efx->mii.phy_id;
+	struct ethtool_cmd prev;
+	u32 required;
+	int ctrl1_bits, reg;
+
+	efx->phy_op->get_settings(efx, &prev);
+
+	if (ecmd->advertising == prev.advertising &&
+	    ecmd->speed == prev.speed &&
+	    ecmd->duplex == prev.duplex &&
+	    ecmd->port == prev.port &&
+	    ecmd->autoneg == prev.autoneg)
 		return 0;
-	return -EOPNOTSUPP;
+
+	/* We can only change these settings for -T PHYs */
+	if (prev.port != PORT_TP || ecmd->port != PORT_TP)
+		return -EINVAL;
+
+	/* Check that PHY supports these settings and work out the
+	 * basic control bits */
+	if (ecmd->duplex) {
+		switch (ecmd->speed) {
+		case SPEED_10:
+			ctrl1_bits = BMCR_FULLDPLX;
+			required = SUPPORTED_10baseT_Full;
+			break;
+		case SPEED_100:
+			ctrl1_bits = BMCR_SPEED100 | BMCR_FULLDPLX;
+			required = SUPPORTED_100baseT_Full;
+			break;
+		case SPEED_1000:
+			ctrl1_bits = BMCR_SPEED1000 | BMCR_FULLDPLX;
+			required = SUPPORTED_1000baseT_Full;
+			break;
+		case SPEED_10000:
+			ctrl1_bits = (BMCR_SPEED1000 | BMCR_SPEED100 |
+				      BMCR_FULLDPLX);
+			required = SUPPORTED_10000baseT_Full;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else {
+		switch (ecmd->speed) {
+		case SPEED_10:
+			ctrl1_bits = 0;
+			required = SUPPORTED_10baseT_Half;
+			break;
+		case SPEED_100:
+			ctrl1_bits = BMCR_SPEED100;
+			required = SUPPORTED_100baseT_Half;
+			break;
+		case SPEED_1000:
+			ctrl1_bits = BMCR_SPEED1000;
+			required = SUPPORTED_1000baseT_Half;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	if (ecmd->autoneg)
+		required |= SUPPORTED_Autoneg;
+	required |= ecmd->advertising;
+	if (required & ~prev.supported)
+		return -EINVAL;
+
+	/* Set the basic control bits */
+	reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+				 MDIO_MMDREG_CTRL1);
+	reg &= ~(BMCR_SPEED1000 | BMCR_SPEED100 | BMCR_FULLDPLX | 0x003c);
+	reg |= ctrl1_bits;
+	mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL1,
+			    reg);
+
+	/* Set the AN registers */
+	if (ecmd->autoneg != prev.autoneg ||
+	    ecmd->advertising != prev.advertising) {
+		bool xnp = false;
+
+		if (efx->phy_op->set_xnp_advertise)
+			xnp = efx->phy_op->set_xnp_advertise(efx,
+							     ecmd->advertising);
+
+		if (ecmd->autoneg) {
+			reg = 0;
+			if (ecmd->advertising & ADVERTISED_10baseT_Half)
+				reg |= ADVERTISE_10HALF;
+			if (ecmd->advertising & ADVERTISED_10baseT_Full)
+				reg |= ADVERTISE_10FULL;
+			if (ecmd->advertising & ADVERTISED_100baseT_Half)
+				reg |= ADVERTISE_100HALF;
+			if (ecmd->advertising & ADVERTISED_100baseT_Full)
+				reg |= ADVERTISE_100FULL;
+			if (xnp)
+				reg |= ADVERTISE_RESV;
+			mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
+					    MDIO_AN_ADVERTISE, reg);
+		}
+
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
+					 MDIO_MMDREG_CTRL1);
+		if (ecmd->autoneg)
+			reg |= BMCR_ANENABLE | BMCR_ANRESTART;
+		else
+			reg &= ~BMCR_ANENABLE;
+		if (xnp)
+			reg |= 1 << MDIO_AN_CTRL_XNP_LBN;
+		else
+			reg &= ~(1 << MDIO_AN_CTRL_XNP_LBN);
+		mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
+				    MDIO_MMDREG_CTRL1, reg);
+	}
+
+	return 0;
+}
+
+void mdio_clause45_set_pause(struct efx_nic *efx)
+{
+	int phy_id = efx->mii.phy_id;
+	int reg;
+
+	if (efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) {
+		/* Set pause capability advertising */
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
+					 MDIO_AN_ADVERTISE);
+		reg &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+		reg |= efx_fc_advertise(efx->wanted_fc);
+		mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
+				    MDIO_AN_ADVERTISE, reg);
+
+		/* Restart auto-negotiation */
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
+					 MDIO_MMDREG_CTRL1);
+		if (reg & BMCR_ANENABLE) {
+			reg |= BMCR_ANRESTART;
+			mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
+					    MDIO_MMDREG_CTRL1, reg);
+		}
+	}
+}
+
+enum efx_fc_type mdio_clause45_get_pause(struct efx_nic *efx)
+{
+	int phy_id = efx->mii.phy_id;
+	int lpa;
+
+	if (!(efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)))
+		return efx->wanted_fc;
+	lpa = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, MDIO_AN_LPA);
+	return efx_fc_resolve(efx->wanted_fc, lpa);
+}
+
+void mdio_clause45_set_flag(struct efx_nic *efx, u8 prt, u8 dev,
+			    u16 addr, int bit, bool sense)
+{
+	int old_val = mdio_clause45_read(efx, prt, dev, addr);
+	int new_val;
+
+	if (sense)
+		new_val = old_val | (1 << bit);
+	else
+		new_val = old_val & ~(1 << bit);
+	if (old_val != new_val)
+		mdio_clause45_write(efx, prt, dev, addr, new_val);
 }
diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/sfc/mdio_10g.h
index db9f358..4091182 100644
--- a/drivers/net/sfc/mdio_10g.h
+++ b/drivers/net/sfc/mdio_10g.h
@@ -33,6 +33,8 @@
 #define MDIO_MMD_TC	(6)
 /* Auto negotiation */
 #define MDIO_MMD_AN	(7)
+/* Clause 22 extension */
+#define MDIO_MMD_C22EXT	29
 
 /* Generic register locations */
 #define MDIO_MMDREG_CTRL1	(0)
@@ -73,14 +75,26 @@
 #define MDIO_ID_MODEL(_id32)	((_id32 >> 4) & 0x3f)
 #define MDIO_ID_OUI(_id32)	(_id32 >> 10)
 
-/* Bits in MMDREG_DEVS0. Someone thoughtfully layed things out
+/* Bits in MMDREG_DEVS0/1. Someone thoughtfully layed things out
  * so the 'bit present' bit number of an MMD is the number of
  * that MMD */
 #define DEV_PRESENT_BIT(_b) (1 << _b)
 
-#define MDIO_MMDREG_DEVS0_PHYXS	 DEV_PRESENT_BIT(MDIO_MMD_PHYXS)
-#define MDIO_MMDREG_DEVS0_PCS	 DEV_PRESENT_BIT(MDIO_MMD_PCS)
-#define MDIO_MMDREG_DEVS0_PMAPMD DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)
+#define MDIO_MMDREG_DEVS_PHYXS	DEV_PRESENT_BIT(MDIO_MMD_PHYXS)
+#define MDIO_MMDREG_DEVS_PCS	DEV_PRESENT_BIT(MDIO_MMD_PCS)
+#define MDIO_MMDREG_DEVS_PMAPMD	DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)
+#define MDIO_MMDREG_DEVS_AN	DEV_PRESENT_BIT(MDIO_MMD_AN)
+#define MDIO_MMDREG_DEVS_C22EXT	DEV_PRESENT_BIT(MDIO_MMD_C22EXT)
+
+/* Bits in MMDREG_SPEED */
+#define MDIO_MMDREG_SPEED_10G_LBN	0
+#define MDIO_MMDREG_SPEED_10G_WIDTH	1
+#define MDIO_MMDREG_SPEED_1000M_LBN	4
+#define MDIO_MMDREG_SPEED_1000M_WIDTH	1
+#define MDIO_MMDREG_SPEED_100M_LBN	5
+#define MDIO_MMDREG_SPEED_100M_WIDTH	1
+#define MDIO_MMDREG_SPEED_10M_LBN	6
+#define MDIO_MMDREG_SPEED_10M_WIDTH	1
 
 /* Bits in MMDREG_STAT2 */
 #define MDIO_MMDREG_STAT2_PRESENT_VAL	(2)
@@ -114,17 +128,30 @@
 #define MDIO_PMAPMD_CTRL2_10_BT		(0xf)
 #define MDIO_PMAPMD_CTRL2_TYPE_MASK	(0xf)
 
+/* PMA 10GBT registers */
+#define MDIO_PMAPMD_10GBT_TXPWR		(131)
+#define MDIO_PMAPMD_10GBT_TXPWR_SHORT_LBN (0)
+#define MDIO_PMAPMD_10GBT_TXPWR_SHORT_WIDTH (1)
+
 /* PHY XGXS lane state */
 #define MDIO_PHYXS_LANE_STATE		(0x18)
 #define MDIO_PHYXS_LANE_ALIGNED_LBN	(12)
 
 /* AN registers */
+#define MDIO_AN_CTRL_XNP_LBN		13
 #define MDIO_AN_STATUS			(1)
 #define MDIO_AN_STATUS_XNP_LBN		(7)
 #define MDIO_AN_STATUS_PAGE_LBN		(6)
 #define MDIO_AN_STATUS_AN_DONE_LBN	(5)
 #define MDIO_AN_STATUS_LP_AN_CAP_LBN	(0)
 
+#define MDIO_AN_ADVERTISE		16
+#define MDIO_AN_ADVERTISE_XNP_LBN	12
+#define MDIO_AN_LPA			19
+#define MDIO_AN_XNP			22
+#define MDIO_AN_LPA_XNP			25
+
+#define MDIO_AN_10GBT_ADVERTISE		32
 #define MDIO_AN_10GBT_STATUS		(33)
 #define MDIO_AN_10GBT_STATUS_MS_FLT_LBN (15) /* MASTER/SLAVE config fault */
 #define MDIO_AN_10GBT_STATUS_MS_LBN     (14) /* MASTER/SLAVE config */
@@ -251,12 +278,29 @@
 extern void mdio_clause45_get_settings(struct efx_nic *efx,
 				       struct ethtool_cmd *ecmd);
 
+/* Read (some of) the PHY settings over MDIO */
+extern void
+mdio_clause45_get_settings_ext(struct efx_nic *efx, struct ethtool_cmd *ecmd,
+			       u32 xnp, u32 xnp_lpa);
+
 /* Set (some of) the PHY settings over MDIO */
 extern int mdio_clause45_set_settings(struct efx_nic *efx,
 				      struct ethtool_cmd *ecmd);
 
+/* Set pause parameters to be advertised through AN (if available) */
+extern void mdio_clause45_set_pause(struct efx_nic *efx);
+
+/* Get pause parameters from AN if available (otherwise return
+ * requested pause parameters)
+ */
+enum efx_fc_type mdio_clause45_get_pause(struct efx_nic *efx);
+
 /* Wait for specified MMDs to exit reset within a timeout */
 extern int mdio_clause45_wait_reset_mmds(struct efx_nic *efx,
 					 unsigned int mmd_mask);
 
+/* Set or clear flag, debouncing */
+extern void mdio_clause45_set_flag(struct efx_nic *efx, u8 prt, u8 dev,
+				   u16 addr, int bit, bool sense);
+
 #endif /* EFX_MDIO_10G_H */
diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c
index a1e6c28..665cafb 100644
--- a/drivers/net/sfc/mtd.c
+++ b/drivers/net/sfc/mtd.c
@@ -76,7 +76,7 @@
 	rc = falcon_spi_cmd(spi, SPI_WRSR, -1, &status, NULL, sizeof(status));
 	if (rc)
 		return rc;
-	rc = falcon_spi_fast_wait(spi);
+	rc = falcon_spi_wait_write(spi);
 	if (rc)
 		return rc;
 
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h
index e596c9a..03feaee 100644
--- a/drivers/net/sfc/net_driver.h
+++ b/drivers/net/sfc/net_driver.h
@@ -42,7 +42,7 @@
 #ifndef EFX_DRIVER_NAME
 #define EFX_DRIVER_NAME	"sfc"
 #endif
-#define EFX_DRIVER_VERSION	"2.2"
+#define EFX_DRIVER_VERSION	"2.3"
 
 #ifdef EFX_ENABLE_DEBUG
 #define EFX_BUG_ON_PARANOID(x) BUG_ON(x)
@@ -327,6 +327,7 @@
  *
  * @efx: Associated Efx NIC
  * @channel: Channel instance number
+ * @name: Name for channel and IRQ
  * @used_flags: Channel is used by net driver
  * @enabled: Channel enabled indicator
  * @irq: IRQ number (MSI and MSI-X only)
@@ -357,6 +358,7 @@
 struct efx_channel {
 	struct efx_nic *efx;
 	int channel;
+	char name[IFNAMSIZ + 6];
 	int used_flags;
 	bool enabled;
 	int irq;
@@ -451,16 +453,20 @@
 
 enum phy_type {
 	PHY_TYPE_NONE = 0,
-	PHY_TYPE_CX4_RTMR = 1,
-	PHY_TYPE_1G_ALASKA = 2,
-	PHY_TYPE_10XPRESS = 3,
-	PHY_TYPE_XFP = 4,
+	PHY_TYPE_TXC43128 = 1,
+	PHY_TYPE_88E1111 = 2,
+	PHY_TYPE_SFX7101 = 3,
+	PHY_TYPE_QT2022C2 = 4,
 	PHY_TYPE_PM8358 = 6,
+	PHY_TYPE_SFT9001A = 8,
+	PHY_TYPE_SFT9001B = 10,
 	PHY_TYPE_MAX	/* Insert any new items before this */
 };
 
 #define PHY_ADDR_INVALID 0xff
 
+#define EFX_IS10G(efx) ((efx)->link_speed == 10000)
+
 enum nic_state {
 	STATE_INIT = 0,
 	STATE_RUNNING = 1,
@@ -501,6 +507,55 @@
 	EFX_FC_AUTO = 4,
 };
 
+/* Supported MAC bit-mask */
+enum efx_mac_type {
+	EFX_GMAC = 1,
+	EFX_XMAC = 2,
+};
+
+static inline unsigned int efx_fc_advertise(enum efx_fc_type wanted_fc)
+{
+	unsigned int adv = 0;
+	if (wanted_fc & EFX_FC_RX)
+		adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+	if (wanted_fc & EFX_FC_TX)
+		adv ^= ADVERTISE_PAUSE_ASYM;
+	return adv;
+}
+
+static inline enum efx_fc_type efx_fc_resolve(enum efx_fc_type wanted_fc,
+					      unsigned int lpa)
+{
+	unsigned int adv = efx_fc_advertise(wanted_fc);
+
+	if (!(wanted_fc & EFX_FC_AUTO))
+		return wanted_fc;
+
+	if (adv & lpa & ADVERTISE_PAUSE_CAP)
+		return EFX_FC_RX | EFX_FC_TX;
+	if (adv & lpa & ADVERTISE_PAUSE_ASYM) {
+		if (adv & ADVERTISE_PAUSE_CAP)
+			return EFX_FC_RX;
+		if (lpa & ADVERTISE_PAUSE_CAP)
+			return EFX_FC_TX;
+	}
+	return 0;
+}
+
+/**
+ * struct efx_mac_operations - Efx MAC operations table
+ * @reconfigure: Reconfigure MAC. Serialised by the mac_lock
+ * @update_stats: Update statistics
+ * @irq: Hardware MAC event callback. Serialised by the mac_lock
+ * @poll: Poll for hardware state. Serialised by the mac_lock
+ */
+struct efx_mac_operations {
+	void (*reconfigure) (struct efx_nic *efx);
+	void (*update_stats) (struct efx_nic *efx);
+	void (*irq) (struct efx_nic *efx);
+	void (*poll) (struct efx_nic *efx);
+};
+
 /**
  * struct efx_phy_operations - Efx PHY operations table
  * @init: Initialise PHY
@@ -508,17 +563,27 @@
  * @reconfigure: Reconfigure PHY (e.g. for new link parameters)
  * @clear_interrupt: Clear down interrupt
  * @blink: Blink LEDs
- * @check_hw: Check hardware
+ * @poll: Poll for hardware state. Serialised by the mac_lock.
+ * @get_settings: Get ethtool settings. Serialised by the mac_lock.
+ * @set_settings: Set ethtool settings. Serialised by the mac_lock.
+ * @set_xnp_advertise: Set abilities advertised in Extended Next Page
+ *	(only needed where AN bit is set in mmds)
  * @mmds: MMD presence mask
  * @loopbacks: Supported loopback modes mask
  */
 struct efx_phy_operations {
+	enum efx_mac_type macs;
 	int (*init) (struct efx_nic *efx);
 	void (*fini) (struct efx_nic *efx);
 	void (*reconfigure) (struct efx_nic *efx);
 	void (*clear_interrupt) (struct efx_nic *efx);
-	int (*check_hw) (struct efx_nic *efx);
+	void (*poll) (struct efx_nic *efx);
 	int (*test) (struct efx_nic *efx);
+	void (*get_settings) (struct efx_nic *efx,
+			      struct ethtool_cmd *ecmd);
+	int (*set_settings) (struct efx_nic *efx,
+			     struct ethtool_cmd *ecmd);
+	bool (*set_xnp_advertise) (struct efx_nic *efx, u32);
 	int mmds;
 	unsigned loopbacks;
 };
@@ -635,7 +700,6 @@
  * @legacy_irq: IRQ number
  * @workqueue: Workqueue for port reconfigures and the HW monitor.
  *	Work items do not hold and must not acquire RTNL.
- * @reset_workqueue: Workqueue for resets.  Work item will acquire RTNL.
  * @reset_work: Scheduled reset workitem
  * @monitor_work: Hardware monitor workitem
  * @membase_phys: Memory BAR value as physical address
@@ -650,6 +714,7 @@
  * @rx_queue: RX DMA queues
  * @channel: Channels
  * @n_rx_queues: Number of RX queues
+ * @n_channels: Number of channels in use
  * @rx_buffer_len: RX buffer length
  * @rx_buffer_order: Order (log2) of number of pages for each RX buffer
  * @irq_status: Interrupt status buffer
@@ -667,10 +732,10 @@
  * @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode,
  *	@port_inhibited, efx_monitor() and efx_reconfigure_port()
  * @port_enabled: Port enabled indicator.
- *	Serialises efx_stop_all(), efx_start_all() and efx_monitor() and
- *	efx_reconfigure_work with kernel interfaces. Safe to read under any
- *	one of the rtnl_lock, mac_lock, or netif_tx_lock, but all three must
- *	be held to modify it.
+ *	Serialises efx_stop_all(), efx_start_all(), efx_monitor(),
+ *	efx_phy_work(), and efx_mac_work() with kernel interfaces. Safe to read
+ *	under any one of the rtnl_lock, mac_lock, or netif_tx_lock, but all
+ *	three must be held to modify it.
  * @port_inhibited: If set, the netif_carrier is always off. Hold the mac_lock
  * @port_initialized: Port initialized?
  * @net_dev: Operating system network device. Consider holding the rtnl lock
@@ -684,6 +749,7 @@
  * @stats_lock: Statistics update lock. Serialises statistics fetches
  * @stats_enabled: Temporarily disable statistics fetches.
  *	Serialised by @stats_lock
+ * @mac_op: MAC interface
  * @mac_address: Permanent MAC address
  * @phy_type: PHY type
  * @phy_lock: PHY access lock
@@ -691,13 +757,17 @@
  * @phy_data: PHY private data (including PHY-specific stats)
  * @mii: PHY interface
  * @phy_mode: PHY operating mode. Serialised by @mac_lock.
+ * @mac_up: MAC link state
  * @link_up: Link status
- * @link_options: Link options (MII/GMII format)
+ * @link_fd: Link is full duplex
+ * @link_fc: Actualy flow control flags
+ * @link_speed: Link speed (Mbps)
  * @n_link_state_changes: Number of times the link has changed state
  * @promiscuous: Promiscuous flag. Protected by netif_tx_lock.
  * @multicast_hash: Multicast hash table
- * @flow_control: Flow control flags - separate RX/TX so can't use link_options
- * @reconfigure_work: work item for dealing with PHY events
+ * @wanted_fc: Wanted flow control flags
+ * @phy_work: work item for dealing with PHY events
+ * @mac_work: work item for dealing with MAC events
  * @loopback_mode: Loopback status
  * @loopback_modes: Supported loopback mode bitmask
  * @loopback_selftest: Offline self-test private state
@@ -711,7 +781,6 @@
 	const struct efx_nic_type *type;
 	int legacy_irq;
 	struct workqueue_struct *workqueue;
-	struct workqueue_struct *reset_workqueue;
 	struct work_struct reset_work;
 	struct delayed_work monitor_work;
 	resource_size_t membase_phys;
@@ -730,6 +799,7 @@
 	struct efx_channel channel[EFX_MAX_CHANNELS];
 
 	int n_rx_queues;
+	int n_channels;
 	unsigned int rx_buffer_len;
 	unsigned int rx_buffer_order;
 
@@ -745,6 +815,7 @@
 	struct falcon_nic_data *nic_data;
 
 	struct mutex mac_lock;
+	struct work_struct mac_work;
 	bool port_enabled;
 	bool port_inhibited;
 
@@ -760,23 +831,27 @@
 	spinlock_t stats_lock;
 	bool stats_enabled;
 
+	struct efx_mac_operations *mac_op;
 	unsigned char mac_address[ETH_ALEN];
 
 	enum phy_type phy_type;
 	spinlock_t phy_lock;
+	struct work_struct phy_work;
 	struct efx_phy_operations *phy_op;
 	void *phy_data;
 	struct mii_if_info mii;
 	enum efx_phy_mode phy_mode;
 
+	bool mac_up;
 	bool link_up;
-	unsigned int link_options;
+	bool link_fd;
+	enum efx_fc_type link_fc;
+	unsigned int link_speed;
 	unsigned int n_link_state_changes;
 
 	bool promiscuous;
 	union efx_multicast_hash multicast_hash;
-	enum efx_fc_type flow_control;
-	struct work_struct reconfigure_work;
+	enum efx_fc_type wanted_fc;
 
 	atomic_t rx_reset;
 	enum efx_loopback_mode loopback_mode;
diff --git a/drivers/net/sfc/phy.h b/drivers/net/sfc/phy.h
index f746536..58c493e 100644
--- a/drivers/net/sfc/phy.h
+++ b/drivers/net/sfc/phy.h
@@ -1,6 +1,6 @@
 /****************************************************************************
  * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007 Solarflare Communications Inc.
+ * Copyright 2007-2008 Solarflare Communications Inc.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published
@@ -11,9 +11,10 @@
 #define EFX_PHY_H
 
 /****************************************************************************
- * 10Xpress (SFX7101) PHY
+ * 10Xpress (SFX7101 and SFT9001) PHYs
  */
-extern struct efx_phy_operations falcon_tenxpress_phy_ops;
+extern struct efx_phy_operations falcon_sfx7101_phy_ops;
+extern struct efx_phy_operations falcon_sft9001_phy_ops;
 
 extern void tenxpress_phy_blink(struct efx_nic *efx, bool blink);
 extern void tenxpress_crc_err(struct efx_nic *efx);
diff --git a/drivers/net/sfc/selftest.c b/drivers/net/sfc/selftest.c
index 362956e..6bb09f2 100644
--- a/drivers/net/sfc/selftest.c
+++ b/drivers/net/sfc/selftest.c
@@ -26,7 +26,6 @@
 #include "selftest.h"
 #include "boards.h"
 #include "workarounds.h"
-#include "mac.h"
 #include "spi.h"
 #include "falcon_io.h"
 #include "mdio_10g.h"
@@ -105,9 +104,11 @@
 		goto out;
 	}
 
-	rc = mdio_clause45_check_mmds(efx, efx->phy_op->mmds, 0);
-	if (rc)
-		goto out;
+	if (EFX_IS10G(efx)) {
+		rc = mdio_clause45_check_mmds(efx, efx->phy_op->mmds, 0);
+		if (rc)
+			goto out;
+	}
 
 out:
 	mutex_unlock(&efx->mac_lock);
@@ -593,12 +594,14 @@
 		efx->loopback_mode = mode;
 		efx_reconfigure_port(efx);
 
-		/* Wait for the PHY to signal the link is up */
+		/* Wait for the PHY to signal the link is up. Interrupts
+		 * are enabled for PHY's using LASI, otherwise we poll()
+		 * quickly */
 		count = 0;
 		do {
 			struct efx_channel *channel = &efx->channel[0];
 
-			falcon_check_xmac(efx);
+			efx->phy_op->poll(efx);
 			schedule_timeout_uninterruptible(HZ / 10);
 			if (channel->work_pending)
 				efx_process_channel_now(channel);
@@ -606,13 +609,12 @@
 			flush_workqueue(efx->workqueue);
 			rmb();
 
-			/* efx->link_up can be 1 even if the XAUI link is down,
-			 * (bug5762). Usually, it's not worth bothering with the
-			 * difference, but for selftests, we need that extra
-			 * guarantee that the link is really, really, up.
-			 */
+			/* We need both the phy and xaui links to be ok.
+			 * rather than relying on the falcon_xmac irq/poll
+			 * regime, just poll xaui directly */
 			link_up = efx->link_up;
-			if (!falcon_xaui_link_ok(efx))
+			if (link_up && EFX_IS10G(efx) &&
+			    !falcon_xaui_link_ok(efx))
 				link_up = false;
 
 		} while ((++count < 20) && !link_up);
@@ -700,8 +702,15 @@
 	 */
 	mutex_lock(&efx->mac_lock);
 	efx->port_inhibited = true;
-	if (efx->loopback_modes)
-		efx->loopback_mode = __ffs(efx->loopback_modes);
+	if (efx->loopback_modes) {
+		/* We need the 312 clock from the PHY to test the XMAC
+		 * registers, so move into XGMII loopback if available */
+		if (efx->loopback_modes & (1 << LOOPBACK_XGMII))
+			efx->loopback_mode = LOOPBACK_XGMII;
+		else
+			efx->loopback_mode = __ffs(efx->loopback_modes);
+	}
+
 	__efx_reconfigure_port(efx);
 	mutex_unlock(&efx->mac_lock);
 
@@ -721,7 +730,6 @@
 	if (ecmd_test.autoneg == AUTONEG_ENABLE) {
 		ecmd_test.autoneg = AUTONEG_DISABLE;
 		ecmd_test.duplex = DUPLEX_FULL;
-		ecmd_test.speed = SPEED_10000;
 	}
 	efx->loopback_mode = LOOPBACK_NONE;
 
@@ -732,9 +740,6 @@
 		return rc;
 	}
 
-	tests->loopback_speed = ecmd_test.speed;
-	tests->loopback_full_duplex = ecmd_test.duplex;
-
 	rc = efx_test_phy(efx, tests);
 	if (rc && !rc2)
 		rc2 = rc;
diff --git a/drivers/net/sfc/selftest.h b/drivers/net/sfc/selftest.h
index fc15df1..252f7d7 100644
--- a/drivers/net/sfc/selftest.h
+++ b/drivers/net/sfc/selftest.h
@@ -39,8 +39,6 @@
 	/* offline tests */
 	int registers;
 	int phy;
-	int loopback_speed;
-	int loopback_full_duplex;
 	struct efx_loopback_self_tests loopback[LOOPBACK_TEST_MAX + 1];
 };
 
diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c
index aa576c5..16b80ac 100644
--- a/drivers/net/sfc/sfe4001.c
+++ b/drivers/net/sfc/sfe4001.c
@@ -1,6 +1,6 @@
 /****************************************************************************
  * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007 Solarflare Communications Inc.
+ * Copyright 2007-2008 Solarflare Communications Inc.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published
@@ -8,10 +8,21 @@
  */
 
 /*****************************************************************************
- * Support for the SFE4001 NIC: driver code for the PCA9539 I/O expander that
- * controls the PHY power rails, and for the MAX6647 temp. sensor used to check
- * the PHY
+ * Support for the SFE4001 and SFN4111T NICs.
+ *
+ * The SFE4001 does not power-up fully at reset due to its high power
+ * consumption.  We control its power via a PCA9539 I/O expander.
+ * Both boards have a MAX6647 temperature monitor which we expose to
+ * the lm90 driver.
+ *
+ * This also provides minimal support for reflashing the PHY, which is
+ * initiated by resetting it with the FLASH_CFG_1 pin pulled down.
+ * On SFE4001 rev A2 and later this is connected to the 3V3X output of
+ * the IO-expander; on the SFN4111T it is connected to Falcon's GPIO3.
+ * We represent reflash mode as PHY_MODE_SPECIAL and make it mutually
+ * exclusive with the network device being open.
  */
+
 #include <linux/delay.h>
 #include "net_driver.h"
 #include "efx.h"
@@ -171,39 +182,30 @@
 	return rc;
 }
 
-static int sfe4001_check_hw(struct efx_nic *efx)
+static int sfn4111t_reset(struct efx_nic *efx)
 {
-	s32 status;
+	efx_oword_t reg;
 
-	/* If XAUI link is up then do not monitor */
-	if (EFX_WORKAROUND_7884(efx) && falcon_xaui_link_ok(efx))
-		return 0;
+	/* GPIO pins are also used for I2C, so block that temporarily */
+	mutex_lock(&efx->i2c_adap.bus_lock);
 
-	/* Check the powered status of the PHY. Lack of power implies that
-	 * the MAX6647 has shut down power to it, probably due to a temp.
-	 * alarm. Reading the power status rather than the MAX6647 status
-	 * directly because the later is read-to-clear and would thus
-	 * start to power up the PHY again when polled, causing us to blip
-	 * the power undesirably.
-	 * We know we can read from the IO expander because we did
-	 * it during power-on. Assume failure now is bad news. */
-	status = i2c_smbus_read_byte_data(efx->board_info.ioexp_client, P1_IN);
-	if (status >= 0 &&
-	    (status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0)
-		return 0;
+	falcon_read(efx, &reg, GPIO_CTL_REG_KER);
+	EFX_SET_OWORD_FIELD(reg, GPIO2_OEN, true);
+	EFX_SET_OWORD_FIELD(reg, GPIO2_OUT, false);
+	falcon_write(efx, &reg, GPIO_CTL_REG_KER);
+	msleep(1000);
+	EFX_SET_OWORD_FIELD(reg, GPIO2_OUT, true);
+	EFX_SET_OWORD_FIELD(reg, GPIO3_OEN, true);
+	EFX_SET_OWORD_FIELD(reg, GPIO3_OUT,
+			    !(efx->phy_mode & PHY_MODE_SPECIAL));
+	falcon_write(efx, &reg, GPIO_CTL_REG_KER);
 
-	/* Use board power control, not PHY power control */
-	sfe4001_poweroff(efx);
-	efx->phy_mode = PHY_MODE_OFF;
+	mutex_unlock(&efx->i2c_adap.bus_lock);
 
-	return (status < 0) ? -EIO : -ERANGE;
+	ssleep(1);
+	return 0;
 }
 
-/* On SFE4001 rev A2 and later, we can control the FLASH_CFG_1 pin
- * using the 3V3X output of the IO-expander.  Allow the user to set
- * this when the device is stopped, and keep it stopped then.
- */
-
 static ssize_t show_phy_flash_cfg(struct device *dev,
 				  struct device_attribute *attr, char *buf)
 {
@@ -231,7 +233,10 @@
 		err = -EBUSY;
 	} else {
 		efx->phy_mode = new_mode;
-		err = sfe4001_poweron(efx);
+		if (efx->board_info.type == EFX_BOARD_SFE4001)
+			err = sfe4001_poweron(efx);
+		else
+			err = sfn4111t_reset(efx);
 		efx_reconfigure_port(efx);
 	}
 	rtnl_unlock();
@@ -251,6 +256,34 @@
 	i2c_unregister_device(efx->board_info.hwmon_client);
 }
 
+static int sfe4001_check_hw(struct efx_nic *efx)
+{
+	s32 status;
+
+	/* If XAUI link is up then do not monitor */
+	if (EFX_WORKAROUND_7884(efx) && efx->mac_up)
+		return 0;
+
+	/* Check the powered status of the PHY. Lack of power implies that
+	 * the MAX6647 has shut down power to it, probably due to a temp.
+	 * alarm. Reading the power status rather than the MAX6647 status
+	 * directly because the later is read-to-clear and would thus
+	 * start to power up the PHY again when polled, causing us to blip
+	 * the power undesirably.
+	 * We know we can read from the IO expander because we did
+	 * it during power-on. Assume failure now is bad news. */
+	status = i2c_smbus_read_byte_data(efx->board_info.ioexp_client, P1_IN);
+	if (status >= 0 &&
+	    (status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0)
+		return 0;
+
+	/* Use board power control, not PHY power control */
+	sfe4001_poweroff(efx);
+	efx->phy_mode = PHY_MODE_OFF;
+
+	return (status < 0) ? -EIO : -ERANGE;
+}
+
 static struct i2c_board_info sfe4001_hwmon_info = {
 	I2C_BOARD_INFO("max6647", 0x4e),
 	.irq		= -1,
@@ -312,3 +345,61 @@
 	i2c_unregister_device(efx->board_info.hwmon_client);
 	return rc;
 }
+
+static int sfn4111t_check_hw(struct efx_nic *efx)
+{
+	s32 status;
+
+	/* If XAUI link is up then do not monitor */
+	if (EFX_WORKAROUND_7884(efx) && efx->mac_up)
+		return 0;
+
+	/* Test LHIGH, RHIGH, FAULT, EOT and IOT alarms */
+	status = i2c_smbus_read_byte_data(efx->board_info.hwmon_client,
+					  MAX664X_REG_RSL);
+	if (status < 0)
+		return -EIO;
+	if (status & 0x57)
+		return -ERANGE;
+	return 0;
+}
+
+static void sfn4111t_fini(struct efx_nic *efx)
+{
+	EFX_INFO(efx, "%s\n", __func__);
+
+	device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
+	i2c_unregister_device(efx->board_info.hwmon_client);
+}
+
+static struct i2c_board_info sfn4111t_hwmon_info = {
+	I2C_BOARD_INFO("max6647", 0x4e),
+	.irq		= -1,
+};
+
+int sfn4111t_init(struct efx_nic *efx)
+{
+	int rc;
+
+	efx->board_info.hwmon_client =
+		i2c_new_device(&efx->i2c_adap, &sfn4111t_hwmon_info);
+	if (!efx->board_info.hwmon_client)
+		return -EIO;
+
+	efx->board_info.blink = tenxpress_phy_blink;
+	efx->board_info.monitor = sfn4111t_check_hw;
+	efx->board_info.fini = sfn4111t_fini;
+
+	rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
+	if (rc)
+		goto fail_hwmon;
+
+	if (efx->phy_mode & PHY_MODE_SPECIAL)
+		sfn4111t_reset(efx);
+
+	return 0;
+
+fail_hwmon:
+	i2c_unregister_device(efx->board_info.hwmon_client);
+	return rc;
+}
diff --git a/drivers/net/sfc/spi.h b/drivers/net/sfc/spi.h
index c4aca13..1b1ceb4 100644
--- a/drivers/net/sfc/spi.h
+++ b/drivers/net/sfc/spi.h
@@ -68,8 +68,8 @@
 };
 
 int falcon_spi_cmd(const struct efx_spi_device *spi, unsigned int command,
-		   int address, const void* in, void *out, unsigned int len);
-int falcon_spi_fast_wait(const struct efx_spi_device *spi);
+		   int address, const void* in, void *out, size_t len);
+int falcon_spi_wait_write(const struct efx_spi_device *spi);
 int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
 		    size_t len, size_t *retlen, u8 *buffer);
 int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c
index 8d41c29..b3ca2dc 100644
--- a/drivers/net/sfc/tenxpress.c
+++ b/drivers/net/sfc/tenxpress.c
@@ -1,6 +1,6 @@
 /****************************************************************************
- * Driver for Solarflare 802.3an compliant PHY
- * Copyright 2007 Solarflare Communications Inc.
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2007-2008 Solarflare Communications Inc.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published
@@ -10,45 +10,76 @@
 #include <linux/delay.h>
 #include <linux/seq_file.h>
 #include "efx.h"
-#include "gmii.h"
 #include "mdio_10g.h"
 #include "falcon.h"
 #include "phy.h"
 #include "falcon_hwdefs.h"
 #include "boards.h"
-#include "mac.h"
+#include "workarounds.h"
+#include "selftest.h"
 
-/* We expect these MMDs to be in the package */
-/* AN not here as mdio_check_mmds() requires STAT2 support */
-#define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PMAPMD | \
-				 MDIO_MMDREG_DEVS0_PCS    | \
-				 MDIO_MMDREG_DEVS0_PHYXS)
+/* We expect these MMDs to be in the package.  SFT9001 also has a
+ * clause 22 extension MMD, but since it doesn't have all the generic
+ * MMD registers it is pointless to include it here.
+ */
+#define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS_PMAPMD	| \
+				 MDIO_MMDREG_DEVS_PCS		| \
+				 MDIO_MMDREG_DEVS_PHYXS		| \
+				 MDIO_MMDREG_DEVS_AN)
 
-#define TENXPRESS_LOOPBACKS ((1 << LOOPBACK_PHYXS) |	\
-			     (1 << LOOPBACK_PCS) |	\
-			     (1 << LOOPBACK_PMAPMD) |	\
-			     (1 << LOOPBACK_NETWORK))
+#define SFX7101_LOOPBACKS ((1 << LOOPBACK_PHYXS) |	\
+			   (1 << LOOPBACK_PCS) |	\
+			   (1 << LOOPBACK_PMAPMD) |	\
+			   (1 << LOOPBACK_NETWORK))
+
+#define SFT9001_LOOPBACKS ((1 << LOOPBACK_GPHY) |	\
+			   (1 << LOOPBACK_PHYXS) |	\
+			   (1 << LOOPBACK_PCS) |	\
+			   (1 << LOOPBACK_PMAPMD) |	\
+			   (1 << LOOPBACK_NETWORK))
 
 /* We complain if we fail to see the link partner as 10G capable this many
  * times in a row (must be > 1 as sampling the autoneg. registers is racy)
  */
 #define MAX_BAD_LP_TRIES	(5)
 
+/* LASI Control */
+#define PMA_PMD_LASI_CTRL	36866
+#define PMA_PMD_LASI_STATUS	36869
+#define PMA_PMD_LS_ALARM_LBN	0
+#define PMA_PMD_LS_ALARM_WIDTH	1
+#define PMA_PMD_TX_ALARM_LBN	1
+#define PMA_PMD_TX_ALARM_WIDTH	1
+#define PMA_PMD_RX_ALARM_LBN	2
+#define PMA_PMD_RX_ALARM_WIDTH	1
+#define PMA_PMD_AN_ALARM_LBN	3
+#define PMA_PMD_AN_ALARM_WIDTH	1
+
 /* Extended control register */
-#define	PMA_PMD_XCONTROL_REG 0xc000
-#define	PMA_PMD_LNPGA_POWERDOWN_LBN 8
-#define	PMA_PMD_LNPGA_POWERDOWN_WIDTH 1
+#define PMA_PMD_XCONTROL_REG	49152
+#define PMA_PMD_EXT_GMII_EN_LBN	1
+#define PMA_PMD_EXT_GMII_EN_WIDTH 1
+#define PMA_PMD_EXT_CLK_OUT_LBN	2
+#define PMA_PMD_EXT_CLK_OUT_WIDTH 1
+#define PMA_PMD_LNPGA_POWERDOWN_LBN 8	/* SFX7101 only */
+#define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1
+#define PMA_PMD_EXT_CLK312_LBN	8	/* SFT9001 only */
+#define PMA_PMD_EXT_CLK312_WIDTH 1
+#define PMA_PMD_EXT_LPOWER_LBN  12
+#define PMA_PMD_EXT_LPOWER_WIDTH 1
+#define PMA_PMD_EXT_SSR_LBN	15
+#define PMA_PMD_EXT_SSR_WIDTH	1
 
 /* extended status register */
-#define PMA_PMD_XSTATUS_REG 0xc001
+#define PMA_PMD_XSTATUS_REG	49153
 #define PMA_PMD_XSTAT_FLP_LBN   (12)
 
 /* LED control register */
-#define PMA_PMD_LED_CTRL_REG	(0xc007)
+#define PMA_PMD_LED_CTRL_REG	49159
 #define PMA_PMA_LED_ACTIVITY_LBN	(3)
 
 /* LED function override register */
-#define PMA_PMD_LED_OVERR_REG	(0xc009)
+#define PMA_PMD_LED_OVERR_REG	49161
 /* Bit positions for different LEDs (there are more but not wired on SFE4001)*/
 #define PMA_PMD_LED_LINK_LBN	(0)
 #define PMA_PMD_LED_SPEED_LBN	(2)
@@ -59,41 +90,80 @@
 #define	PMA_PMD_LED_ON		(1)
 #define	PMA_PMD_LED_OFF		(2)
 #define PMA_PMD_LED_FLASH	(3)
+#define PMA_PMD_LED_MASK	3
 /* All LEDs under hardware control */
 #define PMA_PMD_LED_FULL_AUTO	(0)
 /* Green and Amber under hardware control, Red off */
 #define PMA_PMD_LED_DEFAULT	(PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN)
 
+#define PMA_PMD_SPEED_ENABLE_REG 49192
+#define PMA_PMD_100TX_ADV_LBN    1
+#define PMA_PMD_100TX_ADV_WIDTH  1
+#define PMA_PMD_1000T_ADV_LBN    2
+#define PMA_PMD_1000T_ADV_WIDTH  1
+#define PMA_PMD_10000T_ADV_LBN   3
+#define PMA_PMD_10000T_ADV_WIDTH 1
+#define PMA_PMD_SPEED_LBN        4
+#define PMA_PMD_SPEED_WIDTH      4
 
-/* Special Software reset register */
-#define PMA_PMD_EXT_CTRL_REG 49152
-#define PMA_PMD_EXT_SSR_LBN 15
+/* Serdes control registers - SFT9001 only */
+#define PMA_PMD_CSERDES_CTRL_REG 64258
+/* Set the 156.25 MHz output to 312.5 MHz to drive Falcon's XMAC */
+#define PMA_PMD_CSERDES_DEFAULT	0x000f
 
-/* Misc register defines */
-#define PCS_CLOCK_CTRL_REG 0xd801
+/* Misc register defines - SFX7101 only */
+#define PCS_CLOCK_CTRL_REG	55297
 #define PLL312_RST_N_LBN 2
 
-#define PCS_SOFT_RST2_REG 0xd806
+#define PCS_SOFT_RST2_REG	55302
 #define SERDES_RST_N_LBN 13
 #define XGXS_RST_N_LBN 12
 
-#define	PCS_TEST_SELECT_REG 0xd807	/* PRM 10.5.8 */
+#define	PCS_TEST_SELECT_REG	55303	/* PRM 10.5.8 */
 #define	CLK312_EN_LBN 3
 
 /* PHYXS registers */
+#define PHYXS_XCONTROL_REG	49152
+#define PHYXS_RESET_LBN		15
+#define PHYXS_RESET_WIDTH	1
+
 #define PHYXS_TEST1         (49162)
 #define LOOPBACK_NEAR_LBN   (8)
 #define LOOPBACK_NEAR_WIDTH (1)
 
+#define PCS_10GBASET_STAT1       32
+#define PCS_10GBASET_BLKLK_LBN   0
+#define PCS_10GBASET_BLKLK_WIDTH 1
+
 /* Boot status register */
-#define PCS_BOOT_STATUS_REG	(0xd000)
+#define PCS_BOOT_STATUS_REG	53248
 #define PCS_BOOT_FATAL_ERR_LBN	(0)
 #define PCS_BOOT_PROGRESS_LBN	(1)
 #define PCS_BOOT_PROGRESS_WIDTH	(2)
 #define PCS_BOOT_COMPLETE_LBN	(3)
+
 #define PCS_BOOT_MAX_DELAY	(100)
 #define PCS_BOOT_POLL_DELAY	(10)
 
+/* 100M/1G PHY registers */
+#define GPHY_XCONTROL_REG	49152
+#define GPHY_ISOLATE_LBN	10
+#define GPHY_ISOLATE_WIDTH	1
+#define GPHY_DUPLEX_LBN	  	8
+#define GPHY_DUPLEX_WIDTH	1
+#define GPHY_LOOPBACK_NEAR_LBN	14
+#define GPHY_LOOPBACK_NEAR_WIDTH 1
+
+#define C22EXT_STATUS_REG       49153
+#define C22EXT_STATUS_LINK_LBN  2
+#define C22EXT_STATUS_LINK_WIDTH 1
+
+#define C22EXT_MSTSLV_REG       49162
+#define C22EXT_MSTSLV_1000_HD_LBN 10
+#define C22EXT_MSTSLV_1000_HD_WIDTH 1
+#define C22EXT_MSTSLV_1000_FD_LBN 11
+#define C22EXT_MSTSLV_1000_FD_WIDTH 1
+
 /* Time to wait between powering down the LNPGA and turning off the power
  * rails */
 #define LNPGA_PDOWN_WAIT	(HZ / 5)
@@ -117,6 +187,38 @@
 		atomic_inc(&phy_data->bad_crc_count);
 }
 
+static ssize_t show_phy_short_reach(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+	int reg;
+
+	reg = mdio_clause45_read(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
+				 MDIO_PMAPMD_10GBT_TXPWR);
+	return sprintf(buf, "%d\n",
+		       !!(reg & (1 << MDIO_PMAPMD_10GBT_TXPWR_SHORT_LBN)));
+}
+
+static ssize_t set_phy_short_reach(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+
+	rtnl_lock();
+	mdio_clause45_set_flag(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
+			       MDIO_PMAPMD_10GBT_TXPWR,
+			       MDIO_PMAPMD_10GBT_TXPWR_SHORT_LBN,
+			       count != 0 && *buf != '0');
+	efx_reconfigure_port(efx);
+	rtnl_unlock();
+
+	return count;
+}
+
+static DEVICE_ATTR(phy_short_reach, 0644, show_phy_short_reach,
+		   set_phy_short_reach);
+
 /* Check that the C166 has booted successfully */
 static int tenxpress_phy_check(struct efx_nic *efx)
 {
@@ -148,27 +250,42 @@
 
 static int tenxpress_init(struct efx_nic *efx)
 {
-	int rc, reg;
+	int phy_id = efx->mii.phy_id;
+	int reg;
+	int rc;
 
-	/* Turn on the clock  */
-	reg = (1 << CLK312_EN_LBN);
-	mdio_clause45_write(efx, efx->mii.phy_id,
-			    MDIO_MMD_PCS, PCS_TEST_SELECT_REG, reg);
+	if (efx->phy_type == PHY_TYPE_SFX7101) {
+		/* Enable 312.5 MHz clock */
+		mdio_clause45_write(efx, phy_id,
+				    MDIO_MMD_PCS, PCS_TEST_SELECT_REG,
+				    1 << CLK312_EN_LBN);
+	} else {
+		/* Enable 312.5 MHz clock and GMII */
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+					 PMA_PMD_XCONTROL_REG);
+		reg |= ((1 << PMA_PMD_EXT_GMII_EN_LBN) |
+			(1 << PMA_PMD_EXT_CLK_OUT_LBN) |
+			(1 << PMA_PMD_EXT_CLK312_LBN));
+		mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
+				    PMA_PMD_XCONTROL_REG, reg);
+		mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_C22EXT,
+				       GPHY_XCONTROL_REG, GPHY_ISOLATE_LBN,
+				       false);
+	}
 
 	rc = tenxpress_phy_check(efx);
 	if (rc < 0)
 		return rc;
 
 	/* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */
-	reg = mdio_clause45_read(efx, efx->mii.phy_id,
-				 MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG);
-	reg |= (1 << PMA_PMA_LED_ACTIVITY_LBN);
-	mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
-			    PMA_PMD_LED_CTRL_REG, reg);
-
-	reg = PMA_PMD_LED_DEFAULT;
-	mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
-			    PMA_PMD_LED_OVERR_REG, reg);
+	if (efx->phy_type == PHY_TYPE_SFX7101) {
+		mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PMAPMD,
+				       PMA_PMD_LED_CTRL_REG,
+				       PMA_PMA_LED_ACTIVITY_LBN,
+				       true);
+		mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
+				    PMA_PMD_LED_OVERR_REG, PMA_PMD_LED_DEFAULT);
+	}
 
 	return rc;
 }
@@ -184,22 +301,43 @@
 	efx->phy_data = phy_data;
 	phy_data->phy_mode = efx->phy_mode;
 
-	rc = mdio_clause45_wait_reset_mmds(efx,
-					   TENXPRESS_REQUIRED_DEVS);
-	if (rc < 0)
-		goto fail;
+	if (!(efx->phy_mode & PHY_MODE_SPECIAL)) {
+		if (efx->phy_type == PHY_TYPE_SFT9001A) {
+			int reg;
+			reg = mdio_clause45_read(efx, efx->mii.phy_id,
+						 MDIO_MMD_PMAPMD,
+						 PMA_PMD_XCONTROL_REG);
+			reg |= (1 << PMA_PMD_EXT_SSR_LBN);
+			mdio_clause45_write(efx, efx->mii.phy_id,
+					    MDIO_MMD_PMAPMD,
+					    PMA_PMD_XCONTROL_REG, reg);
+			mdelay(200);
+		}
 
-	rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0);
-	if (rc < 0)
-		goto fail;
+		rc = mdio_clause45_wait_reset_mmds(efx,
+						   TENXPRESS_REQUIRED_DEVS);
+		if (rc < 0)
+			goto fail;
+
+		rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0);
+		if (rc < 0)
+			goto fail;
+	}
 
 	rc = tenxpress_init(efx);
 	if (rc < 0)
 		goto fail;
 
+	if (efx->phy_type == PHY_TYPE_SFT9001B) {
+		rc = device_create_file(&efx->pci_dev->dev,
+					&dev_attr_phy_short_reach);
+		if (rc)
+			goto fail;
+	}
+
 	schedule_timeout_uninterruptible(HZ / 5); /* 200ms */
 
-	/* Let XGXS and SerDes out of reset and resets 10XPress */
+	/* Let XGXS and SerDes out of reset */
 	falcon_reset_xaui(efx);
 
 	return 0;
@@ -210,21 +348,24 @@
 	return rc;
 }
 
+/* Perform a "special software reset" on the PHY. The caller is
+ * responsible for saving and restoring the PHY hardware registers
+ * properly, and masking/unmasking LASI */
 static int tenxpress_special_reset(struct efx_nic *efx)
 {
 	int rc, reg;
 
 	/* The XGMAC clock is driven from the SFC7101/SFT9001 312MHz clock, so
 	 * a special software reset can glitch the XGMAC sufficiently for stats
-	 * requests to fail. Since we don't ofen special_reset, just lock. */
+	 * requests to fail. Since we don't often special_reset, just lock. */
 	spin_lock(&efx->stats_lock);
 
 	/* Initiate reset */
 	reg = mdio_clause45_read(efx, efx->mii.phy_id,
-				 MDIO_MMD_PMAPMD, PMA_PMD_EXT_CTRL_REG);
+				 MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG);
 	reg |= (1 << PMA_PMD_EXT_SSR_LBN);
 	mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
-			    PMA_PMD_EXT_CTRL_REG, reg);
+			    PMA_PMD_XCONTROL_REG, reg);
 
 	mdelay(200);
 
@@ -239,190 +380,254 @@
 	if (rc < 0)
 		goto unlock;
 
+	/* Wait for the XGXS state machine to churn */
+	mdelay(10);
 unlock:
 	spin_unlock(&efx->stats_lock);
 	return rc;
 }
 
-static void tenxpress_set_bad_lp(struct efx_nic *efx, bool bad_lp)
+static void sfx7101_check_bad_lp(struct efx_nic *efx, bool link_ok)
 {
 	struct tenxpress_phy_data *pd = efx->phy_data;
+	int phy_id = efx->mii.phy_id;
+	bool bad_lp;
 	int reg;
 
+	if (link_ok) {
+		bad_lp = false;
+	} else {
+		/* Check that AN has started but not completed. */
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
+					 MDIO_AN_STATUS);
+		if (!(reg & (1 << MDIO_AN_STATUS_LP_AN_CAP_LBN)))
+			return; /* LP status is unknown */
+		bad_lp = !(reg & (1 << MDIO_AN_STATUS_AN_DONE_LBN));
+		if (bad_lp)
+			pd->bad_lp_tries++;
+	}
+
 	/* Nothing to do if all is well and was previously so. */
-	if (!(bad_lp || pd->bad_lp_tries))
+	if (!pd->bad_lp_tries)
 		return;
 
-	reg = mdio_clause45_read(efx, efx->mii.phy_id,
-				 MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG);
-
-	if (bad_lp)
-		pd->bad_lp_tries++;
-	else
-		pd->bad_lp_tries = 0;
-
-	if (pd->bad_lp_tries == MAX_BAD_LP_TRIES) {
-		pd->bad_lp_tries = 0;	/* Restart count */
-		reg &= ~(PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN);
-		reg |= (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN);
-		EFX_ERR(efx, "This NIC appears to be plugged into"
-			" a port that is not 10GBASE-T capable.\n"
-			" This PHY is 10GBASE-T ONLY, so no link can"
-			" be established.\n");
-	} else {
-		reg |= (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN);
+	/* Use the RX (red) LED as an error indicator once we've seen AN
+	 * failure several times in a row, and also log a message. */
+	if (!bad_lp || pd->bad_lp_tries == MAX_BAD_LP_TRIES) {
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+					 PMA_PMD_LED_OVERR_REG);
+		reg &= ~(PMA_PMD_LED_MASK << PMA_PMD_LED_RX_LBN);
+		if (!bad_lp) {
+			reg |= PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN;
+		} else {
+			reg |= PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN;
+			EFX_ERR(efx, "appears to be plugged into a port"
+				" that is not 10GBASE-T capable. The PHY"
+				" supports 10GBASE-T ONLY, so no link can"
+				" be established\n");
+		}
+		mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
+				    PMA_PMD_LED_OVERR_REG, reg);
+		pd->bad_lp_tries = bad_lp;
 	}
-	mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
-			    PMA_PMD_LED_OVERR_REG, reg);
 }
 
-/* Check link status and return a boolean OK value. If the link is NOT
- * OK we have a quick rummage round to see if we appear to be plugged
- * into a non-10GBT port and if so warn the user that they won't get
- * link any time soon as we are 10GBT only, unless caller specified
- * not to do this check (it isn't useful in loopback) */
-static bool tenxpress_link_ok(struct efx_nic *efx, bool check_lp)
+static bool sfx7101_link_ok(struct efx_nic *efx)
 {
-	bool ok = mdio_clause45_links_ok(efx, TENXPRESS_REQUIRED_DEVS);
-
-	if (ok) {
-		tenxpress_set_bad_lp(efx, false);
-	} else if (check_lp) {
-		/* Are we plugged into the wrong sort of link? */
-		bool bad_lp = false;
-		int phy_id = efx->mii.phy_id;
-		int an_stat = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
-						 MDIO_AN_STATUS);
-		int xphy_stat = mdio_clause45_read(efx, phy_id,
-						   MDIO_MMD_PMAPMD,
-						   PMA_PMD_XSTATUS_REG);
-		/* Are we plugged into anything that sends FLPs? If
-		 * not we can't distinguish between not being plugged
-		 * in and being plugged into a non-AN antique. The FLP
-		 * bit has the advantage of not clearing when autoneg
-		 * restarts. */
-		if (!(xphy_stat & (1 << PMA_PMD_XSTAT_FLP_LBN))) {
-			tenxpress_set_bad_lp(efx, false);
-			return ok;
-		}
-
-		/* If it can do 10GBT it must be XNP capable */
-		bad_lp = !(an_stat & (1 << MDIO_AN_STATUS_XNP_LBN));
-		if (!bad_lp && (an_stat & (1 << MDIO_AN_STATUS_PAGE_LBN))) {
-			bad_lp = !(mdio_clause45_read(efx, phy_id,
-					MDIO_MMD_AN, MDIO_AN_10GBT_STATUS) &
-					(1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN));
-		}
-		tenxpress_set_bad_lp(efx, bad_lp);
-	}
-	return ok;
+	return mdio_clause45_links_ok(efx,
+				      MDIO_MMDREG_DEVS_PMAPMD |
+				      MDIO_MMDREG_DEVS_PCS |
+				      MDIO_MMDREG_DEVS_PHYXS);
 }
 
-static void tenxpress_phyxs_loopback(struct efx_nic *efx)
+static bool sft9001_link_ok(struct efx_nic *efx, struct ethtool_cmd *ecmd)
 {
 	int phy_id = efx->mii.phy_id;
-	int ctrl1, ctrl2;
+	u32 reg;
 
-	ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS,
-					   PHYXS_TEST1);
-	if (efx->loopback_mode == LOOPBACK_PHYXS)
-		ctrl2 |= (1 << LOOPBACK_NEAR_LBN);
+	if (efx->loopback_mode == LOOPBACK_GPHY)
+		return true;
+	else if (efx_phy_mode_disabled(efx->phy_mode))
+		return false;
+	else if (efx->loopback_mode)
+		return mdio_clause45_links_ok(efx,
+					      MDIO_MMDREG_DEVS_PMAPMD |
+					      MDIO_MMDREG_DEVS_PCS |
+					      MDIO_MMDREG_DEVS_PHYXS);
+
+	/* We must use the same definition of link state as LASI,
+	 * otherwise we can miss a link state transition
+	 */
+	if (ecmd->speed == 10000) {
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PCS,
+					 PCS_10GBASET_STAT1);
+		return reg & (1 << PCS_10GBASET_BLKLK_LBN);
+	} else {
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_C22EXT,
+					 C22EXT_STATUS_REG);
+		return reg & (1 << C22EXT_STATUS_LINK_LBN);
+	}
+}
+
+static void tenxpress_ext_loopback(struct efx_nic *efx)
+{
+	int phy_id = efx->mii.phy_id;
+
+	mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PHYXS,
+			       PHYXS_TEST1, LOOPBACK_NEAR_LBN,
+			       efx->loopback_mode == LOOPBACK_PHYXS);
+	if (efx->phy_type != PHY_TYPE_SFX7101)
+		mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_C22EXT,
+				       GPHY_XCONTROL_REG,
+				       GPHY_LOOPBACK_NEAR_LBN,
+				       efx->loopback_mode == LOOPBACK_GPHY);
+}
+
+static void tenxpress_low_power(struct efx_nic *efx)
+{
+	int phy_id = efx->mii.phy_id;
+
+	if (efx->phy_type == PHY_TYPE_SFX7101)
+		mdio_clause45_set_mmds_lpower(
+			efx, !!(efx->phy_mode & PHY_MODE_LOW_POWER),
+			TENXPRESS_REQUIRED_DEVS);
 	else
-		ctrl2 &= ~(1 << LOOPBACK_NEAR_LBN);
-	if (ctrl1 != ctrl2)
-		mdio_clause45_write(efx, phy_id, MDIO_MMD_PHYXS,
-				    PHYXS_TEST1, ctrl2);
+		mdio_clause45_set_flag(
+			efx, phy_id, MDIO_MMD_PMAPMD,
+			PMA_PMD_XCONTROL_REG, PMA_PMD_EXT_LPOWER_LBN,
+			!!(efx->phy_mode & PHY_MODE_LOW_POWER));
 }
 
 static void tenxpress_phy_reconfigure(struct efx_nic *efx)
 {
 	struct tenxpress_phy_data *phy_data = efx->phy_data;
-	bool loop_change = LOOPBACK_OUT_OF(phy_data, efx,
-					   TENXPRESS_LOOPBACKS);
+	struct ethtool_cmd ecmd;
+	bool phy_mode_change, loop_reset, loop_toggle, loopback;
 
-	if (efx->phy_mode & PHY_MODE_SPECIAL) {
+	if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) {
 		phy_data->phy_mode = efx->phy_mode;
 		return;
 	}
 
-	/* When coming out of transmit disable, coming out of low power
-	 * mode, or moving out of any PHY internal loopback mode,
-	 * perform a special software reset */
-	if ((efx->phy_mode == PHY_MODE_NORMAL &&
-	     phy_data->phy_mode != PHY_MODE_NORMAL) ||
-	    loop_change) {
-		tenxpress_special_reset(efx);
-		falcon_reset_xaui(efx);
+	tenxpress_low_power(efx);
+
+	phy_mode_change = (efx->phy_mode == PHY_MODE_NORMAL &&
+			   phy_data->phy_mode != PHY_MODE_NORMAL);
+	loopback = LOOPBACK_MASK(efx) & efx->phy_op->loopbacks;
+	loop_toggle = LOOPBACK_CHANGED(phy_data, efx, efx->phy_op->loopbacks);
+	loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, efx->phy_op->loopbacks) ||
+		      LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY));
+
+	if (loop_reset || loop_toggle || loopback || phy_mode_change) {
+		int rc;
+
+		efx->phy_op->get_settings(efx, &ecmd);
+
+		if (loop_reset || phy_mode_change) {
+			tenxpress_special_reset(efx);
+
+			/* Reset XAUI if we were in 10G, and are staying
+			 * in 10G. If we're moving into and out of 10G
+			 * then xaui will be reset anyway */
+			if (EFX_IS10G(efx))
+				falcon_reset_xaui(efx);
+		}
+
+		if (efx->phy_type != PHY_TYPE_SFX7101) {
+			/* Only change autoneg once, on coming out or
+			 * going into loopback */
+			if (loop_toggle)
+				ecmd.autoneg = !loopback;
+			if (loopback) {
+				ecmd.duplex = DUPLEX_FULL;
+				if (efx->loopback_mode == LOOPBACK_GPHY)
+					ecmd.speed = SPEED_1000;
+				else
+					ecmd.speed = SPEED_10000;
+			}
+		}
+
+		rc = efx->phy_op->set_settings(efx, &ecmd);
+		WARN_ON(rc);
 	}
 
 	mdio_clause45_transmit_disable(efx);
 	mdio_clause45_phy_reconfigure(efx);
-	tenxpress_phyxs_loopback(efx);
+	tenxpress_ext_loopback(efx);
 
 	phy_data->loopback_mode = efx->loopback_mode;
 	phy_data->phy_mode = efx->phy_mode;
-	efx->link_up = tenxpress_link_ok(efx, false);
-	efx->link_options = GM_LPA_10000FULL;
-}
 
-static void tenxpress_phy_clear_interrupt(struct efx_nic *efx)
-{
-	/* Nothing done here - LASI interrupts aren't reliable so poll  */
+	if (efx->phy_type == PHY_TYPE_SFX7101) {
+		efx->link_speed = 10000;
+		efx->link_fd = true;
+		efx->link_up = sfx7101_link_ok(efx);
+	} else {
+		efx->phy_op->get_settings(efx, &ecmd);
+		efx->link_speed = ecmd.speed;
+		efx->link_fd = ecmd.duplex == DUPLEX_FULL;
+		efx->link_up = sft9001_link_ok(efx, &ecmd);
+	}
+	efx->link_fc = mdio_clause45_get_pause(efx);
 }
 
-
 /* Poll PHY for interrupt */
-static int tenxpress_phy_check_hw(struct efx_nic *efx)
+static void tenxpress_phy_poll(struct efx_nic *efx)
 {
 	struct tenxpress_phy_data *phy_data = efx->phy_data;
-	bool link_ok;
-	int rc = 0;
+	bool change = false, link_ok;
+	unsigned link_fc;
 
-	link_ok = tenxpress_link_ok(efx, true);
+	if (efx->phy_type == PHY_TYPE_SFX7101) {
+		link_ok = sfx7101_link_ok(efx);
+		if (link_ok != efx->link_up) {
+			change = true;
+		} else {
+			link_fc = mdio_clause45_get_pause(efx);
+			if (link_fc != efx->link_fc)
+				change = true;
+		}
+		sfx7101_check_bad_lp(efx, link_ok);
+	} else {
+		u32 status = mdio_clause45_read(efx, efx->mii.phy_id,
+						MDIO_MMD_PMAPMD,
+						PMA_PMD_LASI_STATUS);
+		if (status & (1 << PMA_PMD_LS_ALARM_LBN))
+			change = true;
+	}
 
-	if (link_ok != efx->link_up)
-		falcon_xmac_sim_phy_event(efx);
+	if (change)
+		falcon_sim_phy_event(efx);
 
 	if (phy_data->phy_mode != PHY_MODE_NORMAL)
-		return 0;
+		return;
 
-	if (atomic_read(&phy_data->bad_crc_count) > crc_error_reset_threshold) {
+	if (EFX_WORKAROUND_10750(efx) &&
+	    atomic_read(&phy_data->bad_crc_count) > crc_error_reset_threshold) {
 		EFX_ERR(efx, "Resetting XAUI due to too many CRC errors\n");
 		falcon_reset_xaui(efx);
 		atomic_set(&phy_data->bad_crc_count, 0);
 	}
-
-	rc = efx->board_info.monitor(efx);
-	if (rc) {
-		EFX_ERR(efx, "Board sensor %s; shutting down PHY\n",
-			(rc == -ERANGE) ? "reported fault" : "failed");
-		if (efx->phy_mode & PHY_MODE_OFF) {
-			/* Assume that board has shut PHY off */
-			phy_data->phy_mode = PHY_MODE_OFF;
-		} else {
-			efx->phy_mode |= PHY_MODE_LOW_POWER;
-			mdio_clause45_set_mmds_lpower(efx, true,
-						      efx->phy_op->mmds);
-			phy_data->phy_mode |= PHY_MODE_LOW_POWER;
-		}
-	}
-
-	return rc;
 }
 
 static void tenxpress_phy_fini(struct efx_nic *efx)
 {
 	int reg;
 
-	/* Power down the LNPGA */
-	reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN);
-	mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
-			    PMA_PMD_XCONTROL_REG, reg);
+	if (efx->phy_type == PHY_TYPE_SFT9001B) {
+		device_remove_file(&efx->pci_dev->dev,
+				   &dev_attr_phy_short_reach);
+	} else {
+		/* Power down the LNPGA */
+		reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN);
+		mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
+				    PMA_PMD_XCONTROL_REG, reg);
 
-	/* Waiting here ensures that the board fini, which can turn off the
-	 * power to the PHY, won't get run until the LNPGA powerdown has been
-	 * given long enough to complete. */
-	schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */
+		/* Waiting here ensures that the board fini, which can turn
+		 * off the power to the PHY, won't get run until the LNPGA
+		 * powerdown has been given long enough to complete. */
+		schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */
+	}
 
 	kfree(efx->phy_data);
 	efx->phy_data = NULL;
@@ -452,13 +657,134 @@
 	return tenxpress_special_reset(efx);
 }
 
-struct efx_phy_operations falcon_tenxpress_phy_ops = {
+static u32 tenxpress_get_xnp_lpa(struct efx_nic *efx)
+{
+	int phy = efx->mii.phy_id;
+	u32 lpa = 0;
+	int reg;
+
+	if (efx->phy_type != PHY_TYPE_SFX7101) {
+		reg = mdio_clause45_read(efx, phy, MDIO_MMD_C22EXT,
+					 C22EXT_MSTSLV_REG);
+		if (reg & (1 << C22EXT_MSTSLV_1000_HD_LBN))
+			lpa |= ADVERTISED_1000baseT_Half;
+		if (reg & (1 << C22EXT_MSTSLV_1000_FD_LBN))
+			lpa |= ADVERTISED_1000baseT_Full;
+	}
+	reg = mdio_clause45_read(efx, phy, MDIO_MMD_AN, MDIO_AN_10GBT_STATUS);
+	if (reg & (1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN))
+		lpa |= ADVERTISED_10000baseT_Full;
+	return lpa;
+}
+
+static void sfx7101_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
+{
+	mdio_clause45_get_settings_ext(efx, ecmd, ADVERTISED_10000baseT_Full,
+				       tenxpress_get_xnp_lpa(efx));
+	ecmd->supported |= SUPPORTED_10000baseT_Full;
+	ecmd->advertising |= ADVERTISED_10000baseT_Full;
+}
+
+static void sft9001_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
+{
+	int phy_id = efx->mii.phy_id;
+	u32 xnp_adv = 0;
+	int reg;
+
+	reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+				 PMA_PMD_SPEED_ENABLE_REG);
+	if (EFX_WORKAROUND_13204(efx) && (reg & (1 << PMA_PMD_100TX_ADV_LBN)))
+		xnp_adv |= ADVERTISED_100baseT_Full;
+	if (reg & (1 << PMA_PMD_1000T_ADV_LBN))
+		xnp_adv |= ADVERTISED_1000baseT_Full;
+	if (reg & (1 << PMA_PMD_10000T_ADV_LBN))
+		xnp_adv |= ADVERTISED_10000baseT_Full;
+
+	mdio_clause45_get_settings_ext(efx, ecmd, xnp_adv,
+				       tenxpress_get_xnp_lpa(efx));
+
+	ecmd->supported |= (SUPPORTED_100baseT_Half |
+			    SUPPORTED_100baseT_Full |
+			    SUPPORTED_1000baseT_Full);
+
+	/* Use the vendor defined C22ext register for duplex settings */
+	if (ecmd->speed != SPEED_10000 && !ecmd->autoneg) {
+		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_C22EXT,
+					 GPHY_XCONTROL_REG);
+		ecmd->duplex = (reg & (1 << GPHY_DUPLEX_LBN) ?
+				DUPLEX_FULL : DUPLEX_HALF);
+	}
+}
+
+static int sft9001_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
+{
+	int phy_id = efx->mii.phy_id;
+	int rc;
+
+	rc = mdio_clause45_set_settings(efx, ecmd);
+	if (rc)
+		return rc;
+
+	if (ecmd->speed != SPEED_10000 && !ecmd->autoneg)
+		mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_C22EXT,
+				       GPHY_XCONTROL_REG, GPHY_DUPLEX_LBN,
+				       ecmd->duplex == DUPLEX_FULL);
+
+	return rc;
+}
+
+static bool sft9001_set_xnp_advertise(struct efx_nic *efx, u32 advertising)
+{
+	int phy = efx->mii.phy_id;
+	int reg = mdio_clause45_read(efx, phy, MDIO_MMD_PMAPMD,
+				     PMA_PMD_SPEED_ENABLE_REG);
+	bool enabled;
+
+	reg &= ~((1 << 2) | (1 << 3));
+	if (EFX_WORKAROUND_13204(efx) &&
+	    (advertising & ADVERTISED_100baseT_Full))
+		reg |= 1 << PMA_PMD_100TX_ADV_LBN;
+	if (advertising & ADVERTISED_1000baseT_Full)
+		reg |= 1 << PMA_PMD_1000T_ADV_LBN;
+	if (advertising & ADVERTISED_10000baseT_Full)
+		reg |= 1 << PMA_PMD_10000T_ADV_LBN;
+	mdio_clause45_write(efx, phy, MDIO_MMD_PMAPMD,
+			    PMA_PMD_SPEED_ENABLE_REG, reg);
+
+	enabled = (advertising &
+		   (ADVERTISED_1000baseT_Half |
+		    ADVERTISED_1000baseT_Full |
+		    ADVERTISED_10000baseT_Full));
+	if (EFX_WORKAROUND_13204(efx))
+		enabled |= (advertising & ADVERTISED_100baseT_Full);
+	return enabled;
+}
+
+struct efx_phy_operations falcon_sfx7101_phy_ops = {
+	.macs		  = EFX_XMAC,
 	.init             = tenxpress_phy_init,
 	.reconfigure      = tenxpress_phy_reconfigure,
-	.check_hw         = tenxpress_phy_check_hw,
+	.poll             = tenxpress_phy_poll,
 	.fini             = tenxpress_phy_fini,
-	.clear_interrupt  = tenxpress_phy_clear_interrupt,
+	.clear_interrupt  = efx_port_dummy_op_void,
 	.test             = tenxpress_phy_test,
+	.get_settings	  = sfx7101_get_settings,
+	.set_settings	  = mdio_clause45_set_settings,
 	.mmds             = TENXPRESS_REQUIRED_DEVS,
-	.loopbacks        = TENXPRESS_LOOPBACKS,
+	.loopbacks        = SFX7101_LOOPBACKS,
+};
+
+struct efx_phy_operations falcon_sft9001_phy_ops = {
+	.macs		  = EFX_GMAC | EFX_XMAC,
+	.init             = tenxpress_phy_init,
+	.reconfigure      = tenxpress_phy_reconfigure,
+	.poll             = tenxpress_phy_poll,
+	.fini             = tenxpress_phy_fini,
+	.clear_interrupt  = efx_port_dummy_op_void,
+	.test             = tenxpress_phy_test,
+	.get_settings	  = sft9001_get_settings,
+	.set_settings	  = sft9001_set_settings,
+	.set_xnp_advertise = sft9001_set_xnp_advertise,
+	.mmds             = TENXPRESS_REQUIRED_DEVS,
+	.loopbacks        = SFT9001_LOOPBACKS,
 };
diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h
index ec50b90..82e03e1 100644
--- a/drivers/net/sfc/workarounds.h
+++ b/drivers/net/sfc/workarounds.h
@@ -17,17 +17,20 @@
 
 #define EFX_WORKAROUND_ALWAYS(efx) 1
 #define EFX_WORKAROUND_FALCON_A(efx) (falcon_rev(efx) <= FALCON_REV_A1)
+#define EFX_WORKAROUND_10G(efx) EFX_IS10G(efx)
+#define EFX_WORKAROUND_SFX7101(efx) ((efx)->phy_type == PHY_TYPE_SFX7101)
+#define EFX_WORKAROUND_SFT9001A(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A)
 
 /* XAUI resets if link not detected */
 #define EFX_WORKAROUND_5147 EFX_WORKAROUND_ALWAYS
 /* RX PCIe double split performance issue */
 #define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS
 /* Bit-bashed I2C reads cause performance drop */
-#define EFX_WORKAROUND_7884 EFX_WORKAROUND_ALWAYS
+#define EFX_WORKAROUND_7884 EFX_WORKAROUND_10G
 /* TX pkt parser problem with <= 16 byte TXes */
 #define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS
 /* Low rate CRC errors require XAUI reset */
-#define EFX_WORKAROUND_10750 EFX_WORKAROUND_ALWAYS
+#define EFX_WORKAROUND_10750 EFX_WORKAROUND_SFX7101
 /* TX_EV_PKT_ERR can be caused by a dangling TX descriptor
  * or a PCIe error (bug 11028) */
 #define EFX_WORKAROUND_10727 EFX_WORKAROUND_ALWAYS
@@ -51,4 +54,9 @@
 /* Leak overlength packets rather than free */
 #define EFX_WORKAROUND_8071 EFX_WORKAROUND_FALCON_A
 
+/* Need to send XNP pages for 100BaseT */
+#define EFX_WORKAROUND_13204 EFX_WORKAROUND_SFT9001A
+/* Need to keep AN enabled */
+#define EFX_WORKAROUND_13963 EFX_WORKAROUND_SFT9001A
+
 #endif /* EFX_WORKAROUNDS_H */
diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/xfp_phy.c
index 91f0246..2d50b6e 100644
--- a/drivers/net/sfc/xfp_phy.c
+++ b/drivers/net/sfc/xfp_phy.c
@@ -7,22 +7,21 @@
  * by the Free Software Foundation, incorporated herein by reference.
  */
 /*
- * Driver for XFP optical PHYs (plus some support specific to the Quake 2032)
+ * Driver for XFP optical PHYs (plus some support specific to the Quake 2022/32)
  * See www.amcc.com for details (search for qt2032)
  */
 
 #include <linux/timer.h>
 #include <linux/delay.h>
 #include "efx.h"
-#include "gmii.h"
 #include "mdio_10g.h"
 #include "xenpack.h"
 #include "phy.h"
-#include "mac.h"
+#include "falcon.h"
 
-#define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PCS |	\
-			   MDIO_MMDREG_DEVS0_PMAPMD |	\
-			   MDIO_MMDREG_DEVS0_PHYXS)
+#define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS_PCS |	\
+			   MDIO_MMDREG_DEVS_PMAPMD |	\
+			   MDIO_MMDREG_DEVS_PHYXS)
 
 #define XFP_LOOPBACKS ((1 << LOOPBACK_PCS) |		\
 		       (1 << LOOPBACK_PMAPMD) |		\
@@ -65,7 +64,7 @@
 	/* Check that all the MMDs we expect are present and responding. We
 	 * expect faults on some if the link is down, but not on the PHY XS */
 	rc = mdio_clause45_check_mmds(efx, XFP_REQUIRED_DEVS,
-				      MDIO_MMDREG_DEVS0_PHYXS);
+				      MDIO_MMDREG_DEVS_PHYXS);
 	if (rc < 0)
 		goto fail;
 
@@ -120,24 +119,12 @@
 	return mdio_clause45_links_ok(efx, XFP_REQUIRED_DEVS);
 }
 
-static int xfp_phy_check_hw(struct efx_nic *efx)
+static void xfp_phy_poll(struct efx_nic *efx)
 {
-	int rc = 0;
 	int link_up = xfp_link_ok(efx);
 	/* Simulate a PHY event if link state has changed */
 	if (link_up != efx->link_up)
-		falcon_xmac_sim_phy_event(efx);
-
-	rc = efx->board_info.monitor(efx);
-	if (rc) {
-		struct xfp_phy_data *phy_data = efx->phy_data;
-		EFX_ERR(efx, "XFP sensor alert; putting PHY into low power\n");
-		efx->phy_mode |= PHY_MODE_LOW_POWER;
-		mdio_clause45_set_mmds_lpower(efx, 1, XFP_REQUIRED_DEVS);
-		phy_data->phy_mode |= PHY_MODE_LOW_POWER;
-	}
-
-	return rc;
+		falcon_sim_phy_event(efx);
 }
 
 static void xfp_phy_reconfigure(struct efx_nic *efx)
@@ -154,7 +141,9 @@
 
 	phy_data->phy_mode = efx->phy_mode;
 	efx->link_up = xfp_link_ok(efx);
-	efx->link_options = GM_LPA_10000FULL;
+	efx->link_speed = 10000;
+	efx->link_fd = true;
+	efx->link_fc = efx->wanted_fc;
 }
 
 
@@ -169,11 +158,14 @@
 }
 
 struct efx_phy_operations falcon_xfp_phy_ops = {
+	.macs		 = EFX_XMAC,
 	.init            = xfp_phy_init,
 	.reconfigure     = xfp_phy_reconfigure,
-	.check_hw        = xfp_phy_check_hw,
+	.poll            = xfp_phy_poll,
 	.fini            = xfp_phy_fini,
 	.clear_interrupt = xfp_phy_clear_interrupt,
+	.get_settings    = mdio_clause45_get_settings,
+	.set_settings	 = mdio_clause45_set_settings,
 	.mmds            = XFP_REQUIRED_DEVS,
 	.loopbacks       = XFP_LOOPBACKS,
 };